优化: UI 去 AI 味——SVG 图标体系替代 emoji/字符图标,首页产品化重设计(文档网格+右侧新建栏),主色统一 #5b5bd6,补键盘焦点可见性

This commit is contained in:
lxy
2026-08-30 03:32:17 +08:00
parent dd9c3db530
commit c7c94283c0
18 changed files with 543 additions and 336 deletions
+26 -25
View File
@@ -11,17 +11,18 @@ import { putAsset, isOssEnabled } from '../../core/assets'
import { appAlert, appConfirm, appPrompt } from '../../core/dialog'
import { markdownToSegments, segmentsToPlain, hasFormatting } from '../../core/richtext'
import AddGrid from './AddGrid.vue'
import Icon from '../common/Icon.vue'
import type { ElementType, ChartType, ShapeType } from '../../core/types'
const CHART_TYPES: Array<{ k: ChartType; label: string; icon: string }> = [
{ k: 'bar', label: '柱状图', icon: '📊' },
{ k: 'hbar', label: '条形图', icon: '📋' },
{ k: 'line', label: '折线图', icon: '📈' },
{ k: 'area', label: '面积图', icon: '🌄' },
{ k: 'pie', label: '饼图', icon: '🥧' },
{ k: 'doughnut', label: '环形图', icon: '🍩' },
{ k: 'radar', label: '雷达图', icon: '🕸' },
{ k: 'progress', label: '进度图', icon: '' }
{ k: 'bar', label: '柱状图', icon: 'bar-chart' },
{ k: 'hbar', label: '条形图', icon: 'bar-chart-h' },
{ k: 'line', label: '折线图', icon: 'line-chart' },
{ k: 'area', label: '面积图', icon: 'area-chart' },
{ k: 'pie', label: '饼图', icon: 'pie' },
{ k: 'doughnut', label: '环形图', icon: 'doughnut' },
{ k: 'radar', label: '雷达图', icon: 'radar' },
{ k: 'progress', label: '进度图', icon: 'progress' }
]
const selected = computed(() => store.getSelected())
@@ -344,7 +345,7 @@ function toggleVideoOpt(key: 'autoplay' | 'loop' | 'muted') {
<label>批量操作</label>
<div class="seg">
<button @click="onMultiCopy" title="Ctrl+C"> 复制</button>
<button class="danger" @click="onMultiDel" title="Delete">🗑 删除</button>
<button class="danger" @click="onMultiDel" title="Delete"><Icon name="trash" :size="13" /> 删除</button>
</div>
</div>
<div class="prop-row">
@@ -424,9 +425,9 @@ function toggleVideoOpt(key: 'autoplay' | 'loop' | 'muted') {
<div v-if="canAlign" class="prop-row">
<label>对齐</label>
<div class="seg">
<button :class="{ active: selected.style.align === 'left' }" @click="onAlign('left')"></button>
<button :class="{ active: selected.style.align === 'center' || !selected.style.align }" @click="onAlign('center')"></button>
<button :class="{ active: selected.style.align === 'right' }" @click="onAlign('right')"></button>
<button :class="{ active: selected.style.align === 'left' }" @click="onAlign('left')" title="左对齐"><Icon name="align-left" :size="14" /></button>
<button :class="{ active: selected.style.align === 'center' || !selected.style.align }" @click="onAlign('center')" title="居中"><Icon name="align-center" :size="14" /></button>
<button :class="{ active: selected.style.align === 'right' }" @click="onAlign('right')" title="右对齐"><Icon name="align-right" :size="14" /></button>
</div>
</div>
@@ -462,7 +463,7 @@ function toggleVideoOpt(key: 'autoplay' | 'loop' | 'muted') {
<div class="prop-row">
<label>图表类型</label>
<select v-model="chartType" @change="onChartTypeChange">
<option v-for="t in CHART_TYPES" :key="t.k" :value="t.k">{{ t.icon }} {{ t.label }}</option>
<option v-for="t in CHART_TYPES" :key="t.k" :value="t.k">{{ t.label }}</option>
</select>
</div>
<div class="prop-row">
@@ -510,8 +511,8 @@ function toggleVideoOpt(key: 'autoplay' | 'loop' | 'muted') {
<div v-if="selected.type === 'image'" class="prop-row">
<label>图片来源</label>
<div class="seg">
<button @click="onLocalImage" title="从本地选择图片">📁 本地图片</button>
<button :disabled="imgBusy" @click="onAiImage">{{ imgBusy ? '生成中' : '🎨 AI 配图' }}</button>
<button @click="onLocalImage" title="从本地选择图片"><Icon name="folder" :size="13" /> 本地图片</button>
<button :disabled="imgBusy" @click="onAiImage"><template v-if="imgBusy">生成中…</template><template v-else><Icon name="palette" :size="13" /> AI 配图</template></button>
</div>
</div>
@@ -519,7 +520,7 @@ function toggleVideoOpt(key: 'autoplay' | 'loop' | 'muted') {
<div v-if="selected.type === 'video'" class="prop-row">
<label>视频来源</label>
<div class="seg">
<button @click="onLocalVideo" title="从本地选择视频文件">📁 本地视频</button>
<button @click="onLocalVideo" title="从本地选择视频文件"><Icon name="folder" :size="13" /> 本地视频</button>
</div>
<input
type="text"
@@ -546,7 +547,7 @@ function toggleVideoOpt(key: 'autoplay' | 'loop' | 'muted') {
<div class="seg">
<button @click="onZ(1)" title="上移"></button>
<button @click="onZ(-1)" title="下移"></button>
<button class="danger" @click="onDel" title="删除">🗑</button>
<button class="danger" @click="onDel" title="删除"><Icon name="trash" :size="13" /></button>
</div>
</div>
@@ -598,9 +599,9 @@ function toggleVideoOpt(key: 'autoplay' | 'loop' | 'muted') {
</div>
<div class="anno-line">
<div class="seg">
<button :class="{ active: anno.align === 'left' }" @click="patchAnno(anno.id, { align: 'left' })"></button>
<button :class="{ active: anno.align === 'center' || !anno.align }" @click="patchAnno(anno.id, { align: 'center' })"></button>
<button :class="{ active: anno.align === 'right' }" @click="patchAnno(anno.id, { align: 'right' })"></button>
<button :class="{ active: anno.align === 'left' }" @click="patchAnno(anno.id, { align: 'left' })" title="左对齐"><Icon name="align-left" :size="14" /></button>
<button :class="{ active: anno.align === 'center' || !anno.align }" @click="patchAnno(anno.id, { align: 'center' })" title="居中"><Icon name="align-center" :size="14" /></button>
<button :class="{ active: anno.align === 'right' }" @click="patchAnno(anno.id, { align: 'right' })" title="右对齐"><Icon name="align-right" :size="14" /></button>
</div>
</div>
@@ -685,9 +686,9 @@ function toggleVideoOpt(key: 'autoplay' | 'loop' | 'muted') {
transition: background .15s, border-color .15s;
}
.anno-head button:hover {
background: var(--ui-primary-soft, #eef2ff);
border-color: var(--ui-primary, #4f46e5);
color: var(--ui-primary, #4f46e5);
background: var(--ui-primary-soft, #eeeefc);
border-color: var(--ui-primary, #5b5bd6);
color: var(--ui-primary, #5b5bd6);
}
.anno-list {
display: flex;
@@ -704,7 +705,7 @@ function toggleVideoOpt(key: 'autoplay' | 'loop' | 'muted') {
background: var(--ui-panel, #f8fafc);
}
.anno-card.open {
border-color: var(--ui-primary, #4f46e5);
border-color: var(--ui-primary, #5b5bd6);
}
.anno-card-head {
display: flex;
@@ -715,7 +716,7 @@ function toggleVideoOpt(key: 'autoplay' | 'loop' | 'muted') {
user-select: none;
}
.anno-card-head:hover {
background: var(--ui-primary-soft, #eef2ff);
background: var(--ui-primary-soft, #eeeefc);
}
.anno-caret {
font-size: 10px;