@zfqh/uniapp 0.3.21 → 0.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/apis/core.js CHANGED
@@ -1,12 +1,6 @@
1
1
  import { GET, POST } from './request';
2
- /** 发送短信验证码 */
3
- export const sendSmsCode = (...args) => POST('/admin-api/system/auth/send-checkCode', ...args);
4
- /** 获取腾讯上传临时Token */
5
- export const getTencentToken = (...args) => GET('/app-api/infra/file/getTencentToken', ...args);
6
2
  /** 获取最新版本 */
7
3
  export const getLastVersion = (...args) => POST('/app-api/system/app/version/get-last-version-v2', ...args);
8
- /** 获取cos签名后的地址 */
9
- export const getCosUrls = (...args) => POST('/app-api/infra/cos/getUrls', ...args);
10
4
  /** 刷新token */
11
5
  export const postRefreshToken = (...args) => POST('/app-api/robot/store/auth/refreshToken?refreshToken=:refreshToken', ...args);
12
6
  /** 获取字典数据 */
@@ -22,9 +22,9 @@ type UploadParams = {
22
22
  };
23
23
  /** 上传配置 */
24
24
  export type UploadConfig = {
25
- /** 上传请求,例如获取Token */
25
+ /** 上传请求(获取临时凭证等),需由业务通过 defineConfig({ upload: { request } }) 配置 */
26
26
  request: (params?: UploadParams) => Promise<UploadResponceData>;
27
27
  };
28
- /** 上传配置 */
28
+ /** 上传配置:内置凭证接口已移除,未配置时调用会抛错 */
29
29
  declare const Upload: UploadConfig;
30
30
  export default Upload;
@@ -1,19 +1,7 @@
1
- import { getTencentToken } from '../apis/core';
2
- import cdn from './cdn';
3
- /** 上传配置 */
1
+ /** 上传配置:内置凭证接口已移除,未配置时调用会抛错 */
4
2
  const Upload = {
5
- /** 上传请求,默认调用 getTencentToken 获取腾讯云临时密钥 */
6
3
  request: async () => {
7
- const { data } = await getTencentToken();
8
- return {
9
- secret_id: data.tmpSecretId,
10
- secret_key: data.tmpSecretKey,
11
- session_token: data.sessionToken,
12
- create_time: data.createTime,
13
- expire_time: data.expirationTime,
14
- bucket: data.bucket,
15
- region: cdn.isAccelerate ? 'accelerate' : data.region,
16
- };
4
+ throw new Error('未配置上传凭证请求:请通过 defineConfig({ upload: { request } }) 配置 upload.request');
17
5
  },
18
6
  };
19
7
  export default Upload;
@@ -3,7 +3,7 @@ export declare function checkRefreshToken(): Promise<boolean>;
3
3
  export interface UploadFileOptions {
4
4
  key?: string;
5
5
  path?: string;
6
- /** 获取上传任务实例,可用于 task.abort() 取消上传;token 过期重试时会再次回调 */
6
+ /** 获取上传任务实例,可用于 task.abort() 取消上传;H5 宿主 webview 走 plus.uploader 时为含 abort / onProgressUpdate / offProgressUpdate 的适配对象;token 过期重试时会再次回调 */
7
7
  getTask?: ((task: UniNamespace.UploadTask) => void) | ((task: any) => void);
8
8
  }
9
9
  export declare function uploadFile(file: any, opts?: UploadFileOptions): Promise<{
package/lib/utils/cos.js CHANGED
@@ -3,6 +3,7 @@ import SHA1 from 'crypto-js/sha1';
3
3
  import Base64 from 'crypto-js/enc-base64';
4
4
  import Crypto from 'crypto-js/core';
5
5
  import upload from '../configs/upload';
6
+ import { IS_WEBVIEW_ENV } from './consts';
6
7
  import { cosCompress } from './index';
7
8
  const data = {};
8
9
  let promise = null;
@@ -20,81 +21,196 @@ export async function checkRefreshToken() {
20
21
  }
21
22
  return promise;
22
23
  }
24
+ /**
25
+ * 是否使用 HTML5+ Uploader:
26
+ * H5 页面被宿主 web-view 嵌入(IS_WEBVIEW_ENV)、plus.uploader 可用,且存在文件路径。
27
+ */
28
+ function canUsePlusUploader(filePath) {
29
+ let available = false;
30
+ // #ifdef WEB || H5
31
+ available =
32
+ IS_WEBVIEW_ENV && typeof filePath === 'string' && !!filePath && typeof plus !== 'undefined' && !!plus.uploader;
33
+ // #endif
34
+ return available;
35
+ }
23
36
  class COS {
37
+ /** 生成 COS 上传签名表单 */
38
+ getFormData(Bucket, Key) {
39
+ const now = data.create_time || Math.round(Date.now() / 1000);
40
+ const exp = now + 1800; // 30分钟过期
41
+ const qKeyTime = now + ';' + exp;
42
+ const qSignAlgorithm = 'sha1';
43
+ const qAk = data.secret_id;
44
+ const policyData = {
45
+ expiration: new Date(exp * 1000).toISOString(),
46
+ conditions: [
47
+ { 'q-sign-algorithm': qSignAlgorithm },
48
+ { 'q-ak': qAk },
49
+ { 'q-sign-time': qKeyTime },
50
+ { bucket: Bucket },
51
+ { key: Key },
52
+ ],
53
+ };
54
+ const policy = JSON.stringify(policyData);
55
+ const signKey = HmacSHA1(qKeyTime, data.secret_key).toString(Crypto.enc.Hex);
56
+ const stringToSign = SHA1(policy).toString(Crypto.enc.Hex);
57
+ const qSignature = HmacSHA1(stringToSign, signKey).toString(Crypto.enc.Hex);
58
+ return {
59
+ key: Key,
60
+ policy: Base64.stringify(Crypto.enc.Utf8.parse(policy)),
61
+ success_action_status: 200,
62
+ 'x-cos-security-token': data.session_token,
63
+ 'q-sign-algorithm': qSignAlgorithm,
64
+ 'q-ak': qAk,
65
+ 'q-key-time': qKeyTime,
66
+ 'q-signature': qSignature,
67
+ };
68
+ }
69
+ /**
70
+ * 统一上传入口:内部按运行环境选择 plus.uploader 或 uni.uploadFile,
71
+ * 两条链路的 success / fail / getTask 回调形态一致。
72
+ */
73
+ uploadHandler(options) {
74
+ const { url, filePath, file, name = 'file', formData = {}, getTask, success, fail } = options;
75
+ // 链路 1:H5 宿主 webview + plus.uploader + 文件路径(canUsePlusUploader 内部判断,命中后不再走 uni.uploadFile)
76
+ // #ifdef WEB || H5
77
+ if (canUsePlusUploader(filePath)) {
78
+ let plusTask;
79
+ let settled = false;
80
+ const progressCallbacks = new Set();
81
+ const plusSuccess = (res) => {
82
+ if (settled) {
83
+ return;
84
+ }
85
+ settled = true;
86
+ success?.(res);
87
+ };
88
+ const plusFail = (err) => {
89
+ if (settled) {
90
+ return;
91
+ }
92
+ settled = true;
93
+ fail?.(err);
94
+ };
95
+ try {
96
+ plusTask = plus.uploader.createUpload(url, { method: 'POST' }, (upload, status) => {
97
+ const statusCode = Number(status) || 0;
98
+ if (statusCode > 0) {
99
+ plusSuccess({ statusCode, data: String(upload.responseText || '') });
100
+ }
101
+ else {
102
+ // 网络失败,未取得 HTTP 状态码
103
+ plusFail({ errMsg: 'uploadFile:fail' });
104
+ }
105
+ });
106
+ plusTask.addEventListener('statechanged', (upload) => {
107
+ const total = Number(upload.totalSize) || 0;
108
+ const sent = Number(upload.uploadedSize) || 0;
109
+ const progress = total > 0 ? Math.round((sent / total) * 100) : 0;
110
+ progressCallbacks.forEach((cb) => cb({ progress, totalBytesSent: sent, totalBytesExpectedToSend: total }));
111
+ });
112
+ Object.keys(formData).forEach((key) => {
113
+ const value = formData[key];
114
+ if (value !== undefined && value !== null) {
115
+ plusTask.addData(key, String(value));
116
+ }
117
+ });
118
+ if (plusTask.addFile(filePath, { key: name })) {
119
+ const taskLike = {
120
+ abort: () => {
121
+ if (settled) {
122
+ return;
123
+ }
124
+ settled = true;
125
+ plusTask.abort();
126
+ fail?.({ errMsg: 'uploadFile:fail abort' });
127
+ },
128
+ onProgressUpdate: (cb) => {
129
+ progressCallbacks.add(cb);
130
+ },
131
+ offProgressUpdate: (cb) => {
132
+ progressCallbacks.delete(cb);
133
+ },
134
+ };
135
+ getTask?.(taskLike);
136
+ plusTask.start();
137
+ return;
138
+ }
139
+ // 已确定走 plus 链路:路径无法被 HTML5+ 读取(如 blob/content)时直接失败,不再回退 uni.uploadFile
140
+ plusFail({ errMsg: 'uploadFile:fail' });
141
+ plusTask.abort();
142
+ }
143
+ catch (e) {
144
+ plusFail({ errMsg: 'uploadFile:fail' });
145
+ plusTask?.abort();
146
+ }
147
+ return;
148
+ }
149
+ // #endif
150
+ // 链路 2:uni.uploadFile(默认链路)
151
+ const task = uni.uploadFile({
152
+ url,
153
+ filePath,
154
+ file,
155
+ name,
156
+ formData,
157
+ success: (res) => {
158
+ success?.({ statusCode: res.statusCode, data: String(res.data ?? '') });
159
+ },
160
+ fail: (err) => {
161
+ fail?.(err);
162
+ },
163
+ });
164
+ getTask?.(task);
165
+ }
166
+ /** 处理上传结果:成功 / token 过期重试 / 失败 */
167
+ handleUploadResult(params, cosHost, statusCode, body, resolve, reject) {
168
+ if (statusCode === 200) {
169
+ resolve({ Location: `${cosHost}/${params.Key}` });
170
+ return;
171
+ }
172
+ const resBody = String(body || '');
173
+ const codeMatch = resBody.match(/<Code>([^<>]+)<\/Code>/);
174
+ if (codeMatch && codeMatch[1] === 'ExpiredToken' && data.expire_time !== 0) {
175
+ data.expire_time = 0;
176
+ checkRefreshToken()
177
+ .then(() => {
178
+ this.uploadFile(params)
179
+ .then((e) => {
180
+ resolve(e);
181
+ })
182
+ .catch((e) => {
183
+ reject(e);
184
+ });
185
+ })
186
+ .catch((e) => {
187
+ reject(e);
188
+ });
189
+ return;
190
+ }
191
+ const err = {
192
+ code: statusCode,
193
+ msg: 'Upload Fail',
194
+ };
195
+ const msgMatch = resBody.match(/<Message>([^<>]+)<\/Message>/);
196
+ if (msgMatch) {
197
+ err.msg = msgMatch[1];
198
+ }
199
+ reject(err);
200
+ }
24
201
  async uploadFile(params) {
25
202
  const { Bucket, Region, Key, FilePath, File } = params;
203
+ const cosHost = `${Bucket}.cos.${Region}.myqcloud.com`;
26
204
  return new Promise((resolve, reject) => {
27
- const cosHost = `${Bucket}.cos.${Region}.myqcloud.com`;
28
- const now = data.create_time || Math.round(Date.now() / 1000);
29
- const exp = now + 1800; // 30分钟过期
30
- const qKeyTime = now + ';' + exp;
31
- const qSignAlgorithm = 'sha1';
32
- const qAk = data.secret_id;
33
- const policyData = {
34
- expiration: new Date(exp * 1000).toISOString(),
35
- conditions: [
36
- { 'q-sign-algorithm': qSignAlgorithm },
37
- { 'q-ak': qAk },
38
- { 'q-sign-time': qKeyTime },
39
- { bucket: Bucket },
40
- { key: Key },
41
- ],
42
- };
43
- const policy = JSON.stringify(policyData);
44
- const signKey = HmacSHA1(qKeyTime, data.secret_key).toString(Crypto.enc.Hex);
45
- const stringToSign = SHA1(policy).toString(Crypto.enc.Hex);
46
- const qSignature = HmacSHA1(stringToSign, signKey).toString(Crypto.enc.Hex);
47
- const FormData = {
48
- key: Key,
49
- policy: Base64.stringify(Crypto.enc.Utf8.parse(policy)),
50
- success_action_status: 200,
51
- 'x-cos-security-token': data.session_token,
52
- 'q-sign-algorithm': qSignAlgorithm,
53
- 'q-ak': qAk,
54
- 'q-key-time': qKeyTime,
55
- 'q-signature': qSignature,
56
- };
57
- const task = uni.uploadFile({
205
+ this.uploadHandler({
58
206
  url: `https://${cosHost}`,
59
207
  filePath: FilePath,
60
208
  file: File,
61
209
  name: 'file',
62
- formData: FormData,
210
+ formData: this.getFormData(Bucket, Key),
211
+ getTask: params.getTask,
63
212
  success: (res) => {
64
- if (res.statusCode === 200) {
65
- resolve({ Location: `${cosHost}/${Key}` });
66
- }
67
- else {
68
- const { data: e, statusCode } = res;
69
- const codeMatch = e.match(/<Code>([^<>]+)<\/Code>/);
70
- if (codeMatch && codeMatch[1] === 'ExpiredToken' && data.expire_time !== 0) {
71
- data.expire_time = 0;
72
- checkRefreshToken()
73
- .then(() => {
74
- this.uploadFile(params)
75
- .then((e) => {
76
- resolve(e);
77
- })
78
- .catch((e) => {
79
- reject(e);
80
- });
81
- })
82
- .catch((e) => {
83
- reject(e);
84
- });
85
- }
86
- else {
87
- const err = {
88
- code: statusCode,
89
- msg: 'Upload Fail',
90
- };
91
- const msgMatch = e.match(/<Message>([^<>]+)<\/Message>/);
92
- if (msgMatch) {
93
- err.msg = msgMatch[1];
94
- }
95
- reject(err);
96
- }
97
- }
213
+ this.handleUploadResult(params, cosHost, res.statusCode, res.data, resolve, reject);
98
214
  },
99
215
  fail: (err) => {
100
216
  reject({
@@ -102,7 +218,6 @@ class COS {
102
218
  });
103
219
  },
104
220
  });
105
- params.getTask?.(task);
106
221
  });
107
222
  }
108
223
  }
@@ -1,5 +1,5 @@
1
1
  import { I18nOptions } from 'vue-i18n';
2
2
  export declare const defaultLocale: any;
3
- export declare const fallbackLocale = "zh-CN";
3
+ export declare const fallbackLocale = "zh-Hans";
4
4
  export declare function createI18n(opts: I18nOptions): import("vue-i18n").I18n<import("vue-i18n").LocaleMessages<import("vue-i18n").VueMessageType>, import("vue-i18n").IntlDateTimeFormats, import("vue-i18n").IntlNumberFormats, boolean>;
5
5
  export declare function getLocaleMessage(key: string, values?: Record<string, string>): string;
@@ -1,7 +1,7 @@
1
1
  import { createI18n as _createI18n } from 'vue-i18n';
2
2
  import query from './query';
3
- export const defaultLocale = query.locale || query.xLanguage || uni.getLocale();
4
- export const fallbackLocale = 'zh-CN';
3
+ export const defaultLocale = query.locale || uni.getLocale();
4
+ export const fallbackLocale = 'zh-Hans';
5
5
  // #ifdef APP
6
6
  // APP下不支持动态变量,需要重写
7
7
  function replacePlaceholders(str, values) {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@zfqh/uniapp",
3
- "version": "0.3.21",
3
+ "version": "0.3.23",
4
4
  "description": "uniapp公共模块",
5
5
  "author": "yinjiazeng@163.com",
6
- "license": "MIT",
6
+ "license": "UNLICENSED",
7
7
  "main": "lib/index.js",
8
8
  "types": "lib/index.d.ts",
9
9
  "publishConfig": {
@@ -22,11 +22,10 @@
22
22
  "docs",
23
23
  "lib",
24
24
  "types",
25
- "CHANGELOG.md",
26
25
  "README.md"
27
26
  ],
28
27
  "dependencies": {
29
- "@gmcb/uv-ui": "0.3.6",
28
+ "@gmcb/uv-ui": "0.3.7",
30
29
  "@tencentcloud/chat-uikit-engine": "^2.3.2",
31
30
  "@tencentcloud/tui-core": "^2.3.2",
32
31
  "crypto-js": "^4.2.0",
@@ -45,5 +44,5 @@
45
44
  "@types/qs": "^6.9.16",
46
45
  "@types/url-parse": "^1.4.11"
47
46
  },
48
- "gitHead": "cc5d98c2352fc99519a4660779a24cf992faad32"
47
+ "gitHead": "5dbc4d414ca9c84f7b65abbf92f8ce0f3750a1f3"
49
48
  }