I.はじめに
以前アプリで HTML テンプレートを使用してエクスポートされた .xlsx の互換性は良くないため、wps でのみ直接開くことができ、MICrosoft の Excel では警告が直接スローされます。
ということで、またしても「ゆるくて幸せ」な探検に入りましたが、今回もいよいよ終了です!
2. xlsx-js-style と renderjs の組み合わせ
前回の記事で述べたように、uniapp のアプリ環境は限られており、関連する API や dom や bom などのオブジェクトのサポートが不足しているため、xlsx-js-style でスタイルを変更した Excel のバイナリ ストリームをエクスポートできても、 Blob オブジェクト。関連する操作は Excel に書き込まれます。ただし、私が無視してきたもう 1 つのことがあります。それはrenderjsです。
renderjs では Blob オブジェクトと一連の制限されたメソッドを使用できます。他にも制限はありますが、Excel のエクスポートのニーズをサポートするには十分です。
renderjs が使用する
1. 基本
uniapp では、renderjs は WXS に似ており、.vue に新しい script タグが追加で宣言され、 module 属性と lang 属性が設定されます (これをビュー層と呼び、既存のスクリプトをロジック層と呼びます)。
<script module="trans" lang="renderjs">
</script>
サードパーティのライブラリを導入します。
import XLSX from "static/xlsx.bundle.js";
ここでのパスに注意してください。APP 側のビュー層のページが参照するリソースのパスは、ルート ディレクトリからの相対パス (例: static/test.js) で計算されます。サードパーティの JS を静的に配置できます。
2.通信
アプリではビュー層とロジック層が分離されており、ビュー層からロジック層の変数やメソッドを直接呼び出すことはできません。
WXS と同様に、ビュー層でメソッドを呼び出すのは非常に簡単です。つまり、module名.方法名
同時にロジック層の変数を:prop
.html を通じてビュー層に渡すことができます。
<view @tap="trans.onClick" :prop="testData">Yes</view>
ビュー層で定義された onClick メソッドは、ロジック層のメソッドを呼び出すことができます。
onClick(event, ownerInstance) {
// 调用逻辑层的方法,并传参
ownerInstance.callMethod('onViewClick', {
test: 'test'
})
}
:change を使用してロジック層のデータ更新を監視します
<view @tap="trans.onClick" :prop="testData" :change:prop="trans.watchData">Yes</view>
変数が更新された後、ビューレイヤーのメソッドが呼び出されます。
watchData(newValue, oldValue, ownerInstance, instance) {
console.log(newValue, 'newValue');
// 更新视图层相关数据
this.acceptData = newValue;
}
xlsx-js-スタイルの取得
すべてをシンプルにして、必要な 2 つの js ファイル(xlsx-js-style) を githubから直接取得し、静的フォルダーに置き、renderjs で参照します。
3、悟る
データ形式は次のとおりです(xlsx-js-styleでは、複雑な構造から関連する属性を抽出し、使用可能なデータに再構成するための関連APIが提供されています。興味のある方は自分で勉強してください)
excelData: [
{
no: "1",
tag: "weqwrqwrqwrqwrq",
remark: "22",
},
{
no: "2",
tag: "342343434343",
remark: "33",
},
{
no: "3",
tag: "25125152512",
remark: "44",
},
]
注: 以下はすべて renderjs 内にあります。!!
コンビネーション ボックスを使用し、上記のデータを Excel として保存し、バイナリ文字列に処理します。
let sheet = XLSX.utils.json_to_sheet(excelData);
// 修改表头 默认表头为对象中的键名
XLSX.utils.sheet_add_aoa(sheet, [["Name", "Birthday", "Age"]], {
origin: "A1", // 从A1单元格开始,A1,B1,C1,...
});
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, sheet, "Sheet1"); // sheet1 是自定义的sheet名
// H5 走这一步直接下载文件了
// XLSX.writeFile(workbook, 'example.xlsx');
// XLSX.write 会根据type返回值,为什么选择binary类型,因为binary好歹是个string,H5+的io写入api只支持写入string类型的
this.binaryData = XLSX.write(workbook, {
bookType: 'xlsx',
bookSST: false,
type: "binary",
});
この時点ではまだ書き込みはできません。plus.io は特定のファイルへの文字列の書き込みをサポートしていますが、write を通じてバイナリ文字列に直接書き込んだ後、ファイルが破損していることが判明するためです。
そこで当然 Blob の使用を検討しますが、Blob を変換した後、URL.createObjectURL(blob) を直接渡し、リンクをクリックしてファイルのダウンロードのクリックをシミュレートします。
しかし!それは単純ではありません。BLOB リンクを作成して直接ダウンロードすると、すべてのダウンロードが失敗することがわかります。作者はここであまり明確ではありません。クライアント システムで js が制限され、ファイルを直接ダウンロードできないためだと言う人もいます。 。
次に、システムに書き込むだけで、最初に .xlsx ファイルを作成し、最後に文字列型のデータを書き込むことができます。
バイナリ文字列を Blob に直接変換することはできません。まず ArrayBuffer に変換してから Blob に変換します。
// 字符串转 ArrayBuffer
s2ab(str) {
const buf = new ArrayBuffer(str.length)
const view = new Uint8Array(buf)
for (let i = 0; i !== str.length; ++i) view[i] = str.charCodeAt(i) & 0xff
return buf
},
this.blobData = new Blob([this.s2ab(this.binaryData)], {
type: 'application/octer-stream',
});
ここにはもう 1 つの落とし穴があります。Blob は本質的にはオブジェクトであるため、Blob 型のデータを直接書き込むことはできません。愚かなことをしないでください。変換された JSON.stringify は空のオブジェクトになります。
私たちが途方に暮れていると、誰かが APP 環境が Base64 をファイルとして保存することをサポートしているというメッセージを送信し、BLOB を Base64 に変換する必要があるので参照を提供してくれました。
var reader = new FileReader();
reader.readAsDataURL(this.blobData)
reader.onload = (evt) => {
this.base64Data = evt.target.result
};
この方法で取得したbase64は直接書き込むことができないため、先頭のbase64識別子を削除する必要があります。
this.base64Data = this.base64Data.replace(/^data:\S+\/\S+;base64,/, '');
これで、ようやくデータ変換が完了しました。あとは、io を介してファイルに書き込むだけです。
// #ifdef APP-PLUS
let that = this
// 将 ownerInstance 挂载到that上,否则写入完成后,无法通过ownerInstance调用逻辑层的方法 !!
that.ownerInstance = ownerInstance
// H5+ 文档 https://www.html5plus.org/doc/zh_cn/io.html
// renderjs中可以直接调用plus api
plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) {
let root = fs.root;
// 直接在 documents 中创建.xlsx文件
root.getFile(
fileName, {
create: true,
},
(fileEntry) => {
fileEntry.createWriter(
(writer) => {
// 写入文件成功完成的回调函数
writer.onwrite = (e) => {
// 逻辑层通过writeOver来处理导出后续逻辑...
that.ownerInstance.callMethod('writeOver', {
test: 'test'
})
};
writer.writeAsBinary(that.base64Data )
},
(err) => {
console.log(err, "创建文件写入器错误");
}
);
},
(err) => {
console.log(err);
}
);
});
// #endif
writer.writeAsBinary
Base64に書き込むことができますwriteAsBinary
名前を見て、xlsx-js-styleをエクスポートするときに設定する型がバイナリだと思ったのですが、そんなに変換する必要はないのでしょうか? 文字化け。
4. まとめ
- アプリ環境でブラウザ関連の API を使用できない場合は、renderjs の使用を検討できます。
- アプリ環境、renderjs は uni 関連のインターフェイスを呼び出すことができません
- H5+ API は既に Base64 形式でのファイルの書き込みをサポートしていますが、BLOB への直接書き込みはサポートしていません (2023/02/17 時点で、writeAsBinary は公式 Web サイトに更新されていません)
- アプリ環境では、Blob リンク経由で直接ダウンロードすると失敗します (Andriod、iOS はテストされていません)
5. 参考記事・関連資料
以上!