|
@@ -0,0 +1,846 @@
|
|
|
+import CustomColorPicker from "@/components/CustomColorPicker";
|
|
|
+import {
|
|
|
+ BoldOutlined,
|
|
|
+ ColumnHeightOutlined,
|
|
|
+ FontSizeOutlined,
|
|
|
+ ItalicOutlined,
|
|
|
+ PictureOutlined,
|
|
|
+ StrikethroughOutlined,
|
|
|
+ SwapOutlined,
|
|
|
+ UnderlineOutlined,
|
|
|
+ VerticalAlignBottomOutlined,
|
|
|
+ VerticalAlignMiddleOutlined,
|
|
|
+ VerticalAlignTopOutlined,
|
|
|
+} from "@ant-design/icons";
|
|
|
+import {
|
|
|
+ Button,
|
|
|
+ Col,
|
|
|
+ Divider,
|
|
|
+ Form,
|
|
|
+ Input,
|
|
|
+ InputNumber,
|
|
|
+ Row,
|
|
|
+ Select,
|
|
|
+ Tooltip,
|
|
|
+} from "antd";
|
|
|
+import { arrowOptions } from "@/pages/flow/data";
|
|
|
+import { useModel } from "umi";
|
|
|
+import { useEffect, useRef, useState } from "react";
|
|
|
+import { ImageFillType, ConnectorType, LineType } from "@/enum";
|
|
|
+import { set, cloneDeep } from "lodash-es";
|
|
|
+import { Cell } from "@antv/x6";
|
|
|
+import { fontFamilyOptions, alignOptionData } from '@/pages/flow/data';
|
|
|
+import { alignCell, matchSize } from '@/utils';
|
|
|
+import { cellStyle } from '@/types'
|
|
|
+
|
|
|
+type FormModel = {
|
|
|
+ opacity: number;
|
|
|
+ width: number;
|
|
|
+ height: number;
|
|
|
+ x: number;
|
|
|
+ y: number;
|
|
|
+ rotation: number;
|
|
|
+ connectorType: ConnectorType;
|
|
|
+ startArrow: string;
|
|
|
+ endArrow: string;
|
|
|
+} & cellStyle;
|
|
|
+export default function GraphStyle() {
|
|
|
+ const { selectedCell } = useModel("graphModel");
|
|
|
+ const [isMulit, setIsMulit] = useState(false);
|
|
|
+ const [hasEdge, setHasEdge] = useState(false);
|
|
|
+ const eventNodeList = useRef<Cell[]>([]);
|
|
|
+
|
|
|
+ const [formModel, setFormModel] = useState<FormModel>({
|
|
|
+ opacity: 100,
|
|
|
+ width: 20,
|
|
|
+ height: 20,
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ rotation: 0,
|
|
|
+ text: {
|
|
|
+ fontFamily: "normal",
|
|
|
+ color: "#000000",
|
|
|
+ fontSize: 14,
|
|
|
+ lineHeight: 1.25,
|
|
|
+ textAlign: "center",
|
|
|
+ textVAlign: "middle",
|
|
|
+ bold: false,
|
|
|
+ italic: false,
|
|
|
+ textDecoration: "none",
|
|
|
+ },
|
|
|
+ fill: {
|
|
|
+ fillType: "color",
|
|
|
+ color1: "#FFFFFF",
|
|
|
+ color2: "#eeeeee",
|
|
|
+ gradientType: "linear",
|
|
|
+ gradientValue: 0,
|
|
|
+ objectFit: ImageFillType.Fill,
|
|
|
+ imageUrl: "",
|
|
|
+ },
|
|
|
+ stroke: {
|
|
|
+ type: "solid",
|
|
|
+ color: "#323232",
|
|
|
+ width: 1,
|
|
|
+ },
|
|
|
+ connectorType: ConnectorType.Normal,
|
|
|
+ startArrow: "",
|
|
|
+ endArrow: "",
|
|
|
+ });
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ const firstNode = selectedCell?.find((item) => item.isNode());
|
|
|
+ const firstEdge = selectedCell?.find((item) => item.isEdge());
|
|
|
+ eventNodeList.current = [];
|
|
|
+ if (firstNode) {
|
|
|
+ const position = firstNode.position();
|
|
|
+ const size = firstNode.size();
|
|
|
+ const data = firstNode.getData();
|
|
|
+ setFormModel({
|
|
|
+ ...formModel,
|
|
|
+ x: position.x,
|
|
|
+ y: position.y,
|
|
|
+ width: size.width,
|
|
|
+ height: size.height,
|
|
|
+ rotation: firstNode.angle(),
|
|
|
+ text: data.text,
|
|
|
+ fill: data.fill,
|
|
|
+ stroke: data.stroke,
|
|
|
+ connectorType: ConnectorType.Normal,
|
|
|
+ startArrow: "",
|
|
|
+ endArrow: "",
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听当前选中节点的属性变化
|
|
|
+ if (!eventNodeList.current.find((item) => item.id === firstNode.id)) {
|
|
|
+ eventNodeList.current.push(firstNode);
|
|
|
+ firstNode.on("change:*", (args) => {
|
|
|
+ if (args.key === "position") {
|
|
|
+ setFormModel((state) => {
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ x: parseInt(args.current.x),
|
|
|
+ y: parseInt(args.current.y),
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (args.key === "size") {
|
|
|
+ setFormModel((state) => {
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ width: args.current.width,
|
|
|
+ height: args.current.height,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (args.key === "data") {
|
|
|
+ setFormModel((state) => {
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ text: args.current.text,
|
|
|
+ fill: args.current.fill,
|
|
|
+ stroke: args.current.stroke,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(firstEdge) {
|
|
|
+ const data = firstEdge.getData();
|
|
|
+ const attrs = firstEdge.attrs || {};
|
|
|
+ const sourceMarker = attrs.line?.sourceMarker as Record<string, any>;
|
|
|
+ const targetMarker = attrs.line?.targetMarker as Record<string, any>;
|
|
|
+ const lineType = attrs.line?.strokeDasharray === LineType.solid
|
|
|
+ ? "solid"
|
|
|
+ : attrs.line?.strokeDasharray === LineType.dashed
|
|
|
+ ? "dashed"
|
|
|
+ : attrs.line?.strokeDasharray === LineType.dotted
|
|
|
+ ? "dotted"
|
|
|
+ : "dashdot";
|
|
|
+ let obj = {};
|
|
|
+ if(!firstNode) {
|
|
|
+ obj = {
|
|
|
+ stroke: {
|
|
|
+ type: lineType,
|
|
|
+ color: attrs.line?.stroke || "#000000",
|
|
|
+ width: attrs.line?.strokeWidth || 1,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ setFormModel((state) => {
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ ...obj,
|
|
|
+ startArrow: sourceMarker?.name,
|
|
|
+ endArrow: targetMarker?.name,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ let nodeCount = 0;
|
|
|
+ selectedCell?.forEach((cell) => {
|
|
|
+ if (cell.isEdge()) {
|
|
|
+ // 存在边线
|
|
|
+ setHasEdge(true);
|
|
|
+ }
|
|
|
+ if (cell.isNode()) nodeCount++;
|
|
|
+ });
|
|
|
+ // 多个节点
|
|
|
+ setIsMulit(nodeCount > 1);
|
|
|
+ }, [selectedCell]);
|
|
|
+
|
|
|
+ // 表单值改变,修改元素属性
|
|
|
+ const handleChange = (model: FormModel) => {
|
|
|
+ selectedCell?.forEach((cell) => {
|
|
|
+ if (cell.isNode()) {
|
|
|
+ cell.setPosition(model.x, model.y);
|
|
|
+ cell.setSize(model.width, model.height);
|
|
|
+ cell.rotate(model.rotation, { absolute: true });
|
|
|
+ cell.setData({
|
|
|
+ text: model.text,
|
|
|
+ fill: model.fill,
|
|
|
+ stroke: model.stroke,
|
|
|
+ opacity: model.opacity,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (cell.isEdge()) {
|
|
|
+ const attr = cell.attrs;
|
|
|
+ const sourceMarker = attr?.line?.sourceMarker as Record<string, any>;
|
|
|
+ const targetMarker = attr?.line?.targetMarker as Record<string, any>;
|
|
|
+ cell.setAttrs({
|
|
|
+ line: {
|
|
|
+ ...(attr?.line || {}),
|
|
|
+ stroke: model.stroke.color,
|
|
|
+ strokeWidth: model.stroke.width,
|
|
|
+ strokeDasharray: LineType[model.stroke.type],
|
|
|
+ targetMarker: {
|
|
|
+ ...(targetMarker || {}),
|
|
|
+ name: model.endArrow,
|
|
|
+ args: {
|
|
|
+ size: model.stroke.width + 8,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ sourceMarker: {
|
|
|
+ ...(sourceMarker || {}),
|
|
|
+ name: model.startArrow,
|
|
|
+ args: {
|
|
|
+ size: model.stroke.width + 8,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 设置对齐方式
|
|
|
+ const handleAlign = (type: "left" | "hcenter" | "right" | "top" | "vcenter" | "bottom" | "h" | "v") => {
|
|
|
+ selectedCell && alignCell(type, selectedCell);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 匹配宽高
|
|
|
+ const handleMatchSize = (type: "width" | "height" | "auto") => {
|
|
|
+ selectedCell && matchSize(type, selectedCell);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 调换渐变色颜色
|
|
|
+ const handleSwapColor = () => {
|
|
|
+ const { color1, color2 } = formModel.fill;
|
|
|
+ handleSetFormModel("fill", {
|
|
|
+ ...formModel.fill,
|
|
|
+ color1: color2,
|
|
|
+ color2: color1,
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 设置表单数据
|
|
|
+ const handleSetFormModel = (key: string, value: any) => {
|
|
|
+ const obj = cloneDeep(formModel);
|
|
|
+ set(obj, key, value);
|
|
|
+ setFormModel(obj);
|
|
|
+ handleChange(obj);
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <section className="px-16px">
|
|
|
+ <div className="bg-white rounded-s flex justify-between items-center mb-8px">
|
|
|
+ {alignOptionData.map((item) => (
|
|
|
+ <Tooltip key={item.id} title={item.name}>
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<i className={"iconfont " + item.icon} />}
|
|
|
+ disabled={!isMulit}
|
|
|
+ onClick={() => handleAlign(item.id)}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ <Form.Item
|
|
|
+ label="不透明度"
|
|
|
+ labelCol={{ span: 14 }}
|
|
|
+ wrapperCol={{ span: 10 }}
|
|
|
+ labelAlign="left"
|
|
|
+ className="mb-8px"
|
|
|
+ colon={false}
|
|
|
+ >
|
|
|
+ <InputNumber
|
|
|
+ className="w-full"
|
|
|
+ step={5}
|
|
|
+ min={0}
|
|
|
+ max={100}
|
|
|
+ formatter={(val) => `${val}%`}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.opacity}
|
|
|
+ onChange={(val) => handleSetFormModel("opacity", val)}
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+ </section>
|
|
|
+ <Divider className="my-8px" />
|
|
|
+ <section className="px-16px">
|
|
|
+ <div className="font-bold mb-8px">布局</div>
|
|
|
+ <Row gutter={8} className="mb-8px">
|
|
|
+ <Col span={12}>
|
|
|
+ <InputNumber
|
|
|
+ className="w-full"
|
|
|
+ step={1}
|
|
|
+ min={20}
|
|
|
+ max={10000}
|
|
|
+ suffix="W"
|
|
|
+ formatter={(val) => `${val}px`}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.width}
|
|
|
+ onChange={(val) => handleSetFormModel("width", val)}
|
|
|
+ />
|
|
|
+ </Col>
|
|
|
+ <Col span={12}>
|
|
|
+ <InputNumber
|
|
|
+ className="w-full"
|
|
|
+ step={1}
|
|
|
+ min={20}
|
|
|
+ max={10000}
|
|
|
+ suffix="H"
|
|
|
+ formatter={(val) => `${val}px`}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.height}
|
|
|
+ onChange={(val) => handleSetFormModel("height", val)}
|
|
|
+ />
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ <Row gutter={8} className="mb-8px">
|
|
|
+ <Col span={12}>
|
|
|
+ <InputNumber
|
|
|
+ className="w-full"
|
|
|
+ step={1}
|
|
|
+ min={0}
|
|
|
+ max={10000}
|
|
|
+ precision={0}
|
|
|
+ suffix="X"
|
|
|
+ formatter={(val) => `${val}px`}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.x}
|
|
|
+ onChange={(val) => handleSetFormModel("x", val)}
|
|
|
+ />
|
|
|
+ </Col>
|
|
|
+ <Col span={12}>
|
|
|
+ <InputNumber
|
|
|
+ className="w-full"
|
|
|
+ step={1}
|
|
|
+ min={0}
|
|
|
+ max={10000}
|
|
|
+ precision={0}
|
|
|
+ suffix="Y"
|
|
|
+ formatter={(val) => `${val}px`}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.y}
|
|
|
+ onChange={(val) => handleSetFormModel("y", val)}
|
|
|
+ />
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ <div className="flex justify-between items-center gap-12px mb-8px">
|
|
|
+ <InputNumber
|
|
|
+ className="flex-1"
|
|
|
+ step={1}
|
|
|
+ min={0}
|
|
|
+ max={360}
|
|
|
+ formatter={(val) => `${val}°`}
|
|
|
+ suffix={<i className="iconfont icon-a-ziyuan126 text-12px" />}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.rotation}
|
|
|
+ onChange={(val) => handleSetFormModel("rotation", val)}
|
|
|
+ />
|
|
|
+ <Button
|
|
|
+ icon={<i className="iconfont icon-shangxiafanzhuan" />}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() => handleSetFormModel("rotation", 0)}
|
|
|
+ />
|
|
|
+ <Button
|
|
|
+ icon={<i className="iconfont icon-zuoyoufanzhuan" />}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() => handleSetFormModel("rotation", 90)}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <Form.Item
|
|
|
+ label="匹配大小"
|
|
|
+ labelCol={{ span: 14 }}
|
|
|
+ wrapperCol={{ span: 10 }}
|
|
|
+ labelAlign="left"
|
|
|
+ className="mb-8px"
|
|
|
+ colon={false}
|
|
|
+ >
|
|
|
+ <div className="bg-white rounded-s flex justify-between items-center mb-8px">
|
|
|
+ <Tooltip title="适配宽度">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<i className="iconfont icon-kuandu" />}
|
|
|
+ disabled={!isMulit}
|
|
|
+ onClick={() => handleMatchSize("width")}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ <Tooltip title="适配高度">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<i className="iconfont icon-gaodu" />}
|
|
|
+ disabled={!isMulit}
|
|
|
+ onClick={() => handleMatchSize("height")}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ <Tooltip title="适配宽高">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<i className="iconfont icon-shiyingkuangao" />}
|
|
|
+ disabled={!isMulit}
|
|
|
+ onClick={() => handleMatchSize("auto")}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ </div>
|
|
|
+ </Form.Item>
|
|
|
+ </section>
|
|
|
+ <Divider className="my-8px" />
|
|
|
+ <section className="px-16px">
|
|
|
+ <div className="font-bold mb-8px">文本</div>
|
|
|
+ <div className="flex items-center gap-12px mb-8px">
|
|
|
+ <Select
|
|
|
+ className="flex-1"
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ options={fontFamilyOptions}
|
|
|
+ value={formModel.text.fontFamily}
|
|
|
+ labelRender={(item) => item.value}
|
|
|
+ onChange={(val) => handleSetFormModel("text.fontFamily", val)}
|
|
|
+ />
|
|
|
+ <CustomColorPicker
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ color={formModel.text.color}
|
|
|
+ onChange={(color) => handleSetFormModel("text.color", color)}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center gap-12px mb-8px">
|
|
|
+ <InputNumber
|
|
|
+ className="flex-1"
|
|
|
+ prefix={<FontSizeOutlined />}
|
|
|
+ step={1}
|
|
|
+ min={12}
|
|
|
+ max={10000}
|
|
|
+ formatter={(val) => `${val}px`}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.text.fontSize}
|
|
|
+ onChange={(val) => handleSetFormModel("text.fontSize", val)}
|
|
|
+ />
|
|
|
+ <Select
|
|
|
+ className="flex-1"
|
|
|
+ suffixIcon={<ColumnHeightOutlined />}
|
|
|
+ options={[
|
|
|
+ { label: "1.0", value: 1 },
|
|
|
+ { label: "1.25", value: 1.25 },
|
|
|
+ { label: "1.5", value: 1.5 },
|
|
|
+ { label: "2.0", value: 2 },
|
|
|
+ { label: "2.5", value: 2.5 },
|
|
|
+ { label: "3.0", value: 3 },
|
|
|
+ ]}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.text.lineHeight}
|
|
|
+ onChange={(val) => handleSetFormModel("text.lineHeight", val)}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center gap-12px mb-8px">
|
|
|
+ <div className="bg-white rounded-s flex justify-between items-center mb-8px">
|
|
|
+ <Tooltip title="左对齐">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<i className="iconfont icon-zuoduiqi" />}
|
|
|
+ className={formModel.text.textAlign === "left" ? "active" : ""}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() => handleSetFormModel("text.textAlign", "left")}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ <Tooltip title="居中">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<i className="iconfont icon-juzhong" />}
|
|
|
+ className={
|
|
|
+ formModel.text.textAlign === "center" ? "active" : ""
|
|
|
+ }
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() => handleSetFormModel("text.textAlign", "center")}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ <Tooltip title="右对齐">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<i className="iconfont icon-youduiqi" />}
|
|
|
+ className={formModel.text.textAlign === "right" ? "active" : ""}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() => handleSetFormModel("text.textAlign", "right")}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ </div>
|
|
|
+ <div className="bg-white rounded-s flex justify-between items-center mb-8px">
|
|
|
+ <Tooltip title="顶部对齐">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<VerticalAlignTopOutlined />}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ className={formModel.text.textVAlign === "top" ? "active" : ""}
|
|
|
+ onClick={() => handleSetFormModel("text.textVAlign", "top")}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ <Tooltip title="垂直居中">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<VerticalAlignMiddleOutlined />}
|
|
|
+ className={
|
|
|
+ formModel.text.textVAlign === "middle" ? "active" : ""
|
|
|
+ }
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() => handleSetFormModel("text.textVAlign", "middle")}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ <Tooltip title="底部对齐">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<VerticalAlignBottomOutlined />}
|
|
|
+ className={
|
|
|
+ formModel.text.textVAlign === "bottom" ? "active" : ""
|
|
|
+ }
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() => handleSetFormModel("text.textVAlign", "bottom")}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center gap-12px mb-8px">
|
|
|
+ <div className="bg-white rounded-s flex justify-between items-center mb-8px">
|
|
|
+ <Tooltip placement="bottom" title="字体加粗">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<BoldOutlined />}
|
|
|
+ className={formModel.text.bold ? "active" : ""}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() =>
|
|
|
+ handleSetFormModel("text.bold", !formModel.text.bold)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ <Tooltip placement="bottom" title="字体倾斜">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<ItalicOutlined />}
|
|
|
+ className={formModel.text.italic ? "active" : ""}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() =>
|
|
|
+ handleSetFormModel("text.italic", !formModel.text.italic)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ <Tooltip placement="bottom" title="下划线">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<UnderlineOutlined />}
|
|
|
+ className={
|
|
|
+ formModel.text.textDecoration === "underline" ? "active" : ""
|
|
|
+ }
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() =>
|
|
|
+ handleSetFormModel(
|
|
|
+ "text.textDecoration",
|
|
|
+ formModel.text.textDecoration === "underline"
|
|
|
+ ? "none"
|
|
|
+ : "underline"
|
|
|
+ )
|
|
|
+ }
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ <Tooltip placement="bottom" title="中划线">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={<StrikethroughOutlined />}
|
|
|
+ className={
|
|
|
+ formModel.text.textDecoration === "line-through"
|
|
|
+ ? "active"
|
|
|
+ : ""
|
|
|
+ }
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={() =>
|
|
|
+ handleSetFormModel(
|
|
|
+ "text.textDecoration",
|
|
|
+ formModel.text.textDecoration === "line-through"
|
|
|
+ ? "none"
|
|
|
+ : "line-through"
|
|
|
+ )
|
|
|
+ }
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+ <Divider className="my-8px" />
|
|
|
+ <section className="px-16px">
|
|
|
+ <div className="font-bold mb-8px">填充</div>
|
|
|
+ <div className="flex items-center gap-12px mb-8px">
|
|
|
+ <Select
|
|
|
+ className="flex-1"
|
|
|
+ options={[
|
|
|
+ { label: "纯色", value: "color" },
|
|
|
+ { label: "渐变", value: "gradient" },
|
|
|
+ { label: "图片", value: "image" },
|
|
|
+ ]}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.fill.fillType}
|
|
|
+ onChange={(value) => handleSetFormModel("fill.fillType", value)}
|
|
|
+ />
|
|
|
+ <div className="flex items-center gap-12px">
|
|
|
+ <CustomColorPicker
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ color={formModel.fill.color1}
|
|
|
+ onChange={(color) => handleSetFormModel("fill.color1", color)}
|
|
|
+ />
|
|
|
+ {formModel.fill.fillType === "gradient" && (
|
|
|
+ <>
|
|
|
+ <Button
|
|
|
+ icon={<SwapOutlined />}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ onClick={handleSwapColor}
|
|
|
+ />
|
|
|
+ <CustomColorPicker
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ color={formModel.fill.color2}
|
|
|
+ onChange={(color) => handleSetFormModel("fill.color2", color)}
|
|
|
+ />
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {formModel.fill.fillType === "image" && (
|
|
|
+ <>
|
|
|
+ <Form.Item
|
|
|
+ label="图片地址"
|
|
|
+ labelCol={{ span: 14 }}
|
|
|
+ wrapperCol={{ span: 10 }}
|
|
|
+ labelAlign="left"
|
|
|
+ className="mb-8px"
|
|
|
+ colon={false}
|
|
|
+ >
|
|
|
+ <Input
|
|
|
+ placeholder="图片地址"
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ suffix={<PictureOutlined />}
|
|
|
+ value={formModel.fill.imageUrl}
|
|
|
+ onChange={(e) =>
|
|
|
+ handleSetFormModel("fill.imageUrl", e.target.value)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="填充方式"
|
|
|
+ labelCol={{ span: 14 }}
|
|
|
+ wrapperCol={{ span: 10 }}
|
|
|
+ labelAlign="left"
|
|
|
+ className="mb-8px"
|
|
|
+ colon={false}
|
|
|
+ >
|
|
|
+ <Select
|
|
|
+ options={[
|
|
|
+ { value: ImageFillType.Fill, label: "填充" },
|
|
|
+ { value: ImageFillType.Auto, label: "自适应" },
|
|
|
+ { value: ImageFillType.Stretch, label: "按图形伸展" },
|
|
|
+ { value: ImageFillType.Original, label: "原始尺寸" },
|
|
|
+ { value: ImageFillType.Tiled, label: "平铺" },
|
|
|
+ ]}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.fill.objectFit}
|
|
|
+ onChange={(value) =>
|
|
|
+ handleSetFormModel("fill.objectFit", value)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ {formModel.fill.fillType === "gradient" && (
|
|
|
+ <div className="flex items-center gap-12px">
|
|
|
+ <Select
|
|
|
+ className="flex-1"
|
|
|
+ options={[
|
|
|
+ { value: "linear", label: "线性渐变" },
|
|
|
+ { value: "radial", label: "径向渐变" },
|
|
|
+ ]}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.fill.gradientType}
|
|
|
+ onChange={(value) => {
|
|
|
+ handleSetFormModel("fill.gradientType", value);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ {formModel.fill.fillType === "gradient" && (
|
|
|
+ <>
|
|
|
+ {formModel.fill.gradientType === "linear" && (
|
|
|
+ <InputNumber
|
|
|
+ className="flex-1"
|
|
|
+ step={1}
|
|
|
+ min={0}
|
|
|
+ max={360}
|
|
|
+ formatter={(val) => `${val}°`}
|
|
|
+ suffix={
|
|
|
+ <i className="iconfont icon-a-ziyuan126 text-12px" />
|
|
|
+ }
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.fill.gradientValue}
|
|
|
+ onChange={(val) =>
|
|
|
+ handleSetFormModel("fill.gradientValue", val)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {formModel.fill.gradientType === "radial" && (
|
|
|
+ <InputNumber
|
|
|
+ className="flex-1"
|
|
|
+ step={1}
|
|
|
+ min={0}
|
|
|
+ max={100}
|
|
|
+ formatter={(val) => `${val}%`}
|
|
|
+ suffix={
|
|
|
+ <i className="iconfont icon-jingxiangjianbian text-12px" />
|
|
|
+ }
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.fill.gradientValue}
|
|
|
+ onChange={(val) =>
|
|
|
+ handleSetFormModel("fill.gradientValue", val)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </section>
|
|
|
+ <Divider className="my-8px" />
|
|
|
+ <section className="px-16px">
|
|
|
+ <div className="font-bold mb-8px">线条</div>
|
|
|
+ <div className="flex gap-12px mb-8px">
|
|
|
+ <Select
|
|
|
+ className="flex-1"
|
|
|
+ options={[
|
|
|
+ { label: "实线", value: "solid" },
|
|
|
+ { label: "虚线", value: "dashed" },
|
|
|
+ { label: "点线", value: "dotted" },
|
|
|
+ { label: "点划线", value: "dashdot" },
|
|
|
+ ]}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.stroke.type}
|
|
|
+ onChange={(value) => handleSetFormModel("stroke.type", value)}
|
|
|
+ />
|
|
|
+ <CustomColorPicker
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ color={formModel.stroke.color}
|
|
|
+ onChange={(color) => handleSetFormModel("stroke.color", color)}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div className="flex gap-12px mb-8px">
|
|
|
+ <InputNumber
|
|
|
+ className="flex-1"
|
|
|
+ step={1}
|
|
|
+ min={1}
|
|
|
+ max={10}
|
|
|
+ formatter={(val) => `${val}px`}
|
|
|
+ disabled={!selectedCell?.length}
|
|
|
+ value={formModel.stroke.width}
|
|
|
+ onChange={(val) => handleSetFormModel("stroke.width", val)}
|
|
|
+ />
|
|
|
+ <div className="bg-white rounded-s flex justify-between items-center">
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={
|
|
|
+ <i className="iconfont icon-a-icon16lianxianleixinghuizhilianxian" />
|
|
|
+ }
|
|
|
+ className={
|
|
|
+ formModel.connectorType === ConnectorType.Rounded
|
|
|
+ ? "active"
|
|
|
+ : ""
|
|
|
+ }
|
|
|
+ disabled={!hasEdge}
|
|
|
+ onClick={() =>
|
|
|
+ handleSetFormModel("connectorType", ConnectorType.Rounded)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={
|
|
|
+ <i className="iconfont icon-a-icon16lianxianleixingbeisaierquxian" />
|
|
|
+ }
|
|
|
+ className={
|
|
|
+ formModel.connectorType === ConnectorType.Smooth ? "active" : ""
|
|
|
+ }
|
|
|
+ disabled={!hasEdge}
|
|
|
+ onClick={() =>
|
|
|
+ handleSetFormModel("connectorType", ConnectorType.Smooth)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ <Button
|
|
|
+ type="text"
|
|
|
+ icon={
|
|
|
+ <i className="iconfont icon-a-icon16lianxianleixinghuizhizhixian" />
|
|
|
+ }
|
|
|
+ className={
|
|
|
+ formModel.connectorType === ConnectorType.Normal ? "active" : ""
|
|
|
+ }
|
|
|
+ disabled={!hasEdge}
|
|
|
+ onClick={() =>
|
|
|
+ handleSetFormModel("connectorType", ConnectorType.Normal)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <Form.Item
|
|
|
+ label="箭头类型"
|
|
|
+ labelAlign="left"
|
|
|
+ colon={false}
|
|
|
+ labelCol={{ span: 6 }}
|
|
|
+ wrapperCol={{ span: 18 }}
|
|
|
+ >
|
|
|
+ <div className="flex gap-12px items-center">
|
|
|
+ <Select
|
|
|
+ options={arrowOptions.map((item) => {
|
|
|
+ return {
|
|
|
+ value: item.name,
|
|
|
+ label: <img className="w-12px mx-50%" src={item.icon} />,
|
|
|
+ };
|
|
|
+ })}
|
|
|
+ disabled={!hasEdge}
|
|
|
+ value={formModel.startArrow}
|
|
|
+ onChange={(value) => handleSetFormModel("startArrow", value)}
|
|
|
+ />
|
|
|
+ <Select
|
|
|
+ options={arrowOptions.map((item) => {
|
|
|
+ return {
|
|
|
+ value: item.name,
|
|
|
+ label: <img className="w-12px mx-50%" src={item.icon} />,
|
|
|
+ };
|
|
|
+ })}
|
|
|
+ disabled={!hasEdge}
|
|
|
+ value={formModel.endArrow}
|
|
|
+ onChange={(value) => handleSetFormModel("endArrow", value)}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </Form.Item>
|
|
|
+ </section>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|