編集者注: このコンテンツは、Grape City の顧客である Zhengcai Cloud のフロントエンド技術チームから提供されたものです。Zhengcai Cloud Company は、世界をリードするクラウド コンピューティング、ビッグデータ、人工知能などのデジタル技術に基づいて、国内初の政府調達クラウド サービス プラットフォームである Zhengcai Cloud Platform を構築し、現在、このプラットフォームは業界で最も広範なサービス範囲となっています。最大のユーザー数と最も活発な取引を誇る、地域、レベル、分野を超えた統合調達クラウド サービス プラットフォームです。
序文
データ視覚化は、科学的視覚化、情報視覚化、および視覚的分析の 3 つの分野で構成されます。
1. 科学的視覚化は主に、建築、気象学、医学、生物学におけるさまざまなシステムなどの 3 次元現象の視覚化に関係します。ボリューム、サーフェス、ライトなどのリアルなレンダリングに重点が置かれており、おそらく何らかの動的コンポーネントも含まれます。
2. 情報の視覚化は、データとデザインを組み合わせた図であり、個人または組織が短時間で効果的な方法で情報を視聴者に広めるために有益なデータ表現の形式です。
3. ビジュアル分析は、グラフィックス、データマイニング、人間とコンピューターのインタラクションなどのテクノロジーを統合して、人間の脳の知能と機械の知能の補完的な利点と相互強化を形成する、視覚的な対話型インターフェイスに基づく分析と推論の科学として定義されます。
ビジュアルレポートはビジュアル分析において最も重要であり、大量のデータを迅速に表示し、データのフィルタリング、関連付け、結合、ドリル、コピークエリ、置換、スタイル設定、条件の挿入などのデータ操作を柔軟に実行できます。書式設定により、マルチカラー スケール、アイコン セット、データ バー、繰り返し値、数式の挿入、テーブル間のリンクなどが可能になります。SpreadJS はビジュアル分析レポートの解決に最も優れていますが、以下ではビジュアル分析における SpreadJS の役割についてのみ説明します。
レポートの視覚化の難しさ
インターネット電子商取引サービス業界では、通常、大量のビジネス情報やユーザー情報が処理され、レポートの主なユーザーはカスタマー サービスやデータ アナリストです。
通常、カスタマー サービスは、大量の作業指示書の作成、顧客の苦情登録、サードパーティ プラットフォームからの生データのインポート、統計の概要、レビューと承認、電子署名、配布などを毎日処理します。通常、業務情報のほとんどは Excel で運ばれており、サーバーは毎日大量の文書を処理する必要がありますが、Excel 文書自体のデータは抽出して保存することが難しいため、テンプレートを組織に配布するのは不便です。テンプレートが更新されるとすぐにオペレーターが操作する必要があり、Web ページに統合するのが難しいなどの問題があります。
データアナリストは、データを取得して要約し、各商品ブランドの売上高、最大値、最小値、平均値などを計算し、価値のあるデータを特定する必要があります。有効なデータを取得し、上司に報告します。
上記のシナリオでは、レポートの視覚化により次の問題が要約される可能性があります。
1. 同時実行性
同社には多数のカスタマー サービス担当者がおり、数千人が同時にオンラインで頻繁に業務を行っており、ビジネス サイクルが短く、データ量が多いため、サーバーの同時パフォーマンス消費が非常に大きくなっています。バックグラウンドで Apache POI を使用して Excel データを抽出および変更し、その中で数式計算を実行できます。これにより、次の 2 つのパフォーマンスのボトルネックが発生します。
1) ファイルを頻繁にアップロードおよびダウンロードする必要があり、サーバーの帯域幅に大きな負荷がかかります。
2) Excel の解析および抽出操作はすべてサーバー側で行われ、頻繁な IO 操作によりサーバーに負荷がかかります。
上記の 2 つのパフォーマンス ポイントは、現在のアーキテクチャでは突破するのが難しく、プロジェクトをリファクタリングする際に最も困難な要件の 1 つでもあります。もちろんハードヒープサーバー構成でも解決策はありますが、他の問題は解決できず、運用保守にも負担がかかります。
2. Excel の操作と互換性に対する高い要件
新しいシステムをすぐに全員が使用できるようにならない場合、このプロジェクトのユーザー数を考慮すると、トレーニング費用を支払うことができなくなります。さらに、既存の Excel レポート テンプレートを直接インポートできる必要があります。そうでないと、すべての Excel レポートを再度開発または設計することはできません。
3. レポート形式は柔軟で変更可能です
さまざまなビジネス シナリオに合わせて、レポート テンプレートも常に変化しています。したがって、オペレーターの設計とレポートが研究開発の介入なしにページ上で完了できることが特に重要です。
4. 式の計算をサポート
商品、受注、原価計算、財務統計などのモジュールが含まれるため、計算式の種類や性能に対する要求が高くなります。
5. ワークフローでのデータのドキュメント化
以前のシステムのワークフローでは、Excel レポートが関与する場合、最初にデータがサーバー側と Excel テンプレートで組み立てられるか、システムがパスに従ってファイル サーバー上で Excel ファイルを見つけてからフローを実行していました。対応するリンクへ。新しいビジネス モジュールの中には、ファイル転送に電子メールのみを使用できるものもあります。
このプロセスでは大量のファイルが生成されるため、ファイル サーバーに大きな負荷がかかり、バックグラウンドで定期的にバッチ データの分割とメンテナンスを行う必要があります。このアップグレード システムはこの問題を解決する必要があります。
選び方を考える
まず、モデル選択の最初のステップは、市場でどの製品を選択できるかを調べることです。市場には、システムに統合してこの種のオンライン フォーム ドキュメント編集をサポートできる製品が多数あります。 . 私はそれらを大きく 2 つのカテゴリーに分けます。
1. クラウドドキュメント型製品
WPS、グラファイト ドキュメント、Office Online に似た製品が多数あります。それら自体の完成度は高く、オンラインコラボレーションを含むほぼすべての機能をユーザーが実現できるようになっており、ある程度の二次開発にも対応しており、プライベートでの導入も可能です。しかし問題は、そのような製品は通常比較的閉鎖的であり、二次的なカスタマイズの開発が比較的難しく、十分に軽量ではないことです。ほとんどの認証方法は時間、同時実行性、ユーザー数などに基づいており、コストが高く、私たちのニーズにはあまり適していません。
2. 制御系製品
LuckySheet、Handsontable、SpreadJS などの標準コントロールはすべて純粋なフロントエンド テーブル コントロールであり、すべて Excel 機能と json データ バインディングをサポートしています。
LuckySheetはMIT国産の商用利用可能なオープンソースソフトウェアです。しかし、調べてみると、まだオンラインになってから 1 ~ 2 か月しか経っておらず、React のような大きな工場から承認されていないため、公式プロジェクトで使用することは不可能でした。これまでに 1 年が経過し、QQ グループやフォーラムなどのコミュニケーション プラットフォームが次々に立ち上げられていますが、まだ脆弱です。
Handsontableは海外の商用フォームコントロールで、二次開発ピットがたくさんあると言われていますが、私たちにとって最大の問題は中国のサポートチームがいないことです。
SpreadJSは Grape City Company の商用 Excel テーブル コントロールですが、興味深いことに、V2EX の LuckySheet の下のコメント エリアで、LuckySheet の作者も SpreadJS が業界のベンチマークであると述べているのを見つけました。数式、グラフ、スタイル、条件付き書式設定など、ほとんどの Excel 機能のインポートをサポートしています (マクロはサポートされていません)。そして最も驚くべきことは、その操作インターフェイスが完全な Excel インターフェイスであり、完全に純粋な JS で開発され、テンプレートとデータのやり取りに json を使用していることです。同時に、SpreadJS には対応するアフターセールス サポート チームもあります。技術的な問題は営業日中いつでも電話またはフォーラムで連絡できます。ビデオ、ドキュメント、サンプル、API マニュアルなどの関連資料も非常に充実しています。技術コンサルタントをトレーニングのために会社に招待することもできます。私たちのような短い建設期間と重い開発タスクを伴うプロジェクト チームにとって、これは確かに多くのエネルギーを節約し、リスクを軽減することができます。
画像ソース: SpreadJS オンライン Excel エディター
では、コントロールとは何でしょうか? コントロールを使用する理由
Wikipedia より引用コンピュータ プログラミングにおいて、コントロール (またはコンポーネント、ウィジェット、またはコントロール) は、ウィンドウやテキスト ボックス
など、表示される情報の配置をユーザーが変更できるグラフィカル ユーザー インターフェイス要素です。コントロール定義は、特定のデータを直接操作するための単一の対話ポイントを提供することを特徴としています。コントロールは、アプリケーション プログラムに含まれる基本的な視覚的な構成要素であり、プログラムが処理し、そのデータと対話するすべてのデータを制御します。
私の理解によれば、コントロールは基本的な機能のみを提供し、二次開発をサポートする機能モジュールです。依存性が比較的軽く可塑性に優れたコントロールであり、対応する開発ドキュメントやAPIも用意されており、開発者向けの基本的な機能パッケージであり、必要に応じて機能をカスタマイズするのに便利です。
SpreadJS 要件のソリューションと利点
1. 同時実行性
SpreadJSはデータとテンプレートを分離した設計なので、記入担当者はページ上で記入を完了するだけで済みます。送信するときは、完成したデータ json のみを送信できます。サーバーはすべての Excel ファイルを一元的に解析する必要がなくなりました。帯域幅の消費も直接半分に節約されます。
2. Excel の操作と互換性に対する高い要件
社内トライアル中に、財務および顧客サービスの女性や姉妹たちは、ユーザー エクスペリエンスは Excel とほぼ同じであり、特別なトレーニングは必要ないと報告しました。また、多数の自社Excelレポートを直接インポート可能(二次開発後のバッチインポートやリモートインポートも実現)、グラフや数式、表スタイルなどの一連の要素をオンライン業務に直接インポートできます。 。
3. レポート形式は柔軟で変更可能です
デザイナーは、オンラインで直接デザインすることも、Excel でデザインしたレポートを Web に取り込み、データ バインディングを行って、JSON 形式で送信して保存することもできます (Spread JS の ssjson 形式には Excel ドキュメントのすべての情報が含まれます)。
4. 式の計算をサポート
450 以上の数式 (Excel では合計 480 以上) をサポートしており、カスタム数式を自分で開発および拡張することもできるため、財務用途には十分です。同時に、シート間参照、絶対参照、関数の名前付け情報など、Excel のあらゆる参照操作もサポートします。
5. ワークフローでのデータのドキュメント化
基本的にファイルへの依存から脱却し、すべてのプロセスのステータスと依存データをデータベースに記録でき、ファイル サーバーには少数のテンプレート ドキュメントのみを保存するだけで済みます (実際、テンプレートの数が多くない場合は、データベースに直接配置することもできますが、既製のファイル サーバーがあります)。これにより、ファイル サーバーのスペース オーバーヘッドが 90% 節約され、運用および保守パートナーは真夜中に笑いながら起きてきます。
SpreadJS の詳細
ここからが重要な点ですが、フロントエンド開発者として私が最も興味を持っているのは、SpreadJS の基礎となる設計と、メモリとパフォーマンスのバランスの最適化です。私はこれについて多くの調査と研究を行ってきました。幸いなことに、この分野の情報を見つけるのは難しくありません。グレープ シティの公式フォーラム (https://gcdn.grapecity. com.cn/forum.php?mod=forumdisplay&fid =225&filter=typeid&typeid=274&fileGuid=QKgTJRrrCD96PXwh)
レンダリングパフォーマンス
すべての Deepin Table コントロール ユーザーにとって、パフォーマンスは最大の関心事であるはずです。データ量が数千件に達することも多く、Excel ではページング (フロントエンドの数式計算や集計など) が不便なので、モデル選定の際には非常に悩みます。後で気づいたのですが、これは考えすぎで、SpreadJS は 500,000 個のデータを簡単にロードでき、ロードにかかる時間は 200 ミリ秒程度です (公式 Web サイトのパフォーマンスデモ例では 50,000 個までしかロードできず、今回は 500,000 個をピックアップして測定しました)。その後、徹底的に調査した結果、この問題を解決するために彼らは次のように考えていることがわかりました。
- リアルタイム レンダリング + ダブル バッファリング (ダブル バッファリングと言い換えられますか?):
Canvas を使用してテーブル部分をレンダリングし、ユーザーに表示されるコンテンツの一部のみをレンダリングするため、1,000 行と 100,000 行のデータの読み込み速度は速く、パフォーマンスはそれほど変わりません。
ダブルバッファリングは、連続レンダリングの継続的なエクスペリエンスの問題を解決し、レンダリング速度をさらに向上させることができます。この言葉を聞いたことがある人は少ないと思われますが、誰もが経験したことがあるはずです. ダブルバッファリングはグラフィックスでは一般にダブルバッファリングと呼ばれています. 実際には、描画命令はバッファ内で完了します. ここでの描画は非常に高速です.描画命令が完了すると、命令のやり取りですぐに完成したグラフィックスが画面に表示されるため、描画の未完成がなくなり効率が高くなります。これはゲームでは実際に非常に一般的で、主人公がマップ上で走っているとき、ゲーム エンジンはキャラクターの移動方向に応じてリアルタイムでマップをロードしてレンダリングします。これにより、大きなマップをロードする際の長い待ち時間が回避されます。一度。
画像出典:グレープシティオープンクラス [SpreadJS パフォーマンス最適化]
(https://gcdn.grapecity.com.cn/forum.php?mod=viewthread&tid=86035&extra=page=1&filter=typeid&typeid=274&fileGuid=QKgTJRrrCD96PXwh)
SpreadJS パフォーマンスの最適化 - グレープシティ オープンクラス - グレープシティ製品テクノロジー コミュニティ (grapecity.com.cn)
- スパース配列:
SpreadJS は、表形式データのストレージを最適化するためにスパース配列データ構造を使用します。スパース配列は、2 次元配列 (チェス盤、マップなど) のメモリ使用量を最適化するためによく使用されますが、アクセス パフォーマンスが遅いという当然の欠点があります。
そこで当時この質問に答えてストレス テストを行ったところ、数百万レベルの走査に 200 ミリ秒以上かかりました。パフォーマンスは私たちのニーズを満たします。
コンピューティングエンジン
公式の紹介によると、フォーミュラ エンジンには実際には 2 つの主要な実装部分が含まれており、1 つは計算ロジック システム、もう 1 つは参照システムです。
- 引用システム
Excel での数式の計算は、C1 が B1 を参照し、B1 が A1 を参照するなど、元のデータに依存します。SpreadJS は関数のこの部分をすでに非常にネイティブにカプセル化しているため、開発者はそれについてまったく心配する必要はありません。 (参照バックトラッキングやその他の特別なニーズがない限り)。
Excel には、直接参照、シート間参照、相対/絶対参照、命名情報への参照、表の行と列の式への参照、ブック間参照などが存在します (リストはすべて網羅されているわけではありません。興味のある学生は検索して参照できます)。自分で学びます)。SpreadJS のランタイムは Web ページ上にあるため、ワークブック間の参照については考慮する必要はありません。少なくとも現時点ではサポートされていないことは明らかです。
- 計算ロジック
セルに入力できる SUM、IF、MATCH、VLOOKUP などの計算式は、小さな「ロジック パッケージ」のようなものです。現在、SpreadJS には 460 以上のネイティブ数式関数がありますが、Excel には 490 以上しかありません。SpreadJS は数式をカスタマイズでき、エクスペリエンスはネイティブのフォーミュラと同じです。
実際、基礎となる実装では、複数のバージョンの反復の後、これらの式は独立した「論理アイランド」ではなくなります。数式の実装は最下層での抽象化と再利用が多く、新バージョンではパフォーマンスが向上しつつも、旧バージョンに比べてコード量が大幅に削減され、より使いやすくなったという。フロントエンドエンジニアリングのパッケージ化。
ネストされた数式計算を実現するために、SpreadJS はユーザーが設定した数式の計算ロジックを解析するために最下位に AST ツリーを構築します。公式サンプルのコードからは、数式の最下位に Expression のセットが構築され、図に示すように、 を呼び出すための対応するパブリック インターフェイスがあります。
画像出典:【SpreadJS数式構造ツリー表示】
https://gcdn.grapecity.com.cn/showtopic-79188-1-1.html?fileGuid=QKgTJRrrCD96PXwh
- パフォーマンス
まず、フロントエンド技術として、数式計算の技術要件に基づいて、考えられるパフォーマンスのボトルネックとその影響を分析します。開発ではユーザーイベント、ダーティデータ、連携などの機能を数多く使用してきましたが、これらの機能が正しく動作するためには、常に正しい計算結果が得られることが重要な前提条件となります。これは、Formula が優先順位の高い同期的な方法で計算を実行できるようにするためです。
マルチスレッドがコンピューティングの負荷を分散するのに役立つことは誰もが知っていますが、設計と実装の難しさについては話さないようにしましょう。たとえ Web ワーカーがサポートされているとしても、JavaScript は厳密に言えばシングルスレッド言語としかみなせません。サブスレッドはメインスレッドによって完全に制御され、メインスレッドをブロックしたり一時停止したりすることはできません。そのため、Web Workerを導入しても上記の同期実行は保証されません。
上記の分析により、式の計算パフォーマンスの制限は JavaScript の計算能力に依存していることがわかります。Node.js の計算能力を直感的に反映できる関連画像を見つけました (Node.js は V8 エンジンであり、最速の JS エンジンとして認識されています)。
画像は「Node.jsを徹底解説」より引用
私たちのテストによると、上記のコンピューティング パフォーマンスはネイティブ JS のパフォーマンスに近く、この領域における SpreadJS の最適化はすでに物理的な限界に非常に近づいています。現時点では、このアプリケーション シナリオでは、このコンピューティング パフォーマンスは十分ですが、将来的には大量のデータと数式コンピューティングの要件が発生する可能性を排除するものではありません。公式は、この点に関する関連ソリューションも提供しています。こちらを参照してください。
公式は、数式計算のブロックキャッシュを実現するために、キャッシュ技術をさらに開発中であると言われています。つまり、参照チェーン上の値が変化しても、参照チェーン全体の数式を計算する必要はありません。とてもパワフルなサウンドですし、アイデアも信頼できるので、早く発売されることを願っています。
スタイルシステム
Excel のスタイル システムは非常に複雑です。枠線、フォント、配置、データ形式、条件付き書式など、すべての機能ポイントが非常に柔軟かつ巨大な実装になっています。SpreadJS について初めて知ったとき、私もその Style クラスに驚きました。私が想像できるのは、枠線、背景、フォント、配置などですが、セルの種類、データ形式、表のボタン、ドロップダウン、透かしなども「表示」されます。スタイルが重すぎるとため息が出るほどですが、大量のセルスタイルをカスタマイズするとメモリもパフォーマンスも確実に悪くなります。しかし、実際のアプリケーションではボトルネックは見つかりませんでした。ここでは、図に示すように、階層構造が設計に使用されていることがわかります。
画像出典:グレープシティオープンクラス [SpreadJS パフォーマンス最適化]
SpreadJSの使い方は?
1. テーブルをレンダリングする
図6.1-1 バインディングデータと式
まず、テーブル全体の本体となるグローバルスプレッドオブジェクトを取得し、スプレッドを複数のシートに分割します。SpreadJS は初期化後に Spread オブジェクトを返します。
- Vueバージョンスプレッドオブジェクト
<gc-spread-sheets @workbookInitialized='spreadInitHandle(\$event)' />
methods:{
spreadInitHandle: function (spread) {
this.spread = sprea
},
}
-
データをバインド、式をバインド
tableDataBind() { // 数据源,可以从后台请求拿到 var dataSource = { // 注意这里加了一层bindPath,用于映射表格的绑定路径 bindPath_table: [{ c1: 100, c2: 90, c3: 30, c4: 40 }, { c1: 88, c2: 66, c3: 55, c4:100 }, { c1: 30, c2: 89, c3: 100, c4: 40 },{ c1: 40, c2: 66, c3: 88, c4: 40 }] }; // 表格绑定和单元格绑定数据源,需要用SpreadJS的CellBindingSource包装一下 var spreadNS = GC.Spread.Sheets; var dataSource1 = new spreadNS.Bindings.CellBindingSource(dataSource); var table2 = this.activeSheet.tables.add("tableName", 0, 0, 1, 5, spreadNS.Tables.TableThemes.light6); table2.showFooter(true); table2.autoGenerateColumns(false); var c1 = new spreadNS.Tables.TableColumn(1); c1.name("语文"); c1.dataField("c1"); var c2 = new spreadNS.Tables.TableColumn(2); c2.name("数学"); c2.dataField("c2"); var c3 = new spreadNS.Tables.TableColumn(3); c3.name("英语"); c3.dataField("c3"); var c4 = new spreadNS.Tables.TableColumn(4); c4.name("化学"); c4.dataField("c4"); var c5 = new spreadNS.Tables.TableColumn(5); c5.name("合计"); table2.bindColumns([c1, c2, c3, c4, c5]); table2.bindingPath("bindPath_table"); // 设置公式 table2.setColumnDataFormula(4, "=[@语文]+[@数学]+[@英语]+[@化学]"); table2.setColumnFormula(4, "=SUBTOTAL(109,[合计])"); // 设置允许单元格的内容超出单元格,与绑定无关 this.activeSheet.options.allowCellOverflow = true; // 绑定dataSource this.activeSheet.setDataSource(dataSource1); this.spread.resumePaint(); }
図6.1-2 関数名と関数コードの対応表
条件付き書式設定のレンダリング
条件付きフォーマットのレンダリング: データのレンダリングが完了すると、データが正常に表示されることが保証されますが、これだけではデータ アナリストのニーズを満たすには十分ではなく、最大値と最小値などの有効なデータを明確に表示することも必要です。値は赤色で表示され、進行状況バーは変化する状態を示します。アイコンは上昇または下降、2 色のグラデーション、3 色のグラデーションなどを示します。
- アイコンセット:効果は図の通り
-
実装コード
iconset() { var activeSheet = this.activeSheet; var iconSetRule = new GC.Spread.Sheets.ConditionalFormatting.IconSetRule(); // 演示demo先写死区域 iconSetRule.ranges([new GC.Spread.Sheets.Range(0,0, 5, 5)]); // IconSetType图标志的类型:箭头,圆圈和execl 打通的,excel有哪些这这边就支持哪些 iconSetRule.iconSetType(GC.Spread.Sheets.ConditionalFormatting.IconSetType.threeArrowsColored); var iconCriteria = iconSetRule.iconCriteria(); iconCriteria[0] = new GC.Spread.Sheets.ConditionalFormatting.IconCriterion( true, GC.Spread.Sheets.ConditionalFormatting.IconValueType.number, 60 );(<60) iconCriteria[1] = new GC.Spread.Sheets.ConditionalFormatting.IconCriterion( true, GC.Spread.Sheets.ConditionalFormatting.IconValueType.number, 90 );(60<= <90) iconCriteria[2] = new GC.Spread.Sheets.ConditionalFormatting.IconCriterion( true, GC.Spread.Sheets.ConditionalFormatting.IconValueType.number, 90 );(>=90) iconSetRule.reverseIconOrder(false); iconSetRule.showIconOnly(false); activeSheet.conditionalFormats.addRule(iconSetRule); }
-
プログレスバー: 効果は図に示すとおりです
-
実装コード
dataBar(){ var activeSheet = this.activeSheet; activeSheet.conditionalFormats.addDataBarRule( GC.Spread.Sheets.ConditionalFormatting.ScaleValueType.number,0,//最小数 GC.Spread.Sheets.ConditionalFormatting.ScaleValueType.number, 100,//最大值 "orange",//颜色 [new GC.Spread.Sheets.Range(0,0, 5, 4)] ); },
-
繰り返し値: 効果は図に示すとおりです
-
実装コード
duplicateValue() { var activeSheet = this.activeSheet; var style = new GC.Spread.Sheets.Style(); style.backColor = "yellow"; style.foreColor = "red"; var ranges = [new GC.Spread.Sheets.Range(0,0, 5, 4)]; activeSheet.conditionalFormats.addDuplicateRule(style, ranges); } -
テキスト 6 を含むセル: 効果は図に示すとおりです。
-
実装コード
includeText() { var activeSheet = this.activeSheet; var style = new GC.Spread.Sheets.Style(); style.backColor = "red"; var ranges = [new GC.Spread.Sheets.Range(0,0, 5, 5)]; activeSheet.conditionalFormats.addSpecificTextRule( GC.Spread.Sheets.ConditionalFormatting.TextComparisonOperators.contains, "6", style, ranges ); }
-
上記の実現結果を組み合わせると、図のようになります。
最後に書きます
この記事では主に、データ視覚化の方向での私自身の探求のいくつかを紹介します。市場レポート、電子メール購読レポート、オンライン コラボレーション、および視覚分析を作成する準備をしている学生に役立ちます。
記事が長いため、概念的な部分が多く、間違いがあることは避けられませんが、皆さんに修正していただければ幸いです。ありがとうございます。
============================
編集者からの言いたいこと: Grape City の製品を評価し、上記のコンテンツを提供してくださった Zhengcai Cloud のフロントエンド技術チームに感謝します。グレープ シティ製品の使用経験がある場合は、ぜひ貢献してください。WeChat パブリック アカウントのバックグラウンドでご連絡ください。