update at 2026-02-12 19:18:45
This commit is contained in:
455
src/App.vue
455
src/App.vue
@@ -7,24 +7,33 @@
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="tool-item theme-trigger">
|
||||
<div ref="themeTriggerRef" class="tool-item theme-trigger">
|
||||
<span class="tool-label">选择主题</span>
|
||||
<button class="icon-btn" type="button" @click="toggleThemePicker">
|
||||
<button class="icon-btn" type="button" @click.stop="toggleThemePicker">
|
||||
<img :src="iconChooseColor" alt="choose-color" />
|
||||
</button>
|
||||
|
||||
<div v-if="showThemePicker" class="theme-popover">
|
||||
<div
|
||||
v-if="showThemePicker"
|
||||
ref="themePopoverRef"
|
||||
class="theme-popover"
|
||||
:style="themePopoverStyle"
|
||||
@click.stop
|
||||
>
|
||||
<div class="theme-header">选择配色主题</div>
|
||||
<div class="theme-list">
|
||||
<div class="theme-wheel-wrap">
|
||||
<div ref="themeWheelRef" class="theme-wheel" @scroll="onThemeWheelScroll">
|
||||
<button
|
||||
v-for="theme in themes"
|
||||
v-for="(theme, index) in themes"
|
||||
:key="theme.id"
|
||||
class="theme-row"
|
||||
:class="{ selected: selectedThemeId === theme.id }"
|
||||
:style="{ opacity: getThemeRowOpacity(index) }"
|
||||
type="button"
|
||||
@click="pickTheme(theme.id)"
|
||||
>
|
||||
<img :src="selectedThemeId === theme.id ? iconRadioOn : iconRadioOff" alt="主题选择" />
|
||||
<div class="palette">
|
||||
<img :src="selectedThemeId === theme.id ? iconRadioOn : iconRadioOff" alt="主题选择" />
|
||||
<div class="palette" :style="{ gridTemplateColumns: `repeat(${theme.colors.length}, 1fr)` }">
|
||||
<span
|
||||
v-for="color in theme.colors"
|
||||
:key="`${theme.id}-${color}`"
|
||||
@@ -34,6 +43,8 @@
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="theme-mask" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -80,76 +91,100 @@
|
||||
<h2>源数据</h2>
|
||||
|
||||
<div class="field-block">
|
||||
<div class="field-title-wrap">
|
||||
<img :src="iconExpand" alt="展开" class="expand-icon" />
|
||||
<button class="field-title-wrap" type="button" @click="toggleSection('sourceData')">
|
||||
<img
|
||||
:src="sectionVisible.sourceData ? iconZhedie : iconExpand"
|
||||
:alt="sectionVisible.sourceData ? '折叠' : '展开'"
|
||||
class="expand-icon"
|
||||
/>
|
||||
<h3>数据列</h3>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div
|
||||
v-for="(header, index) in columnHeaders"
|
||||
:key="`source-data-${index}`"
|
||||
class="column-row"
|
||||
>
|
||||
<img :src="iconData" alt="数据列" class="column-icon" />
|
||||
<span class="column-label">{{ header }}</span>
|
||||
<button class="select-btn" type="button" @click="mapping.sourceDataColumn = index">
|
||||
<img
|
||||
:src="mapping.sourceDataColumn === index ? iconRadioOn : iconRadioOff"
|
||||
alt="单选"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<template v-if="sectionVisible.sourceData">
|
||||
<div class="column-list">
|
||||
<div
|
||||
v-for="(header, index) in columnHeaders"
|
||||
:key="`source-data-${index}`"
|
||||
class="column-row"
|
||||
>
|
||||
<img :src="iconData" alt="数据列" class="column-icon" />
|
||||
<span class="column-label">{{ header }}</span>
|
||||
<button class="select-btn" type="button" @click="mapping.sourceDataColumn = index">
|
||||
<img
|
||||
:src="mapping.sourceDataColumn === index ? iconRadioOn : iconRadioOff"
|
||||
alt="单选"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="field-block">
|
||||
<div class="field-title-wrap">
|
||||
<img :src="iconExpand" alt="展开" class="expand-icon" />
|
||||
<button class="field-title-wrap" type="button" @click="toggleSection('sourceDesc')">
|
||||
<img
|
||||
:src="sectionVisible.sourceDesc ? iconZhedie : iconExpand"
|
||||
:alt="sectionVisible.sourceDesc ? '折叠' : '展开'"
|
||||
class="expand-icon"
|
||||
/>
|
||||
<h3>描述列</h3>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div
|
||||
v-for="(header, index) in columnHeaders"
|
||||
:key="`source-desc-${index}`"
|
||||
class="column-row"
|
||||
>
|
||||
<img :src="iconDescription" alt="描述列" class="column-icon" />
|
||||
<span class="column-label">{{ header }}</span>
|
||||
<button class="select-btn" type="button" @click="toggleSourceDescription(index)">
|
||||
<img
|
||||
:src="
|
||||
mapping.sourceDescriptionColumns.includes(index) ? iconCheckboxOn : iconCheckboxOff
|
||||
"
|
||||
alt="复选"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<template v-if="sectionVisible.sourceDesc">
|
||||
<div class="column-list">
|
||||
<div
|
||||
v-for="(header, index) in columnHeaders"
|
||||
:key="`source-desc-${index}`"
|
||||
class="column-row"
|
||||
>
|
||||
<img :src="iconDescription" alt="描述列" class="column-icon" />
|
||||
<span class="column-label">{{ header }}</span>
|
||||
<button class="select-btn" type="button" @click="toggleSourceDescription(index)">
|
||||
<img
|
||||
:src="
|
||||
mapping.sourceDescriptionColumns.includes(index) ? iconCheckboxOn : iconCheckboxOff
|
||||
"
|
||||
alt="复选"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="panel block-panel">
|
||||
<h2>目标数据</h2>
|
||||
<div class="field-block">
|
||||
<div class="field-title-wrap">
|
||||
<img :src="iconExpand" alt="展开" class="expand-icon" />
|
||||
<button class="field-title-wrap" type="button" @click="toggleSection('targetDesc')">
|
||||
<img
|
||||
:src="sectionVisible.targetDesc ? iconZhedie : iconExpand"
|
||||
:alt="sectionVisible.targetDesc ? '折叠' : '展开'"
|
||||
class="expand-icon"
|
||||
/>
|
||||
<h3>描述列</h3>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div
|
||||
v-for="(header, index) in columnHeaders"
|
||||
:key="`target-desc-${index}`"
|
||||
class="column-row"
|
||||
>
|
||||
<img :src="iconDescription" alt="描述列" class="column-icon" />
|
||||
<span class="column-label">{{ header }}</span>
|
||||
<button class="select-btn" type="button" @click="toggleTargetDescription(index)">
|
||||
<img
|
||||
:src="
|
||||
mapping.targetDescriptionColumns.includes(index) ? iconCheckboxOn : iconCheckboxOff
|
||||
"
|
||||
alt="复选"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<template v-if="sectionVisible.targetDesc">
|
||||
<div class="column-list">
|
||||
<div
|
||||
v-for="(header, index) in columnHeaders"
|
||||
:key="`target-desc-${index}`"
|
||||
class="column-row"
|
||||
>
|
||||
<img :src="iconDescription" alt="描述列" class="column-icon" />
|
||||
<span class="column-label">{{ header }}</span>
|
||||
<button class="select-btn" type="button" @click="toggleTargetDescription(index)">
|
||||
<img
|
||||
:src="
|
||||
mapping.targetDescriptionColumns.includes(index) ? iconCheckboxOn : iconCheckboxOff
|
||||
"
|
||||
alt="复选"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
@@ -166,13 +201,35 @@
|
||||
<option value="target-to-source">target -> source</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<label class="slider-label">
|
||||
间距
|
||||
<input v-model.number="nodeGap" type="range" min="8" max="42" />
|
||||
<div class="slider-track-wrap">
|
||||
<span class="slider-value" :style="getSliderValueStyle(nodeGap, 8, 42)">{{ nodeGap }}</span>
|
||||
<input
|
||||
v-model.number="nodeGap"
|
||||
class="slider-input"
|
||||
:style="getSliderTrackStyle(nodeGap, 8, 42)"
|
||||
type="range"
|
||||
min="8"
|
||||
max="42"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
<label>
|
||||
<label class="slider-label">
|
||||
边距
|
||||
<input v-model.number="chartPadding" type="range" min="10" max="70" />
|
||||
<div class="slider-track-wrap">
|
||||
<span class="slider-value" :style="getSliderValueStyle(chartPadding, 10, 70)">
|
||||
{{ chartPadding }}
|
||||
</span>
|
||||
<input
|
||||
v-model.number="chartPadding"
|
||||
class="slider-input"
|
||||
:style="getSliderTrackStyle(chartPadding, 10, 70)"
|
||||
type="range"
|
||||
min="10"
|
||||
max="70"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -180,8 +237,6 @@
|
||||
<div v-if="buildError" class="error-text">{{ buildError }}</div>
|
||||
<div v-if="parseError" class="error-text">{{ parseError }}</div>
|
||||
|
||||
<div class="example-line">示例:{{ previewExample }}</div>
|
||||
|
||||
<div ref="chartRef" class="chart-area" />
|
||||
|
||||
<div v-if="buildWarnings.length > 0" class="warning-area">
|
||||
@@ -198,7 +253,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch, watchEffect } from 'vue';
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch, watchEffect, type CSSProperties } from 'vue';
|
||||
import * as echarts from 'echarts/core';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
import { SankeyChart } from 'echarts/charts';
|
||||
@@ -208,10 +263,12 @@ import {
|
||||
applyDirection,
|
||||
buildSankeyData,
|
||||
parseDataFile,
|
||||
parseXlsxBuffer,
|
||||
type MappingConfig,
|
||||
type RawTable,
|
||||
type SankeyBuildResult
|
||||
} from './core';
|
||||
import { THEME_PRESETS } from './theme-presets';
|
||||
import iconWebLogo from '../assets/icons/webicon.png';
|
||||
import iconTitle from '../assets/icons/星程桑基图.svg';
|
||||
import iconChooseColor from '../assets/icons/choose-color.svg';
|
||||
@@ -226,33 +283,12 @@ import iconRadioOff from '../assets/icons/radiobutton-no.svg';
|
||||
import iconCheckboxOn from '../assets/icons/checkbox.svg';
|
||||
import iconCheckboxOff from '../assets/icons/checkbox-no.svg';
|
||||
import iconExpand from '../assets/icons/expand.svg';
|
||||
import iconZhedie from '../assets/icons/zhedie.svg';
|
||||
|
||||
echarts.use([SankeyChart, TooltipComponent, CanvasRenderer]);
|
||||
|
||||
/**
|
||||
* 主题色板列表。
|
||||
* 颜色来自 Figma 示例,保证与设计稿观感一致。
|
||||
*/
|
||||
const themes = [
|
||||
{
|
||||
id: 'morandi',
|
||||
colors: ['#F4F1DE', '#EAB69F', '#E07A5F', '#8F5D5D', '#3D405B', '#5F797B', '#81B29A', '#9EB998']
|
||||
},
|
||||
{
|
||||
id: 'purple',
|
||||
colors: ['#F72585', '#B5179E', '#7209B7', '#560BAD', '#480CA8', '#3A0CA3', '#3F37C9', '#4895EF', '#4CC9F0']
|
||||
},
|
||||
{
|
||||
id: 'fog',
|
||||
colors: ['#E8EDDF', '#CFDBD5', '#B7B7A4', '#A5A58D', '#6B705C', '#4F5D75', '#5D576B', '#6D597A']
|
||||
},
|
||||
{
|
||||
id: 'sunset',
|
||||
colors: ['#355070', '#515575', '#6D597A', '#915F78', '#B56576', '#CD6873', '#E56B6F', '#E88C7D', '#EAAC8B']
|
||||
}
|
||||
] as const;
|
||||
|
||||
const selectedThemeId = ref<(typeof themes)[number]['id']>('purple');
|
||||
const themes = THEME_PRESETS;
|
||||
const selectedThemeId = ref<string>('figma-violet');
|
||||
const showThemePicker = ref(false);
|
||||
const uploadMessage = ref('点击上传或将csv/xls文件拖到这里上传');
|
||||
const parseError = ref('');
|
||||
@@ -260,7 +296,14 @@ const buildError = ref('');
|
||||
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const fileInputRef = ref<HTMLInputElement | null>(null);
|
||||
const themeWheelRef = ref<HTMLDivElement | null>(null);
|
||||
const themeTriggerRef = ref<HTMLDivElement | null>(null);
|
||||
const themePopoverRef = ref<HTMLDivElement | null>(null);
|
||||
let chartInstance: echarts.EChartsType | null = null;
|
||||
let themeWheelRafId = 0;
|
||||
const themePopoverStyle = ref<CSSProperties>({});
|
||||
|
||||
const THEME_ROW_HEIGHT = 42;
|
||||
|
||||
const rawTable = ref<RawTable | null>(null);
|
||||
const buildResult = ref<SankeyBuildResult | null>(null);
|
||||
@@ -275,8 +318,22 @@ const mapping = reactive<MappingConfig>({
|
||||
const direction = ref<'source-to-target' | 'target-to-source'>('source-to-target');
|
||||
const nodeGap = ref(24);
|
||||
const chartPadding = ref(30);
|
||||
/**
|
||||
* 左侧字段区块的展开/折叠状态。
|
||||
* true 表示展开,false 表示折叠。
|
||||
*/
|
||||
const sectionVisible = reactive({
|
||||
sourceData: true,
|
||||
sourceDesc: true,
|
||||
targetDesc: true
|
||||
});
|
||||
|
||||
const selectedTheme = computed(() => themes.find((item) => item.id === selectedThemeId.value) ?? themes[0]);
|
||||
const selectedThemeIndex = computed(() => {
|
||||
const index = themes.findIndex((item) => item.id === selectedThemeId.value);
|
||||
return index >= 0 ? index : 0;
|
||||
});
|
||||
|
||||
const selectedTheme = computed(() => themes[selectedThemeIndex.value] ?? themes[0]);
|
||||
|
||||
const columnHeaders = computed(() => {
|
||||
const headers = rawTable.value?.headers ?? [];
|
||||
@@ -289,25 +346,6 @@ const columnHeaders = computed(() => {
|
||||
|
||||
const buildWarnings = computed(() => buildResult.value?.meta.warnings.slice(0, 8) ?? []);
|
||||
|
||||
const previewExample = computed(() => {
|
||||
const table = rawTable.value;
|
||||
if (!table || table.rows.length === 0) {
|
||||
return '宁波北欧10-2582 -> 嘉兴四级算力池-11623-小模型';
|
||||
}
|
||||
|
||||
const firstRow = table.rows[0];
|
||||
const sourceParts =
|
||||
mapping.sourceDescriptionColumns.length > 0
|
||||
? mapping.sourceDescriptionColumns.map((column) => firstRow[column] ?? '').filter(Boolean)
|
||||
: [firstRow[mapping.sourceDataColumn ?? 0] ?? ''];
|
||||
|
||||
const targetParts = mapping.targetDescriptionColumns
|
||||
.map((column) => firstRow[column] ?? '')
|
||||
.filter(Boolean);
|
||||
|
||||
return `${sourceParts.join(mapping.delimiter)} -> ${targetParts.join(mapping.delimiter)}`;
|
||||
});
|
||||
|
||||
const chartNodes = computed(() => {
|
||||
const links = buildResult.value ? applyDirection(buildResult.value.links, direction.value) : [];
|
||||
const names = new Set<string>();
|
||||
@@ -423,14 +461,158 @@ function closeThemePicker(): void {
|
||||
}
|
||||
|
||||
function toggleThemePicker(): void {
|
||||
showThemePicker.value = !showThemePicker.value;
|
||||
const nextVisible = !showThemePicker.value;
|
||||
showThemePicker.value = nextVisible;
|
||||
|
||||
if (nextVisible) {
|
||||
void nextTick(() => {
|
||||
updateThemePopoverPosition();
|
||||
centerThemeByIndex(selectedThemeIndex.value, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function pickTheme(themeId: (typeof themes)[number]['id']): void {
|
||||
function pickTheme(themeId: string): void {
|
||||
selectedThemeId.value = themeId;
|
||||
showThemePicker.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击弹窗外部时关闭主题窗口,保持和设计稿一致的关闭行为。
|
||||
*/
|
||||
function handleGlobalPointerDown(event: PointerEvent): void {
|
||||
if (!showThemePicker.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = event.target as Node | null;
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
const popover = themePopoverRef.value;
|
||||
const trigger = themeTriggerRef.value;
|
||||
if (popover?.contains(target) || trigger?.contains(target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
showThemePicker.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配色滚轮滚动时,自动将“可视中心行”设为选中项。
|
||||
*/
|
||||
function onThemeWheelScroll(): void {
|
||||
if (themeWheelRafId !== 0) {
|
||||
cancelAnimationFrame(themeWheelRafId);
|
||||
}
|
||||
|
||||
themeWheelRafId = requestAnimationFrame(() => {
|
||||
const container = themeWheelRef.value;
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const centerIndex = Math.round(container.scrollTop / THEME_ROW_HEIGHT);
|
||||
const safeIndex = Math.max(0, Math.min(themes.length - 1, centerIndex));
|
||||
selectedThemeId.value = themes[safeIndex]?.id ?? selectedThemeId.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 让指定主题滚动到中间行,匹配 Figma 选择器交互。
|
||||
*/
|
||||
function centerThemeByIndex(index: number, smooth: boolean): void {
|
||||
const container = themeWheelRef.value;
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetTop = index * THEME_ROW_HEIGHT;
|
||||
const maxTop = Math.max(0, container.scrollHeight - container.clientHeight);
|
||||
const safeTop = Math.max(0, Math.min(maxTop, targetTop));
|
||||
|
||||
container.scrollTo({
|
||||
top: safeTop,
|
||||
behavior: smooth ? 'smooth' : 'auto'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 主题弹窗使用 fixed 定位并在视口内钳制,避免窄窗口时被裁切。
|
||||
* 同时动态设置顶部箭头位置,确保箭头对准主题按钮中心。
|
||||
*/
|
||||
function updateThemePopoverPosition(): void {
|
||||
if (!showThemePicker.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trigger = themeTriggerRef.value;
|
||||
const popover = themePopoverRef.value;
|
||||
if (!trigger || !popover) {
|
||||
return;
|
||||
}
|
||||
|
||||
const triggerRect = trigger.getBoundingClientRect();
|
||||
const popoverWidth = popover.offsetWidth || 292;
|
||||
const viewportWidth = window.innerWidth;
|
||||
const safePadding = 8;
|
||||
|
||||
let left = triggerRect.left + triggerRect.width / 2 - popoverWidth / 2;
|
||||
left = Math.max(safePadding, Math.min(viewportWidth - popoverWidth - safePadding, left));
|
||||
|
||||
const top = triggerRect.bottom + 8;
|
||||
const triggerCenterX = triggerRect.left + triggerRect.width / 2;
|
||||
const arrowLeft = Math.max(20, Math.min(popoverWidth - 20, triggerCenterX - left));
|
||||
|
||||
themePopoverStyle.value = {
|
||||
left: `${left}px`,
|
||||
top: `${top}px`,
|
||||
'--theme-arrow-left': `${arrowLeft}px`
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中项不透明度为 100%,其它项随着距离中心递减。
|
||||
*/
|
||||
function getThemeRowOpacity(index: number): number {
|
||||
const distance = Math.abs(index - selectedThemeIndex.value);
|
||||
if (distance === 0) {
|
||||
return 1;
|
||||
}
|
||||
if (distance === 1) {
|
||||
return 0.75;
|
||||
}
|
||||
if (distance === 2) {
|
||||
return 0.5;
|
||||
}
|
||||
return 0.3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算滑块上方数字气泡位置,让数字随滑块移动。
|
||||
*/
|
||||
function getSliderValueStyle(value: number, min: number, max: number): CSSProperties {
|
||||
const range = Math.max(1, max - min);
|
||||
const ratio = Math.max(0, Math.min(1, (value - min) / range));
|
||||
const thumbWidth = 18;
|
||||
const offset = (0.5 - ratio) * thumbWidth;
|
||||
return {
|
||||
left: `calc(${ratio * 100}% + ${offset}px)`
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成滑条左右双色背景:左侧主色,右侧灰色。
|
||||
*/
|
||||
function getSliderTrackStyle(value: number, min: number, max: number): CSSProperties {
|
||||
const range = Math.max(1, max - min);
|
||||
const ratio = Math.max(0, Math.min(1, (value - min) / range));
|
||||
return {
|
||||
'--slider-percent': `${ratio * 100}%`
|
||||
};
|
||||
}
|
||||
|
||||
function toggleSourceDescription(column: number): void {
|
||||
if (mapping.sourceDescriptionColumns.includes(column)) {
|
||||
mapping.sourceDescriptionColumns = mapping.sourceDescriptionColumns.filter((item) => item !== column);
|
||||
@@ -440,6 +622,13 @@ function toggleSourceDescription(column: number): void {
|
||||
mapping.sourceDescriptionColumns = [...mapping.sourceDescriptionColumns, column].sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换左侧区块展开状态,支持源数据、源描述、目标描述。
|
||||
*/
|
||||
function toggleSection(section: keyof typeof sectionVisible): void {
|
||||
sectionVisible[section] = !sectionVisible[section];
|
||||
}
|
||||
|
||||
function toggleTargetDescription(column: number): void {
|
||||
if (mapping.targetDescriptionColumns.includes(column)) {
|
||||
mapping.targetDescriptionColumns = mapping.targetDescriptionColumns.filter((item) => item !== column);
|
||||
@@ -502,6 +691,31 @@ async function onDropFile(event: DragEvent): Promise<void> {
|
||||
await loadDataFile(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面首次进入时默认加载示例文件,确保用户无需上传即可看到效果。
|
||||
*/
|
||||
async function loadDefaultExampleFile(): Promise<void> {
|
||||
if (rawTable.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/data/example0.xlsx', { cache: 'no-store' });
|
||||
if (!response.ok) {
|
||||
throw new Error(`示例文件加载失败: HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const buffer = await response.arrayBuffer();
|
||||
const parsed = parseXlsxBuffer(buffer);
|
||||
rawTable.value = parsed;
|
||||
setDefaultMappingByColumns(parsed.headers.length);
|
||||
uploadMessage.value = `已加载: example0.xlsx(${parsed.rows.length} 行)`;
|
||||
parseError.value = '';
|
||||
} catch (error) {
|
||||
parseError.value = error instanceof Error ? error.message : '示例文件加载失败';
|
||||
}
|
||||
}
|
||||
|
||||
function formatFileTimestamp(): string {
|
||||
const now = new Date();
|
||||
const pad = (value: number) => String(value).padStart(2, '0');
|
||||
@@ -563,12 +777,23 @@ onMounted(() => {
|
||||
|
||||
chartInstance = echarts.init(container, undefined, { renderer: 'canvas' });
|
||||
chartInstance.setOption(chartOption.value);
|
||||
void loadDefaultExampleFile();
|
||||
window.addEventListener('resize', syncChartSize);
|
||||
window.addEventListener('resize', updateThemePopoverPosition);
|
||||
window.addEventListener('scroll', updateThemePopoverPosition, true);
|
||||
window.addEventListener('pointerdown', handleGlobalPointerDown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', syncChartSize);
|
||||
window.removeEventListener('resize', updateThemePopoverPosition);
|
||||
window.removeEventListener('scroll', updateThemePopoverPosition, true);
|
||||
window.removeEventListener('pointerdown', handleGlobalPointerDown);
|
||||
chartInstance?.dispose();
|
||||
chartInstance = null;
|
||||
if (themeWheelRafId !== 0) {
|
||||
cancelAnimationFrame(themeWheelRafId);
|
||||
themeWheelRafId = 0;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
219
src/styles.css
219
src/styles.css
@@ -149,23 +149,20 @@ body {
|
||||
}
|
||||
|
||||
.theme-popover {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 280px;
|
||||
position: fixed;
|
||||
width: 292px;
|
||||
border: 1px solid var(--primary-7);
|
||||
border-radius: 24px 24px 0 0;
|
||||
background: #fff;
|
||||
z-index: 10;
|
||||
padding: 8px;
|
||||
padding: 8px 8px 10px;
|
||||
}
|
||||
|
||||
.theme-popover::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: 50%;
|
||||
left: var(--theme-arrow-left, 50%);
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: #fff;
|
||||
@@ -177,13 +174,53 @@ body {
|
||||
.theme-header {
|
||||
text-align: center;
|
||||
color: var(--primary-6);
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 15px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.theme-list {
|
||||
max-height: 180px;
|
||||
overflow: auto;
|
||||
.theme-wheel-wrap {
|
||||
--theme-row-height: 42px;
|
||||
position: relative;
|
||||
height: calc(var(--theme-row-height) * 5);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.theme-wheel {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding-block: calc(var(--theme-row-height) * 2);
|
||||
margin: 0;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #d6d8dc #fff;
|
||||
}
|
||||
|
||||
.theme-wheel::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.theme-wheel::-webkit-scrollbar-track {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.theme-wheel::-webkit-scrollbar-thumb {
|
||||
background: #d6d8dc;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.theme-mask {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
#ffffff 0%,
|
||||
rgba(255, 255, 255, 0.74) 20%,
|
||||
rgba(255, 255, 255, 0) 35%,
|
||||
rgba(255, 255, 255, 0) 65%,
|
||||
rgba(255, 255, 255, 0.74) 80%,
|
||||
#ffffff 100%
|
||||
);
|
||||
}
|
||||
|
||||
.theme-row {
|
||||
@@ -193,8 +230,14 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px;
|
||||
height: 42px;
|
||||
padding: 5px 6px;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.theme-row.selected {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.theme-row img {
|
||||
@@ -205,8 +248,28 @@ body {
|
||||
.palette {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(9, 1fr);
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 选中分割线挂在色条本身,滚动时与选中项一起移动,长度与色条完全一致。 */
|
||||
.theme-row.selected .palette::before,
|
||||
.theme-row.selected .palette::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-top: 1px solid var(--fill-3);
|
||||
}
|
||||
|
||||
.theme-row.selected .palette::before {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.theme-row.selected .palette::after {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.palette-cell {
|
||||
@@ -245,13 +308,20 @@ body {
|
||||
|
||||
.field-block {
|
||||
margin-top: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.field-title-wrap {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.field-title-wrap h3 {
|
||||
@@ -261,17 +331,54 @@ body {
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.column-list {
|
||||
position: relative;
|
||||
--connector-x: 10px;
|
||||
--icon-left: 20px;
|
||||
--row-height: 32px;
|
||||
--connector-top-offset: -6px;
|
||||
}
|
||||
|
||||
/* 展开后显示分类到列表项的树状连线,严格限制到首尾项中线。 */
|
||||
.column-list::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: var(--connector-x);
|
||||
top: var(--connector-top-offset);
|
||||
bottom: calc(var(--row-height) / 2);
|
||||
border-left: 1px solid var(--fill-4);
|
||||
}
|
||||
|
||||
.column-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid var(--fill-4);
|
||||
height: 32px;
|
||||
padding-bottom: 6px;
|
||||
padding-left: var(--icon-left);
|
||||
}
|
||||
|
||||
.column-row::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: var(--connector-x);
|
||||
top: calc(50% - 0.5px);
|
||||
width: calc(var(--icon-left) - var(--connector-x));
|
||||
border-top: 1px solid var(--fill-4);
|
||||
}
|
||||
|
||||
.column-row::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: var(--icon-left);
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-top: 1px solid var(--fill-4);
|
||||
}
|
||||
|
||||
.column-icon {
|
||||
@@ -330,8 +437,82 @@ body {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.preview-controls select,
|
||||
.preview-controls input {
|
||||
.preview-controls .slider-label {
|
||||
align-items: center;
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.slider-track-wrap {
|
||||
position: relative;
|
||||
width: 130px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.slider-input {
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
margin: 0;
|
||||
background: transparent;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.slider-input::-webkit-slider-runnable-track {
|
||||
height: 4px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
#8552a1 0,
|
||||
#8552a1 var(--slider-percent, 0%),
|
||||
#d9d9d9 var(--slider-percent, 0%),
|
||||
#d9d9d9 100%
|
||||
);
|
||||
}
|
||||
|
||||
.slider-input::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #d9d9d9;
|
||||
background: #fff;
|
||||
margin-top: -7px;
|
||||
}
|
||||
|
||||
.slider-input::-moz-range-track {
|
||||
height: 4px;
|
||||
border-radius: 999px;
|
||||
background: #d9d9d9;
|
||||
}
|
||||
|
||||
.slider-input::-moz-range-thumb {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #d9d9d9;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.slider-input::-moz-range-progress {
|
||||
height: 4px;
|
||||
border-radius: 999px;
|
||||
background: #8552a1;
|
||||
}
|
||||
|
||||
.slider-value {
|
||||
position: absolute;
|
||||
top: -13px;
|
||||
transform: translateX(-50%);
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
color: var(--primary-6);
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.preview-controls select {
|
||||
accent-color: var(--primary-7);
|
||||
}
|
||||
|
||||
|
||||
787
src/theme-presets.ts
Normal file
787
src/theme-presets.ts
Normal file
@@ -0,0 +1,787 @@
|
||||
/**
|
||||
* Figma 节点 3267:86 的全部配色预设。
|
||||
* 每组颜色按设计稿色带从左到右排列。
|
||||
*/
|
||||
export interface ThemePreset {
|
||||
id: string;
|
||||
name: string;
|
||||
colors: string[];
|
||||
}
|
||||
|
||||
export const THEME_PRESETS: ThemePreset[] = [
|
||||
{
|
||||
"id": "figma-aegean",
|
||||
"name": "Figma-Aegean",
|
||||
"colors": [
|
||||
"#264653",
|
||||
"#287271",
|
||||
"#2A9D8F",
|
||||
"#8AB17D",
|
||||
"#E9C46A",
|
||||
"#EFB366",
|
||||
"#F4A261",
|
||||
"#F1965D",
|
||||
"#EE8959",
|
||||
"#E76F51"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-violet",
|
||||
"name": "Figma-Violet",
|
||||
"colors": [
|
||||
"#F72585",
|
||||
"#B5179E",
|
||||
"#7209B7",
|
||||
"#560BAD",
|
||||
"#480CA8",
|
||||
"#3A0CA3",
|
||||
"#3F37C9",
|
||||
"#4361EE",
|
||||
"#4895EF",
|
||||
"#4CC9F0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-1",
|
||||
"name": "Figma-01",
|
||||
"colors": [
|
||||
"#322F4F",
|
||||
"#433E71",
|
||||
"#554C93",
|
||||
"#98958C",
|
||||
"#DBDD85",
|
||||
"#D8DD7D",
|
||||
"#D5DB74",
|
||||
"#CED56E",
|
||||
"#C8CF67",
|
||||
"#BAC35A"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-2",
|
||||
"name": "Figma-02",
|
||||
"colors": [
|
||||
"#2B3B51",
|
||||
"#355971",
|
||||
"#3F7690",
|
||||
"#91A483",
|
||||
"#E2D075",
|
||||
"#E4C66F",
|
||||
"#E4BD69",
|
||||
"#E0B464",
|
||||
"#DBAA5F",
|
||||
"#D19654"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-3",
|
||||
"name": "Figma-03",
|
||||
"colors": [
|
||||
"#274D4C",
|
||||
"#2B7171",
|
||||
"#2F9595",
|
||||
"#8B9395",
|
||||
"#E79094",
|
||||
"#EC878A",
|
||||
"#EF7D7F",
|
||||
"#EC7578",
|
||||
"#E86D6F",
|
||||
"#E15D5F"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-4",
|
||||
"name": "Figma-04",
|
||||
"colors": [
|
||||
"#3D3D3D",
|
||||
"#5B5B5B",
|
||||
"#797979",
|
||||
"#9F9F9F",
|
||||
"#C4C4C4",
|
||||
"#BCBCBC",
|
||||
"#B3B3B3",
|
||||
"#AAAAAA",
|
||||
"#A1A1A1",
|
||||
"#8F8F8F"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-5",
|
||||
"name": "Figma-05",
|
||||
"colors": [
|
||||
"#F4F1DE",
|
||||
"#EAB69F",
|
||||
"#E07A5F",
|
||||
"#8F5D5D",
|
||||
"#3D405B",
|
||||
"#5F797B",
|
||||
"#81B29A",
|
||||
"#9EB998",
|
||||
"#BABF95",
|
||||
"#F2CC8F"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-6",
|
||||
"name": "Figma-06",
|
||||
"colors": [
|
||||
"#F2F3E3",
|
||||
"#D6DAA5",
|
||||
"#B9C167",
|
||||
"#7C805D",
|
||||
"#3E3D52",
|
||||
"#68667A",
|
||||
"#938FA1",
|
||||
"#A8A6A1",
|
||||
"#BBBBA1",
|
||||
"#E3E6A1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-7",
|
||||
"name": "Figma-07",
|
||||
"colors": [
|
||||
"#F3F1E0",
|
||||
"#E0C7A1",
|
||||
"#CD9B62",
|
||||
"#856D5D",
|
||||
"#3D3F57",
|
||||
"#63707A",
|
||||
"#89A19D",
|
||||
"#A2B09C",
|
||||
"#BABD9A",
|
||||
"#EBD896"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-8",
|
||||
"name": "Figma-08",
|
||||
"colors": [
|
||||
"#F3E6E7",
|
||||
"#E7A8A9",
|
||||
"#DA6A6B",
|
||||
"#8C5D5D",
|
||||
"#3D4F4E",
|
||||
"#607A7A",
|
||||
"#83A4A5",
|
||||
"#9FA6A7",
|
||||
"#BAA7A8",
|
||||
"#F0A9AB"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-9",
|
||||
"name": "Figma-09",
|
||||
"colors": [
|
||||
"#EFEFEF",
|
||||
"#C2C2C2",
|
||||
"#959595",
|
||||
"#6B6B6B",
|
||||
"#424242",
|
||||
"#717171",
|
||||
"#A0A0A0",
|
||||
"#ADADAD",
|
||||
"#B8B8B8",
|
||||
"#D0D0D0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-10",
|
||||
"name": "Figma-10",
|
||||
"colors": [
|
||||
"#A8B868",
|
||||
"#798575",
|
||||
"#4A5282",
|
||||
"#393F7C",
|
||||
"#313679",
|
||||
"#282C75",
|
||||
"#3C3C9D",
|
||||
"#4E4CC3",
|
||||
"#645FD4",
|
||||
"#7A71E4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-11",
|
||||
"name": "Figma-11",
|
||||
"colors": [
|
||||
"#EF476F",
|
||||
"#F78C6B",
|
||||
"#FFD166",
|
||||
"#83D483",
|
||||
"#06D6A0",
|
||||
"#001914",
|
||||
"#118AB2",
|
||||
"#0F7799",
|
||||
"#0C637F",
|
||||
"#073B4C"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-12",
|
||||
"name": "Figma-12",
|
||||
"colors": [
|
||||
"#D06A79",
|
||||
"#984B8D",
|
||||
"#5E2BA1",
|
||||
"#482398",
|
||||
"#3D1F94",
|
||||
"#311B90",
|
||||
"#3D39B6",
|
||||
"#4857DC",
|
||||
"#567BE3",
|
||||
"#629FEB"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-13",
|
||||
"name": "Figma-13",
|
||||
"colors": [
|
||||
"#B0BC63",
|
||||
"#CED674",
|
||||
"#EDF186",
|
||||
"#A19B9B",
|
||||
"#5444B0",
|
||||
"#493DAB",
|
||||
"#3E35A6",
|
||||
"#362E8E",
|
||||
"#2C2676",
|
||||
"#1A1646"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-14",
|
||||
"name": "Figma-14",
|
||||
"colors": [
|
||||
"#D07E6A",
|
||||
"#E3AF6F",
|
||||
"#F6E073",
|
||||
"#91B98D",
|
||||
"#2C90A6",
|
||||
"#2A79A9",
|
||||
"#2761AD",
|
||||
"#225494",
|
||||
"#1B467B",
|
||||
"#102949"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-15",
|
||||
"name": "Figma-15",
|
||||
"colors": [
|
||||
"#E65D5C",
|
||||
"#F1797A",
|
||||
"#FC9498",
|
||||
"#87A6A9",
|
||||
"#10B7B9",
|
||||
"#14ACAC",
|
||||
"#17A09F",
|
||||
"#148A88",
|
||||
"#107271",
|
||||
"#094443"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-16",
|
||||
"name": "Figma-16",
|
||||
"colors": [
|
||||
"#7D7D7D",
|
||||
"#A8A8A8",
|
||||
"#D2D2D2",
|
||||
"#B2B2B2",
|
||||
"#919191",
|
||||
"#7E7E7E",
|
||||
"#6A6A6A",
|
||||
"#5B5B5B",
|
||||
"#4C4C4C",
|
||||
"#2D2D2D"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-17",
|
||||
"name": "Figma-17",
|
||||
"colors": [
|
||||
"#5F0F40",
|
||||
"#7D092F",
|
||||
"#9A031E",
|
||||
"#CB4721",
|
||||
"#FB8B24",
|
||||
"#EF781C",
|
||||
"#E36414",
|
||||
"#AE5E26",
|
||||
"#795838",
|
||||
"#0F4C5C"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-18",
|
||||
"name": "Figma-18",
|
||||
"colors": [
|
||||
"#414731",
|
||||
"#515A23",
|
||||
"#616C15",
|
||||
"#99A32C",
|
||||
"#D1D942",
|
||||
"#C2CB37",
|
||||
"#B3BC2C",
|
||||
"#909636",
|
||||
"#6C6F41",
|
||||
"#252157"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-19",
|
||||
"name": "Figma-19",
|
||||
"colors": [
|
||||
"#502939",
|
||||
"#672F2A",
|
||||
"#7E351A",
|
||||
"#B27225",
|
||||
"#E6B030",
|
||||
"#D99F27",
|
||||
"#CB8E1E",
|
||||
"#9F782D",
|
||||
"#72623C",
|
||||
"#1A375A"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-20",
|
||||
"name": "Figma-20",
|
||||
"colors": [
|
||||
"#5B2A28",
|
||||
"#771E1C",
|
||||
"#921211",
|
||||
"#C43133",
|
||||
"#F55054",
|
||||
"#E94347",
|
||||
"#DC363A",
|
||||
"#AA3E40",
|
||||
"#774547",
|
||||
"#125554"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-21",
|
||||
"name": "Figma-21",
|
||||
"colors": [
|
||||
"#2C2C2C",
|
||||
"#303030",
|
||||
"#333333",
|
||||
"#6A6A6A",
|
||||
"#A0A0A0",
|
||||
"#919191",
|
||||
"#808080",
|
||||
"#6F6F6F",
|
||||
"#5E5E5E",
|
||||
"#3B3B3B"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-22",
|
||||
"name": "Figma-22",
|
||||
"colors": [
|
||||
"#366260",
|
||||
"#516765",
|
||||
"#6C6B6A",
|
||||
"#8E6D6C",
|
||||
"#B16E6D",
|
||||
"#C76E6D",
|
||||
"#DE6D6D",
|
||||
"#E38384",
|
||||
"#E6999A"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-23",
|
||||
"name": "Figma-23",
|
||||
"colors": [
|
||||
"#39476C",
|
||||
"#515371",
|
||||
"#695F75",
|
||||
"#876F74",
|
||||
"#A67F73",
|
||||
"#BA8971",
|
||||
"#CE936E",
|
||||
"#D7AA7E",
|
||||
"#DEC08F"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-24",
|
||||
"name": "Figma-24",
|
||||
"colors": [
|
||||
"#3F3D66",
|
||||
"#52526B",
|
||||
"#656770",
|
||||
"#7E8270",
|
||||
"#979D70",
|
||||
"#A7AE6F",
|
||||
"#B7C06D",
|
||||
"#C5CC81",
|
||||
"#D2D794"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-25",
|
||||
"name": "Figma-25",
|
||||
"colors": [
|
||||
"#4B4B4B",
|
||||
"#575757",
|
||||
"#626262",
|
||||
"#707070",
|
||||
"#7E7E7E",
|
||||
"#878787",
|
||||
"#8F8F8F",
|
||||
"#A5A5A5",
|
||||
"#BABABA"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-26",
|
||||
"name": "Figma-26",
|
||||
"colors": [
|
||||
"#363636",
|
||||
"#474747",
|
||||
"#585858",
|
||||
"#9C9C9C",
|
||||
"#E0E0E0",
|
||||
"#A5A5A5",
|
||||
"#696969",
|
||||
"#565656",
|
||||
"#424242"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-27",
|
||||
"name": "Figma-27",
|
||||
"colors": [
|
||||
"#293B3B",
|
||||
"#235959",
|
||||
"#1D7575",
|
||||
"#84A6A6",
|
||||
"#ECD7D8",
|
||||
"#D58A8A",
|
||||
"#BD3C3D",
|
||||
"#993333",
|
||||
"#732829"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-28",
|
||||
"name": "Figma-28",
|
||||
"colors": [
|
||||
"#2B363B",
|
||||
"#2B4559",
|
||||
"#295477",
|
||||
"#899BA6",
|
||||
"#EAE2D5",
|
||||
"#CBA886",
|
||||
"#AC6F37",
|
||||
"#8B5B2F",
|
||||
"#694626"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-29",
|
||||
"name": "Figma-29",
|
||||
"colors": [
|
||||
"#2F2E3B",
|
||||
"#353159",
|
||||
"#3A3376",
|
||||
"#908EA6",
|
||||
"#E7E8D6",
|
||||
"#BEC388",
|
||||
"#949D3A",
|
||||
"#788031",
|
||||
"#5B6127"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-30",
|
||||
"name": "Figma-30",
|
||||
"colors": [
|
||||
"#283D3B",
|
||||
"#21585A",
|
||||
"#197278",
|
||||
"#83A8A6",
|
||||
"#EDDDD4",
|
||||
"#D99185",
|
||||
"#E9B5AF",
|
||||
"#9E3A2E",
|
||||
"#772E25"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-31",
|
||||
"name": "Figma-31",
|
||||
"colors": [
|
||||
"#BDBDBD",
|
||||
"#9D9D9D",
|
||||
"#7E7E7E",
|
||||
"#6B6B6B",
|
||||
"#585858",
|
||||
"#5D5D5D",
|
||||
"#606060",
|
||||
"#626262",
|
||||
"#707070",
|
||||
"#7D7D7D"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-32",
|
||||
"name": "Figma-32",
|
||||
"colors": [
|
||||
"#FB5860",
|
||||
"#F74046",
|
||||
"#F2292C",
|
||||
"#F23433",
|
||||
"#F23E39",
|
||||
"#B86E68",
|
||||
"#9C867F",
|
||||
"#7F9E96",
|
||||
"#5FB4AE",
|
||||
"#3DCAC5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-33",
|
||||
"name": "Figma-33",
|
||||
"colors": [
|
||||
"#F3D321",
|
||||
"#E7B019",
|
||||
"#DC8C10",
|
||||
"#D67039",
|
||||
"#D05460",
|
||||
"#A2529A",
|
||||
"#8C51B8",
|
||||
"#7550D5",
|
||||
"#5F5FE3",
|
||||
"#476CEF"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-34",
|
||||
"name": "Figma-34",
|
||||
"colors": [
|
||||
"#E6EB40",
|
||||
"#D1DA2F",
|
||||
"#BDC91E",
|
||||
"#AEBE36",
|
||||
"#9FB24D",
|
||||
"#838F81",
|
||||
"#757E9C",
|
||||
"#666CB6",
|
||||
"#5F5FC8",
|
||||
"#5650DA"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-35",
|
||||
"name": "Figma-35",
|
||||
"colors": [
|
||||
"#FFBE0B",
|
||||
"#FD8A09",
|
||||
"#FB5607",
|
||||
"#FD2B3B",
|
||||
"#FF006E",
|
||||
"#C11CAD",
|
||||
"#A22ACD",
|
||||
"#8338EC",
|
||||
"#5F5FF6",
|
||||
"#3A86FF"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-36",
|
||||
"name": "Figma-36",
|
||||
"colors": [
|
||||
"#6E6E6E",
|
||||
"#555555",
|
||||
"#3C3C3C",
|
||||
"#333333",
|
||||
"#2F2F2F",
|
||||
"#2A2A2A",
|
||||
"#4A4A4A",
|
||||
"#686868",
|
||||
"#888888",
|
||||
"#A8A8A8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-37",
|
||||
"name": "Figma-37",
|
||||
"colors": [
|
||||
"#EC5B57",
|
||||
"#AD635D",
|
||||
"#6C6B64",
|
||||
"#526660",
|
||||
"#45645D",
|
||||
"#37615B",
|
||||
"#3E8983",
|
||||
"#44B0AB",
|
||||
"#4BC8C4",
|
||||
"#52DFDD"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-38",
|
||||
"name": "Figma-38",
|
||||
"colors": [
|
||||
"#05668D",
|
||||
"#04738F",
|
||||
"#028090",
|
||||
"#019493",
|
||||
"#00A896",
|
||||
"#01B698",
|
||||
"#02C39A",
|
||||
"#79DBAC",
|
||||
"#B5E7B5",
|
||||
"#F0F3BD"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-39",
|
||||
"name": "Figma-39",
|
||||
"colors": [
|
||||
"#292281",
|
||||
"#2D2586",
|
||||
"#31278B",
|
||||
"#382D93",
|
||||
"#3F329B",
|
||||
"#4437A1",
|
||||
"#4A3BA6",
|
||||
"#9D96BA",
|
||||
"#C7C4C4",
|
||||
"#F1F0CD"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-40",
|
||||
"name": "Figma-40",
|
||||
"colors": [
|
||||
"#164588",
|
||||
"#184E8B",
|
||||
"#19568E",
|
||||
"#1B6393",
|
||||
"#1E7098",
|
||||
"#22799B",
|
||||
"#25829F",
|
||||
"#8ABAB1",
|
||||
"#BED6BB",
|
||||
"#F0F2C3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-41",
|
||||
"name": "Figma-41",
|
||||
"colors": [
|
||||
"#097C7A",
|
||||
"#098281",
|
||||
"#088988",
|
||||
"#089393",
|
||||
"#089D9E",
|
||||
"#0AA4A6",
|
||||
"#0BABAD",
|
||||
"#7DC0C2",
|
||||
"#B7CACC",
|
||||
"#F0D4D6"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-42",
|
||||
"name": "Figma-42",
|
||||
"colors": [
|
||||
"#4D4D4D",
|
||||
"#555555",
|
||||
"#5C5C5C",
|
||||
"#676767",
|
||||
"#737373",
|
||||
"#7C7C7C",
|
||||
"#848484",
|
||||
"#B8B8B8",
|
||||
"#D2D2D2",
|
||||
"#EBEBEB"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-43",
|
||||
"name": "Figma-43",
|
||||
"colors": [
|
||||
"#16697A",
|
||||
"#2F8498",
|
||||
"#489FB5",
|
||||
"#65B0C1",
|
||||
"#9DCAD2",
|
||||
"#B8D4D8",
|
||||
"#EDE7E3",
|
||||
"#F2D7B5",
|
||||
"#F6C787",
|
||||
"#FFA62B"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-44",
|
||||
"name": "Figma-44",
|
||||
"colors": [
|
||||
"#352E74",
|
||||
"#4E4892",
|
||||
"#6862AE",
|
||||
"#817BBB",
|
||||
"#ADAACF",
|
||||
"#C2C0D6",
|
||||
"#EAEBE4",
|
||||
"#E7E9BF",
|
||||
"#E4E79A",
|
||||
"#DDE44F"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-45",
|
||||
"name": "Figma-45",
|
||||
"colors": [
|
||||
"#254D77",
|
||||
"#3E6795",
|
||||
"#5782B2",
|
||||
"#7297BE",
|
||||
"#A5BBD1",
|
||||
"#BDCAD7",
|
||||
"#EBE8E3",
|
||||
"#EDDFB9",
|
||||
"#EDD68F",
|
||||
"#EEC33A"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-46",
|
||||
"name": "Figma-46",
|
||||
"colors": [
|
||||
"#1A7271",
|
||||
"#338F8E",
|
||||
"#4CABAA",
|
||||
"#68B9B8",
|
||||
"#9FCECE",
|
||||
"#B9D6D6",
|
||||
"#ECE4E4",
|
||||
"#F0C3C5",
|
||||
"#F3A2A5",
|
||||
"#FA6065"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "figma-47",
|
||||
"name": "Figma-47",
|
||||
"colors": [
|
||||
"#525252",
|
||||
"#6C6C6C",
|
||||
"#878787",
|
||||
"#9B9B9B",
|
||||
"#BDBDBD",
|
||||
"#CCCCCC",
|
||||
"#E8E8E8",
|
||||
"#DBDBDB",
|
||||
"#CDCDCD",
|
||||
"#B2B2B2"
|
||||
]
|
||||
}
|
||||
];
|
||||
Reference in New Issue
Block a user