成人欧美一区二区三区的电影,日韩一级一欧美一级国产,国产成人国拍亚洲精品,无码人妻精品一区二区三区毛片,伊人久久无码大香线蕉综合

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

Canvas 高性能K線圖的架構(gòu)方案

freeflydom
2025年10月10日 11:30 本文熱度 1357

前言

證券行業(yè),最難的前端組件,也就是k線圖了。
指標(biāo)還可以添加、功能還可以擴(kuò)展, 但思路要清晰。
作為一個(gè)從證券行業(yè)畢業(yè)的前端從業(yè)者,
我想分享下自己的項(xiàng)目經(jīng)驗(yàn)。

1、H5 K線圖,支持無(wú)限左右滑動(dòng)、樣式可隨意定制;
2、純canvas制作,不借助任何第三方圖表庫(kù);
3、閱讀本文,需要有 canvas 基礎(chǔ)知識(shí);

滑動(dòng)K線圖組件    Github Page 預(yù)覽地址

股票詳情頁(yè)源碼    Github Page 預(yù)覽地址

注意:以上的 demo 還有一些 bug, 沒(méi)時(shí)間修復(fù), 預(yù)覽地址是直接在 github 上部署的, 所以最好通過(guò) vpn科學(xué)上網(wǎng),否則可能訪問(wèn)不了,然后再在移動(dòng)端打開(kāi)頁(yè)面。 另外, 上面的股票詳情頁(yè), 還沒(méi)有做自適應(yīng),等我有時(shí)間再改。

一、先看最終的效果

1、GIF動(dòng)圖如下


 

2、支持樣式自定義

用可以屏幕取色器,獲取東方財(cái)富的配色 codeinword.com/eyedropper

圖一、圖二,是參考東方財(cái)富黑白皮膚的配色, 圖三是參考騰訊自選股的配色。

  

二、canvas 注意事項(xiàng)

1、整數(shù)坐標(biāo),會(huì)導(dǎo)致模糊

canvas 在畫(huà)線段, 通常會(huì)出現(xiàn)以下代碼:

cxt.moveTo(x1, y1);
cxt.lineTo(x2, y2);
cxt.lineWidth = 1;
cxt.stroke();

假設(shè)上面的兩個(gè)點(diǎn)是(1,10)和(5,10),那么畫(huà)出來(lái)的實(shí)際上是一條橫線,
理論上橫線的粗度是1px,且該橫線被 y=10 切成兩半,
上半部分粗度是 0.5px, 下半部分粗度也是 0.5px,
這樣橫線的整體粗度才會(huì)是 1px。

但是 canvas 不是這樣處理的, canvas 默認(rèn)線條會(huì)與整數(shù)對(duì)齊,
也就是橫線的上部分不會(huì)是 y=9.5px, 而是 y=9px;
橫線的下半部分也不是 y=10.5px, 而是 y=11px;
從而橫線的粗度看起來(lái)不是1px,而是2px。

并且由于粗度被拉伸,顏色也會(huì)被淡化,那怎么解決這個(gè)問(wèn)題呢?

處理方式也很簡(jiǎn)單, 通過(guò) cxt.translate(0.5, 0.5) 將坐標(biāo)往下移動(dòng) 0.5 個(gè)像素,
然后接下來(lái)的所有點(diǎn), 都保證是整數(shù)即可, 這樣就能保證不會(huì)被拉伸。

典型的代碼如下:

cxt.translate(0.5, 0.5);
cxt.moveTo(Math.floor(x1), Math.floor(y1));
cxt.lineTo(Math.floor(x2), Math.floor(y2));
cxt.lineWidth = 1;
cxt.stroke();

在我的代碼中, 也體現(xiàn)了類似的處理。

2、如何處理高像素比帶來(lái)的模糊

設(shè)備像素比越高,理論上應(yīng)該越清晰,因?yàn)樵瓉?lái)用一個(gè)小方塊來(lái)渲染1px, 現(xiàn)在用2個(gè)(dpr=2的情況)小方塊來(lái)渲染,應(yīng)該更清晰才對(duì),但是canvas不是這樣的。

例如,通過(guò)js獲取父容器 div 的寬度是 width, 這時(shí)候如果設(shè)置 canvas.width = width,在設(shè)備像素比為2的時(shí)候, canvas 畫(huà)出來(lái)的寬度為css對(duì)應(yīng)寬度的一半, 如果強(qiáng)制通過(guò) css 將 canvas 寬度設(shè)置為 width, 則 canvas 會(huì)被拉長(zhǎng)一倍, 導(dǎo)致出現(xiàn)鋸齒模糊。

注意了嗎?上面所說(shuō)的 canvas.width=width 與 css 設(shè)置的 #canvas { width: width } 起到的效果是不一樣的。不要隨便通過(guò) css 去設(shè)置 canvas 的寬高, 容易被拉伸變形或者導(dǎo)致模糊。

通用的處理方式是:

//初始化高清Canvas
function initHDCanvas() {
  const rect = hdCanvas.getBoundingClientRect();
  
  //設(shè)置Canvas內(nèi)部尺寸為顯示尺寸乘以設(shè)備像素比
  const dpr = window.devicePixelRatio || 1;
  hdCanvas.width = rect.width * dpr;
  hdCanvas.height = rect.height * dpr;
  
  //設(shè)置Canvas顯示尺寸保持不變
  hdCanvas.style.width = rect.width + 'px';
  hdCanvas.style.height = rect.height + 'px';
  
  //獲取上下文并縮放
  const ctx = hdCanvas.getContext('2d');
  ctx.scale(dpr, dpr);
  
  //接下來(lái),你可以自由發(fā)揮
}

三、樣式配置

為了方便樣式自定義, 我獨(dú)立出一個(gè)默認(rèn)的配置對(duì)象 defaultKlineConfig, 參數(shù)的含義如下圖所示,其實(shí)下圖這個(gè)風(fēng)格的標(biāo)注, 是通過(guò) excalidraw 這個(gè)軟件畫(huà)的, 也是 canvas 做的開(kāi)源軟件, 可見(jiàn) canvas 在前端可視化領(lǐng)域的重要性, 這個(gè)扯遠(yuǎn)了,打住。

如上圖, 整個(gè)canvas 畫(huà)板, 分成 5 部分,
每一部分的高度, 都可以設(shè)置,
其中主圖和副圖的高度,是通過(guò)比例來(lái)計(jì)算的:
mainChartHeight = restHeight * mainChartHeightPercent
其中,restHeigh 是畫(huà)板總高度 height 減去其他幾部分計(jì)算的, 如下:
restHeight = height - lineMessageHeight - tradeMessageHeight - xLabelHeight

十字交叉線的顏色, X軸 與 Y軸 的 tooltip 背景色、字體大小的參數(shù)如下:

四、均線計(jì)算

從上面的圖可以看出, 需要畫(huà) 5日均線、10日均線、20日均線, 成交量快線(10日)、成交量慢線(20日) 但是, 接口沒(méi)有給出當(dāng)日的均線值, 需要自己計(jì)算。

5日均線 = (過(guò)去4個(gè)成交日的收盤價(jià)總和 + 今日收盤價(jià))/ 5

10日均線 = (過(guò)去9個(gè)成交日的收盤價(jià)總和 + 今日收盤價(jià))/ 10

20日均線 = (過(guò)去19個(gè)成交日的收盤價(jià)總和 + 今日收盤價(jià))/ 20

成交量快線 = (過(guò)去9日成交量 + 今日成交量)/ 10

成交量慢線 = (過(guò)去19日成交量 + 今日成交量)/ 20

所以, 當(dāng)獲取 lmt(一屏的蠟燭圖個(gè)數(shù))個(gè)數(shù)據(jù)時(shí), 為了計(jì)算均線, 需要至少將前 19 個(gè)(我的代碼寫(xiě)20)數(shù)據(jù)都獲取到。當(dāng)前一個(gè)均線已經(jīng)獲取到, 下一個(gè)均線就不需要再累加20個(gè)值再得平均數(shù), 可以省一點(diǎn)計(jì)算:

今日20日均線值 = (昨日均線值 * 20 - 前面第20個(gè)的收盤價(jià) + 今日收盤價(jià))/ 20;

五、分層渲染

為了減少重繪,提高性能,可以將K線圖做分層渲染。那分幾層合適?我認(rèn)為是三層。

  1. 第一層, 不動(dòng)層
  2. 第二層,變動(dòng)層
  3. 第三層,交互層

不動(dòng)層

首先, 網(wǎng)格是固定的, 也就是說(shuō),當(dāng)頁(yè)面拖拽、或者長(zhǎng)按出現(xiàn)十字交叉的時(shí)候,底部的網(wǎng)格線是不變的,如果每次拖拽,都需要重繪網(wǎng)格,那這個(gè)其實(shí)是沒(méi)有必要的開(kāi)銷,可以將網(wǎng)格放在最底層(例如 z-index:0),一次性繪制后,就不要再重繪。

變動(dòng)層

由于拖拽的時(shí)候,蠟燭柱體,均線,Y軸刻度, X軸刻度, 都需要重繪, 這一塊是無(wú)法改變的事實(shí), 所以, 變動(dòng)層放在中間層(例如 z-index:1),也是最繁忙的一層,并且該層不響應(yīng)觸摸事件。

交互層

最后, 交互層由于要捕捉用戶的觸摸行為, 所以,這一層要在最上層(例如 z-index:2)。

交互層監(jiān)聽(tīng)觸摸事件:當(dāng)頁(yè)面快速滑動(dòng), 則響應(yīng)拖拽事件, 即K線圖的時(shí)間線會(huì)左右滑動(dòng);當(dāng)用戶長(zhǎng)按之后才滑動(dòng), 則出現(xiàn)十字交叉浮層。

交互層的好處是, 當(dāng)響應(yīng)十字交叉浮層時(shí), 只需要繪制橫線、豎線、對(duì)應(yīng)X軸和Y軸的值,而不需要重繪蠟燭柱體和均線, 可以減少重繪,最大程度減少渲染壓力。

六、基礎(chǔ)幾何繪制

網(wǎng)格線

首先計(jì)算出主圖的高度 this.mainChartHeight, 將主圖從上到下等分為4部分,再在左右兩邊畫(huà)出豎線,形成主圖的網(wǎng)格,副圖是成交量圖, 只需畫(huà)一個(gè)矩形邊框即可,用 strokeRect 即可畫(huà)出。

//畫(huà)出網(wǎng)格線
  private drawGridLine() {
    //獲取配置參數(shù)
    const { gridColor, lineMessageHeight, xLabelHeight, width, height } = this.config;
    //畫(huà)出K線圖的5條橫線
    const split = this.mainChartHeight / 4;
    this.canvasCxt.beginPath();
    this.canvasCxt.lineWidth = 0.5;
    this.canvasCxt.strokeStyle = gridColor;
    for (let i = 0; i <= 4; i++) {
      const splitHeight = Math.floor(split * i) + lineMessageHeight!;
      this.drawLine(0, splitHeight, width, splitHeight);
    }
    //畫(huà)出K線圖的2條豎線
    this.drawLine(0, lineMessageHeight!, 0, lineMessageHeight! + this.mainChartHeight);
    this.drawLine(width, lineMessageHeight!, width, lineMessageHeight! + this.mainChartHeight);
    //畫(huà)出成交量的矩形
    this.canvasCxt.strokeRect(
      0,
      height - xLabelHeight! - this.subChartHeight,
      width,
      this.subChartHeight,
    );
  }
  
  //畫(huà)出兩個(gè)點(diǎn)形成的直線
  private drawLine(x1: number, y1: number, x2: number, y2: number) {
    this.canvasCxt.moveTo(x1, y1);
    this.canvasCxt.lineTo(x2, y2);
    this.canvasCxt.stroke();
  }

畫(huà)各類均線

1、首先計(jì)算出一屏的股價(jià)最大值 max , 股價(jià)最小值 min ,成交量最大值 maxAmount。

2、當(dāng)某一個(gè)點(diǎn)的均線為 value, 根據(jù)最大值、最小值、索引index, 計(jì)算出坐標(biāo)點(diǎn)(x, y), 畫(huà)均線的時(shí)候, 第一個(gè)點(diǎn)用 moveTo(x0, y0),其他點(diǎn)用 lineTo(xn yn), 最后 stroke 連起來(lái)即可。

3、當(dāng)然, 每一條線設(shè)置下顏色, 即 stokeStyle。

  //畫(huà)出各類均線
  private drawLines(max: number, min: number, maxAmount: number) {
    //將寬度分成n個(gè)小區(qū)間, 一個(gè)小區(qū)間畫(huà)一個(gè)蠟燭, 每個(gè)區(qū)間的寬度是 splitW
    const splitW = this.config.width / this.config.lmt!;
    //畫(huà)一下5日均線
    this.canvasCxt.beginPath();
    this.canvasCxt.strokeStyle = this.config.ma5Color;
    this.canvasCxt.lineWidth = 1;
    let isTheFirstItem = true;
    for (
      let i = this.startIndex;
      i < this.arrayList.length && i < this.startIndex + this.config.lmt!;
      i++
    ) {
      const index = i - this.startIndex;
      let value = this.arrayList[i].ju5;
      if (value === 0) {
        continue;
      }
      const x = Math.floor(index * splitW + 0.5 * splitW);
      const y = Math.floor(
        ((max - value) / (max - min)) * this.mainChartHeight + this.config.lineMessageHeight!,
      );
      if (isTheFirstItem) {
        this.canvasCxt.moveTo(x, y);
        isTheFirstItem = false;
      } else {
        this.canvasCxt.lineTo(x, y);
      }
    }
    this.canvasCxt.stroke();
  }

畫(huà)出蠟燭柱體

 

當(dāng)收盤價(jià)大于等于開(kāi)盤價(jià), 選用上面左邊紅色的樣式; 當(dāng)收盤價(jià)小于開(kāi)盤價(jià), 選用上面右邊綠色的樣式。

以紅色蠟燭為例, 最高點(diǎn) A(x0, y0),最低點(diǎn)是 B(x1, y1),
高度 height、寬度 width 都是相對(duì)于坐標(biāo)軸的,
紅色矩形左上角的頂點(diǎn)是 D(x, y)。

為了畫(huà)出紅色蠟燭, 先后順序別搞混:

  1. AB 這條豎線,通過(guò) moveTo,lineTo 畫(huà)出來(lái);
  2. 定義一個(gè)矩形 cxt.rect(x, y, width, heigth);
  3. 通過(guò) fill 將矩形內(nèi)部填充為白色, 這時(shí)候白色矩形會(huì)覆蓋掉紅色豎線的一部分;
  4. 再通過(guò) stroke 描出矩形的紅色邊框

按照上面這個(gè)順序, 豎線會(huì)被覆蓋掉一部分,同時(shí),矩形內(nèi)部的白色填充不會(huì)擠壓矩形的紅色邊框, 如果先 stroke 再 fill,容易出現(xiàn)白色填充覆蓋紅色邊框,矩形可能會(huì)變模糊,或者使得紅色變淡,極其不友好,所以按照我上面的順序,可以減少不必要的麻煩。

畫(huà)出文字

canvas 畫(huà)出文字, 典型的代碼如下

 this.canvasCxt.beginPath();
    this.canvasCxt.font = `${this.config.yLabelFontSize}px "Segoe UI", Arial, sans-serif`;
    this.canvasCxt.textBaseline = 'alphabetic';
    this.canvasCxt.fillStyle = this.config.yLabelColor;

注意textBaseline 默認(rèn)對(duì)齊方式是 alphabetic, 但 middle 往往更好用, 能實(shí)現(xiàn)垂直居中,但我發(fā)現(xiàn)垂直居中也不是很居中,所以會(huì)特意加減1、2個(gè)像素;

當(dāng)然還有個(gè)textAlign, 能實(shí)現(xiàn)水平對(duì)齊方式, 左右對(duì)齊都可以, 例如上圖最左、最右的時(shí)間標(biāo)簽。

七、交互設(shè)計(jì)

根據(jù)上面的GIF動(dòng)圖, 可以知道, 本次做的移動(dòng)端 K 線圖, 最重要的兩個(gè)交互是:

  1. 快速拖拽,K線圖隨時(shí)間軸左右滑動(dòng)
  2. 長(zhǎng)按滑動(dòng),出現(xiàn)十字交叉tooltip

上面的交互,其實(shí)是比較復(fù)雜的,所以需要先設(shè)計(jì)一個(gè)簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu):

  1. 首先頁(yè)面存放一個(gè)列表 arrayList
  2. 保存一個(gè)數(shù)字標(biāo)識(shí) startIndex,表示當(dāng)前屏幕從 startIndex 開(kāi)始畫(huà)蠟燭圖

當(dāng)用戶往右快速拖拽時(shí), startIndex 根據(jù)用戶拖拽的距離, 適當(dāng)變?。?當(dāng)用戶往左快速拖拽時(shí), startIndex 根據(jù)用戶拖拽的距離, 適當(dāng)變大。

那 arrayList 到底多長(zhǎng)合適, 因?yàn)楣善笨赡苡惺畮啄甑臄?shù)據(jù), 甚至上百年的數(shù)據(jù), 我不能一次性拉取這個(gè)股票的所有數(shù)據(jù)吧?

當(dāng)然,站在軟件性能、消耗等角度,也不應(yīng)該一次性拉取所有的數(shù)據(jù), 我的答案是 arraylist 最多保存5屏的數(shù)據(jù)量,用戶看到的屏幕, 應(yīng)該是接近中間這一屏,也就是第3屏的數(shù)據(jù), 左右兩邊各保存2屏數(shù)據(jù),這樣,用戶拖拽的時(shí)候,可以比較流暢,而不是每次拖拽都要等拉取數(shù)據(jù)再去渲染。

那什么時(shí)候拉取新的數(shù)據(jù)呢? 用戶觸摸完后,當(dāng)startIndex左邊的數(shù)據(jù)少于2屏,開(kāi)始拉取左邊的數(shù)據(jù); 用戶觸摸完后,當(dāng)startIndex右邊的數(shù)據(jù)少于2屏,開(kāi)始拉取右邊的數(shù)據(jù);

那如果用戶一直往右拖拽, 是不是就一直往左邊添加數(shù)據(jù), 這個(gè) arraylist 是不是會(huì)變得很長(zhǎng)?

當(dāng)然不是,例如,當(dāng)我往 arraylist 的左邊添加數(shù)據(jù)的時(shí)候,startIndex 也會(huì)跟著變動(dòng), 因?yàn)橛脩艨吹降牡谝粭l柱體,在 arraylist 的索引已經(jīng)變了。當(dāng)我往 arraylist 的某一邊添加數(shù)據(jù)后, arraylist 的另一邊如果數(shù)據(jù)超過(guò) 2 屏, 要適當(dāng)裁掉一些數(shù)據(jù), 這樣 arraylist 的總數(shù), 始終保持在 5 屏左右,就不會(huì)占用太多的存放空間。

總體思想是, 從 startIndex 開(kāi)始渲染屏幕的第一條柱體, 當(dāng)前屏幕的左右兩邊, 都預(yù)留2屏數(shù)據(jù),防止用戶拖拽頻繁調(diào)用接口, 導(dǎo)致卡頓; 同時(shí)也控制了 arraylist 的長(zhǎng)度, 這是虛擬列表的變形,這樣設(shè)計(jì),可以做一個(gè) 高性能 的k線圖。

八、觸摸事件解耦

根據(jù)上面的分析:

  1. 快速拖拽, K線圖左右移動(dòng)
  2. 長(zhǎng)按再滑動(dòng), 出現(xiàn)十字交叉tooltip

以上兩種拖拽,都在 touchmove 事件中觸發(fā), 那怎么區(qū)分開(kāi)呢? 典型的 touchstart、 touchmove 、 touchend 解耦如下:

let timer = null;
let startX = 0;
let startY = 0;
let isLongPress = false;
canvas.addEventListener('touchstart', (e) => {
    startX = e.touches[0].clientX;
    startY = e.touches[0].clientY;
    isLongPress = false;
    
    timer = setTimeout(() => {
        isLongPress = true;
        // 顯示十字光標(biāo)hover
        showCrossHair(e);
    }, 500);
});
canvas.addEventListener('touchmove', (e) => {
    if (isLongPress) {
        // 長(zhǎng)按移動(dòng)時(shí)更新十字光標(biāo)位置
        updateCrossHair(e);
    } else {
        // 快按拖動(dòng)時(shí)移動(dòng)K線圖
        clearTimeout(timer);
        moveKLineChart(e);
    }
});
canvas.addEventListener('touchend', () => {
    clearTimeout(timer);
    if (isLongPress) {
        // 長(zhǎng)按結(jié)束隱藏十字光標(biāo)
        hideCrossHair();
    }
    isLongPress = false;
});
// 關(guān)閉十字光標(biāo)
function hideCrossHair() {
    // 隱藏邏輯
}

根據(jù)上面的框架, 再詳細(xì)補(bǔ)充下代碼就可以了。 然后再在 touchend 事件中, 新增或減少 arraylist 的數(shù)據(jù)量。

九、性能優(yōu)化

其實(shí), 做到上面的設(shè)計(jì),性能已經(jīng)很好了,可以監(jiān)控幀率來(lái)看下滑動(dòng)的流暢程度。

總結(jié)下我做了什么操作,來(lái)提高整體的性能:

1、分層渲染

將K線圖畫(huà)在3個(gè)canvas上。

  1. 不動(dòng)層只需要繪畫(huà)一次;
  2. 變動(dòng)層根據(jù)需要而變動(dòng);
  3. 交互層獨(dú)立出來(lái),不會(huì)影響其它層,變動(dòng)層的大量蠟燭柱體、均線等也不會(huì)受交互層的影響

2、離屏渲染

當(dāng)需要在K線上標(biāo)注一些icon時(shí), 這些 icon 可以先離屏渲染, 需要的時(shí)候, 再copy到變動(dòng)層對(duì)應(yīng)的位置,這樣比臨時(shí)抱佛腳去畫(huà),要省很多時(shí)間,也能提高新能。

3、設(shè)置數(shù)據(jù)緩沖區(qū)

就是屏幕只渲染一屏數(shù)據(jù), 但是在當(dāng)前屏的左右兩邊,各緩存了2屏數(shù)據(jù), 超過(guò)5屏數(shù)據(jù)的時(shí)候,及時(shí)裁掉多余的數(shù)據(jù), 這樣arraylist的數(shù)據(jù)量始終保持在5屏, 控制了數(shù)據(jù)量,有效的控制了占用空間。

4、節(jié)流防抖

touchmove 會(huì)很頻繁觸發(fā), 可通過(guò)節(jié)流來(lái)控制,減少不必要的渲染。

十、部署到GitHub Pages

1、安裝gh-pages包

npm install --save-dev gh-pages

2、package.json 添加如下配置

注意, Stock 這個(gè)需要對(duì)應(yīng)github的倉(cāng)庫(kù)名

{
  "homepage": "https://fhrddx.github.io/Stock",
  "scripts": {
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
  }
}

3、運(yùn)行部署命令

npm run build
npm run deploy

最后, 訪問(wèn)上面的鏈接

fhrddx.github.io/Stock/?

這樣, github pages 部署成功, 訪問(wèn)上面鏈接, 可以看到如下效果。

github page 的部署需要將倉(cāng)庫(kù)設(shè)置為 public, 這個(gè)我挺反感的, 可以用 vercel 部署, 也就是將 github 賬號(hào)與 vercel 關(guān)聯(lián)起來(lái), 項(xiàng)目的 package.js 的 homepage 設(shè)置為 “.” , 然后 vercel 可以點(diǎn)擊一下, 一鍵部署, 常見(jiàn)的命令行如下:

# 1. 安裝 Vercel CLI
npm install -g vercel
# 2. 在項(xiàng)目根目錄登錄 Vercel
vercel login
# 3. 部署項(xiàng)目
npm run build
vercel --prod
# 或者直接部署 build 文件夾
vercel --prod --build

轉(zhuǎn)自https://juejin.cn/post/7556154928059334666


該文章在 2025/10/10 11:31:22 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved