Files
yys-editor/docs/2design/DataModel.md
2025-12-24 16:49:50 +08:00

10 KiB
Raw Blame History

数据模型v1

目标:在保持 LogicFlow 原生 GraphData 结构不改造的前提下只定义顶层导出结构Root Document和节点业务字段并通过 schemaVersion 管理导入导出与迁移,让精力集中在阴阳师业务数据上。

顶层导出结构Root Document

  • 约定导出/本地持久化数据统一为如下结构,并包含 schemaVersion
{
  "schemaVersion": "1.0.0",
  "fileList": [
    {
      "id": "file-1",
      "label": "File 1",
      "name": "File 1",
      "type": "FLOW",
      "visible": true,
      "graphRawData": { "nodes": [], "edges": [] },
      "transform": {
        "SCALE_X": 1,
        "SCALE_Y": 1,
        "TRANSLATE_X": 0,
        "TRANSLATE_Y": 0
      },
      "createdAt": 0,
      "updatedAt": 0
    }
  ],
  "activeFileId": "file-1",
  "activeFile": "File 1"
}
  • 存量数据(无 schemaVersion)在加载时默认视为 "0.x",并通过迁移器补齐。

Graph/Node 基础模型

Graph/Node 结构整体沿用 LogicFlow 的 GraphData 定义nodes/edges、id/type/x/y/width/height 等);我们只约定节点 properties 中的业务字段,并在需要时适配 LogicFlow 的样式/图层信息,而不是重新定义一套独立的画布数据模型。

// Graph 文档(嵌入在每个 file.graphRawData 内)
interface GraphDocument {
  nodes: GraphNode[];
  edges: GraphEdge[];
}

interface GraphNode {
  id: string;
  type: string;          // imageNode | textNode | vectorNode | shikigamiSelect | yuhunSelect | propertySelect | ...
  x?: number;            // LogicFlow 原生定位
  y?: number;
  width?: number;        // 渲染冗余:由 properties.style.width 同步而来
  height?: number;       // 渲染冗余:由 properties.style.height 同步而来
  properties: NodeProperties;
}

interface GraphEdge {
  id: string;
  type?: string;
  sourceNodeId: string;
  targetNodeId: string;
  properties?: Record<string, any>;
}

interface NodeProperties {
  style: NodeStyle;      // 通用样式
  meta?: NodeMeta;       // 通用元信息(层级/锁定/可见/分组等)
  // 具体节点类型的扩展字段(如下)
  image?: ImageProps;
  text?: TextProps;
  vector?: VectorProps;
  shikigami?: ShikigamiProps;
  yuhun?: YuhunProps;
  property?: PropertyRuleProps;
}

interface NodeStyle {
  // 尺寸与变换(单一事实来源)
  width: number;      // px
  height: number;     // px
  rotate?: number;    // deg逆时针为正围绕节点中心旋转

  // 形状/外观
  fill?: string;          // 颜色:#RGB/#RRGGBB/#RRGGBBAA/rgba()/transparent 等
  stroke?: string;        // 描边色
  strokeWidth?: number;   // px≥ 0
  radius?: number | [number, number, number, number]; // 圆角rect 有效path/polygon 无效
  opacity?: number;       // 0..1

  // 阴影
  shadow?: {
    color?: string;
    blur?: number;        // px
    offsetX?: number;     // px
    offsetY?: number;     // px
  };

  // 文本样式(当 text 节点或带文本的复合节点需要)
  textStyle?: {
    color?: string;
    fontFamily?: string;
    fontSize?: number;    // px> 0
    fontWeight?: number | string; // 400/700 或 normal/bold
    lineHeight?: number;  // 1..3
    align?: 'left' | 'center' | 'right';
    verticalAlign?: 'top' | 'middle' | 'bottom'; // 多行:用容器高度+行高模拟
    letterSpacing?: number; // px
    padding?: [number, number, number, number]; // 上右下左,≥ 0
    background?: string;  // 可选
  };
}
interface NodeMeta {
  z?: number;            // 显式层级,缺省按插入顺序
  locked?: boolean;      // 锁定(不可选/不可拖动)
  visible?: boolean;     // 可见性
  groupId?: string;      // 组合/分组标识
  name?: string;         // 可选显示名
  createdAt?: number;
  updatedAt?: number;
}

说明:上述 GraphDocumentGraphNodeGraphEdgeNodeStyleNodeMeta 主要用于说明结构,实际实现直接使用 LogicFlow 提供的 GraphData样式/图层字段属于可选扩展v1 只要求节点 properties 中的业务字段与阴阳师相关业务数据。

节点类型扩展字段

  • ImageimageNode
interface ImageProps {
  url: string;                         // 图片地址(本地 base64 或 assetId 由上层解析)
  fit?: 'fill' | 'contain' | 'cover';  // 渲染适配
}
  • TexttextNode
interface TextProps {
  content: string;                     // 文本内容(富文本后续扩展)
  rich?: boolean;                      // 是否富文本v1 仅纯文本)
}
  • VectorvectorNode
interface VectorProps {
  kind: 'path' | 'rect' | 'ellipse' | 'polygon';
  path?: string;                       // 当 kind=path 时的 SVG Path 数据M/L/C...
  points?: Array<[number, number]>;    // 当 polygon 时的顶点(可选)
}
  • Shikigami/阴阳师节点(保留现有结构)
interface ShikigamiProps { name: string; avatar: string; rarity: string; }
interface YuhunProps { name: string; type: string; avatar: string; shortName?: string; }
// PropertyRuleProps属性/御魂要求)延用现有字段,后续按规则引擎再细化
interface PropertyRuleProps { [k: string]: any }

同步与渲染约定

  • 源数据以 properties.style.width/height 为准;渲染时将其同步到节点 width/height

  • 层级以 properties.meta.z 为准;渲染前对 nodes 进行稳定排序(先 z再 createdAt

  • 通用样式仅描述;具体生效由各节点视图组件(.vue解释执行。

  • vector.kind='rect'radius 生效;path/polygon 忽略;fill/stroke/strokeWidth/opacity 对所有 kind 生效。

  • 旋转以节点中心为基点;正值逆时针;不单独提供 transformOrigin 字段(如需将以兼容方式新增)。

默认值v1

const DefaultNodeStyle: NodeStyle = {
  width: 180, height: 120, rotate: 0,
  fill: '#ffffff', stroke: '#dcdfe6', strokeWidth: 1, radius: 4, opacity: 1,
  shadow: { color: 'rgba(0,0,0,0.1)', blur: 4, offsetX: 0, offsetY: 2 },
  textStyle: { color: '#303133', fontFamily: 'system-ui', fontSize: 14, fontWeight: 400, lineHeight: 1.4, align: 'left', verticalAlign: 'top', letterSpacing: 0, padding: [8,8,8,8] }
};

useStore 接入点与 schemaVersion

  • 保存/导出:在 saveStateToLocalStorageexportData 输出中添加 schemaVersion: "1.0.0"

  • 读取/导入:loadStateFromLocalStorage/importData 检测 schemaVersion;无则走迁移器补齐:

    • node.width/height 存在但 properties.style 缺失,创建 style 并拷贝宽高;
    • 给所有节点补 meta.visible=truemeta.locked=false
    • 生成 createdAt/updatedAt(以当前时间或从已有字段推断)。
  • 常量:const CURRENT_SCHEMA_VERSION = '1.0.0' 存放于 src/ts/useStore.tssrc/ts/schema.ts

  • 导出:同时写出 activeFileIdactiveFile(名称),以兼容旧版。

  • 导入:优先使用 activeFileId 匹配;若缺失则回退按 activeFile(名称)匹配;两者都缺则选首个文件。

迁移策略v0 → v1

  1. 判定:无 schemaVersion 视为 v0。
  2. 节点迁移:
    • 将节点的 width/height 写入 properties.style
    • 若节点已有颜色/文本样式散落在 properties,合并到 properties.style.*textStyle
    • 未定义的字段保持原样存放于 properties,由节点视图兼容。
  3. 文件级:给每个 file 增加 id/createdAt/updatedAt(可选)。
  4. 顶层:补 schemaVersion='1.0.0'

示例(含三类基础节点)

{
  "schemaVersion": "1.0.0",
  "fileList": [
    {
      "id": "file-1",
      "label": "海报示例",
      "name": "海报示例",
      "type": "FLOW",
      "visible": true,
      "graphRawData": {
        "nodes": [
          {
            "id": "n-image-1",
            "type": "imageNode",
            "x": 200, "y": 160,
            "properties": {
              "style": { "width": 300, "height": 200, "radius": 8, "opacity": 1 },
              "meta": { "z": 1, "visible": true, "locked": false },
              "image": { "url": "/assets/banner.png", "fit": "cover" }
            }
          },
          {
            "id": "n-text-1",
            "type": "textNode",
            "x": 220, "y": 180,
            "properties": {
              "style": {
                "width": 260, "height": 80,
                "textStyle": { "color": "#111", "fontFamily": "Microsoft YaHei", "fontSize": 24, "fontWeight": 700, "align": "left" }
              },
              "meta": { "z": 2 },
              "text": { "content": "阴阳师阵容编辑器" }
            }
          },
          {
            "id": "n-vector-1",
            "type": "vectorNode",
            "x": 180, "y": 300,
            "properties": {
              "style": { "width": 360, "height": 6, "fill": "#409EFF" },
              "meta": { "z": 0 },
              "vector": { "kind": "rect" }
            }
          }
        ],
        "edges": []
      },
      "transform": { "SCALE_X": 1, "SCALE_Y": 1, "TRANSLATE_X": 0, "TRANSLATE_Y": 0 }
    }
  ],
  "activeFileId": "file-1",
  "activeFile": "海报示例"
}

校验要点(建议)

  • width/height > 0opacity ∈ [0,1]strokeWidth ≥ 0radius ≥ 0 或四元组均合法。
  • 文本fontSize > 0lineHeight ∈ [1, 3]padding 四元组 ≥ 0。
  • z 可为空(使用插入顺序),若存在必须为整数。
  • radius 仅在 vector.kind='rect' 生效,其他 kind 忽略。

实施清单(与代码关联)

  1. src/ts/useStore.ts
    • 导出/保存时写入 schemaVersion;导入/读取时若缺失则调用 migrateToV1(state)
    • 定义 CURRENT_SCHEMA_VERSION = '1.0.0';新增 migrateToV1(按上文迁移策略)。
  2. src/components/flow/FlowEditor.vue
    • 渲染前对 nodesmeta.zcreatedAt 稳定排序;
    • render 前将 properties.style.width/height 同步到节点尺寸。
  3. 节点视图(*.vue
    • image/text/vector 节点按 properties.style 解析样式;
    • 旧字段兼容期内保留 fallback逐步迁出。
  4. 属性面板 PropertyPanel.vue
    • 统一读取/写入 properties.style.*;文本属性写入 textStyletext.content

附注v1 仅定义统一样式容器与最小字段集合;后续可在不破坏兼容的前提下增量扩展(如 blendMode、滤镜等并通过 schemaVersion 控制迁移。