ow
No files matched your search
@@ -1,30 +1,31 @@
|
|||||||
{
|
{
|
||||||
"name" : "安全小秘书",
|
"name": "安全小秘书",
|
||||||
"appid" : "__UNI__A3FEFA1",
|
"appid": "__UNI__A3FEFA1",
|
||||||
"description" : "本地 SQLite 密码保险箱,图案密码登录",
|
"description": "本地 SQLite 密码保险箱,图案密码登录",
|
||||||
"versionName" : "1.0.0",
|
"versionName": "1.0.0",
|
||||||
"versionCode" : "100",
|
"versionCode": "100",
|
||||||
"transformPx" : false,
|
"transformPx": false,
|
||||||
/* 5+App特有相关 */
|
/* 5+App特有相关 */
|
||||||
"app-plus" : {
|
"app-plus": {
|
||||||
"usingComponents" : true,
|
"usingComponents": true,
|
||||||
"nvueStyleCompiler" : "uni-app",
|
"nvueStyleCompiler": "uni-app",
|
||||||
"compilerVersion" : 3,
|
"compilerVersion": 3,
|
||||||
"splashscreen" : {
|
"splashscreen": {
|
||||||
"alwaysShowBeforeRender" : true,
|
"alwaysShowBeforeRender": true,
|
||||||
"waiting" : true,
|
"waiting": true,
|
||||||
"autoclose" : true,
|
"autoclose": true,
|
||||||
"delay" : 0
|
"delay": 0
|
||||||
},
|
},
|
||||||
/* 模块配置 */
|
/* 模块配置 */
|
||||||
"modules" : {
|
"modules": {
|
||||||
"sqlite" : {}
|
"sqlite": {},
|
||||||
|
"Camera": {}
|
||||||
},
|
},
|
||||||
/* 应用发布信息 */
|
/* 应用发布信息 */
|
||||||
"distribute" : {
|
"distribute": {
|
||||||
/* android打包配置 */
|
/* android打包配置 */
|
||||||
"android" : {
|
"android": {
|
||||||
"permissions" : [
|
"permissions": [
|
||||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
@@ -41,40 +42,77 @@
|
|||||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||||
],
|
],
|
||||||
"abiFilters" : [ "arm64-v8a", "x86" ]
|
"abiFilters": [
|
||||||
|
"arm64-v8a",
|
||||||
|
"x86"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
/* ios打包配置 */
|
/* ios打包配置 */
|
||||||
"ios" : {
|
"ios": {
|
||||||
"dSYMs" : false
|
"dSYMs": false
|
||||||
},
|
},
|
||||||
/* SDK配置 */
|
/* SDK配置 */
|
||||||
"sdkConfigs" : {},
|
"sdkConfigs": {
|
||||||
"splashscreen" : {
|
"share": {}
|
||||||
"useOriginalMsgbox" : true
|
},
|
||||||
|
"splashscreen": {
|
||||||
|
"useOriginalMsgbox": true
|
||||||
|
},
|
||||||
|
"icons": {
|
||||||
|
"android": {
|
||||||
|
"hdpi": "unpackage/res/icons/72x72.png",
|
||||||
|
"xhdpi": "unpackage/res/icons/96x96.png",
|
||||||
|
"xxhdpi": "unpackage/res/icons/144x144.png",
|
||||||
|
"xxxhdpi": "unpackage/res/icons/192x192.png"
|
||||||
|
},
|
||||||
|
"ios": {
|
||||||
|
"appstore": "unpackage/res/icons/1024x1024.png",
|
||||||
|
"ipad": {
|
||||||
|
"app": "unpackage/res/icons/76x76.png",
|
||||||
|
"app@2x": "unpackage/res/icons/152x152.png",
|
||||||
|
"notification": "unpackage/res/icons/20x20.png",
|
||||||
|
"notification@2x": "unpackage/res/icons/40x40.png",
|
||||||
|
"proapp@2x": "unpackage/res/icons/167x167.png",
|
||||||
|
"settings": "unpackage/res/icons/29x29.png",
|
||||||
|
"settings@2x": "unpackage/res/icons/58x58.png",
|
||||||
|
"spotlight": "unpackage/res/icons/40x40.png",
|
||||||
|
"spotlight@2x": "unpackage/res/icons/80x80.png"
|
||||||
|
},
|
||||||
|
"iphone": {
|
||||||
|
"app@2x": "unpackage/res/icons/120x120.png",
|
||||||
|
"app@3x": "unpackage/res/icons/180x180.png",
|
||||||
|
"notification@2x": "unpackage/res/icons/40x40.png",
|
||||||
|
"notification@3x": "unpackage/res/icons/60x60.png",
|
||||||
|
"settings@2x": "unpackage/res/icons/58x58.png",
|
||||||
|
"settings@3x": "unpackage/res/icons/87x87.png",
|
||||||
|
"spotlight@2x": "unpackage/res/icons/80x80.png",
|
||||||
|
"spotlight@3x": "unpackage/res/icons/120x120.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/* 快应用特有相关 */
|
/* 快应用特有相关 */
|
||||||
"quickapp" : {},
|
"quickapp": {},
|
||||||
/* 小程序特有相关 */
|
/* 小程序特有相关 */
|
||||||
"mp-weixin" : {
|
"mp-weixin": {
|
||||||
"appid" : "",
|
"appid": "",
|
||||||
"setting" : {
|
"setting": {
|
||||||
"urlCheck" : false
|
"urlCheck": false
|
||||||
},
|
},
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-alipay" : {
|
"mp-alipay": {
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-baidu" : {
|
"mp-baidu": {
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-toutiao" : {
|
"mp-toutiao": {
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"uniStatistics" : {
|
"uniStatistics": {
|
||||||
"enable" : false
|
"enable": false
|
||||||
},
|
},
|
||||||
"vueVersion" : "3"
|
"vueVersion": "3"
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,6 @@
|
|||||||
<text class="file-name" @click="onPickBackup(b)">{{ b.name }}</text>
|
<text class="file-name" @click="onPickBackup(b)">{{ b.name }}</text>
|
||||||
<view class="file-acts">
|
<view class="file-acts">
|
||||||
<view class="ico ico-path" @click.stop="onShowPath(b)"></view>
|
<view class="ico ico-path" @click.stop="onShowPath(b)"></view>
|
||||||
<view class="ico ico-share" @click.stop="onShareFile(b)"></view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
@@ -243,7 +242,7 @@ import { listPasswords, getPassword, savePassword, deletePassword, getSessionKey
|
|||||||
import { generatePassword } from '@/utils/crypto';
|
import { generatePassword } from '@/utils/crypto';
|
||||||
import { pickImageAsBase64 } from '@/utils/image';
|
import { pickImageAsBase64 } from '@/utils/image';
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
import { exportBackupFile, shareBackupFile, listBackupFiles, readBackupFile, clearBackupFiles, pickSecFile, readBackupFromUri } from '@/utils/backup';
|
import { exportBackupFile, listBackupFiles, readBackupFile, clearBackupFiles, pickSecFile, readBackupFromUri } from '@/utils/backup';
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
const ADVANCED = '高级';
|
const ADVANCED = '高级';
|
||||||
@@ -496,9 +495,8 @@ async function onExport() {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const dump = await getExportPlainDump();
|
const dump = await getExportPlainDump();
|
||||||
const url = await exportBackupFile(dump, pwd);
|
await exportBackupFile(dump, pwd);
|
||||||
uni.showToast({ title: '备份已生成', icon: 'none' });
|
uni.showToast({ title: '备份已生成,可在导入列表查看', icon: 'none' });
|
||||||
await shareBackupFile(url);
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
uni.showToast({ title: e?.message || '导出失败', icon: 'none' });
|
uni.showToast({ title: e?.message || '导出失败', icon: 'none' });
|
||||||
}
|
}
|
||||||
@@ -526,15 +524,15 @@ async function onPickBackup(item: { name: string; localUrl: string }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onPickFromFiles() {
|
async function onPickFromFiles() {
|
||||||
const uri = await pickSecFile();
|
const picked = await pickSecFile();
|
||||||
if (!uri) {
|
if (!picked.uri) {
|
||||||
uni.showToast({ title: '未选择文件', icon: 'none' });
|
uni.showToast({ title: picked.error || '未选择文件', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const pwd = await askPassword('输入所选备份文件的密码');
|
const pwd = await askPassword('输入所选备份文件的密码');
|
||||||
if (pwd === null) return;
|
if (pwd === null) return;
|
||||||
try {
|
try {
|
||||||
const dump = await readBackupFromUri(uri, pwd);
|
const dump = await readBackupFromUri(picked.uri, pwd);
|
||||||
confirmRestore(dump, pwd);
|
confirmRestore(dump, pwd);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
uni.showToast({ title: e?.message || '恢复失败', icon: 'none' });
|
uni.showToast({ title: e?.message || '恢复失败', icon: 'none' });
|
||||||
@@ -586,14 +584,10 @@ function onShowPath(item: { name: string; localUrl: string }) {
|
|||||||
uni.showModal({ title: '文件路径', content: item.localUrl, showCancel: false, confirmText: '知道了' });
|
uni.showModal({ title: '文件路径', content: item.localUrl, showCancel: false, confirmText: '知道了' });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onShareFile(item: { name: string; localUrl: string }) {
|
|
||||||
await shareBackupFile(item.localUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onClearBackupFiles() {
|
function onClearBackupFiles() {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '清空备份文件',
|
title: '清空备份文件',
|
||||||
content: '将删除本机 _doc/backups 下所有 .sec 备份(已分享到外部的副本不受影响)。确定继续?',
|
content: '将删除本机 _doc/backups 下所有 .sec 备份(已转移到外部的副本不受影响)。确定继续?',
|
||||||
confirmText: '清空',
|
confirmText: '清空',
|
||||||
confirmColor: '#e53935',
|
confirmColor: '#e53935',
|
||||||
success: async (res) => {
|
success: async (res) => {
|
||||||
@@ -1053,7 +1047,6 @@ async function onSave() {
|
|||||||
.file-acts {
|
.file-acts {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 18px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ico-path {
|
.ico-path {
|
||||||
@@ -1062,12 +1055,6 @@ async function onSave() {
|
|||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%235a6472'%3E%3Cpath d='M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%235a6472'%3E%3Cpath d='M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
.ico-share {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%235a6472'%3E%3Cpath d='M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.list {
|
.list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,23 +77,44 @@ export async function exportBackupFile(
|
|||||||
w.write(content);
|
w.write(content);
|
||||||
}, rej);
|
}, rej);
|
||||||
});
|
});
|
||||||
return fileEntry.toLocalURL ? fileEntry.toLocalURL() : fileEntry.fullPath;
|
// 返回 _doc 相对路径:可跨设备(真机/模拟器包名不同)
|
||||||
|
return `_doc/backups/${name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 调起系统分享,把备份文件另存到网盘/微信收藏等(失败静默,不影响已生成文件) */
|
/** 用 Java 文件 IO 读取绝对路径文本(不经过 content resolver,真实机型可用) */
|
||||||
export function shareBackupFile(localUrl: string): Promise<void> {
|
function readTextByNativePath(p: any, path: string): string {
|
||||||
const p = plusAny();
|
const File = p.android.importClass("java.io.File");
|
||||||
return new Promise((resolve) => {
|
const FileInputStream = p.android.importClass("java.io.FileInputStream");
|
||||||
try {
|
const fis = new FileInputStream(new File(path));
|
||||||
p.share.sendWithSystem(
|
try {
|
||||||
{ type: "file", files: [localUrl] },
|
let out = "";
|
||||||
() => resolve(),
|
let b = fis.read();
|
||||||
() => resolve(),
|
// 备份文件为 JSON 文本且体积很小,逐字节读取避免跨语言 byte[] 桥接问题
|
||||||
);
|
while (b !== -1) {
|
||||||
} catch {
|
out += String.fromCharCode(b);
|
||||||
resolve();
|
b = fis.read();
|
||||||
}
|
}
|
||||||
});
|
// Latin-1 逐字节拼接后还原 UTF-8 再解码
|
||||||
|
return decodeURIComponent(escape(out));
|
||||||
|
} finally {
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 通过 content resolver 输入流读取文本 */
|
||||||
|
function readTextByStream(p: any, is: any): string {
|
||||||
|
const BufferedReader = p.android.importClass("java.io.BufferedReader");
|
||||||
|
const InputStreamReader = p.android.importClass("java.io.InputStreamReader");
|
||||||
|
const reader = new BufferedReader(new InputStreamReader(is));
|
||||||
|
let out = "";
|
||||||
|
let line = reader.readLine();
|
||||||
|
while (line != null) {
|
||||||
|
out += line;
|
||||||
|
line = reader.readLine();
|
||||||
|
if (line != null) out += "\n";
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 列出 _doc/backups 下的备份文件 */
|
/** 列出 _doc/backups 下的备份文件 */
|
||||||
@@ -107,7 +128,8 @@ export async function listBackupFiles(): Promise<BackupItem[]> {
|
|||||||
.filter((e) => e.name && e.name.endsWith(".sec"))
|
.filter((e) => e.name && e.name.endsWith(".sec"))
|
||||||
.map((e) => ({
|
.map((e) => ({
|
||||||
name: e.name,
|
name: e.name,
|
||||||
localUrl: e.toLocalURL ? e.toLocalURL() : e.fullPath,
|
// 统一用 _doc 相对路径,跨设备(包名不同)保持稳定
|
||||||
|
localUrl: `_doc/backups/${e.name}`,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,10 +191,27 @@ export async function clearBackupFiles(): Promise<number> {
|
|||||||
return targets.length;
|
return targets.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 调起系统文件选择器,返回所选文件的 content URI(取消或失败返回 null) */
|
/** 调起系统文件选择器,返回所选文件(content URI 或绝对路径);取消/失败返回 null,异常信息在 error */
|
||||||
export function pickSecFile(): Promise<string | null> {
|
export function pickSecFile(): Promise<{ uri: string | null; error?: string }> {
|
||||||
const p = plusAny();
|
const p = plusAny();
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
let settled = false;
|
||||||
|
const done = (r: { uri: string | null; error?: string }) => {
|
||||||
|
if (!settled) {
|
||||||
|
settled = true;
|
||||||
|
resolve(r);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 优先 HTML5+ 原生选择器:不依赖 activity result 回传,真实机型稳定可用
|
||||||
|
if (typeof p.io.chooseFile === "function") {
|
||||||
|
p.io.chooseFile(
|
||||||
|
(path: string) => done({ uri: path }),
|
||||||
|
(err: any) =>
|
||||||
|
done({ uri: null, error: err?.message || "文件选择失败" }),
|
||||||
|
{ extension: ".sec" },
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const main = p.android.runtimeMainActivity();
|
const main = p.android.runtimeMainActivity();
|
||||||
const Intent = p.android.importClass("android.content.Intent");
|
const Intent = p.android.importClass("android.content.Intent");
|
||||||
@@ -189,9 +228,9 @@ export function pickSecFile(): Promise<string | null> {
|
|||||||
if (requestCode === REQUEST) {
|
if (requestCode === REQUEST) {
|
||||||
if (resultCode === -1 && data) {
|
if (resultCode === -1 && data) {
|
||||||
const uri = data.getData();
|
const uri = data.getData();
|
||||||
resolve(uri ? uri.toString() : null);
|
done({ uri: uri ? uri.toString() : null });
|
||||||
} else {
|
} else {
|
||||||
resolve(null);
|
done({ uri: null });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -201,13 +240,18 @@ export function pickSecFile(): Promise<string | null> {
|
|||||||
Intent.createChooser(intent, "选择备份文件"),
|
Intent.createChooser(intent, "选择备份文件"),
|
||||||
REQUEST,
|
REQUEST,
|
||||||
);
|
);
|
||||||
} catch {
|
// 新版运行时结果不回传 JS 钩子,超时兜底解除挂起
|
||||||
resolve(null);
|
setTimeout(
|
||||||
|
() => done({ uri: null, error: "文件选择器无响应" }),
|
||||||
|
5 * 60 * 1000,
|
||||||
|
);
|
||||||
|
} catch (e: any) {
|
||||||
|
done({ uri: null, error: e?.message || "调起文件选择器失败" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 读取 content URI 文本并用密码解密(供“从文件选择 .sec”恢复) */
|
/** 读取所选文件(content URI 或绝对路径)并用密码解密 */
|
||||||
export async function readBackupFromUri(
|
export async function readBackupFromUri(
|
||||||
uriStr: string,
|
uriStr: string,
|
||||||
password: string,
|
password: string,
|
||||||
@@ -215,25 +259,26 @@ export async function readBackupFromUri(
|
|||||||
const p = plusAny();
|
const p = plusAny();
|
||||||
const content: string = await new Promise((resolve, reject) => {
|
const content: string = await new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
|
if (!uriStr.startsWith("content://")) {
|
||||||
|
// chooseFile 返回的是本机文件路径:解析为绝对路径后用原生 IO 读取
|
||||||
|
let path = uriStr;
|
||||||
|
try {
|
||||||
|
path = p.io.convertLocalFileSystemURL(uriStr) || uriStr;
|
||||||
|
} catch {
|
||||||
|
// 已是绝对路径,转换失败时沿用原值
|
||||||
|
}
|
||||||
|
path = path.replace(/^file:\/\//, "");
|
||||||
|
const File = p.android.importClass("java.io.File");
|
||||||
|
if (!new File(path).exists()) throw new Error("找不到所选文件");
|
||||||
|
resolve(readTextByNativePath(p, path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
const main = p.android.runtimeMainActivity();
|
const main = p.android.runtimeMainActivity();
|
||||||
const Uri = p.android.importClass("android.net.Uri");
|
const Uri = p.android.importClass("android.net.Uri");
|
||||||
const uri = Uri.parse(uriStr);
|
const uri = Uri.parse(uriStr);
|
||||||
const resolver = main.getContentResolver();
|
const resolver = main.getContentResolver();
|
||||||
const is = resolver.openInputStream(uri);
|
const is = resolver.openInputStream(uri);
|
||||||
const BufferedReader = p.android.importClass("java.io.BufferedReader");
|
resolve(readTextByStream(p, is));
|
||||||
const InputStreamReader = p.android.importClass(
|
|
||||||
"java.io.InputStreamReader",
|
|
||||||
);
|
|
||||||
const reader = new BufferedReader(new InputStreamReader(is));
|
|
||||||
let out = "";
|
|
||||||
let line = reader.readLine();
|
|
||||||
while (line != null) {
|
|
||||||
out += line;
|
|
||||||
line = reader.readLine();
|
|
||||||
if (line != null) out += "\n";
|
|
||||||
}
|
|
||||||
reader.close();
|
|
||||||
resolve(out);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e instanceof Error ? e : new Error("读取所选文件失败"));
|
reject(e instanceof Error ? e : new Error("读取所选文件失败"));
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 592 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 19 KiB |