欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

Fabric.js 簡單介紹和使用

admin
2023年5月23日 16:3 本文熱度 1438

簡介

Fabric.js是一個可以簡化canvas程序編寫的庫。 Fabric.js為canvas提供所缺少的對象模型, svg parser, 交互和一整套其他不可或缺的工具。基于MIT協議開源,在github上有許多人貢獻代碼。

Why fabric?

canvas提供一個好的畫布能力, 但其api超級爛。如果你就想畫個簡單圖形, 其實也可以, 不過做一些復雜的圖形繪制, 編寫一些復雜的效果,就不是那么好了。
fabric就是為此而開發。

用對象的方式去編寫代碼

舉個例子
傳統的畫正方形代碼

// reference canvas element (with id="c")

var canvasEl = document.getElementById('c');


// get 2d context to draw on (the "bitmap" mentioned earlier)

var ctx = canvasEl.getContext('2d');


// set fill color of context

ctx.fillStyle = 'red';


// create rectangle at a 100,100 point, with 20x20 dimensions

ctx.fillRect(100, 100, 20, 20);


使用fabric

// create a wrapper around native canvas element (with id="c")

var canvas = new fabric.Canvas('c');

 

// create a rectangle object

var rect = new fabric.Rect({

    left: 100,

    top: 100,

    fill: 'red',

    width: 20,

    height: 20

});

 

// "add" rectangle onto canvas

canvas.add(rect);

好的 其實并沒有什么差別 不過我們試著旋轉一下角度

var canvasEl = document.getElementById('c');

var ctx = canvasEl.getContext('2d');

ctx.fillStyle = 'red';


ctx.translate(100, 100);

ctx.rotate(Math.PI / 180 * 45);

ctx.fillRect(-10, -10, 20, 20);


fabric

var canvas = new fabric.Canvas('c');


// create a rectangle with angle=45

var rect = new fabric.Rect({

  left: 100,

  top: 100,

  fill: 'red',

  width: 20,

  height: 20,

  angle: 45

});


canvas.add(rect);


如果我們想重新調整位置 怎么辦

var canvasEl = document.getElementById('c');


...

ctx.strokRect(100, 100, 20, 20);

...


// erase entire canvas area

ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);

ctx.fillRect(20, 50, 20, 20);


fabric

var canvas = new fabric.Canvas('c');

...

canvas.add(rect);

...


rect.set({ left: 20, top: 50 });

canvas.renderAll();


objects

  1. fabric.Circle

  2. fabric.Ellipse

  3. fabric.Line

  4. fabric.Polygon

  5. fabric.Polyline

  6. fabric.Rect

  7. fabric.Triangle

畫一個三角形 和一個 圓形

// create a wrapper around native canvas element (with id="c")

var canvas = new fabric.Canvas('c');


var circle = new fabric.Circle({

    radius: 20, fill: 'green', left: 100, top: 100

});

var triangle = new fabric.Triangle({

    width: 20, height: 30, fill: 'blue', left: 50, top: 50

});


canvas.add(circle, triangle);

Manipulating objects

可以簡單的使用set來控制對象屬性
positioning — left, top;
dimension — width, height;
rendering — fill, opacity, stroke, strokeWidth;
scaling and rotation — scaleX, scaleY, angle;
and even those related to flipping — flipX, flipY.

rect.set('fill', 'red');

rect.set({ strokeWidth: 5, stroke: 'rgba(100,200,200,0.5)' });

rect.set('angle', 15).set('flipY', true);

有了set 其實也就有了get,對象可以創建時設置屬性,也可以先實例化再賦值。

var rect = new fabric.Rect({ width: 10, height: 20, fill: '#f55', opacity: 0.7 });


// or functionally identical


var rect = new fabric.Rect();

rect.set({ width: 10, height: 20, fill: '#f55', opacity: 0.7 });


另外這里的fabric.Rect是函數,大家可以使用class繼承。

默認值

var rect = new fabric.Rect(); // notice no options passed in


rect.getWidth(); // 0

rect.getHeight(); // 0


rect.getLeft(); // 0

rect.getTop(); // 0


rect.getFill(); // rgb(0,0,0)

rect.getStroke(); // null


rect.getOpacity(); // 1


Hierarchy and Inheritance

fabric.Object 是圖像基類

你可以自己擴充方法

fabric.Object.prototype.getAngleInRadians = function() {

  return this.getAngle() / 180 * Math.PI;

};


var rect = new fabric.Rect({ angle: 45 });

rect.getAngleInRadians(); // 0.785...


var circle = new fabric.Circle({ angle: 30, radius: 10 });

circle.getAngleInRadians(); // 0.523...


circle instanceof fabric.Circle; // true

circle instanceof fabric.Object; // true


Canvas

fabric.Canvas 是canvas的wrapper

var canvas = new fabric.Canvas('c');

var rect = new fabric.Rect();


canvas.add(rect); // add object


canvas.item(0); // reference fabric.Rect added earlier (first object)

canvas.getObjects(); // get all objects on canvas (rect will be first and only)


canvas.remove(rect); // remove previously-added fabric.Rect


經典的設計 有options 有對象方法

var canvas = new fabric.Canvas('c', {

  backgroundColor: 'rgb(100,100,200)',

  selectionColor: 'blue',

  selectionLineWidth: 2

  // ...

});


// or


var canvas = new fabric.Canvas('c');

canvas.setBackgroundImage(http://...');

canvas.onFpsupdate = function(){ /* ... */ };

// ...


Images

使用fabric.Image你可以輕松的加載一個圖片
html

<canvas id="c"></canvas>

<img src="my_image.png" id="my-image">


js

var canvas = new fabric.Canvas('c');

var imgElement = document.getElementById('my-image');

var imgInstance = new fabric.Image(imgElement, {

  left: 100,

  top: 100,

  angle: 30,

  opacity: 0.85

});

canvas.add(imgInstance);

當然也可以通過url加載一張圖片到canvas

fabric.Image.fromURL('my_image.png', function(oImg) {

  canvas.add(oImg);

});


可以對加載的圖片進行預處理

fabric.Image.fromURL('my_image.png', function(oImg) {

  // scale image down, and flip it, before adding it onto canvas

  oImg.scale(0.5).setFlipX(true);

  canvas.add(oImg);

});


Path and PathGroup

我們已經看了簡單的形狀,然后圖像。更復雜、豐富的形狀和內容呢?
路徑包括一系列的命令,這基本上模仿一個筆從一個點到另一個。在“移動”,“線”,“曲線”,或“弧”等命令的幫助下,路徑可以形成令人難以置信的復雜形狀。同組的路徑(路徑組的幫助),開放更多的可能性。
類似于svg的path

var canvas = new fabric.Canvas('c');

var path = new fabric.Path('M 0 0 L 200 100 L 170 200 z');

path.set({ left: 120, top: 120 });

canvas.add(path);

“M” 代表 “move” 命令, 告訴筆到 0, 0 點.
“L” 代表 “line” 畫一條0, 0 到 200, 100 的線.
another “L” creates a line to 170, 200.
z” tells forces drawing pen to close current path and finalize the shape.

...

var path = new fabric.Path('M 0 0 L 300 100 L 200 300 z');

...

path.set({ fill: 'red', stroke: 'green', opacity: 0.5 });

canvas.add(path);


path也可以設置canvas屬性

當然,太困然了,所以你可以使用 fabric.loadSVGfromString or fabric.loadSVGfromURL 方法。

Afterword

看些demo吧。


上面我們學習了基礎用法,現在我們開始一些好玩的。

Animation

我們先回顧設置一下正方形角度方法

rect.set('angle', 45);

這是沒有動畫的

Fabric object都有animate方法

rect.animate('angle', 45, {
  onChange: canvas.renderAll.bind(canvas)
});

那么正方形會從0到45有一個動畫過度

從左到右進行變動

rect.animate('left', '+=100', { onChange: canvas.renderAll.bind(canvas) });

逆時針轉5度

rect.animate('angle', '-=5', { onChange: canvas.renderAll.bind(canvas) });

當然animate還支持這些方法

  1. from: Allows to specify starting value of animatable property (if we don't want 2. current value to be used).

  2. duration: Defaults to 500 (ms). Can be used to change duration of an animation.

  3. onComplete: Callback that's invoked at the end of the animation.

  4. easing: Easing function.

rect.animate('left', '+=100', {
    onChange: canvas.renderAll.bind(canvas),
    duration: 3000,
    easing: fabric.util.ease.easeOutBounce
});

Image filters

圖片可以使用filter效果

fabric.Image.fromURL('pug.jpg', function(img) {
  // add filter
  img.filters.push(new fabric.Image.filters.Grayscale());
  // apply filters and re-render canvas when done
  img.applyFilters(canvas.renderAll.bind(canvas));
  // add image onto canvas
  canvas.add(img);
});

filter一次可以使用多個效果

當然 你也可以自己定義filter

fabric.Image.filters.Redify = fabric.util.createClass({
  type: 'Redify',
  applyTo: function(canvasEl) {    var context = canvasEl.getContext('2d'),
        imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
        data = imageData.data;
        for (var i = 0, len = data.length; i < len; i += 4) {
              data[i + 1] = 0;
              data[i + 2] = 0;
    }
    context.putImageData(imageData, 0, 0);
  }
});
fabric.Image.filters.Redify.fromObject = function(object) {
  return new fabric.Image.filters.Redify(object);
};

Colors

fabric 支持 hex rgb rgba顏色

new fabric.Color('#f55');
new fabric.Color('#123123');
new fabric.Color('356735');
new fabric.Color('rgb(100,0,100)');
new fabric.Color('rgba(10, 20, 30, 0.5)');

并且支持相互轉換

new fabric.Color('#f55').toRgb(); // "rgb(255,85,85)"
new fabric.Color('rgb(100,100,100)').toHex(); // "646464"
new fabric.Color('fff').toHex(); // "FFFFFF"

兩種顏色可以疊加 并且你可以使用一些特定效果

var redish = new fabric.Color('#f55');
var greenish = new fabric.Color('#5f5');
redish.overlayWith(greenish).toHex(); // "AAAA55"
redish.toGrayscale().toHex(); // "A1A1A1"

Gradients

可以使用漸變色

var circle = new fabric.Circle({  left: 100,  top: 100,  radius: 50});
circle.setGradient('fill', {
  x1: 0,  y1: -circle.height / 2,  x2: 0,  y2: circle.height / 2,  colorStops: {
    0: '#000',    1: '#fff'
  }
});

首先確定兩個點 在其距離中以百分比定位顏色

circle.setGradient('fill', {
  x1: -circle.width / 2,
  y1: 0,  x2: circle.width / 2,  y2: 0,  colorStops: {
      0: "red",    0.2: "orange",    0.4: "yellow",    0.6: "green",    0.8: "blue",    1: "purple"
  }
});

Text

  1. Multiline support. Native text methods unfortunately simply ignore new lines.

  2. Text alignment. Left, center, right. Useful when working with multiline text.

  3. Text background. Background also respects text alignment.

  4. Text decoration. Underline, overline, strike-through.

  5. Line height. Useful when working with multiline text.

如何添加文字

var text = new fabric.Text('hello world', { left: 100, top: 100 });
canvas.add(text);

fontFamily

var comicSansText = new fabric.Text("I'm in Comic Sans", {fontFamily: 'Comic Sans'});

fontSize

var text40 = new fabric.Text("I'm at fontSize 40", {fontSize: 40});
var text20 = new fabric.Text("I'm at fontSize 20", {fontSize: 20});

fontWeight

var normalText = new fabric.Text("I'm a normal text", {fontWeight: 'normal'});
var boldText = new fabric.Text("I'm at bold text", {fontWeight: 'bold'});

textDecoration

var underlineText = new fabric.Text("I'm an underlined text", {textDecoration: 'underline'});
var strokeThroughText = new fabric.Text("I'm a stroke-through text", {textDecoration: 'line-through'});
var overlineText = new fabric.Text("I'm an overline text", {textDecoration: 'overline'});

shadow

var shadowText1 = new fabric.Text("I'm a text with shadow", {  shadow: 'rgba(0,0,0,0.3) 5px 5px 5px'});
var shadowText2 = new fabric.Text("And another shadow", {  shadow: 'rgba(0,0,0,0.2) 0 0 5px'});
var shadowText3 = new fabric.Text("Lorem ipsum dolor sit", {  shadow: 'green -5px -5px 3px'});

fontStyle

var italicText = new fabric.Text("A very fancy italic text", {  fontStyle: 'italic',  fontFamily: 'Delicious'});
var anotherItalicText = new fabric.Text("another italic text", {  fontStyle: 'italic',  fontFamily: 'Hoefler Text'});

stroke & strokeWidth

var textWithStroke = new fabric.Text("Text with a stroke", {  stroke: '#ff1318',  strokeWidth: 1});
var loremIpsumDolor = new fabric.Text("Lorem ipsum dolor", {  fontFamily: 'Impact',  stroke: '#c3bfbf',  strokeWidth: 3});

textAlign

var text = 'this is\na multiline\ntext\naligned right!';
var alignedRightText = new fabric.Text(text, {  textAlign: 'right'});

lineHeight

var lineHeight3 = new fabric.Text('Lorem ipsum ...', {  lineHeight: 3});
var lineHeight1 = new fabric.Text('Lorem ipsum ...', {  lineHeight: 1});

textBackgroundColor

var text = 'this is\na multiline\ntext\nwith\ncustom lineheight\n&background';
var textWithBackground = new fabric.Text(text, {  textBackgroundColor: 'rgb(0,200,0)'});

Events

怎么可以沒有事件呢

事件以on off使用 canvas 可以捕捉事件

mouseevent
"mouse:down", "mouse:move", and "mouse:up".

renderevent
"after:render".

selectionevent
"before:selection:cleared", "selection:created", "selection:cleared".

objectevent
object ones: "object:modified", "object:selected", "object:moving", "object:scaling", "object:rotating", "object:added", and "object:removed"

var canvas = new fabric.Canvas('...');
canvas.on('mouse:down', function(options) {
    console.log(options.e.clientX, options.e.clientY);
});

同樣這些事件也可以用任何fabric對象監聽

var rect = new fabric.Rect({ width: 100, height: 50, fill: 'green' });
rect.on('selected', function() {
 console.log('selected a rectangle');
});
var circle = new fabric.Circle({ radius: 75, fill: 'blue' });
circle.on('selected', function() {
 console.log('selected a circle');
});


上面我們學習了基礎和高級特性,現在介紹更神奇的東西。

Groups

話說這個功能我最喜歡,組成群組可以統一修改其中所有組件屬性,如何定義:

var circle = new fabric.Circle({
  radius: 100,
  fill: '#eef',
  scaleY: 0.5,
  originX: 'center',
  originY: 'center'
});
var text = new fabric.Text('hello world', {
  fontSize: 30,
  originX: 'center',
  originY: 'center'
});
var group = new fabric.Group([ circle, text ], {
  left: 150,
  top: 100,
  angle: -10
});
canvas.add(group);

圖片描述

現在我們就可以對其中的對象集修改

group.item(0).setFill('red');
group.item(1).set({
  text: 'trololo',
  fill: 'white'
});

圖片描述

group中的元素相對于group定位

圖片描述

但是由于要確保之前得到卻切位置 所以要異步

fabric.Image.fromURL('/assets/pug.jpg', function(img) {
  var img1 = img.scale(0.1).set({ left: 100, top: 100 });
  fabric.Image.fromURL('/assets/pug.jpg', function(img) {
    var img2 = img.scale(0.1).set({ left: 175, top: 175 });
    fabric.Image.fromURL('/assets/pug.jpg', function(img) {
      var img3 = img.scale(0.1).set({ left: 250, top: 250 });
      canvas.add(new fabric.Group([ img1, img2, img3], { left: 200, top: 200 }))
    });
  });
});

group 可以動態添加

group.add(new fabric.Rect({
  ...
  originX: 'center',
  originY: 'center'
}));

添加并修改group

group.addWithupdate(new fabric.Rect({
  ...
  left: group.getLeft(),
  top: group.getTop(),
  originX: 'center',
  originY: 'center'
}));

當然 你可以使用canvas上已有的進行克隆 組合

// create a group with copies of existing (2) objects
var group = new fabric.Group([
  canvas.item(0).clone(),
  canvas.item(1).clone()
]);
// remove all objects and re-render
canvas.clear().renderAll();
// add group onto canvas
canvas.add(group);

Serialization

序列化是為了相互傳輸

toObject, toJSON

canvas 實現了toJSON接口 可以被序列化

var canvas = new fabric.Canvas('c');
JSON.stringify(canvas); // '{"objects":[],"background":"rgba(0, 0, 0, 0)"}'

canvas 可以隨時被修改 json數據會被修改

canvas.backgroundColor = 'red';
JSON.stringify(canvas); // '{"objects":[],"background":"red"}'

添加新對象 也會改變json數據

canvas.add(new fabric.Rect({  left: 50,  top: 50,  height: 20,  width: 20,  fill: 'green'}));
console.log(JSON.stringify(canvas));
'{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0}],"background":"rgba(0, 0, 0, 0)"}'

再添加一個

canvas.add(new fabric.Circle({  left: 100,  top: 100,  radius: 50,  fill: 'red'}));
console.log(JSON.stringify(canvas));
'{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0},{"type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"radius":50}],"background":"rgba(0, 0, 0, 0)"}'
toObject

可以轉化成js object對象

{ "background" : "rgba(0, 0, 0, 0)",
  "objects" : [
    {
      "angle" : 0,
      "fill" : "green",
      "flipX" : false,
      "flipY" : false,
      "hasBorders" : true,
      "hasControls" : true,
      "hasRotatingPoint" : false,
      "height" : 20,
      "left" : 50,
      "opacity" : 1,
      "overlayFill" : null,
      "perPixelTargetFind" : false,
      "scaleX" : 1,
      "scaleY" : 1,
      "selectable" : true,
      "stroke" : null,
      "strokeDashArray" : null,
      "strokeWidth" : 1,
      "top" : 50,
      "transparentCorners" : true,
      "type" : "rect",
      "width" : 20
    }
  ]}

每個fabric對象有toObject方法 這和toJSON 也有關 可以自定義

var rect = new fabric.Rect();
rect.toObject = function() {
  return { name: 'trololo' };
};
canvas.add(rect);
console.log(JSON.stringify(canvas));
'{"objects":[{"name":"trololo"}],"background":"rgba(0, 0, 0, 0)"}'

當然我們可以保留原有的數據 新增數據

var rect = new fabric.Rect();rect.toObject = (function(toObject) {
  return function() {
    return fabric.util.object.extend(toObject.call(this), {
      name: this.name
    });
  };
})(rect.toObject);
canvas.add(rect);rect.name = 'trololo';
console.log(JSON.stringify(canvas));
'{"objects":[{"type":"rect","left":0,"top":0,"width":0,"height":0,"fill":"rgb(0,0,0)","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0,"name":"trololo"}],"background":"rgba(0, 0, 0, 0)"}'
toSvg

怎么能不支持轉成svg呢

canvas.add(new fabric.Rect({ left: 50,  top: 50,  height: 20,  width: 20,  fill: 'green'}));
console.log(canvas.toSVG());
'<?xml version="1.0" standalone="no" ?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800" height="700" xml:space="preserve"><desc>created with Fabric.js 0.9.21</desc><rect x="-10" y="-10" rx="0" ry="0" width="20" height="20" style="stroke: none; stroke-width: 1; stroke-dasharray: ; fill: green; opacity: 1;" transform="translate(50 50)" /></svg>'

Deserialization, SVG parser

fabric.Canvas#loadfromJSON
fabric.Canvas#loadfromDatalessJSON
fabric.loadSVGfromURL
fabric.loadSVGfromString

var canvas = new fabric.Canvas();
canvas.loadfromJSON('{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0},{"type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"radius":50}],"background":"rgba(0, 0, 0, 0)"}');

通常情況下 svg 會被序列化 但是可以使用 fabric.Canvas#toDatalessJSON

canvas.item(0).sourcePath = '/assets/dragon.svg';
console.log(JSON.stringify(canvas.toDatalessJSON()));
{"objects":[{"type":"path","left":143,"top":143,"width":175,"height":151,"fill":"#231F20","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":-19,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"path":"/assets/dragon.svg"}],"background":"rgba(0, 0, 0, 0)"}

Subclassing

構造類

var Point = fabric.util.createClass({
  initialize: function(x, y) {    this.x = x || 0;    this.y = y || 0;
  },
  toString: function() {    return this.x + '/' + this.y;
  }
});

繼承類

var ColoredPoint = fabric.util.createClass(Point, {
  initialize: function(x, y, color) {    this.callSuper('initialize', x, y);    this.color = color || '#000';
  },
  toString: function() {    return this.callSuper('toString') + ' (color: ' + this.color + ')';
  }
});

繼承默認類

var LabeledRect = fabric.util.createClass(fabric.Rect, {
  type: 'labeledRect',
  initialize: function(options) {
    options || (options = { });    this.callSuper('initialize', options);    this.set('label', options.label || '');
  },
  toObject: function() {    return fabric.util.object.extend(this.callSuper('toObject'), {
      label: this.get('label')
    });
  },
  _render: function(ctx) {    this.callSuper('_render', ctx);
    ctx.font = '20px Helvetica';
    ctx.fillStyle = '#333';
    ctx.fillText(this.label, -this.width/2, -this.height/2 + 20);
  }
});

不過其實沒必要的。


該文章在 2023/5/23 16:49:36 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved