style: 全站使用背景分层替代装饰边框并统一直角
This commit is contained in:
@@ -94,13 +94,14 @@ Oxfmt 不负责代码质量,Oxlint 不负责 Vue 的完整类型推导;Vue S
|
|||||||
- 拆解页固定精简状态栏与结果标签,设置、预览及恢复操作收进“拆解设置与恢复”面板;人物、场景、道具、分镜和任务明细共用独立结果滚动区,执行记录单独滚动。结果区占满剩余高度,切换标签回到顶部,轮询刷新或开关设置不重置阅读位置;失败与校验提示在面板关闭时仍然可见。
|
- 拆解页固定精简状态栏与结果标签,设置、预览及恢复操作收进“拆解设置与恢复”面板;人物、场景、道具、分镜和任务明细共用独立结果滚动区,执行记录单独滚动。结果区占满剩余高度,切换标签回到顶部,轮询刷新或开关设置不重置阅读位置;失败与校验提示在面板关闭时仍然可见。
|
||||||
- 剧集正文按编号连续展示全部已写入剧集;点击目录定位正文,滚动正文同步高亮并保持当前目录项可见。目录分行显示集数和完整标题,执行记录可按需展开。切换角色/世界观等标签不会销毁阅读器或重置位置。
|
- 剧集正文按编号连续展示全部已写入剧集;点击目录定位正文,滚动正文同步高亮并保持当前目录项可见。目录分行显示集数和完整标题,执行记录可按需展开。切换角色/世界观等标签不会销毁阅读器或重置位置。
|
||||||
- 主体身份、分镜设计和镜头生产共用 `DirectoryItem`:编号、完整标题和状态分层展示,桌面目录为 240–280px,窄屏改为可横向滚动的条目。目录标题与筛选固定,只有列表滚动。拆解结果的导出按钮与标签同排右对齐;形态图片筛选按内容区宽度自动排成一行或两列网格。
|
- 主体身份、分镜设计和镜头生产共用 `DirectoryItem`:编号、完整标题和状态分层展示,桌面目录为 240–280px,窄屏改为可横向滚动的条目。目录标题与筛选固定,只有列表滚动。拆解结果的导出按钮与标签同排右对齐;形态图片筛选按内容区宽度自动排成一行或两列网格。
|
||||||
- 侧栏导航与主体/剧集/镜头目录采用直角满行背景,移除列表两侧的内缩,悬停和选中态都铺满所在列。条目内部保留文字间距;窄屏横向目录仍保持独立滚动,不影响表单与弹窗的圆角。
|
- 侧栏导航与主体/剧集/镜头目录采用直角满行背景,移除列表两侧的内缩,悬停和选中态都铺满所在列。条目内部保留文字间距;窄屏横向目录仍保持独立滚动。
|
||||||
- 主体目录以母版缩略图搭配名称、编号与状态,默认保留行背景和细分隔线。人物优先复用选角接口的母版;缺少地址的条目进入可视区域后才读取正式身份图库,不为整份目录轮询。无母版或加载失败显示人物/场景/道具图标,候选与形态图不会替代母版;点击缩略图只切换主体,刷新目录可重试读取。
|
- 主体目录以母版缩略图搭配名称、编号与状态,默认用行背景和轻微间距区分条目。人物优先复用选角接口的母版;缺少地址的条目进入可视区域后才读取正式身份图库,不为整份目录轮询。无母版或加载失败显示人物/场景/道具图标,候选与形态图不会替代母版;点击缩略图只切换主体,刷新目录可重试读取。
|
||||||
- 分镜设计与镜头生产共用两级镜头目录:分组头展示 `BEAT 01` 和镜头数,组内仅显示“镜头 01”、标题及状态。分组头在本组内吸顶;窄屏横向模式才在条目上补回 Beat 归属。点击仍使用正式 Shot ID,不会混淆不同 Beat 的同号镜头。
|
- 分镜设计与镜头生产共用两级镜头目录:分组头展示 `BEAT 01` 和镜头数,组内仅显示“镜头 01”、标题及状态。分组头在本组内吸顶;窄屏横向模式才在条目上补回 Beat 归属。点击仍使用正式 Shot ID,不会混淆不同 Beat 的同号镜头。
|
||||||
- 身份、分镜、生产与图库的批量配置、说明和诊断统一放入 `WorkspaceTools`(Naive UI Drawer),按需覆盖当前工作区,不挤压列表。面板支持 Esc 或关闭按钮;关闭不会取消任务或清除配置。历史回执可从入口查看,新回执主动打开诊断;查询错误始终在外层可见。
|
- 身份、分镜、生产与图库的批量配置、说明和诊断统一放入 `WorkspaceTools`(Naive UI Drawer),按需覆盖当前工作区,不挤压列表。面板支持 Esc 或关闭按钮;关闭不会取消任务或清除配置。历史回执可从入口查看,新回执主动打开诊断;查询错误始终在外层可见。
|
||||||
- 生产步骤的操作按钮保留上下间距;`ConfirmAction` 用稳定根元素承接外部间距,避免多根弹窗组件吞掉 `class`。
|
- 生产步骤的操作按钮保留上下间距;`ConfirmAction` 用稳定根元素承接外部间距,避免多根弹窗组件吞掉 `class`。
|
||||||
- 右上角提供浅色、暗黑、跟随系统三种模式,默认跟随系统,选择保存在本机浏览器。存储被禁用时仍可在当前会话切换。
|
- 右上角提供浅色、暗黑、跟随系统三种模式,默认跟随系统,选择保存在本机浏览器。存储被禁用时仍可在当前会话切换。
|
||||||
- 采用微信风格配色:浅色为浅灰背景与白色内容,暗色为近黑背景与深灰内容;绿色用于主操作、选中态和进度。按钮文字、目录文字与主绿色分别配置以保证对比度,错误/警告仍使用独立语义色。Naive UI 和业务内容共用主题,浏览器主题色随之切换;加载前应用偏好,减少主题闪烁。
|
- 采用微信风格配色:浅色为浅灰背景与白色内容,暗色为近黑背景与深灰内容;绿色用于主操作、选中态和进度。按钮文字、目录文字与主绿色分别配置以保证对比度,错误/警告仍使用独立语义色。Naive UI 和业务内容共用主题,浏览器主题色随之切换;加载前应用偏好,减少主题闪烁。
|
||||||
|
- 全站采用低边框、直角样式:面板、提示框、标签、图片卡片和表单用背景层次代替静态描边;表格与长记录列表使用交替底色,抽屉用灰底承托内容。保留输入框聚焦/校验反馈、复选框、选中指示和必要的浮层阴影;开关、加载等功能图形不强制改形状。
|
||||||
- 窄屏默认收起侧栏,目录与正文改用紧凑布局;执行记录仍可查看。长弹窗在自身内部滚动。
|
- 窄屏默认收起侧栏,目录与正文改用紧凑布局;执行记录仍可查看。长弹窗在自身内部滚动。
|
||||||
|
|
||||||
布局样式集中在 `src/admin.css`,业务排版保留在 `src/styles.css`。新增页面应使用 `WorkspacePage`(内容优先页开启 `compact`),低频操作使用 `WorkspaceTools`;避免通过 `body` 或 `window.scrollTo` 管理滚动。
|
布局样式集中在 `src/admin.css`,业务排版保留在 `src/styles.css`。新增页面应使用 `WorkspacePage`(内容优先页开启 `compact`),低频操作使用 `WorkspaceTools`;避免通过 `body` 或 `window.scrollTo` 管理滚动。
|
||||||
|
|||||||
+1
-2
@@ -84,7 +84,6 @@ const menuOptions = computed<MenuOption[]>(() => [
|
|||||||
<a href="#main-content" class="skip-link">跳到主要内容</a>
|
<a href="#main-content" class="skip-link">跳到主要内容</a>
|
||||||
<NLayout has-sider class="admin-layout">
|
<NLayout has-sider class="admin-layout">
|
||||||
<NLayoutSider
|
<NLayoutSider
|
||||||
bordered
|
|
||||||
collapse-mode="width"
|
collapse-mode="width"
|
||||||
:collapsed="collapsed"
|
:collapsed="collapsed"
|
||||||
:width="208"
|
:width="208"
|
||||||
@@ -106,7 +105,7 @@ const menuOptions = computed<MenuOption[]>(() => [
|
|||||||
/>
|
/>
|
||||||
</NLayoutSider>
|
</NLayoutSider>
|
||||||
<NLayout class="admin-main">
|
<NLayout class="admin-main">
|
||||||
<NLayoutHeader bordered class="admin-topbar">
|
<NLayoutHeader class="admin-topbar">
|
||||||
<div class="flex min-w-0 items-center gap-3">
|
<div class="flex min-w-0 items-center gap-3">
|
||||||
<NButton
|
<NButton
|
||||||
quaternary
|
quaternary
|
||||||
|
|||||||
+26
-15
@@ -5,6 +5,8 @@
|
|||||||
--app-body: #ededed;
|
--app-body: #ededed;
|
||||||
--app-surface: #ffffff;
|
--app-surface: #ffffff;
|
||||||
--app-subtle: #f7f7f7;
|
--app-subtle: #f7f7f7;
|
||||||
|
--app-control: #f0f0f0;
|
||||||
|
--app-control-hover: #e5e5e5;
|
||||||
--app-border: #e5e5e5;
|
--app-border: #e5e5e5;
|
||||||
--app-inverse: #ffffff;
|
--app-inverse: #ffffff;
|
||||||
--app-accent: #07c160;
|
--app-accent: #07c160;
|
||||||
@@ -22,6 +24,8 @@
|
|||||||
--app-body: #111111;
|
--app-body: #111111;
|
||||||
--app-surface: #191919;
|
--app-surface: #191919;
|
||||||
--app-subtle: #232323;
|
--app-subtle: #232323;
|
||||||
|
--app-control: #2b2b2b;
|
||||||
|
--app-control-hover: #353535;
|
||||||
--app-border: #333333;
|
--app-border: #333333;
|
||||||
--app-inverse: #111111;
|
--app-inverse: #111111;
|
||||||
--app-accent-text: #5cd693;
|
--app-accent-text: #5cd693;
|
||||||
@@ -100,6 +104,21 @@ body {
|
|||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
/* 图标按钮保留原有方形点击热区;仅取消 circle 属性产生的圆形外观。 */
|
||||||
|
.n-button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.admin-sider .n-menu-divider {
|
||||||
|
height: 12px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.workspace-tools-drawer .n-drawer-header,
|
||||||
|
.workspace-tools-drawer .n-drawer-footer {
|
||||||
|
background: var(--app-subtle);
|
||||||
|
}
|
||||||
|
.workspace-tools-drawer .n-drawer-body {
|
||||||
|
background: var(--app-body);
|
||||||
|
}
|
||||||
.theme-select {
|
.theme-select {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
}
|
}
|
||||||
@@ -121,7 +140,6 @@ body {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: 9px 20px;
|
padding: 9px 20px;
|
||||||
background: var(--app-surface);
|
background: var(--app-surface);
|
||||||
border-bottom: 1px solid var(--app-border);
|
|
||||||
}
|
}
|
||||||
.project-header .n-page-header__main,
|
.project-header .n-page-header__main,
|
||||||
.project-header .n-page-header__title {
|
.project-header .n-page-header__title {
|
||||||
@@ -267,8 +285,7 @@ body {
|
|||||||
gap: 8px 16px;
|
gap: 8px 16px;
|
||||||
padding: 7px 12px;
|
padding: 7px 12px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border: 1px solid var(--app-border);
|
border-radius: 0;
|
||||||
border-radius: 4px;
|
|
||||||
background: var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
@@ -446,7 +463,6 @@ body {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 0 12px 12px;
|
padding: 0 12px 12px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-bottom: 1px solid var(--app-border);
|
|
||||||
}
|
}
|
||||||
.directory-filters > * {
|
.directory-filters > * {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@@ -470,8 +486,7 @@ body {
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: 10px 14px;
|
padding: 10px 14px;
|
||||||
border-block: 1px solid var(--app-border);
|
background: var(--app-control);
|
||||||
background: var(--app-subtle);
|
|
||||||
font:
|
font:
|
||||||
600 11px/1.6 ui-monospace,
|
600 11px/1.6 ui-monospace,
|
||||||
monospace;
|
monospace;
|
||||||
@@ -533,13 +548,12 @@ body {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.identity-subject-list-content {
|
.identity-subject-list-content {
|
||||||
gap: 0;
|
gap: 2px;
|
||||||
padding: 0 0 8px;
|
padding: 0 0 8px;
|
||||||
}
|
}
|
||||||
.n-button.identity-subject-item {
|
.n-button.identity-subject-item {
|
||||||
min-height: 86px;
|
min-height: 86px;
|
||||||
padding: 10px 14px;
|
padding: 10px 14px;
|
||||||
border-bottom: 1px solid var(--app-border);
|
|
||||||
}
|
}
|
||||||
.n-button.identity-subject-item:not(.selected) {
|
.n-button.identity-subject-item:not(.selected) {
|
||||||
background: var(--app-surface);
|
background: var(--app-surface);
|
||||||
@@ -616,8 +630,8 @@ body {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
padding: 1px 5px;
|
padding: 1px 5px;
|
||||||
border: 1px solid var(--app-border);
|
border-radius: 0;
|
||||||
border-radius: 3px;
|
background: var(--app-control);
|
||||||
}
|
}
|
||||||
.directory-status svg {
|
.directory-status svg {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -746,7 +760,6 @@ body {
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
background: var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
border-right: 1px solid var(--app-border);
|
|
||||||
}
|
}
|
||||||
.reader-directory-heading {
|
.reader-directory-heading {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -827,7 +840,6 @@ body {
|
|||||||
}
|
}
|
||||||
.reader-chapter + .reader-chapter {
|
.reader-chapter + .reader-chapter {
|
||||||
margin-top: 48px;
|
margin-top: 48px;
|
||||||
border-top: 1px solid var(--app-border);
|
|
||||||
padding-top: 36px;
|
padding-top: 36px;
|
||||||
}
|
}
|
||||||
.reader-chapter:last-child {
|
.reader-chapter:last-child {
|
||||||
@@ -1026,6 +1038,7 @@ body {
|
|||||||
height: auto;
|
height: auto;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
|
background: var(--app-subtle);
|
||||||
}
|
}
|
||||||
.n-button.image-history-item.selected {
|
.n-button.image-history-item.selected {
|
||||||
background: var(--app-selected);
|
background: var(--app-selected);
|
||||||
@@ -1066,7 +1079,6 @@ body {
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
border-top: 1px solid var(--app-border);
|
|
||||||
}
|
}
|
||||||
.n-button.casting-item {
|
.n-button.casting-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -1076,7 +1088,7 @@ body {
|
|||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: var(--app-surface);
|
background: var(--app-subtle);
|
||||||
}
|
}
|
||||||
.n-button.casting-item.selected {
|
.n-button.casting-item.selected {
|
||||||
background: var(--app-selected);
|
background: var(--app-selected);
|
||||||
@@ -1223,7 +1235,6 @@ body {
|
|||||||
.directory-panel {
|
.directory-panel {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-right: 0;
|
border-right: 0;
|
||||||
border-bottom: 1px solid var(--app-border);
|
|
||||||
}
|
}
|
||||||
.directory-heading {
|
.directory-heading {
|
||||||
padding: 12px 14px 8px;
|
padding: 12px 14px 8px;
|
||||||
|
|||||||
@@ -59,8 +59,9 @@ describe('统一目录条目', () => {
|
|||||||
expect(css).toMatch(/\.directory-item-title\s*\{[^}]*overflow-wrap:\s*anywhere/)
|
expect(css).toMatch(/\.directory-item-title\s*\{[^}]*overflow-wrap:\s*anywhere/)
|
||||||
expect(css).toMatch(/\.directory-list-content\s*\{[^}]*flex-direction:\s*row;[^}]*width:\s*max-content/)
|
expect(css).toMatch(/\.directory-list-content\s*\{[^}]*flex-direction:\s*row;[^}]*width:\s*max-content/)
|
||||||
expect(css).toContain('grid-template-columns: clamp(240px, 22%, 280px) minmax(0, 1fr)')
|
expect(css).toContain('grid-template-columns: clamp(240px, 22%, 280px) minmax(0, 1fr)')
|
||||||
expect(css).toMatch(/\.identity-subject-list-content\s*\{[^}]*gap:\s*0;[^}]*padding:\s*0 0 8px/)
|
expect(css).toMatch(/\.identity-subject-list-content\s*\{[^}]*gap:\s*2px;[^}]*padding:\s*0 0 8px/)
|
||||||
expect(css).toMatch(/\.n-button\.identity-subject-item\s*\{[^}]*min-height:\s*86px;[^}]*border-bottom:/)
|
expect(css).toMatch(/\.n-button\.identity-subject-item\s*\{[^}]*min-height:\s*86px;[^}]*padding:\s*10px 14px/)
|
||||||
|
expect(css).not.toMatch(/\.n-button\.identity-subject-item\s*\{[^}]*border-bottom:/)
|
||||||
expect(css).toMatch(/\.identity-thumbnail\s*\{[^}]*width:\s*48px;[^}]*height:\s*64px/)
|
expect(css).toMatch(/\.identity-thumbnail\s*\{[^}]*width:\s*48px;[^}]*height:\s*64px/)
|
||||||
expect(css).toMatch(/\.n-button\.identity-subject-item:not\(\.selected\)\s*\{[^}]*var\(--app-surface\)/)
|
expect(css).toMatch(/\.n-button\.identity-subject-item:not\(\.selected\)\s*\{[^}]*var\(--app-surface\)/)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import { h } from 'vue'
|
||||||
|
import { mount, type VueWrapper } from '@vue/test-utils'
|
||||||
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
import { NAlert, NButton, NCard, NConfigProvider, NInput, NSelect, NTable, NTag } from 'naive-ui'
|
||||||
|
import { readFileSync, readdirSync } from 'node:fs'
|
||||||
|
import { useTheme } from '../../composables/useTheme'
|
||||||
|
|
||||||
|
let wrapper: VueWrapper | undefined
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper?.unmount()
|
||||||
|
wrapper = undefined
|
||||||
|
localStorage.clear()
|
||||||
|
delete document.documentElement.dataset.theme
|
||||||
|
document.documentElement.style.colorScheme = ''
|
||||||
|
vi.unstubAllGlobals()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('全站平面主题', () => {
|
||||||
|
it.each([false, true])('模式 %s 的真实控件无静态描边、使用直角,并保留聚焦与错误反馈', dark => {
|
||||||
|
localStorage.setItem('drama-studio-theme', dark ? 'dark' : 'light')
|
||||||
|
vi.stubGlobal('matchMedia', () => ({ matches: dark, addEventListener() {}, removeEventListener() {} }))
|
||||||
|
wrapper = mount({
|
||||||
|
setup() {
|
||||||
|
const { theme, overrides } = useTheme()
|
||||||
|
return () =>
|
||||||
|
h(NConfigProvider, { theme: theme.value, themeOverrides: overrides.value }, () => [
|
||||||
|
h(NButton, {}, () => '生成'),
|
||||||
|
h(NInput, { class: 'normal-input' }),
|
||||||
|
h(NInput, { status: 'error', class: 'error-input' }),
|
||||||
|
h(NSelect, { options: [{ label: '第一集', value: 1 }] }),
|
||||||
|
h(NAlert, { type: 'error' }, () => '生成失败'),
|
||||||
|
h(NCard, {}, () => '内容'),
|
||||||
|
h(NTag, {}, () => '待设计'),
|
||||||
|
h(NTable, { striped: true }, () => h('tbody', [h('tr', [h('td', '记录')])]))
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const value = (selector: string, name: string) =>
|
||||||
|
(wrapper!.get(selector).element as HTMLElement).style.getPropertyValue(name)
|
||||||
|
const control = dark ? '#2b2b2b' : '#f0f0f0'
|
||||||
|
for (const selector of ['.n-button', '.normal-input', '.n-base-selection', '.n-alert', '.n-card', '.n-tag']) {
|
||||||
|
expect(value(selector, '--n-border-radius')).toBe('0px')
|
||||||
|
}
|
||||||
|
for (const selector of ['.n-button', '.normal-input', '.n-base-selection']) {
|
||||||
|
expect(value(selector, '--n-border')).toBe('none')
|
||||||
|
expect(value(selector, '--n-color')).toBe(control)
|
||||||
|
expect(value(selector, '--n-border-focus')).toContain(dark ? '#5cd693' : '#087c42')
|
||||||
|
}
|
||||||
|
expect(value('.n-alert', '--n-border')).toBe('none')
|
||||||
|
expect(value('.n-tag', '--n-border')).toBe('none')
|
||||||
|
expect(value('.n-card', '--n-border-color')).toBe('transparent')
|
||||||
|
expect(value('.n-table', '--n-border-color')).toBe('transparent')
|
||||||
|
expect(value('.error-input', '--n-border-error')).toContain(dark ? '#fa7373' : '#a93232')
|
||||||
|
expect(wrapper.get('.error-input').classes()).toContain('n-input--error-status')
|
||||||
|
// 不全局抹掉边框色:未勾选复选框和错误态继续由 Naive 的主题处理。
|
||||||
|
const theme = wrapper.getComponent(NConfigProvider).props('themeOverrides')!
|
||||||
|
expect(theme.common?.borderColor).not.toBe('transparent')
|
||||||
|
expect(theme.Checkbox).not.toHaveProperty('border')
|
||||||
|
expect(theme.Tabs?.barColor).toBe('#07c160')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('业务面板、列表分组和标签以背景区分,保留键盘焦点与选中指示', () => {
|
||||||
|
// happy-dom 不计算布局;此项保护 CSS 契约,不能替代浏览器视觉验收。
|
||||||
|
const admin = readFileSync('src/admin.css', 'utf8')
|
||||||
|
const css = readFileSync('src/styles.css', 'utf8')
|
||||||
|
expect(admin + css).not.toMatch(/border-radius:\s*[1-9]/)
|
||||||
|
expect(admin + css).not.toMatch(/border(?:-[\w]+)?:\s*1px (?:solid|dashed) var\(--(?:app-border|color-line)\)/)
|
||||||
|
expect(css).toMatch(/\.surface-inset\s*\{\s*background:\s*var\(--app-subtle\)/)
|
||||||
|
expect(css).toMatch(/\.record-list > article:nth-child\(even\)\s*\{\s*background:/)
|
||||||
|
expect(admin).toMatch(/\.directory-group-heading\s*\{[^}]*background:\s*var\(--app-control\)/)
|
||||||
|
expect(admin).toMatch(/\.directory-status\s*\{[^}]*background:\s*var\(--app-control\)/)
|
||||||
|
expect(admin).toMatch(/\.n-button\.directory-item:focus-visible\s*\{[^}]*outline:\s*2px/)
|
||||||
|
expect(admin).toMatch(
|
||||||
|
/\.n-button\.directory-item\.selected\s*\{[^}]*var\(--app-selected\)[^}]*var\(--app-accent\)/
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('所有页面不再通过工具类添加装饰边框或圆角', () => {
|
||||||
|
const files = readdirSync('src', { recursive: true, encoding: 'utf8' }).filter(path => path.endsWith('.vue'))
|
||||||
|
for (const path of files) {
|
||||||
|
const source = readFileSync(`src/${path}`, 'utf8')
|
||||||
|
for (const match of source.matchAll(/\bclass="([^"]*)"/g)) {
|
||||||
|
expect(match[1]).not.toMatch(
|
||||||
|
/(?:^|\s)(?:rounded(?:-[\w-]+)?|border|border-[trbl](?:-\d+)?|divide-[xy])(?:\s|$)/
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -89,7 +89,7 @@ describe('微信风格明暗主题', () => {
|
|||||||
|
|
||||||
it.each([false, true])('明暗模式 %s 的组件表面、语义色与业务 CSS 一致,主要文字保持可读', dark => {
|
it.each([false, true])('明暗模式 %s 的组件表面、语义色与业务 CSS 一致,主要文字保持可读', dark => {
|
||||||
const state = setup(dark)
|
const state = setup(dark)
|
||||||
const { common, Menu, Button, Layout } = state.overrides.value
|
const { common, Menu, Button, Layout, Input, Select } = state.overrides.value
|
||||||
const css = readFileSync('src/admin.css', 'utf8')
|
const css = readFileSync('src/admin.css', 'utf8')
|
||||||
const lightTokens = css.match(/:root\s*\{([^}]+)\}/)![1]!
|
const lightTokens = css.match(/:root\s*\{([^}]+)\}/)![1]!
|
||||||
const darkTokens = css.match(/:root\[data-theme='dark'\]\s*\{([^}]+)\}/)![1]!
|
const darkTokens = css.match(/:root\[data-theme='dark'\]\s*\{([^}]+)\}/)![1]!
|
||||||
@@ -119,8 +119,13 @@ describe('微信风格明暗主题', () => {
|
|||||||
itemColorActiveCollapsed: tokens.selected,
|
itemColorActiveCollapsed: tokens.selected,
|
||||||
itemTextColorActive: tokens['accent-text']
|
itemTextColorActive: tokens['accent-text']
|
||||||
})
|
})
|
||||||
// 圆角调整只用于导航,表单、普通按钮和弹窗仍保留原有主题。
|
// 全站直角;输入区域与普通按钮共享底色,保持明暗模式一致。
|
||||||
expect(common?.borderRadius).toBe('6px')
|
expect(common?.borderRadius).toBe('0px')
|
||||||
|
expect(common?.borderRadiusSmall).toBe('0px')
|
||||||
|
expect(Input?.color).toBe(tokens.control)
|
||||||
|
expect(Button?.color).toBe(tokens.control)
|
||||||
|
expect(Button?.colorHover).toBe(tokens['control-hover'])
|
||||||
|
expect(Select?.peers?.InternalSelection?.color).toBe(tokens.control)
|
||||||
expect(Button?.textColorPrimary).toBe(tokens['on-accent'])
|
expect(Button?.textColorPrimary).toBe(tokens['on-accent'])
|
||||||
for (const background of [common!.primaryColor!, common!.primaryColorHover!, common!.primaryColorPressed!]) {
|
for (const background of [common!.primaryColor!, common!.primaryColorHover!, common!.primaryColorPressed!]) {
|
||||||
expect(contrast(String(Button!.textColorPrimary), background)).toBeGreaterThanOrEqual(4.5)
|
expect(contrast(String(Button!.textColorPrimary), background)).toBeGreaterThanOrEqual(4.5)
|
||||||
|
|||||||
@@ -39,6 +39,18 @@ export function useTheme() {
|
|||||||
const info = dark ? '#91a8d2' : '#576b95'
|
const info = dark ? '#91a8d2' : '#576b95'
|
||||||
const danger = dark ? '#fa7373' : '#a93232'
|
const danger = dark ? '#fa7373' : '#a93232'
|
||||||
const warning = dark ? '#e8b66a' : '#916019'
|
const warning = dark ? '#e8b66a' : '#916019'
|
||||||
|
const control = dark ? '#2b2b2b' : '#f0f0f0'
|
||||||
|
const controlHover = dark ? '#353535' : '#e5e5e5'
|
||||||
|
// 静态边界交给底色;聚焦与错误边框仍由控件的状态主题负责。
|
||||||
|
const focusBorder = `1px solid ${accentText}`
|
||||||
|
const selection = {
|
||||||
|
color: control,
|
||||||
|
colorActive: control,
|
||||||
|
border: 'none',
|
||||||
|
borderHover: 'none',
|
||||||
|
borderFocus: focusBorder,
|
||||||
|
borderActive: focusBorder
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
common: {
|
common: {
|
||||||
primaryColor: primary,
|
primaryColor: primary,
|
||||||
@@ -61,7 +73,8 @@ export function useTheme() {
|
|||||||
warningColorHover: warning,
|
warningColorHover: warning,
|
||||||
warningColorPressed: warning,
|
warningColorPressed: warning,
|
||||||
warningColorSuppl: warning,
|
warningColorSuppl: warning,
|
||||||
borderRadius: '6px',
|
borderRadius: '0px',
|
||||||
|
borderRadiusSmall: '0px',
|
||||||
bodyColor: body,
|
bodyColor: body,
|
||||||
cardColor: surface,
|
cardColor: surface,
|
||||||
modalColor: surface,
|
modalColor: surface,
|
||||||
@@ -80,6 +93,15 @@ export function useTheme() {
|
|||||||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif"
|
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif"
|
||||||
},
|
},
|
||||||
Button: {
|
Button: {
|
||||||
|
color: control,
|
||||||
|
colorHover: controlHover,
|
||||||
|
colorPressed: controlHover,
|
||||||
|
colorFocus: controlHover,
|
||||||
|
border: 'none',
|
||||||
|
borderHover: 'none',
|
||||||
|
borderPressed: 'none',
|
||||||
|
borderDisabled: 'none',
|
||||||
|
borderFocus: focusBorder,
|
||||||
textColorPrimary: onPrimary,
|
textColorPrimary: onPrimary,
|
||||||
textColorHoverPrimary: onPrimary,
|
textColorHoverPrimary: onPrimary,
|
||||||
textColorPressedPrimary: onPrimary,
|
textColorPressedPrimary: onPrimary,
|
||||||
@@ -88,6 +110,55 @@ export function useTheme() {
|
|||||||
textColorTextPrimary: accentText,
|
textColorTextPrimary: accentText,
|
||||||
textColorGhostPrimary: accentText
|
textColorGhostPrimary: accentText
|
||||||
},
|
},
|
||||||
|
Input: {
|
||||||
|
color: control,
|
||||||
|
colorHover: controlHover,
|
||||||
|
colorFocus: control,
|
||||||
|
border: 'none',
|
||||||
|
borderHover: 'none',
|
||||||
|
borderDisabled: 'none',
|
||||||
|
borderFocus: focusBorder,
|
||||||
|
groupLabelBorder: 'none'
|
||||||
|
},
|
||||||
|
Select: { peers: { InternalSelection: selection } },
|
||||||
|
Card: { borderColor: 'transparent' },
|
||||||
|
Drawer: { headerBorderBottom: 'none', footerBorderTop: 'none' },
|
||||||
|
Alert: {
|
||||||
|
border: 'none',
|
||||||
|
borderInfo: 'none',
|
||||||
|
borderSuccess: 'none',
|
||||||
|
borderWarning: 'none',
|
||||||
|
borderError: 'none'
|
||||||
|
},
|
||||||
|
Collapse: { dividerColor: 'transparent' },
|
||||||
|
Table: {
|
||||||
|
borderColor: 'transparent',
|
||||||
|
borderColorModal: 'transparent',
|
||||||
|
borderColorPopover: 'transparent',
|
||||||
|
thColor: control,
|
||||||
|
thColorModal: control,
|
||||||
|
thColorPopover: control,
|
||||||
|
tdColorStriped: subtle,
|
||||||
|
tdColorStripedModal: subtle,
|
||||||
|
tdColorStripedPopover: subtle
|
||||||
|
},
|
||||||
|
DataTable: {
|
||||||
|
borderColor: 'transparent',
|
||||||
|
borderColorModal: 'transparent',
|
||||||
|
borderColorPopover: 'transparent',
|
||||||
|
thColor: control,
|
||||||
|
tdColorStriped: subtle,
|
||||||
|
tdColorHover: selected
|
||||||
|
},
|
||||||
|
Radio: {
|
||||||
|
buttonBorderColor: 'transparent',
|
||||||
|
buttonBorderColorActive: 'transparent',
|
||||||
|
buttonBorderColorHover: 'transparent',
|
||||||
|
buttonColor: control,
|
||||||
|
buttonColorActive: selected,
|
||||||
|
buttonTextColorActive: accentText,
|
||||||
|
buttonBoxShadowFocus: `inset 0 0 0 1px ${accentText}`
|
||||||
|
},
|
||||||
Layout: {
|
Layout: {
|
||||||
color: body,
|
color: body,
|
||||||
headerColor: surface,
|
headerColor: surface,
|
||||||
@@ -104,12 +175,25 @@ export function useTheme() {
|
|||||||
itemIconColorActive: accentText,
|
itemIconColorActive: accentText,
|
||||||
itemIconColorActiveHover: accentText
|
itemIconColorActiveHover: accentText
|
||||||
},
|
},
|
||||||
Tabs: { tabTextColorActiveLine: accentText, tabTextColorHoverLine: accentText, barColor: primary },
|
Tabs: {
|
||||||
Tag: { textColorPrimary: accentText },
|
tabTextColorActiveLine: accentText,
|
||||||
|
tabTextColorHoverLine: accentText,
|
||||||
|
barColor: primary,
|
||||||
|
tabBorderColor: 'transparent'
|
||||||
|
},
|
||||||
|
Tag: {
|
||||||
|
textColorPrimary: accentText,
|
||||||
|
border: 'none',
|
||||||
|
borderPrimary: 'none',
|
||||||
|
borderInfo: 'none',
|
||||||
|
borderSuccess: 'none',
|
||||||
|
borderWarning: 'none',
|
||||||
|
borderError: 'none'
|
||||||
|
},
|
||||||
Checkbox: { checkMarkColor: onPrimary },
|
Checkbox: { checkMarkColor: onPrimary },
|
||||||
Switch: { railColorActive: primary },
|
Switch: { railColorActive: primary },
|
||||||
Progress: { fillColor: primary }
|
Progress: { fillColor: primary }
|
||||||
}
|
} satisfies GlobalThemeOverrides
|
||||||
})
|
})
|
||||||
/** 系统切换只更新 system 模式下的实际显示。 */
|
/** 系统切换只更新 system 模式下的实际显示。 */
|
||||||
function syncSystem(event: MediaQueryListEvent) {
|
function syncSystem(event: MediaQueryListEvent) {
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ function exportResult() {
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="tab === 'tasks'" class="p-5">
|
<div v-if="tab === 'tasks'" class="p-5">
|
||||||
<NScrollbar x-scrollable v-if="snapshot?.tasks?.length" class="table-scroll">
|
<NScrollbar x-scrollable v-if="snapshot?.tasks?.length" class="table-scroll">
|
||||||
<NTable :single-line="false" class="project-table"
|
<NTable :bordered="false" :single-line="true" striped class="project-table"
|
||||||
><thead>
|
><thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>分组</th>
|
<th>分组</th>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const purposeLabels: Record<string, string> = {
|
|||||||
{{ episode?.beatShots.reduce((sum, beat) => sum + beat.shots.length, 0) ?? 0 }} 个镜头</span
|
{{ episode?.beatShots.reduce((sum, beat) => sum + beat.shots.length, 0) ?? 0 }} 个镜头</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-7 border-l-2 border-accent pl-4">
|
<div class="mb-7 surface-inset p-4">
|
||||||
<p class="font-medium">{{ plan.storyGoal }}</p>
|
<p class="font-medium">{{ plan.storyGoal }}</p>
|
||||||
<p class="mt-2 text-sm leading-6 text-muted">{{ plan.emotionalArc }}</p>
|
<p class="mt-2 text-sm leading-6 text-muted">{{ plan.emotionalArc }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const filtered = computed(() =>
|
|||||||
></NInput>
|
></NInput>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="filtered.length" class="divide-y divide-line">
|
<div v-if="filtered.length" class="record-list">
|
||||||
<article v-for="subject in filtered" :key="subject.profileId" class="py-5 first:pt-0">
|
<article v-for="subject in filtered" :key="subject.profileId" class="py-5 first:pt-0">
|
||||||
<div class="mb-3 flex flex-wrap items-center gap-3">
|
<div class="mb-3 flex flex-wrap items-center gap-3">
|
||||||
<h3 class="font-semibold">{{ subject.name }}</h3>
|
<h3 class="font-semibold">{{ subject.name }}</h3>
|
||||||
@@ -66,7 +66,7 @@ const filtered = computed(() =>
|
|||||||
<div
|
<div
|
||||||
v-for="form in forms.filter(item => item.profileId === subject.profileId)"
|
v-for="form in forms.filter(item => item.profileId === subject.profileId)"
|
||||||
:key="form.formId"
|
:key="form.formId"
|
||||||
class="mt-3 border-l-2 border-line pl-4"
|
class="mt-3 surface-inset p-4"
|
||||||
>
|
>
|
||||||
<p class="text-sm font-medium">
|
<p class="text-sm font-medium">
|
||||||
{{ form.formName || form.name
|
{{ form.formName || form.name
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ function exportScript() {
|
|||||||
</div>
|
</div>
|
||||||
<NScrollbar v-if="tab !== 'episodes'" class="panel-scroll"
|
<NScrollbar v-if="tab !== 'episodes'" class="panel-scroll"
|
||||||
><div v-if="tab === 'characters'" class="p-6 lg:p-8">
|
><div v-if="tab === 'characters'" class="p-6 lg:p-8">
|
||||||
<div v-if="project?.characters.length" class="divide-y divide-line">
|
<div v-if="project?.characters.length" class="record-list">
|
||||||
<article
|
<article
|
||||||
v-for="character in project.characters"
|
v-for="character in project.characters"
|
||||||
:key="character.id"
|
:key="character.id"
|
||||||
@@ -208,11 +208,7 @@ function exportScript() {
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="tab === 'review'" class="p-6 lg:p-8">
|
<div v-if="tab === 'review'" class="p-6 lg:p-8">
|
||||||
<div v-if="project?.reviews.length" class="space-y-5">
|
<div v-if="project?.reviews.length" class="space-y-5">
|
||||||
<article
|
<article v-for="review in project.reviews" :key="review.id" class="pb-5">
|
||||||
v-for="review in project.reviews"
|
|
||||||
:key="review.id"
|
|
||||||
class="border-b border-line pb-5"
|
|
||||||
>
|
|
||||||
<div class="mb-3 flex justify-between gap-3">
|
<div class="mb-3 flex justify-between gap-3">
|
||||||
<span
|
<span
|
||||||
class="text-sm font-medium"
|
class="text-sm font-medium"
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ watch(
|
|||||||
</nav>
|
</nav>
|
||||||
<article class="production-detail min-w-0">
|
<article class="production-detail min-w-0">
|
||||||
<NScrollbar class="panel-scroll" content-class="p-5 lg:p-7">
|
<NScrollbar class="panel-scroll" content-class="p-5 lg:p-7">
|
||||||
<header class="mb-5 border-b border-line pb-5">
|
<header class="mb-5 pb-5">
|
||||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
<p class="eyebrow">BEAT {{ shot.beatNo }} / SHOT {{ shot.shotNo }}</p>
|
<p class="eyebrow">BEAT {{ shot.beatNo }} / SHOT {{ shot.shotNo }}</p>
|
||||||
|
|||||||
@@ -401,7 +401,7 @@ async function inspectSpecs() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NCollapse v-if="keyframeSpec || videoSpec" class="mt-5 border-t border-line pt-4 text-xs"
|
<NCollapse v-if="keyframeSpec || videoSpec" class="mt-5 pt-4 text-xs"
|
||||||
><NCollapseItem name="details"
|
><NCollapseItem name="details"
|
||||||
><template #header>本镜确定性生成规格</template>
|
><template #header>本镜确定性生成规格</template>
|
||||||
<div class="mt-4 grid gap-5 lg:grid-cols-2">
|
<div class="mt-4 grid gap-5 lg:grid-cols-2">
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ function clearFilters() {
|
|||||||
<div class="project-table-area">
|
<div class="project-table-area">
|
||||||
<NDataTable
|
<NDataTable
|
||||||
class="project-data-table"
|
class="project-data-table"
|
||||||
|
:bordered="false"
|
||||||
|
:bottom-bordered="false"
|
||||||
|
striped
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data="filtered"
|
:data="filtered"
|
||||||
:row-key="row => row.id"
|
:row-key="row => row.id"
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ watch(
|
|||||||
修改或覆盖上游设计后,后端不会自动使旧状态、旧提示词失效;请按顺序重生成下游内容。自动修复次数仅用于
|
修改或覆盖上游设计后,后端不会自动使旧状态、旧提示词失效;请按顺序重生成下游内容。自动修复次数仅用于
|
||||||
VisualState。
|
VisualState。
|
||||||
</p>
|
</p>
|
||||||
<NCollapse class="mt-3 border-t border-line pt-3"
|
<NCollapse class="mt-3 pt-3"
|
||||||
><NCollapseItem name="details"
|
><NCollapseItem name="details"
|
||||||
><template #header>视频提示词批量操作</template>
|
><template #header>视频提示词批量操作</template>
|
||||||
<p class="my-3 text-xs leading-6 text-muted">
|
<p class="my-3 text-xs leading-6 text-muted">
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ function exportReceipt() {
|
|||||||
{{ receipt.result.skippedEpisodes }} · 失败 {{ receipt.result.failedEpisodes }}
|
{{ receipt.result.skippedEpisodes }} · 失败 {{ receipt.result.failedEpisodes }}
|
||||||
</p>
|
</p>
|
||||||
<NScrollbar x-scrollable class="table-scroll">
|
<NScrollbar x-scrollable class="table-scroll">
|
||||||
<NTable :single-line="false" class="project-table"
|
<NTable :bordered="false" :single-line="true" striped class="project-table"
|
||||||
><thead>
|
><thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>剧集</th>
|
<th>剧集</th>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ defineProps<{ shot: DesignedShot }>()
|
|||||||
>
|
>
|
||||||
<p v-else class="text-sm text-muted">此镜头尚无已保存的导演设计。</p>
|
<p v-else class="text-sm text-muted">此镜头尚无已保存的导演设计。</p>
|
||||||
</section>
|
</section>
|
||||||
<section class="border-t border-line pt-6">
|
<section class="pt-6">
|
||||||
<h3 class="mb-4 text-sm font-semibold">
|
<h3 class="mb-4 text-sm font-semibold">
|
||||||
即时视觉状态 <span class="ml-2 font-mono text-[10px] font-normal text-muted">VisualState</span>
|
即时视觉状态 <span class="ml-2 font-mono text-[10px] font-normal text-muted">VisualState</span>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -60,7 +60,7 @@ defineProps<{ shot: DesignedShot }>()
|
|||||||
<article
|
<article
|
||||||
v-for="subject in shot.visualState.subjects"
|
v-for="subject in shot.visualState.subjects"
|
||||||
:key="subject.subjectId"
|
:key="subject.subjectId"
|
||||||
class="mt-5 border-l-2 border-line pl-4"
|
class="mt-5 surface-inset p-4"
|
||||||
>
|
>
|
||||||
<div class="mb-3 flex flex-wrap items-center gap-2">
|
<div class="mb-3 flex flex-wrap items-center gap-2">
|
||||||
<code class="subject-ref">{{ subject.subjectRef }}</code
|
<code class="subject-ref">{{ subject.subjectRef }}</code
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ function exportResult(kind: 'spec' | 'prompt') {
|
|||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</section>
|
</section>
|
||||||
<section class="border-t border-line pt-6">
|
<section class="pt-6">
|
||||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||||
<h3 class="text-sm font-semibold">镜头生成规格</h3>
|
<h3 class="text-sm font-semibold">镜头生成规格</h3>
|
||||||
<NButton
|
<NButton
|
||||||
@@ -179,7 +179,7 @@ function exportResult(kind: 'spec' | 'prompt') {
|
|||||||
</NScrollbar>
|
</NScrollbar>
|
||||||
</template>
|
</template>
|
||||||
</section>
|
</section>
|
||||||
<section class="border-t border-line pt-6">
|
<section class="pt-6">
|
||||||
<h3 class="text-sm font-semibold">视频提示词</h3>
|
<h3 class="text-sm font-semibold">视频提示词</h3>
|
||||||
<p class="mt-2 text-xs leading-6 text-muted">
|
<p class="mt-2 text-xs leading-6 text-muted">
|
||||||
已有提示词可直接复用,没有则依据 GenerationSpec 与参考图调用模型生成。此处只处理
|
已有提示词可直接复用,没有则依据 GenerationSpec 与参考图调用模型生成。此处只处理
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ describe('Beat 两级镜头目录', () => {
|
|||||||
// happy-dom 不计算吸顶位置,保护 CSS 边界,实际滚动仍需浏览器验收。
|
// happy-dom 不计算吸顶位置,保护 CSS 边界,实际滚动仍需浏览器验收。
|
||||||
const css = readFileSync('src/admin.css', 'utf8')
|
const css = readFileSync('src/admin.css', 'utf8')
|
||||||
expect(css).toMatch(
|
expect(css).toMatch(
|
||||||
/\.directory-group-heading\s*\{[^}]*position:\s*sticky;[^}]*top:\s*0;[^}]*background:\s*var\(--app-subtle\)/
|
/\.directory-group-heading\s*\{[^}]*position:\s*sticky;[^}]*top:\s*0;[^}]*background:\s*var\(--app-control\)/
|
||||||
)
|
)
|
||||||
expect(css).toMatch(/\.directory-mobile-beat\s*\{\s*display:\s*none/)
|
expect(css).toMatch(/\.directory-mobile-beat\s*\{\s*display:\s*none/)
|
||||||
const mobile = css.slice(css.lastIndexOf('@media (max-width: 760px)'))
|
const mobile = css.slice(css.lastIndexOf('@media (max-width: 760px)'))
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ watch(
|
|||||||
<dd>{{ casting.ready }}</dd>
|
<dd>{{ casting.ready }}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
<div class="mt-5 flex flex-wrap items-end gap-4 border-t border-line pt-4">
|
<div class="mt-5 flex flex-wrap items-end gap-4 pt-4">
|
||||||
<label class="w-28">
|
<label class="w-28">
|
||||||
<span class="field-label">并发数</span>
|
<span class="field-label">并发数</span>
|
||||||
<NInputNumber
|
<NInputNumber
|
||||||
@@ -543,12 +543,12 @@ watch(
|
|||||||
@save="save"
|
@save="save"
|
||||||
@dirty="dirty = $event"
|
@dirty="dirty = $event"
|
||||||
/>
|
/>
|
||||||
<div v-if="anchor" class="mt-5 flex items-center gap-4 border-t border-line pt-5">
|
<div v-if="anchor" class="mt-5 flex items-center gap-4 pt-5">
|
||||||
<AssetImage
|
<AssetImage
|
||||||
:src="anchor.imageUrl"
|
:src="anchor.imageUrl"
|
||||||
:alt="`${subject.name}当前身份母版`"
|
:alt="`${subject.name}当前身份母版`"
|
||||||
preview
|
preview
|
||||||
class="h-20 w-20 shrink-0 rounded"
|
class="h-20 w-20 shrink-0"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-sm font-medium">当前身份母版</p>
|
<p class="text-sm font-medium">当前身份母版</p>
|
||||||
@@ -575,7 +575,7 @@ watch(
|
|||||||
@anchor="chooseAnchor"
|
@anchor="chooseAnchor"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div class="mt-5 border-t border-line pt-5">
|
<div class="mt-5 pt-5">
|
||||||
<h4 class="text-xs font-medium">此主体的形态</h4>
|
<h4 class="text-xs font-medium">此主体的形态</h4>
|
||||||
<div class="mt-3 flex flex-wrap gap-2">
|
<div class="mt-3 flex flex-wrap gap-2">
|
||||||
<NTag v-for="form in subject.forms" :key="form.id" size="small" :bordered="false"
|
<NTag v-for="form in subject.forms" :key="form.id" size="small" :bordered="false"
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function chooseAnchor() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="mt-5 border-t border-line pt-5" aria-label="身份参考图库">
|
<section class="mt-5 pt-5" aria-label="身份参考图库">
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<h3 class="text-sm font-medium">身份参考图 · {{ images.length }}</h3>
|
<h3 class="text-sm font-medium">身份参考图 · {{ images.length }}</h3>
|
||||||
<NTag size="small" :bordered="false">{{ anchor ? '已选母版' : '尚无母版' }}</NTag>
|
<NTag size="small" :bordered="false">{{ anchor ? '已选母版' : '尚无母版' }}</NTag>
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ function resetFilters() {
|
|||||||
@confirm="generateStale"
|
@confirm="generateStale"
|
||||||
/></div
|
/></div
|
||||||
></NAlert>
|
></NAlert>
|
||||||
<NCollapse class="mt-4 border-t border-line pt-4"
|
<NCollapse class="mt-4 pt-4"
|
||||||
><NCollapseItem name="details"
|
><NCollapseItem name="details"
|
||||||
><template #header>项目批量生图</template>
|
><template #header>项目批量生图</template>
|
||||||
<div class="mt-4 flex flex-wrap items-end gap-4">
|
<div class="mt-4 flex flex-wrap items-end gap-4">
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ function remove(image: VisualStyleImage) {
|
|||||||
请填写有效图片地址和整数排序。
|
请填写有效图片地址和整数排序。
|
||||||
</p>
|
</p>
|
||||||
<div v-if="images.length" class="mt-5 grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
<div v-if="images.length" class="mt-5 grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||||||
<article v-for="image in images" :key="image.id" class="min-w-0 rounded border border-line p-3">
|
<article v-for="image in images" :key="image.id" class="surface-inset min-w-0 p-3">
|
||||||
<AssetImage
|
<AssetImage
|
||||||
:src="image.imageUrl"
|
:src="image.imageUrl"
|
||||||
:alt="`${categories[image.category]}风格参考图`"
|
:alt="`${categories[image.category]}风格参考图`"
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const history = computed(() => workflowCheckpoints(props.checkpoints, props.work
|
|||||||
>
|
>
|
||||||
</NTimelineItem>
|
</NTimelineItem>
|
||||||
</NTimeline>
|
</NTimeline>
|
||||||
<p class="mt-5 border-t border-line pt-4 text-[11px] leading-5 text-muted">
|
<p class="mt-5 pt-4 text-[11px] leading-5 text-muted">
|
||||||
记录在节点或批次结束后更新,长时间无新记录不一定意味着任务失败。
|
记录在节点或批次结束后更新,长时间无新记录不一定意味着任务失败。
|
||||||
</p>
|
</p>
|
||||||
</NScrollbar>
|
</NScrollbar>
|
||||||
|
|||||||
+48
-61
@@ -80,7 +80,7 @@
|
|||||||
}
|
}
|
||||||
.casting-stats div {
|
.casting-stats div {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 4px;
|
border-radius: 0;
|
||||||
background: var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
}
|
}
|
||||||
.casting-stats dt {
|
.casting-stats dt {
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
padding: 9px 14px;
|
padding: 9px 14px;
|
||||||
min-height: 36px;
|
min-height: 36px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 5px;
|
border-radius: 0;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -154,19 +154,16 @@
|
|||||||
.button-primary {
|
.button-primary {
|
||||||
background: var(--app-accent);
|
background: var(--app-accent);
|
||||||
color: var(--app-on-accent);
|
color: var(--app-on-accent);
|
||||||
border-color: var(--app-accent);
|
|
||||||
}
|
}
|
||||||
.button-primary:hover:not(:disabled) {
|
.button-primary:hover:not(:disabled) {
|
||||||
background: var(--app-accent-hover);
|
background: var(--app-accent-hover);
|
||||||
}
|
}
|
||||||
.button-secondary {
|
.button-secondary {
|
||||||
background: var(--app-surface);
|
background: var(--app-control);
|
||||||
border-color: var(--app-border);
|
|
||||||
color: var(--app-ink);
|
color: var(--app-ink);
|
||||||
}
|
}
|
||||||
.button-secondary:hover:not(:disabled) {
|
.button-secondary:hover:not(:disabled) {
|
||||||
background: var(--app-subtle);
|
background: var(--app-control-hover);
|
||||||
border-color: var(--app-border);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-button {
|
.text-button {
|
||||||
@@ -182,8 +179,18 @@
|
|||||||
}
|
}
|
||||||
.panel {
|
.panel {
|
||||||
background: var(--app-surface);
|
background: var(--app-surface);
|
||||||
border: 1px solid var(--color-line);
|
border-radius: 0;
|
||||||
border-radius: 7px;
|
}
|
||||||
|
|
||||||
|
/* 次级信息用低对比底色成组,避免用边框把工作台切成碎片。 */
|
||||||
|
.surface-inset {
|
||||||
|
background: var(--app-subtle);
|
||||||
|
}
|
||||||
|
.record-list > article {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.record-list > article:nth-child(even) {
|
||||||
|
background: var(--app-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-field {
|
.search-field {
|
||||||
@@ -191,8 +198,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
background: var(--app-surface);
|
background: var(--app-surface);
|
||||||
border: 1px solid var(--color-line);
|
border-radius: 0;
|
||||||
border-radius: 5px;
|
|
||||||
padding: 7px 10px;
|
padding: 7px 10px;
|
||||||
color: var(--color-muted);
|
color: var(--color-muted);
|
||||||
}
|
}
|
||||||
@@ -204,8 +210,9 @@
|
|||||||
width: 145px;
|
width: 145px;
|
||||||
color: var(--color-ink);
|
color: var(--color-ink);
|
||||||
}
|
}
|
||||||
.search-field:focus-within {
|
.search-field:has(> input):focus-within {
|
||||||
border-color: var(--color-accent);
|
outline: 2px solid var(--color-accent);
|
||||||
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
.table-scroll {
|
.table-scroll {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -218,22 +225,23 @@
|
|||||||
}
|
}
|
||||||
.project-table th {
|
.project-table th {
|
||||||
padding: 13px 20px;
|
padding: 13px 20px;
|
||||||
background: var(--app-surface);
|
background: var(--app-control);
|
||||||
color: var(--color-muted);
|
color: var(--color-muted);
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
border-bottom: 1px solid var(--color-line);
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.project-table td {
|
.project-table td {
|
||||||
padding: 21px 20px;
|
padding: 21px 20px;
|
||||||
border-bottom: 1px solid var(--app-subtle);
|
|
||||||
}
|
}
|
||||||
.project-table tr:last-child td {
|
.project-table tr:last-child td {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
.project-table tbody tr:hover {
|
.project-table tbody tr:nth-child(even) td {
|
||||||
background: var(--app-surface);
|
background: var(--app-subtle);
|
||||||
|
}
|
||||||
|
.project-table tbody tr:hover td {
|
||||||
|
background: var(--app-selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
.production-controls {
|
.production-controls {
|
||||||
@@ -256,8 +264,7 @@
|
|||||||
width: 36px;
|
width: 36px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border: 1px solid var(--app-border);
|
border-radius: 0;
|
||||||
border-radius: 5px;
|
|
||||||
background: var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
color: var(--app-ink);
|
color: var(--app-ink);
|
||||||
}
|
}
|
||||||
@@ -269,7 +276,7 @@
|
|||||||
.production-stats div,
|
.production-stats div,
|
||||||
.production-status-grid div {
|
.production-status-grid div {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 4px;
|
border-radius: 0;
|
||||||
background: var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
}
|
}
|
||||||
.production-stats dt,
|
.production-stats dt,
|
||||||
@@ -297,13 +304,11 @@
|
|||||||
.production-shot-list {
|
.production-shot-list {
|
||||||
max-height: 760px;
|
max-height: 760px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-right: 1px solid var(--color-line);
|
|
||||||
background: var(--app-surface);
|
background: var(--app-surface);
|
||||||
padding-block: 8px;
|
padding-block: 8px;
|
||||||
}
|
}
|
||||||
.production-stage {
|
.production-stage {
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
border-top: 1px solid var(--color-line);
|
|
||||||
}
|
}
|
||||||
.production-stage:first-of-type {
|
.production-stage:first-of-type {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
@@ -321,9 +326,8 @@
|
|||||||
}
|
}
|
||||||
.keyframe-card {
|
.keyframe-card {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid var(--color-line);
|
border-radius: 0;
|
||||||
border-radius: 5px;
|
background: var(--app-subtle);
|
||||||
background: var(--app-surface);
|
|
||||||
}
|
}
|
||||||
.keyframe-card .asset-image {
|
.keyframe-card .asset-image {
|
||||||
aspect-ratio: 16 / 9;
|
aspect-ratio: 16 / 9;
|
||||||
@@ -332,9 +336,8 @@
|
|||||||
.video-record {
|
.video-record {
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid var(--color-line);
|
border-radius: 0;
|
||||||
border-radius: 5px;
|
background: var(--app-subtle);
|
||||||
background: var(--app-surface);
|
|
||||||
}
|
}
|
||||||
.production-video {
|
.production-video {
|
||||||
width: min(44%, 390px);
|
width: min(44%, 390px);
|
||||||
@@ -361,7 +364,6 @@
|
|||||||
max-height: none;
|
max-height: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
border-bottom: 1px solid var(--color-line);
|
|
||||||
}
|
}
|
||||||
.production-status-grid,
|
.production-status-grid,
|
||||||
.keyframe-grid {
|
.keyframe-grid {
|
||||||
@@ -389,7 +391,6 @@
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
border-top: 1px solid var(--color-line);
|
|
||||||
}
|
}
|
||||||
.field-label {
|
.field-label {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -400,10 +401,9 @@
|
|||||||
}
|
}
|
||||||
.input {
|
.input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: var(--app-surface);
|
background: var(--app-control);
|
||||||
color: var(--color-ink);
|
color: var(--color-ink);
|
||||||
border: 1px solid var(--app-border);
|
border-radius: 0;
|
||||||
border-radius: 5px;
|
|
||||||
padding: 9px 11px;
|
padding: 9px 11px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
@@ -417,8 +417,7 @@
|
|||||||
}
|
}
|
||||||
.alert {
|
.alert {
|
||||||
padding: 12px 15px;
|
padding: 12px 15px;
|
||||||
border: 1px solid var(--app-border);
|
border-radius: 0;
|
||||||
border-radius: 5px;
|
|
||||||
background: var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
color: var(--app-ink);
|
color: var(--app-ink);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -426,9 +425,8 @@
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
.alert-error {
|
.alert-error {
|
||||||
border-color: var(--app-border);
|
|
||||||
background: var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
color: var(--app-ink);
|
color: var(--app-danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stage-strip {
|
.stage-strip {
|
||||||
@@ -460,8 +458,7 @@
|
|||||||
}
|
}
|
||||||
.history-panel {
|
.history-panel {
|
||||||
padding: 23px 21px;
|
padding: 23px 21px;
|
||||||
background: var(--app-surface);
|
background: var(--app-subtle);
|
||||||
border-left: 1px solid var(--color-line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-image-grid {
|
.form-image-grid {
|
||||||
@@ -521,7 +518,6 @@
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
border-top: 1px solid var(--color-line);
|
|
||||||
}
|
}
|
||||||
.generation-row > div:last-child {
|
.generation-row > div:last-child {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -534,7 +530,6 @@
|
|||||||
.shot-list {
|
.shot-list {
|
||||||
max-height: 1000px;
|
max-height: 1000px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-right: 1px solid var(--color-line);
|
|
||||||
background: var(--app-surface);
|
background: var(--app-surface);
|
||||||
padding: 0 8px 20px;
|
padding: 0 8px 20px;
|
||||||
}
|
}
|
||||||
@@ -544,8 +539,8 @@
|
|||||||
gap: 14px;
|
gap: 14px;
|
||||||
}
|
}
|
||||||
.reference-card {
|
.reference-card {
|
||||||
border: 1px solid var(--color-line);
|
background: var(--app-subtle);
|
||||||
border-radius: 4px;
|
border-radius: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.reference-placeholder {
|
.reference-placeholder {
|
||||||
@@ -558,7 +553,7 @@
|
|||||||
}
|
}
|
||||||
.storyboard-json {
|
.storyboard-json {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-radius: 4px;
|
border-radius: 0;
|
||||||
background: var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
font:
|
font:
|
||||||
11px/1.9 ui-monospace,
|
11px/1.9 ui-monospace,
|
||||||
@@ -570,7 +565,6 @@
|
|||||||
line-height: 1.9;
|
line-height: 1.9;
|
||||||
padding: 17px 0 22px;
|
padding: 17px 0 22px;
|
||||||
margin-bottom: 23px;
|
margin-bottom: 23px;
|
||||||
border-bottom: 1px solid var(--color-line);
|
|
||||||
}
|
}
|
||||||
.script-body {
|
.script-body {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@@ -582,7 +576,6 @@
|
|||||||
.script-notes {
|
.script-notes {
|
||||||
margin-top: 36px;
|
margin-top: 36px;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
border-top: 1px solid var(--color-line);
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
.script-notes dt {
|
.script-notes dt {
|
||||||
@@ -614,7 +607,7 @@
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
background: var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
border-radius: 4px;
|
border-radius: 0;
|
||||||
font:
|
font:
|
||||||
11px/1.9 ui-monospace,
|
11px/1.9 ui-monospace,
|
||||||
monospace;
|
monospace;
|
||||||
@@ -629,34 +622,32 @@
|
|||||||
|
|
||||||
.group-preview {
|
.group-preview {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
border-top: 1px solid var(--color-line);
|
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
}
|
}
|
||||||
.group-chip {
|
.group-chip {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
border: 1px solid var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
background: var(--app-surface);
|
border-radius: 0;
|
||||||
border-radius: 4px;
|
|
||||||
padding: 8px 11px;
|
padding: 8px 11px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
.recovery-strip {
|
.recovery-strip {
|
||||||
|
background: var(--app-subtle);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
padding: 17px;
|
padding: 17px;
|
||||||
border: 1px dashed var(--app-border);
|
border-radius: 0;
|
||||||
border-radius: 5px;
|
|
||||||
}
|
}
|
||||||
.subject-ref {
|
.subject-ref {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
background: var(--app-subtle);
|
background: var(--app-subtle);
|
||||||
padding: 3px 6px;
|
padding: 3px 6px;
|
||||||
border-radius: 3px;
|
border-radius: 0;
|
||||||
color: var(--app-ink);
|
color: var(--app-ink);
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
@@ -677,7 +668,6 @@
|
|||||||
.beat-section {
|
.beat-section {
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
padding-top: 23px;
|
padding-top: 23px;
|
||||||
border-top: 1px solid var(--color-line);
|
|
||||||
}
|
}
|
||||||
.beat-heading {
|
.beat-heading {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -694,9 +684,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 17px;
|
gap: 17px;
|
||||||
padding: 17px;
|
padding: 17px;
|
||||||
border: 1px solid var(--app-subtle);
|
border-radius: 0;
|
||||||
border-radius: 5px;
|
background: var(--app-subtle);
|
||||||
background: var(--app-surface);
|
|
||||||
}
|
}
|
||||||
.shot-label {
|
.shot-label {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -737,7 +726,6 @@
|
|||||||
}
|
}
|
||||||
.history-panel {
|
.history-panel {
|
||||||
border-left: 0;
|
border-left: 0;
|
||||||
border-top: 1px solid var(--color-line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.breakdown-config {
|
.breakdown-config {
|
||||||
@@ -760,7 +748,6 @@
|
|||||||
.shot-list {
|
.shot-list {
|
||||||
max-height: 240px;
|
max-height: 240px;
|
||||||
border-right: 0;
|
border-right: 0;
|
||||||
border-bottom: 1px solid var(--color-line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-container {
|
.page-container {
|
||||||
|
|||||||
Reference in New Issue
Block a user