ソースを参照

添加实例对象。

sybotan 4 年 前
コミット
23441d86e4

+ 73 - 0
docs/.vuepress/components/example/web/graph/Circle1.vue

@@ -0,0 +1,73 @@
+<template>
+    <canvas id="circle" width="740" height="250" />
+</template>
+
+<script lang="ts">
+    import {SCanvasView, SColor, SPainter, SRect, SLineCapStyle} from "@saga-web/draw/lib";
+
+    class TestView extends SCanvasView {
+
+        constructor() {
+            super("circle")
+        }
+
+        onDraw(canvas: SPainter): void {
+            canvas.clearRect(new SRect(0,0,800,400));
+
+            canvas.pen.color = SColor.Blue;
+            canvas.brush.color = SColor.Red;
+            canvas.drawCircle(100,100,50);
+
+            canvas.pen.lineWidth = 10;
+            canvas.pen.lineDash = [10,11];
+            canvas.pen.lineCapStyle = SLineCapStyle.Butt;
+            canvas.pen.color = new SColor("#585858");
+            canvas.brush.color = new SColor("#585858");
+
+            canvas.pen.dashOffset = new Date().getTime()/50%60;
+            canvas.drawCircle(230,100,40);
+
+            canvas.pen.dashOffset = -new Date().getTime()/50%60;
+            canvas.drawCircle(315,100,40);
+
+            canvas.pen.color = SColor.Transparent;
+            canvas.brush.color = SColor.White;
+            canvas.drawCircle(230,100,15);
+            canvas.drawCircle(315,100,15);
+
+            canvas.pen.color = SColor.Red;
+            for (let i = 0; i < 360; i += 10) {
+                let q1 = i * Math.PI / 180;
+                let q2 = (i + 120) * Math.PI / 180;
+                canvas.drawLine(
+                    450 + 50 * Math.cos(q1),
+                    100 + 50 * Math.sin(q1),
+                    450 + 50 * Math.cos(q2),
+                    100 + 50 * Math.sin(q2));
+            }
+
+            canvas.pen.color = SColor.Blue;
+            for (let i = 0; i < 360; i += 10) {
+                let q = i * Math.PI / 180;
+                canvas.drawLine(
+                    650,
+                    100,
+                    650 + 50 * Math.cos(q),
+                    100 + 50 * Math.sin(q));
+            }
+
+            this.update();
+        }
+    }
+
+    export default {
+        name: "Circle1",
+        mounted(): void {
+            new TestView();
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 61 - 0
docs/.vuepress/components/example/web/graph/DrawLine1.vue

@@ -0,0 +1,61 @@
+<template>
+    <canvas id="drawLine1" width="800" height="100" />
+</template>
+
+<script lang="ts">
+    import { SCanvasView, SColor, SPainter } from "@saga-web/draw/lib";
+
+    class TestView extends SCanvasView {
+
+        constructor() {
+            super("drawLine1")
+        }
+
+        onDraw(canvas: SPainter): void {
+            // 清除画布
+            canvas.clearRect(0,0,800,100);
+
+            // 在此编写绘制操作相关命令
+            canvas.drawLine(0,0, 100, 100);
+
+            canvas.pen.lineWidth = 1;
+
+            canvas.pen.color = SColor.Blue;
+            for (let i = 0; i < 360; i += 10) {
+                let q = i * Math.PI / 180;
+                canvas.drawLine(
+                    200,
+                    50,
+                    200 + 50 * Math.cos(q),
+                    50 + 50 * Math.sin(q));
+            }
+
+            canvas.pen.color = SColor.Red;
+            for (let i = 0; i < 360; i += 10) {
+                let q1 = i * Math.PI / 180;
+                let q2 = (i + 120) * Math.PI / 180;
+                canvas.drawLine(
+                    350 + 50 * Math.cos(q1),
+                    50 + 50 * Math.sin(q1),
+                    350 + 50 * Math.cos(q2),
+                    50 + 50 * Math.sin(q2));
+            }
+
+            canvas.pen.color = new SColor('#03a9f4');
+            canvas.pen.lineWidth = 5;
+            canvas.drawLine(450, 50, 700, 50);
+            canvas.pen.lineWidth = 3;
+            canvas.pen.dashOffset = new Date().getTime()/50%80;
+            canvas.pen.lineDash = [30,50];
+            canvas.pen.color = SColor.White;
+            canvas.drawLine(450, 50, 700, 50);
+            this.update();
+        }
+    }
+
+    export default {
+        mounted() {
+            new TestView();
+        }
+    }
+</script>

+ 48 - 0
docs/.vuepress/components/example/web/graph/DrawLine2.vue

@@ -0,0 +1,48 @@
+<template>
+    <canvas id="drawLine2" width="800" height="100" />
+</template>
+
+<script lang="ts">
+    import { SCanvasView, SColor, SPainter } from "@saga-web/draw/lib";
+
+    class TestView extends SCanvasView {
+
+        constructor() {
+            super("drawLine2")
+        }
+
+        onDraw(canvas: SPainter): void {
+            // 在此编写绘制操作相关命令
+            canvas.drawLine(0,0, 100, 100);
+
+            canvas.pen.lineWidth = 1;
+
+            canvas.pen.color = SColor.Blue;
+            for (let i = 0; i < 360; i += 10) {
+                let q = i * Math.PI / 180;
+                canvas.drawLine(
+                    200,
+                    50,
+                    200 + 50 * Math.cos(q),
+                    50 + 50 * Math.sin(q));
+            }
+
+            canvas.pen.color = SColor.Red;
+            for (let i = 0; i < 360; i += 10) {
+                let q1 = i * Math.PI / 180;
+                let q2 = (i + 120) * Math.PI / 180;
+                canvas.drawLine(
+                    350 + 50 * Math.cos(q1),
+                    50 + 50 * Math.sin(q1),
+                    350 + 50 * Math.cos(q2),
+                    50 + 50 * Math.sin(q2));
+            }
+        }
+    }
+
+    export default {
+        mounted() {
+            new TestView();
+        }
+    }
+</script>

+ 24 - 0
docs/.vuepress/components/example/web/graph/DrawPolyline1.vue

@@ -0,0 +1,24 @@
+<template>
+    <canvas id="drawPolyline1" width="800" height="100" />
+</template>
+
+<script lang="ts">
+    import {SCanvasView, SColor, SPainter, SPoint} from "@saga-web/draw/lib";
+
+    class TestView extends SCanvasView {
+        arr:SPoint[]=[new SPoint(10,10),new SPoint(10,40),new SPoint(30,30)]
+        constructor() {
+            super("drawPolyline1")
+        }
+
+        onDraw(canvas: SPainter): void {
+            canvas.drawPolyline(this.arr);
+        }
+    }
+
+    export default {
+        mounted() {
+            new TestView();
+        }
+    }
+</script>

+ 53 - 0
docs/.vuepress/components/example/web/graph/DrawRect1.vue

@@ -0,0 +1,53 @@
+<template>
+    <canvas id="drawRect1" width="800" height="180" />
+</template>
+
+<script lang="ts">
+    import { SCanvasView, SColor, SPainter, SPoint, SRect, SSize } from "@saga-web/draw/lib";
+
+    class TestView extends SCanvasView {
+
+        constructor() {
+            super("drawRect1")
+        }
+
+        onDraw(canvas: SPainter): void {
+            canvas.clearRect(0,0,800,200);
+
+            canvas.pen.color = SColor.Blue;
+            canvas.brush.color = SColor.Red;
+            canvas.drawRect(10, 10, 80, 80);
+
+            canvas.pen.color = SColor.Transparent;
+            canvas.brush.color = SColor.Red;
+            canvas.drawRect(new SPoint(110, 10), new SSize(80, 80));
+
+            canvas.pen.color = SColor.Blue;
+            canvas.brush.color = SColor.Transparent;
+            canvas.drawRect(new SRect(210, 10, 80, 80));
+
+            canvas.pen.lineWidth = 1;
+            canvas.pen.color = SColor.Blue;
+            canvas.brush.color = SColor.Transparent;
+            for (let i = 1; i < 100; i += 10) {
+                canvas.drawRect(310 + i, i, 80, 80);
+            }
+
+            canvas.pen.lineWidth = 2;
+            canvas.pen.color = SColor.Blue;
+            canvas.brush.color = SColor.Red;
+            let k = new Date().getTime()/100%10;
+            for (let i = 1; i < k*10; i += 10) {
+                canvas.drawRect(510 + i, i, 80, 80);
+            }
+
+            this.update();
+        }
+    }
+
+    export default {
+        mounted() {
+            new TestView();
+        }
+    }
+</script>

+ 53 - 0
docs/.vuepress/components/example/web/graph/MapDemo.vue

@@ -0,0 +1,53 @@
+<template>
+    <div>
+        <el-button @click="showMap(1)">查看地图1</el-button>
+        <el-button @click="showMap(2)">查看地图2</el-button>
+        <canvas id="mapDemo" width="740" height="400" tabindex="0"></canvas>
+    </div>
+</template>
+
+<script>
+    import { SGraphScene, SGraphView } from "@saga-web/graph/lib";
+    import { SFloorParser } from "@saga-web/big/lib";
+    export default {
+        name: "mapDemo",
+        data(){
+            return{
+                view:null,
+                scene:null,
+                map1: require('../../../../public/assets/map/1.json'),
+                map2: require('../../../../public/assets/map/2.json'),
+            }
+        },
+        mounted(){
+            this.view = new SGraphView("mapDemo");
+            this.init()
+        },
+        methods:{
+            init(){
+                this.showMap(1);
+            },
+            showMap(id){
+                this.scene = new SGraphScene();
+                this.view.scene = this.scene;
+                let parser = new SFloorParser();
+                parser.parseData(this[`map${id}`].EntityList[0].Elements);
+                parser.spaceList.forEach(t => this.scene.addItem(t));
+                parser.wallList.forEach(t => this.scene.addItem(t));
+                parser.virtualWallList.forEach(t => this.scene.addItem(t));
+                parser.doorList.forEach(t => this.scene.addItem(t));
+                parser.columnList.forEach(t => this.scene.addItem(t));
+                parser.casementList.forEach(t => this.scene.addItem(t));
+                this.view.fitSceneToView();
+            }
+        }
+    }
+</script>
+
+
+<style scoped>
+    canvas{
+        border: 1px solid #eeeeee;
+        outline: none;
+    }
+</style>

+ 184 - 0
docs/.vuepress/components/example/web/graph/scene/Align.vue

@@ -0,0 +1,184 @@
+<template>
+    <div>
+        <el-button @click="addCircle">Circle</el-button>
+        <el-button @click="addRect">Rect</el-button>
+        <el-select placeholder="请选择" @change="changeAlign" v-model="value">
+            <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
+        </el-select>
+        <canvas id="align" width="740" height="400" tabindex="0"></canvas>
+    </div>
+</template>
+
+<script>
+    import {SGraphItem, SGraphScene, SGraphView, SGraphLayoutType} from "@saga-web/graph/lib";
+    import { SColor, SPainter, SRect } from "@saga-web/draw/lib";
+
+    class RectItem extends SGraphItem {
+        width = 200;
+        height = 100;
+        text = '';
+        constructor(parent) {
+            super(parent);
+            this.moveable = true;
+            this.selectable = true;
+            this.text = new Date().getMilliseconds().toString()
+        }
+
+        boundingRect() {
+            return new SRect(0, 0, this.width, this.height);
+        }
+
+        onDraw(canvas) {
+            canvas.pen.color = SColor.Transparent;
+            canvas.pen.lineWidth = canvas.toPx(1);
+            canvas.brush.color = this.selected ? SColor.Red : new SColor("#00fcee");
+            canvas.drawRect(0, 0, this.width, this.height);
+
+            canvas.pen.lineDash = [10,10];
+            canvas.pen.color = SColor.Yellow;
+            canvas.brush.color = SColor.Transparent;
+            canvas.drawRect(this.boundingRect());
+
+            canvas.brush.color = SColor.Black;
+            canvas.drawText(`${this.x},${this.y}`,0,0);
+
+        }
+    }
+
+    class CircleItem extends SGraphItem {
+        r = 75;
+        text = '';
+
+        constructor(parent) {
+            super(parent);
+            this.moveable = true;
+            this.selectable = true;
+            this.text = new Date().getMilliseconds().toString()
+        }
+
+        boundingRect() {
+            return new SRect(0, 0, this.r * 2, this.r * 2);
+        }
+
+        onDraw(canvas) {
+            canvas.pen.color = SColor.Transparent;
+            canvas.pen.lineWidth = canvas.toPx(1);
+            canvas.brush.color = this.selected ? SColor.Red : new SColor("#00fcee");
+            canvas.drawCircle(this.r, this.r, this.r);
+
+            canvas.pen.color = SColor.Yellow;
+            canvas.brush.color = SColor.Transparent;
+            canvas.pen.lineDash = [10,10];
+            canvas.drawRect(this.boundingRect());
+
+            canvas.brush.color = SColor.Black;
+            canvas.drawText(`${this.x},${this.y}`,0,0);
+        }
+    }
+
+    class SScene extends SGraphScene {
+        /** 定义命令 */
+        cmd = 0;
+
+        constructor() {
+            super();
+        }
+
+
+        onMouseUp(event) {
+            switch(this.cmd) {
+                case 1:
+                    this.addCircle(event.x, event.y);
+                    break;
+                case 2:
+                    this.addRect(event.x, event.y);
+                    break;
+                default:
+                    super.onMouseUp(event);
+            }
+            this.cmd = 0;
+            return false
+        }
+
+        addCircle(x, y) {
+            let item = new CircleItem(null);
+            item.moveTo(x - 50, y - 50);
+            this.addItem(item);
+        }
+
+        addRect(x, y) {
+            let item = new RectItem(null);
+            item.moveTo(x - 50, y - 50);
+            this.addItem(item);
+        }
+    }
+
+    class TestView extends SGraphView {
+        constructor() {
+            super("align");
+        }
+    }
+
+    export default {
+        data() {
+            return {
+                scene: new SScene(),
+                value: -1,
+                options:[
+                    {
+                        value:SGraphLayoutType.Left,
+                        label:'左对齐'
+                    },
+                    {
+                        value:SGraphLayoutType.Right,
+                        label:'右对齐'
+                    },
+                    {
+                        value:SGraphLayoutType.Top,
+                        label:'顶对齐'
+                    },
+                    {
+                        value:SGraphLayoutType.Bottom,
+                        label:'底对齐'
+                    },
+                    {
+                        value:SGraphLayoutType.Center,
+                        label:'水平居中对齐'
+                    },
+                    {
+                        value:SGraphLayoutType.Middle,
+                        label:'垂直居中对齐'
+                    },
+                    {
+                        value:SGraphLayoutType.Vertical,
+                        label:'垂直分布'
+                    },
+                    {
+                        value:SGraphLayoutType.Horizontal,
+                        label:'水平分布'
+                    }
+                ]
+            }
+        },
+        mounted() {
+            let view = new TestView();
+            view.scene = this.scene;//new SScene(); //this.data.scene;
+        },
+        methods: {
+            addCircle() {
+                this.scene.cmd = 1;
+            },
+            addRect() {
+                this.scene.cmd = 2;
+            },
+            changeAlign(v){
+                this.scene.selectContainer.layout(v)
+                // console.log(this.scene.selectContainer.layout(SGraphLayoutType.Left))
+            }
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 193 - 0
docs/.vuepress/components/example/web/graph/scene/ClockItem.vue

@@ -0,0 +1,193 @@
+<template>
+    <canvas id="clockItem1" width="400" height="400" />
+</template>
+
+<script lang="ts">
+    import { SColor, SPainter, SRect } from "@saga-web/draw";
+    import { SGraphItem, SGraphScene, SGraphView } from "@saga-web/graph";
+
+    class ClockItem extends SGraphItem {
+        /** 宽度 */
+        width = 100;
+        /** 高度 */
+        height = 100;
+
+        /** 半径 */
+        get radius(): number {
+            return Math.min(this.width, this.height) / 2.0;
+        }
+
+        /**
+         * 构造函数
+         *
+         * @param   parent      指向父Item
+         * @param   width       宽度
+         * @param   height      高度
+         */
+        constructor(parent: SGraphItem | null, width: number, height: number) {
+            super(parent);
+            this.width = width;
+            this.height = height;
+        }
+
+        /**
+         * 对象边界区域
+         *
+         * @return  边界区域
+         */
+        boundingRect(): SRect {
+            return new SRect(0, 0, this.width, this.height);
+        }
+
+        /**
+         * Item绘制操作
+         *
+         * @param   canvas      画布
+         */
+        onDraw(canvas: SPainter): void {
+            canvas.translate(this.width / 2, this.height / 2);
+            const t = new Date();
+            this.drawScale(canvas);
+            this.drawHour(canvas, t.getHours(), t.getMinutes(), t.getSeconds());
+            this.drawMinute(canvas, t.getMinutes(), t.getSeconds());
+            this.drawSecond(canvas, t.getSeconds() + t.getMilliseconds() / 1000.0);
+
+            this.update();
+        }
+
+        /**
+         * 绘制表刻度
+         *
+         * @param   canvas      画布
+         */
+        private drawScale(canvas: SPainter): void {
+            const scaleLength = Math.max(this.radius / 10.0, 2.0);
+            const scaleLength1 = scaleLength * 1.2;
+            const strokeWidth = Math.max(this.radius / 100.0, 2.0);
+            const strokeWidth1 = strokeWidth * 2.0;
+
+            canvas.save();
+            canvas.pen.color = SColor.Blue;
+
+            for (let i = 1; i <= 12; i++) {
+                // 12小时刻度
+                canvas.pen.lineWidth = strokeWidth1;
+                canvas.drawLine(
+                    0,
+                    -this.radius,
+                    0,
+                    -this.radius + scaleLength1
+                );
+
+                if (this.radius >= 40) {
+                    // 如果半度大于40显示分钟刻度
+                    canvas.rotate((6 * Math.PI) / 180);
+                    for (let j = 1; j <= 4; j++) {
+                        // 分钟刻度
+                        canvas.pen.lineWidth = strokeWidth;
+                        canvas.drawLine(
+                            0,
+                            -this.radius,
+                            0,
+                            -this.radius + scaleLength
+                        );
+                        canvas.rotate((6 * Math.PI) / 180);
+                    }
+                } else {
+                    canvas.rotate((30 * Math.PI) / 180);
+                }
+            }
+
+            canvas.restore();
+        }
+
+        /**
+         * 绘制时针
+         *
+         * @param   canvas      画布
+         * @param   hour        时
+         * @param   minute      分
+         * @param   second      秒
+         */
+        private drawHour(
+            canvas: SPainter,
+            hour: number,
+            minute: number,
+            second: number
+        ): void {
+            canvas.save();
+            canvas.pen.color = SColor.Black;
+            canvas.rotate(
+                ((hour * 30.0 + (minute * 30.0) / 60 + (second * 30.0) / 3600) *
+                    Math.PI) /
+                180
+            );
+            canvas.drawLine(
+                0,
+                this.radius / 10.0,
+                0,
+                -this.radius / 2.0
+            );
+            canvas.restore();
+        }
+
+        /**
+         * 绘制秒针
+         *
+         * @param   canvas      画布
+         * @param   minute      分
+         * @param   second      秒
+         */
+        private drawMinute(canvas: SPainter, minute: number, second: number): void {
+            canvas.save();
+            canvas.pen.color = SColor.Black;
+            canvas.rotate(((minute * 6 + (second * 6) / 60.0) * Math.PI) / 180);
+            canvas.drawLine(
+                0,
+                this.radius / 10.0,
+                0,
+                (-this.radius * 2.0) / 3.0
+            );
+            canvas.restore();
+        }
+
+        /**
+         * 绘制秒针
+         *
+         * @param   canvas      画布
+         * @param   second      秒
+         */
+        private drawSecond(canvas: SPainter, second: number): void {
+            canvas.save();
+            canvas.pen.color = SColor.Red;
+            canvas.rotate((second * 6 * Math.PI) / 180);
+            canvas.drawLine(
+                0,
+                this.radius / 5.0,
+                0,
+                -this.radius + this.radius / 10.0
+            );
+            canvas.restore();
+        }
+    }
+
+    class TestView extends SGraphView {
+        clock1 = new ClockItem(null, 300, 300);
+
+        constructor() {
+            super("clockItem1");
+            this.scene = new SGraphScene();
+            this.scene.addItem(this.clock1);
+        }
+    }
+
+    export default {
+        mounted(): void {
+            new TestView();
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 64 - 0
docs/.vuepress/components/example/web/graph/scene/ImageItem.vue

@@ -0,0 +1,64 @@
+<template>
+    <div>
+        <el-row>
+            <el-button @click="Full">铺满</el-button>
+            <el-button @click="Equivalency">等比缩放</el-button>
+            <el-button @click="AutoFit">自适应</el-button>
+        </el-row>
+        <canvas id="ImageItem1" width="740" height="400"/>
+    </div>
+</template>
+
+<script lang="ts">
+    import { SGraphScene, SGraphView, SImageShowType, SImageItem } from "@saga-web/graph";
+
+    class SScene extends SGraphScene {
+        imageItem: SImageItem = new SImageItem(null);
+        constructor() {
+            super();
+            this.imageItem.moveable = true;
+            this.imageItem.width = 400;
+            this.imageItem.height = 300;
+            this.imageItem.url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591685374103&di=cd5a56b2e3f5e12d7145b967afbcfb7a&imgtype=0&src=http%3A%2F%2Fcdn.duitang.com%2Fuploads%2Fitem%2F201408%2F05%2F20140805191039_cuTPn.jpeg";
+            this.addItem(this.imageItem);
+        }
+    }
+
+    class ImageView extends SGraphView {
+        constructor() {
+            super("ImageItem1");
+        }
+    }
+
+    export default {
+        mounted(): void {
+            let view = new ImageView();
+            // @ts-ignore
+            view.scene = this.scene;
+            view.fitSceneToView();
+        },
+        data() {
+            return {
+                scene: new SScene(),
+            }
+        },
+        methods: {
+            Full() {
+                // @ts-ignore
+                this.scene.imageItem.showType = SImageShowType.Full;
+            },
+            Equivalency() {
+                // @ts-ignore
+                this.scene.imageItem.showType = SImageShowType.Equivalency;
+            },
+            AutoFit() {
+                // @ts-ignore
+                this.scene.imageItem.showType = SImageShowType.AutoFit;
+            },
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 56 - 0
docs/.vuepress/components/example/web/graph/scene/PolylineItem.vue

@@ -0,0 +1,56 @@
+<template>
+    <div>
+        <el-button size="mini" @click="init">清空画布,并初始化折线item</el-button>
+        <el-button size="mini" @click="undo">undo</el-button>
+        <el-button size="mini" @click="redo">redo</el-button>
+        <canvas id="editPolyline" width="740" height="400" tabindex="0"></canvas>
+    </div>
+</template>
+
+<script>
+    import {SGraphScene, SGraphView} from "@saga-web/graph/lib";
+    import {SPolylineItem,SItemStatus} from "@saga-web/big/lib";
+    export default {
+        name: "editPolyline",
+        data() {
+            return {
+                scene: null,
+                view: null,
+                item: null,
+            };
+        },
+        mounted() {
+            this.view = new SGraphView("editPolyline");
+            this.scene = new SGraphScene();
+            this.view.scene = this.scene;
+            this.init()
+        },
+        methods:{
+            init(){
+                this.scene.root.children = [];
+                this.item = new SPolylineItem(null,[]);
+                this.item.status = SItemStatus.Create;
+                this.scene.addItem(this.item);
+                this.scene.grabItem = this.item;
+                this.view.update();
+            },
+            undo(){
+                if(this.scene.grabItem) {
+                    this.scene.grabItem.undo()
+                }
+            },
+            redo(){
+                if(this.scene.grabItem) {
+                    this.scene.grabItem.redo()
+                }
+            }
+        }
+    }
+</script>
+
+<style scoped>
+canvas{
+    border: 1px solid #eeeeee;
+    outline: none;
+}
+</style>

+ 47 - 0
docs/.vuepress/components/example/web/graph/scene/SEditLine.vue

@@ -0,0 +1,47 @@
+<template>
+    <div>
+        <el-button @click="create">创建</el-button>
+        <el-button @click="undo">undo</el-button>
+        <el-button @click="redo">redo</el-button>
+        <canvas id="editLine" width="740" height="400"  tabindex="0" />
+    </div>
+</template>
+<script>
+    import { SGraphScene, SGraphView } from "@saga-web/graph/";
+    import { SItemStatus } from "@saga-web/big/lib/enums/SItemStatus";
+    import { SLineItem } from "@saga-web/big/lib";
+
+    export default {
+        data() {
+            return {
+                scene: null,
+                view: null
+            };
+        },
+        mounted() {
+            this.view = new SGraphView("editLine");
+            this.scene = new SGraphScene();
+            this.view.scene = this.scene;
+        },
+        methods: {
+            create() {
+                this.scene.root.children = [];
+                const lineItem = new SLineItem(null, [] );
+                lineItem.status = SItemStatus.Create;
+                this.scene.addItem(lineItem);
+                this.scene.grabItem = lineItem;
+                this.view.fitSceneToView();
+            },
+            undo(){
+                if(this.scene.grabItem) {
+                    this.scene.grabItem.undo();
+                }
+            },
+            redo(){
+                if(this.scene.grabItem) {
+                    this.scene.grabItem.redo();
+                }
+            }
+        }
+    };
+</script>

+ 568 - 0
docs/.vuepress/components/example/web/graph/scene/SEditPolygon.vue

@@ -0,0 +1,568 @@
+<template>
+  <div>
+    <el-button @click="show">展示</el-button>
+    <el-button @click="create">创建</el-button>
+    <el-button @click="edit">编辑</el-button>
+    <canvas id="editPolygon" width="800" height="400"  tabindex="0" />
+  </div>
+</template>
+<script lang='ts'>
+import { SGraphItem, SGraphView, SGraphScene } from "@saga-web/graph/";
+import { SMouseEvent } from "@saga-web/base/";
+import {
+  SColor,
+  SLineCapStyle,
+  SPainter,
+  SPath2D,
+  SPoint,
+  SPolygonUtil,
+  SRect,
+  SLine
+} from "@saga-web/draw";
+import { SRelationState } from "@saga-web/big/lib/enums/SRelationState";
+import { SMathUtil } from "@saga-web/big/lib/utils/SMathUtil";
+
+export default {
+  data() {
+    return {
+      scene: null,
+      view: null
+    };
+  },
+  mounted(): void {
+    this.view = new SGraphView("editPolygon");
+  },
+  methods: {
+    show() {
+      const scene = new SGraphScene();
+      this.view.scene = scene;
+      const spolygonItem = new SPolygonItem(null);
+      spolygonItem.setStatus = SRelationState.Normal;
+      const PointList: SPoint[] = [
+        new SPoint(0, 0),
+        new SPoint(50, 0),
+        new SPoint(50, 50),
+        new SPoint(0, 50)
+      ];
+      spolygonItem.setPointList = PointList;
+      scene.addItem(spolygonItem);
+      this.view.fitSceneToView();
+    },
+    create() {
+      const scene = new SGraphScene();
+      this.view.scene = scene;
+      const spolygonItem = new SPolygonItem(null);
+      spolygonItem.setStatus = SRelationState.Create;
+      scene.addItem(spolygonItem);
+      scene.grabItem = spolygonItem;
+      this.view.fitSceneToView();
+    },
+    edit() {
+      const scene = new SGraphScene();
+      this.view.scene = scene;
+      const spolygonItem = new SPolygonItem(null);
+      spolygonItem.setStatus = SRelationState.Edit;
+      const PointList: SPoint[] = [
+        new SPoint(0, 0),
+        new SPoint(50, 0),
+        new SPoint(50, 60),
+        new SPoint(0, 50)
+      ];
+      spolygonItem.setPointList = PointList;
+      scene.addItem(spolygonItem);
+      scene.grabItem = spolygonItem;
+      this.view.fitSceneToView();
+    }
+  }
+};
+
+/**
+ * @author hanyaolong
+ */
+
+class SPolygonItem extends SGraphItem {
+  /** X坐标最小值  */
+  private minX = Number.MAX_SAFE_INTEGER;
+  /** X坐标最大值  */
+  private maxX = Number.MIN_SAFE_INTEGER;
+  /** Y坐标最小值  */
+  private minY = Number.MAX_SAFE_INTEGER;
+  /** Y坐标最大值  */
+  private maxY = Number.MIN_SAFE_INTEGER;
+  /** 轮廓线坐标  */
+  private pointList: SPoint[] = [];
+  // 获取当前状态
+  get getPointList(): SPoint[] {
+    return this.pointList;
+  }
+  // 编辑当前状态
+  set setPointList(arr: SPoint[]) {
+    this.pointList = arr;
+    if (arr) {
+      this._xyzListToSPointList(arr);
+    }
+    this.update();
+  }
+  /** 是否闭合    */
+  closeFlag: boolean = false;
+  // 当前状态 1:show 2 创建中,3 编辑中
+  _status: number = SRelationState.Normal;
+  // 获取当前状态
+  get getStatus(): number {
+    return this._status;
+  }
+  // 编辑当前状态
+  set setStatus(value: number) {
+    this._status = value;
+    this.update();
+  }
+  /** 边框颜色 */
+  borderColor: SColor = new SColor("#0091FF");
+  /** 填充颜色 */
+  brushColor: SColor = new SColor("#1EE887");
+  /** border宽 只可输入像素宽*/
+  borderLine: number = 4;
+  /** 鼠标移动点  */
+  private lastPoint = new SPoint();
+  /** 当前鼠标获取顶点对应索引 */
+  private curIndex: number = -1;
+  /** 灵敏像素 */
+  private len: number = 15;
+  /** 场景像素  */
+  private scenceLen: number = 15;
+  /** 场景像素  */
+  private isAlt: boolean = false;
+
+  /**
+   * 构造函数
+   *
+   * @param parent    指向父对象
+   */
+
+  constructor(parent: SGraphItem | null) {
+    super(parent);
+  }
+
+  ///////////////以下为对pointList 数组的操作方法
+
+  /**
+   * 储存新的多边形顶点
+   * @param x   点位得x坐标
+   * @param y   点位得y坐标
+   * @param i   储存所在索引
+   * @return SPoint
+   */
+
+  insertPoint(x: number, y: number, i: number | null = null): SPoint {
+    const point = new SPoint(x, y);
+    if (i == null) {
+      this.pointList.push(point);
+    } else {
+      this.pointList.splice(i, 0, point);
+    }
+    this.update();
+    return point;
+  }
+
+  /**
+   * 删除点位
+   * @param i   删除点所在的索引
+   * @return SPoint
+   */
+
+  deletePoint(i: number | null = null): SPoint | null | undefined {
+    let point = null;
+    if (i != null) {
+      if (this.pointList.length - 1 >= i) {
+        point = this.pointList[i];
+        this.pointList.splice(i, 1);
+      } else {
+        point = null;
+      }
+    } else {
+      point = this.pointList.pop();
+    }
+    this.update();
+    return point;
+  }
+
+  /**
+   * 多边形顶点的移动位置
+   * @param x   点位得x坐标
+   * @param y   点位得y坐标
+   * @param i   点位得i坐标
+   * @return SPoint
+   */
+
+  movePoint(x: number, y: number, i: number): SPoint | null | undefined {
+    let point = null;
+    if (this.pointList.length) {
+      this.pointList[i].x = x;
+      this.pointList[i].y = y;
+    }
+    point = this.pointList[i];
+    return point;
+  }
+
+  /**
+   * 打印出多边形数组
+   * @return SPoint[]
+   */
+
+  PrintPointList(): SPoint[] {
+    return this.pointList;
+  }
+
+  ///////////////////////////
+
+  /**
+   * xyz数据转换为SPoint格式函数;获取外接矩阵参数
+   *
+   * @param arr    外层传入的数据PointList
+   */
+
+  protected _xyzListToSPointList(arr: SPoint[]): void {
+    if (arr.length) {
+      this.pointList = arr.map(it => {
+        let x = it.x,
+          y = it.y;
+        if (x < this.minX) {
+          this.minX = x;
+        }
+        if (y < this.minY) {
+          this.minY = y;
+        }
+        if (x > this.maxX) {
+          this.maxX = x;
+        }
+        if (y > this.maxY) {
+          this.maxY = y;
+        }
+        return new SPoint(x, y);
+      });
+    } else {
+      this.pointList = [];
+    }
+  }
+
+  ////////////以下为三种状态下的绘制多边形方法
+
+  /**
+   * 展示状态 --绘制多边形数组
+   * @param painter    绘制类
+   * @param pointList    绘制多边形数组
+   *
+   */
+
+  protected drawShowPolygon(painter: SPainter, pointList: SPoint[]): void {
+    painter.pen.lineCapStyle = SLineCapStyle.Square;
+    painter.pen.color = this.borderColor;
+    painter.pen.lineWidth = painter.toPx(this.borderLine);
+    painter.brush.color = this.brushColor;
+    painter.drawPolygon([...pointList]);
+  }
+
+  /**
+   *
+   * 创建状态 --绘制多边形数组
+   * @param painter    绘制类
+   * @param pointList    绘制多边形数组
+   *
+   */
+
+  protected drawCreatePolygon(painter: SPainter, pointList: SPoint[]): void {
+    painter.pen.lineCapStyle = SLineCapStyle.Square;
+    painter.pen.color = this.borderColor;
+    painter.pen.lineWidth = painter.toPx(4);
+    painter.drawPolyline(pointList);
+    if (this.lastPoint) {
+      painter.drawLine(pointList[pointList.length - 1], this.lastPoint);
+    }
+    painter.pen.color = SColor.Transparent;
+    painter.brush.color = this.brushColor;
+    painter.pen.lineWidth = painter.toPx(4);
+    painter.drawPolygon([...pointList, this.lastPoint]);
+  }
+
+  /**
+   *
+   * 编辑状态 --绘制多边形数组
+   *
+   * @param painter    绘制类
+   * @param pointList    绘制多边形数组
+   *
+   * */
+
+  protected drawEditPolygon(painter: SPainter, pointList: SPoint[]): void {
+    // 展示多边形
+    this.drawShowPolygon(painter, pointList);
+    // 绘制顶点块
+    pointList.forEach((item, index) => {
+      painter.drawCircle(item.x, item.y, painter.toPx(8));
+    });
+  }
+
+  ////////////////////////////////
+
+  /**
+   * 编辑状态操作多边形数组
+   *
+   * @param event    鼠标事件
+   *
+   *
+   * */
+
+  protected editPolygonPoint(event: SMouseEvent): void {
+    //  判断是否为删除状态 isAlt = true为删除状态
+    if (this.isAlt) {
+      // 1 判断是否点击在多边形顶点
+      let lenIndex = -1; // 当前点击到的点位索引;
+      let curenLen = this.scenceLen; // 当前的灵敏度
+      this.pointList.forEach((item, index) => {
+        let dis = SMathUtil.pointDistance(event.x, event.y, item.x, item.y);
+        if (dis < curenLen) {
+          curenLen = dis;
+          lenIndex = index;
+        }
+      });
+      // 若点击到,对该索引对应的点做删除
+      if (lenIndex != -1) {
+        if (this.pointList.length <= 3) {
+          return;
+        }
+        this.deletePoint(lenIndex);
+      }
+    } else {
+      // 1 判断是否点击在多边形顶点
+      this.curIndex = -1;
+      let lenIndex = -1; // 当前点击到的点位索引;
+      let curenLen = this.scenceLen; // 当前的灵敏度
+      this.pointList.forEach((item, index) => {
+        let dis = SMathUtil.pointDistance(event.x, event.y, item.x, item.y);
+        if (dis < curenLen) {
+          curenLen = dis;
+          lenIndex = index;
+        }
+      });
+      this.curIndex = lenIndex;
+
+      // 2判断是否点击在多边形得边上
+      if (-1 == lenIndex) {
+        let len = SMathUtil.pointToLine(
+            new SPoint(event.x, event.y),
+            new SLine(this.pointList[0], this.pointList[1])
+          ),
+          index = 0;
+        if (this.pointList.length > 2) {
+          for (let i = 1; i < this.pointList.length; i++) {
+            let dis = SMathUtil.pointToLine(
+              new SPoint(event.x, event.y),
+              new SLine(this.pointList[i], this.pointList[i + 1])
+            );
+            if (i + 1 == this.pointList.length) {
+              dis = SMathUtil.pointToLine(
+                new SPoint(event.x, event.y),
+                new SLine(this.pointList[i], this.pointList[0])
+              );
+            }
+            if (dis.MinDis < len.MinDis) {
+              len = dis;
+              index = i;
+            }
+          }
+        }
+        // 如果再线上则为新增点
+        if (len.Point) {
+          if (len.MinDis <= this.scenceLen) {
+            this.pointList.splice(index + 1, 0, len.Point);
+          }
+        }
+      }
+      // 刷新视图
+      this.update();
+    }
+  }
+
+  ///////////////////////////////以下为鼠标事件
+
+  /**
+   * 鼠标双击事件
+   *
+   * @param	event         事件参数
+   * @return	boolean
+   */
+
+  onDoubleClick(event: SMouseEvent): boolean {
+    // 如果位show状态 双击改对象则需改为编辑状态
+    if (SRelationState.Normal == this._status) {
+      this._status = SRelationState.Edit;
+    } else if (SRelationState.Edit == this._status) {
+      this._status = SRelationState.Normal;
+    }
+    this.update();
+    return true;
+  } // Function onDoubleClick()
+
+  /**
+   * 键盘事件
+   *
+   * @param	event         事件参数
+   * @return	boolean
+   */
+
+  onKeyDown(event: KeyboardEvent): boolean {
+    console.log(event);
+    if (this._status == SRelationState.Normal) {
+      return false;
+    } else if (this._status == SRelationState.Create) {
+      if (event.code == "Enter") {
+        this._status = SRelationState.Normal;
+      }
+    } else if (this._status == SRelationState.Edit) {
+      if (event.key == "Alt") {
+        this.isAlt = true;
+      }
+    } else {
+    }
+    this.update();
+    return true;
+  } // Function onKeyDown()
+
+  /**
+   * 键盘键抬起事件
+   *
+   * @param	event         事件参数
+   * @return	boolean
+   */
+  onKeyUp(event: KeyboardEvent): boolean {
+    if (this._status == SRelationState.Edit) {
+      if (event.key == "Alt") {
+        this.isAlt = false;
+      }
+    }
+    this.update();
+    return true;
+  } // Function onKeyUp()
+
+  /**
+   * 鼠标按下事件
+   *
+   * @param	event         事件参数
+   * @return	boolean
+   */
+  onMouseDown(event: SMouseEvent): boolean {
+    console.log("aaaaaa");
+    // 如果状态为编辑状态则添加点
+    if (this._status == SRelationState.Create) {
+      // 新增顶点
+      this.insertPoint(event.x, event.y);
+    } else if (this._status == SRelationState.Edit) {
+      // 对多边形数组做编辑操作
+      this.editPolygonPoint(event);
+    }
+    return true;
+  } // Function onMouseDown()
+
+  /**
+   * 鼠标移入事件
+   *
+   * @param	event         事件参数
+   * @return	boolean
+   */
+  onMouseEnter(event: SMouseEvent): boolean {
+    return true;
+  } // Function onMouseEnter()
+
+  /**
+   * 鼠标移出事件
+   *
+   * @param	event         事件参数
+   * @return	boolean
+   */
+
+  onMouseLeave(event: SMouseEvent): boolean {
+    return true;
+  } // Function onMouseLeave()
+
+  /**
+   * 鼠标移动事件
+   *
+   * @param	event         事件参数
+   * @return	boolean
+   */
+
+  onMouseMove(event: SMouseEvent): boolean {
+    if (this._status == SRelationState.Create) {
+      this.lastPoint.x = event.x;
+      this.lastPoint.y = event.y;
+    } else if (this._status == SRelationState.Edit) {
+      if (-1 != this.curIndex) {
+        this.pointList[this.curIndex].x = event.x;
+        this.pointList[this.curIndex].y = event.y;
+      }
+    }
+    this.update();
+    return true;
+  } // Function onMouseMove()
+
+  /**
+   * 鼠标抬起事件
+   *
+   * @param	event         事件参数
+   * @return	boolean
+   */
+
+  onMouseUp(event: SMouseEvent): boolean {
+    if (this._status == SRelationState.Edit) {
+      this.curIndex = -1;
+    }
+    return true;
+  } // Function onMouseUp()
+
+  /**
+   * 适配事件
+   *
+   * @param	event         事件参数
+   * @return	boolean
+   */
+
+  onResize(event: SMouseEvent): boolean {
+    return true;
+  } // Function onResize()
+
+  /**
+   * Item对象边界区域
+   *
+   * @return SRect
+   */
+  boundingRect(): SRect {
+    return new SRect(
+      this.minX,
+      this.minY,
+      this.maxX - this.minX,
+      this.maxY - this.minY
+    );
+  } // Function boundingRect()
+
+  /**
+   * Item绘制操作
+   *
+   * @param   painter       painter对象
+   */
+
+  onDraw(painter: SPainter): void {
+    this.scenceLen = painter.toPx(this.len);
+    // 当状态为展示状态
+    if (this._status == SRelationState.Normal) {
+      // 闭合多边形
+      this.drawShowPolygon(painter, this.pointList);
+    } else if (this._status == SRelationState.Create) {
+      // 创建状态
+      this.drawCreatePolygon(painter, this.pointList);
+    } else {
+      // 编辑状态
+      this.drawEditPolygon(painter, this.pointList);
+    }
+  } // Function onDraw()
+}
+</script>

+ 234 - 0
docs/.vuepress/components/example/web/graph/scene/SImgTextItem.vue

@@ -0,0 +1,234 @@
+<template>
+    <div>
+        <el-button @click="changemaodian">切换锚点显示状态</el-button>
+        <el-button @click="changetext">切换文本显示状态</el-button>
+        <canvas id="editPolygon" width="740" height="400" tabindex="0"></canvas>
+    </div>
+</template>
+
+<script lang="ts">
+    import {SGraphItem, SGraphScene, SGraphView, SObjectItem, STextItem, SImageItem, SAnchorItem} from "@saga-web/graph/lib";
+    import { SItemStatus }from "@saga-web/big/lib";
+    import {SColor, SPainter, SRect, SSize} from "@saga-web/draw/lib";
+    import {SMouseEvent} from "@saga-web/base/lib";
+
+    /**
+     * 图例item  icon
+     *
+     * */
+    class SImgTextItem extends SObjectItem {
+
+        /** item状态  */
+        _status: SItemStatus = SItemStatus.Normal;
+        get status(): SItemStatus {
+            return this._status;
+        }
+        set status(v: SItemStatus) {
+            this._status = v;
+            this.update();
+        }
+
+        /** 是否显示文字  */
+        _showText: boolean = true;
+        get showText(): boolean {
+            return this._showText;
+        }
+        set showText(v: boolean) {
+            if (v === this._showText) {
+                return
+            }
+            this._showText = v;
+            this.textItem.visible = v;
+        }
+
+        /** 是否显示锚点  */
+        _showAnchor: boolean = false;
+        get showAnchor():boolean{
+            return this._showAnchor;
+        }
+        set showAnchor(v:boolean){
+            this._showAnchor = v;
+            this.anchorList.forEach(t => {
+                t.visible = v;
+            })
+        }
+
+        /** X轴坐标 */
+        get x(): number {
+            return this.pos.x;
+        } // Get x
+        set x(v: number) {
+            this.pos.x = v;
+            this.$emit("changePos");
+            this.update();
+        } // Set x
+        /** Y轴坐标 */
+        get y(): number {
+            return this.pos.y;
+        } // Get y
+        set y(v: number) {
+            this.pos.y = v;
+            this.$emit("changePos");
+            this.update();
+        } // Set y
+
+        /** img Item    */
+        img: SImageItem = new SImageItem(this);
+
+        /** text item   */
+        textItem: STextItem = new STextItem(this);
+
+        /**
+         * 构造体
+         *
+         * */
+        constructor(parent: SGraphItem | null) {
+            super(parent);
+            this.img.url = `http://adm.sagacloud.cn:8080/doc/assets/img/logo.png`;
+            this.img.width = 32;
+            this.img.height = 32;
+            let anchorPoint = [{ x: 0, y: this.img.height / 2 }, { x: 0, y: -this.img.height / 2 }, { x: -this.img.width / 2, y: 0 }, { x: this.img.width / 2, y: 0 }];
+            this.anchorList = anchorPoint.map(t => {
+                let item = new SAnchorItem(this);
+                item.moveTo(t.x, t.y);
+                return item;
+            });
+            this.update();
+            this.textItem.text = "x2";
+            this.textItem.moveTo(18, -6);
+            this.moveable = true;
+            this.selectable = true;
+            this.textItem.enabled = false;
+            this.img.enabled = false;
+        }
+
+        onMouseEnter(event: SMouseEvent): boolean {
+            this.showAnchor = true;
+            return true;
+        }
+
+        onMouseLeave(event: SMouseEvent): boolean {
+            this.showAnchor = false;
+            return true;
+        }
+
+        onMouseMove(event: SMouseEvent): boolean {
+            return super.onMouseMove(event);
+        }
+
+        /**
+         * 鼠标按下事件
+         *
+         * */
+        onMouseDown(event: SMouseEvent): boolean {
+            console.log(this.textItem)
+            if (this.status == SItemStatus.Normal) {
+                return super.onMouseDown(event);
+            } else if (this.status == SItemStatus.Edit) {
+                return super.onMouseDown(event);
+            }
+            return true;
+        } // Function onMouseDown()
+
+        /**
+         * 宽高发发生变化
+         *
+         * @param   oldSize 改之前的大小
+         * @param   newSize 改之后大小
+         * */
+        onResize(oldSize: SSize, newSize: SSize) {
+            console.log(arguments);
+        } // Function onResize()
+
+        /**
+         * 鼠标双击事件
+         *
+         * @param   event   鼠标事件
+         * @return  是否处理事件
+         * */
+        onDoubleClick(event: SMouseEvent): boolean {
+            this.status = SItemStatus.Edit;
+            return true;
+        } // Function onDoubleClick()
+
+        /**
+         * 宽高发发生变化
+         *
+         * @return  SRect   所有子对象的并集
+         * */
+        boundingRect(): SRect {
+            let rect = this.img.boundingRect().adjusted(this.img.pos.x,this.img.pos.y,0,0);
+            if (this.showText) {
+                rect = rect.unioned(this.textItem.boundingRect().adjusted(this.textItem.pos.x,this.textItem.pos.y,0,0))
+            }
+            return rect;
+        } // Function boundingRect()
+
+        /**
+         * Item绘制操作
+         *
+         * @param   painter painter对象
+         */
+        onDraw(painter: SPainter): void {
+            painter.pen.lineWidth = painter.toPx(1);
+            painter.pen.color = new SColor("#00FF00");
+            painter.brush.color = SColor.Transparent;
+            if (this.showAnchor) {
+                painter.brush.color = SColor.Gray
+            }
+            painter.drawRect(this.boundingRect());
+        } // Function onDraw()
+    }
+
+
+    export default {
+        name: "ImgTextItem",
+        data() {
+            return {
+                scene: null,
+                view: null,
+                input:'',
+            };
+        },
+        mounted() {
+            console.log(22222222222222222)
+            // @ts-ignore
+            this.view = new SGraphView("editPolygon");
+            // @ts-ignore
+            this.scene = new SGraphScene();
+            // @ts-ignore
+            this.view.scene = this.scene;
+            // @ts-ignore
+            this.init()
+        },
+        methods:{
+            init(){
+                // @ts-ignore
+                this.item = new SImgTextItem(null);
+                // @ts-ignore
+                this.item.moveable = true;
+
+                // @ts-ignore
+                this.scene.addItem(this.item);
+                // this.view.fitSceneToView();
+            },
+            changemaodian(){
+                // @ts-ignore
+                this.item.showAnchor = !this.item.showAnchor;
+            },
+            changetext(){
+                // @ts-ignore
+                this.item.showText = !this.item.showText;
+            }
+        }
+    }
+</script>
+
+<style scoped>
+    canvas{
+        border:1px solid #ccc;
+    }
+    canvas:focus{
+        outline: none;
+    }
+</style>

+ 117 - 0
docs/.vuepress/components/example/web/graph/scene/TextItem.vue

@@ -0,0 +1,117 @@
+<template>
+	<div>
+		<el-row>
+			<el-input v-model="text" @change="handleChangeText" type="textarea" :autosize="{ minRows: 1, maxRows: 1}" placeholder="请输入内容" style="width:200px;"></el-input>
+			<el-input-number v-model="size" @change="handleChangeSize" :min="1" label="字号"></el-input-number>
+			<el-color-picker v-model="color" @change="handleChangeColor" style="top: 15px;"></el-color-picker>
+			<el-button @click="undo">Undo</el-button>
+			<el-button @click="redo">Redo</el-button>
+			<el-button @click="reset">Reset</el-button>
+		</el-row>
+		<canvas id="TextItem1" width="740" height="400" tabindex="0"/>
+	</div>
+</template>
+
+<script>
+	import { SUndoStack } from "@saga-web/base/lib";
+    import { SFont } from "@saga-web/draw/lib";
+	import { SGraphScene, SGraphView, SGraphPropertyCommand, SGraphMoveCommand, STextItem } from "@saga-web/graph/lib";
+
+	class SScene extends SGraphScene {
+		undoStack = new SUndoStack();
+		textItem = new STextItem(null);
+
+		constructor() {
+			super();
+			this.textItem.moveable = true;
+			this.textItem.x = 100;
+			this.textItem.y = 100;
+			this.addItem(this.textItem);
+			this.textItem.connect("onMove", this, this.onItemMove.bind(this));
+		}
+
+		updateText(str) {
+			if (this.textItem.text !== str) {
+				let old = this.textItem.text;
+				this.textItem.text = str;
+				this.undoStack.push(new SGraphPropertyCommand(this, this.textItem, "text", old, str));
+			}
+		}
+
+		updateColor(color) {
+			if (this.textItem.color !== color) {
+				let old = this.textItem.color;
+				this.textItem.color = color;
+				this.undoStack.push(new SGraphPropertyCommand(this, this.textItem, "color", old, color));
+			}
+		}
+
+		updateSize(size) {
+			if (this.textItem.font.size !== size) {
+				let old = new SFont(this.textItem.font);
+				let font = new SFont(this.textItem.font);
+				font.size = size;
+				this.textItem.font = font;
+				this.undoStack.push(new SGraphPropertyCommand(this, this.textItem, "font", old, font));
+			}
+		}
+
+		onItemMove(item, ...arg) {
+            this.undoStack.push(new SGraphMoveCommand(this, item, arg[0][0], arg[0][1]));
+        }
+
+	}
+
+	class TestView extends SGraphView {
+        constructor() {
+			super("TextItem1");
+        }
+	}
+
+    export default {
+		data() {
+			return {
+				scene: new SScene(),
+				text: '测试文本',
+				size: 20,
+				color: "#333333",
+			}
+		},
+        mounted() {
+			let view = new TestView();
+			this.scene.updateText(this.text);
+			this.scene.updateColor(this.color);
+			this.scene.updateSize(this.size);
+			view.scene = this.scene;
+		},
+		methods: {
+			handleChangeText(text) {
+				this.scene.updateText(text);
+			},
+			handleChangeColor(color) {
+				this.scene.updateColor(color);
+			},
+			handleChangeSize(size) {
+				this.scene.updateSize(size);
+			},
+			undo() {
+				this.scene.undoStack.undo();
+			},
+			redo() {
+				this.scene.undoStack.redo();
+			},
+			reset() {
+				this.text = "测试文本";
+				this.size = 20;
+				this.color = "#333333";
+				this.scene.updateText(this.text);
+				this.scene.updateColor(this.color);
+				this.scene.updateSize(this.size);
+			}
+		},
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 155 - 0
docs/.vuepress/components/example/web/graph/scene/Undo1.vue

@@ -0,0 +1,155 @@
+<template>
+    <div>
+        <el-button @click="addCircle">Circle</el-button>
+        <el-button @click="addRect">Rect</el-button>
+        <el-button @click="deleteItem">Delete</el-button>
+        <el-button @click="undo">Undo</el-button>
+        <el-button @click="redo">Redo</el-button>
+        <el-button @click="log">日志</el-button>
+        <canvas id="undoFrame" width="800" height="400" />
+    </div>
+</template>
+
+<script lang="ts">
+    import { SMouseEvent, SUndoStack } from "@saga-web/base";
+    import { SColor, SPainter, SRect } from "@saga-web/draw";
+    import { SGraphItem, SGraphScene, SGraphView, SGraphAddCommand, SGraphMoveCommand } from "@saga-web/graph";
+    import {SPoint} from "@saga-web/draw/lib";
+
+    class RectItem extends SGraphItem {
+        width = 100;
+        height = 100;
+        id = new Date().getTime()
+        constructor(parent: SGraphItem | null) {
+            super(parent);
+            this.moveable = true;
+            this.selectable = true;
+            this.selected = true;
+            this.isTransform = true;
+            this.rolate = 60;
+        }
+
+        boundingRect(): SRect {
+            return new SRect(0, 0, this.width, this.height);
+        }
+
+        onDraw(canvas: SPainter): void {
+            canvas.pen.color = SColor.Blue;
+            canvas.pen.lineWidth = 5;
+            canvas.brush.color = this.selected ? SColor.Red : SColor.Green;
+            canvas.drawRect(0, 0, this.width, this.height)
+        }
+    }
+
+    class CircleItem extends SGraphItem {
+        r = 50;
+        id = new Date().getTime()
+
+        constructor(parent: SGraphItem | null) {
+            super(parent);
+            this.moveable = true;
+            this.selectable = true;
+            this.isTransform = false;
+        }
+
+        boundingRect(): SRect {
+            return new SRect(0, 0, this.r * 2, this.r * 2);
+        }
+
+        onDraw(canvas: SPainter): void {
+            canvas.pen.color = SColor.Blue;
+            canvas.pen.lineWidth = 5;
+            canvas.brush.color = this.selected ? SColor.Red : SColor.Green;
+            canvas.drawCircle(this.r, this.r, this.r);
+        }
+    }
+
+    class SScene extends SGraphScene {
+        undoStack = new SUndoStack();
+        /** 定义命令 */
+        cmd = 0;
+
+        constructor() {
+            super();
+        }
+
+
+        onMouseUp(event: SMouseEvent): boolean {
+            switch(this.cmd) {
+                case 1:
+                    this.addCircle(event.x, event.y);
+                    break;
+                case 2:
+                    this.addRect(event.x, event.y);
+                    break;
+                default:
+                    super.onMouseUp(event);
+            }
+            this.cmd = 0;
+            return false
+        }
+
+        private addCircle(x: number, y: number): void {
+            let item = new CircleItem(null);
+            item.moveTo(x - 50, y - 50);
+            this.addItem(item);
+            this.undoStack.push(new SGraphAddCommand(this, item));
+            item.connect("onMove", this, this.onItemMove.bind(this));
+        }
+
+        private addRect(x: number, y: number): void {
+            let item = new RectItem(null);
+            item.moveTo(x - 50, y - 50);
+            this.addItem(item);
+            this.undoStack.push(new SGraphAddCommand(this, item));
+            item.connect("onMove", this, this.onItemMove.bind(this));
+        }
+
+        onItemMove(item: SGraphItem, ...arg: any): void {
+            this.undoStack.push(new SGraphMoveCommand(this, item, arg[0][0] as SPoint, arg[0][1] as SPoint));
+        }
+    }
+
+    class TestView extends SGraphView {
+        constructor() {
+            super("undoFrame");
+        }
+    }
+
+    export default {
+        data() {
+            return {
+                scene: new SScene(),
+            }
+        },
+        mounted(): void {
+            let view = new TestView();
+            view.scene = this.scene;//new SScene(); //this.data.scene;
+        },
+        methods: {
+            addCircle() {
+                this.scene.cmd = 1;
+            },
+            addRect() {
+                this.scene.cmd = 2;
+            },
+            deleteItem() {
+
+            },
+            undo() {
+                this.scene.undoStack.undo();
+            },
+            redo() {
+                this.scene.undoStack.redo();
+            },
+            log() {
+                let str = this.scene.undoStack.toLog();
+                console.log(str)
+            }
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 82 - 0
docs/.vuepress/components/example/web/graph/style/shadow.vue

@@ -0,0 +1,82 @@
+<template>
+    <div>
+        阴影扩散范围:<el-input-number @change="changeblur" v-model="blurl" size="mini" style="width: 100px"></el-input-number>
+        x轴偏移量:<el-input-number @change="changeX" v-model="X" size="mini" style="width: 100px"></el-input-number>
+        y轴偏移量:<el-input-number @change="changeY" v-model="Y" size="mini" style="width: 100px"></el-input-number>
+        <el-color-picker @change="changeColor" v-model="color" size="mini" style="vertical-align: middle"></el-color-picker>
+        <canvas id="shadow" width="740" height="400" tabindex="0"></canvas>
+    </div>
+</template>
+
+<script>
+    import { SCanvasView, SColor, SPainter } from "@saga-web/draw/lib";
+
+    class shadowView extends SCanvasView {
+
+        constructor(id) {
+            super(id);
+            this.shadowBlur = 10;
+            this.shadowColor = new SColor('#00000060');
+            this.shadowOffsetX = 10;
+            this.shadowOffsetY = 10;
+        }
+
+        onDraw(canvas) {
+            canvas.clearRect(0,0,740,400);
+
+            canvas.pen.lineWidth = 1;
+            canvas.pen.color = SColor.Black;
+            canvas.brush.color = SColor.White;
+            canvas.shadow.shadowBlur = this.shadowBlur;
+            canvas.shadow.shadowColor = this.shadowColor;
+            canvas.shadow.shadowOffsetX = this.shadowOffsetX;
+            canvas.shadow.shadowOffsetY = this.shadowOffsetY;
+            canvas.drawRect(300,50,100,100);
+            canvas.drawRect(270,100,100,100);
+        }
+    }
+    export default {
+        name: "shadow",
+        data(){
+            return {
+                view: null,
+                blurl: 10,
+                X:10,
+                Y:10,
+                color:"#CCCCCC"
+            }
+        },
+        mounted() {
+            this.view = new shadowView('shadow');
+        },
+        methods:{
+            // 修改扩散距离
+            changeblur(v) {
+                this.view.shadowBlur = v;
+                this.view.update()
+            },
+            // x轴偏移量
+            changeX(v){
+                this.view.shadowOffsetX = v;
+                this.view.update()
+            },
+            // y轴偏移量
+            changeY(v){
+                this.view.shadowOffsetY = v;
+                this.view.update()
+            },
+            // 修改颜色
+            changeColor(v){
+                this.view.shadowColor = new SColor(v);
+                this.view.update()
+            }
+        }
+    }
+</script>
+
+<style scoped>
+    canvas{
+        border: 1px solid #eeeeee;
+        outline: none;
+    }
+</style>

+ 6 - 6
package.json

@@ -1,7 +1,7 @@
 {
   "remote": {
     "host": "47.94.89.44",
-    "path": "/opt/tomcat9/webapps/doc",
+    "path": "/opt/tomcat9/webapps/doc/web",
     "user": "user1",
     "password": "@)!^sagacloud",
     "local": "docs/.vuepress/dist"
@@ -12,11 +12,11 @@
     "publish": "node publish.js"
   },
   "dependencies": {
-    "@saga-web/base": "2.1.24",
-    "@saga-web/big": "1.0.86",
-    "@saga-web/draw": "2.1.104",
-    "@saga-web/graph": "2.1.114",
-    "@saga-web/feng-map": "1.0.25",
+    "@persage-web/base": "2.2.1",
+    "@persage-web/big": "2.2.1",
+    "@persage-web/draw": "2.2.1",
+    "@persage-web/graph": "2.2.1",
+    "@persage-web/feng-map": "2.2.1",
     "axios": "^0.18.1",
     "element-ui": "^2.12.0",
     "vue": "^2.6.10",