yx-web-sdk 0.0.26 → 0.0.27

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.
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../index.vue_vue_type_style_index_0_lang-DectxdQg.mjs";
1
+ import { _ as _sfc_main } from "../index.vue_vue_type_style_index_0_lang-Bzp6GXSr.mjs";
2
2
  export {
3
3
  _sfc_main as ChatCard,
4
4
  _sfc_main as default
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../index.vue_vue_type_style_index_0_lang-CJ6N1ut8.mjs";
1
+ import { _ as _sfc_main } from "../index.vue_vue_type_style_index_0_lang-BHF09625.mjs";
2
2
  export {
3
3
  _sfc_main as ChatContent,
4
4
  _sfc_main as default
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../index.vue_vue_type_style_index_0_lang-D8SCARR2.mjs";
1
+ import { _ as _sfc_main } from "../index.vue_vue_type_style_index_0_lang-Bv3It4GH.mjs";
2
2
  export {
3
3
  _sfc_main as ChatPage,
4
4
  _sfc_main as default
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../index.vue_vue_type_script_setup_true_lang-BpYIybBN.mjs";
1
+ import { _ as _sfc_main } from "../index.vue_vue_type_script_setup_true_lang-DWgq-XoC.mjs";
2
2
  export {
3
3
  _sfc_main as ChatRoom,
4
4
  _sfc_main as default
@@ -2,7 +2,7 @@ import { defineComponent, computed, ref, onMounted, onUnmounted, openBlock, crea
2
2
  import { useRouter } from "vue-router";
3
3
  import { Spin, Result, Button, Empty, message } from "ant-design-vue";
4
4
  import { s as setGlobalRequest, c as useImSdkStore, a as useServerStore, u as useChannelStore, b as useUserStore, d as useGlobalStore } from "../index.vue_vue_type_style_index_0_lang-Bb0HV9Zp.mjs";
5
- import { _ as _sfc_main$1 } from "../index.vue_vue_type_style_index_0_lang-D8SCARR2.mjs";
5
+ import { _ as _sfc_main$1 } from "../index.vue_vue_type_style_index_0_lang-Bv3It4GH.mjs";
6
6
  import { S as ServerChannelSidebar } from "../index-BvfJ77uD.mjs";
7
7
  import { M as MembersSidebar } from "../index-DwPi-usb.mjs";
8
8
  import { _ as _export_sfc } from "../_plugin-vue_export-helper-1tPrXgE0.mjs";
@@ -1,5 +1,5 @@
1
1
  import { defineComponent, ref, computed, watch, openBlock, createBlock, createElementBlock } from "vue";
2
- import { _ as _sfc_main$1 } from "./index.vue_vue_type_style_index_0_lang-CJ6N1ut8.mjs";
2
+ import { _ as _sfc_main$1 } from "./index.vue_vue_type_style_index_0_lang-BHF09625.mjs";
3
3
  import { u as useChannelStore, a as useServerStore, b as useUserStore } from "./index.vue_vue_type_style_index_0_lang-Bb0HV9Zp.mjs";
4
4
  const _hoisted_1 = { key: 1 };
5
5
  const HISTORY_LIMIT = 100;
@@ -2,7 +2,7 @@ import { createVNode, ref, computed, watch, nextTick, defineComponent, openBlock
2
2
  import { useRouter } from "vue-router";
3
3
  import { Input, Image, Popover, Button, Upload, message } from "ant-design-vue";
4
4
  import { A as AitPersonPopover } from "./index-BhLJLbtt.mjs";
5
- import { L as LoadingOutlined, _ as _sfc_main$2, a as _sfc_main$3 } from "./index.vue_vue_type_style_index_0_lang-DectxdQg.mjs";
5
+ import { L as LoadingOutlined, _ as _sfc_main$2, a as _sfc_main$3 } from "./index.vue_vue_type_style_index_0_lang-Bzp6GXSr.mjs";
6
6
  import { _ as _sfc_main$1 } from "./index.vue_vue_type_style_index_0_lang-BxXBf_VA.mjs";
7
7
  import { a as useServerStore, g as getServerMembersByAccids, u as useChannelStore, e as getImageMsgDisplaySize, f as filterStr, i as isLt } from "./index.vue_vue_type_style_index_0_lang-Bb0HV9Zp.mjs";
8
8
  import { useEventListener, useDebounceFn } from "@vueuse/core";
@@ -757,41 +757,56 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
757
757
  }
758
758
  };
759
759
  const handlePaste = async (e) => {
760
+ var _a;
760
761
  if (props.sendDisabled || attachmentUploading.value) return;
761
762
  const clipboardData = e.clipboardData;
762
763
  if (!clipboardData) return;
763
- const items = clipboardData.items;
764
- for (let i = 0; i < items.length; i++) {
765
- const item = items[i];
766
- if (item.type.startsWith("image/")) {
767
- const file = item.getAsFile();
768
- if (file) {
769
- e.preventDefault();
770
- if (!isLt(file.size, MAX_IMG_SIZE)) {
771
- message.error("图片大小最大支持20M");
772
- return;
773
- }
774
- attachmentUploading.value = true;
775
- try {
776
- const uploadResult = await channelStore.uploadImage({
777
- file,
778
- onUploadStart: () => {
779
- attachmentUploading.value = true;
780
- },
781
- onUploadDone: () => {
782
- attachmentUploading.value = false;
783
- }
784
- });
785
- attachment.value = uploadResult;
786
- } catch (error) {
787
- console.error("上传图片失败:", error);
788
- message.error("上传图片失败");
789
- attachmentUploading.value = false;
790
- }
764
+ let imageFile = null;
765
+ if (clipboardData.items && clipboardData.items.length) {
766
+ for (let i = 0; i < clipboardData.items.length; i++) {
767
+ const item = clipboardData.items[i];
768
+ if (item.type.startsWith("image/")) {
769
+ imageFile = item.getAsFile();
770
+ break;
771
+ }
772
+ }
773
+ }
774
+ if (!imageFile && clipboardData.files && clipboardData.files.length) {
775
+ for (let i = 0; i < clipboardData.files.length; i++) {
776
+ if (clipboardData.files[i].type.startsWith("image/")) {
777
+ imageFile = clipboardData.files[i];
778
+ break;
791
779
  }
792
- return;
793
780
  }
794
781
  }
782
+ if (!imageFile) return;
783
+ e.preventDefault();
784
+ const existingFile = (_a = attachment.value) == null ? void 0 : _a.__file;
785
+ if (existingFile && existingFile.size === imageFile.size && existingFile.type === imageFile.type) {
786
+ return;
787
+ }
788
+ if (!isLt(imageFile.size, MAX_IMG_SIZE)) {
789
+ message.error("图片大小最大支持20M");
790
+ return;
791
+ }
792
+ attachmentUploading.value = true;
793
+ try {
794
+ const uploadResult = await channelStore.uploadImage({
795
+ file: imageFile,
796
+ onUploadStart: () => {
797
+ attachmentUploading.value = true;
798
+ },
799
+ onUploadDone: () => {
800
+ attachmentUploading.value = false;
801
+ }
802
+ });
803
+ uploadResult.__file = imageFile;
804
+ attachment.value = uploadResult;
805
+ } catch (error) {
806
+ console.error("上传图片失败:", error);
807
+ message.error("上传图片失败");
808
+ attachmentUploading.value = false;
809
+ }
795
810
  };
796
811
  const uploadImgHandler = async (file) => {
797
812
  if (!file.type.startsWith("image/")) {
@@ -1,10 +1,10 @@
1
1
  import { defineComponent, ref, h, watch, computed, openBlock, createElementBlock, createElementVNode, toDisplayString, createCommentVNode, createVNode } from "vue";
2
2
  import { List, Typography } from "ant-design-vue";
3
- import { _ as _sfc_main$1 } from "./index.vue_vue_type_script_setup_true_lang-BpYIybBN.mjs";
3
+ import { _ as _sfc_main$1 } from "./index.vue_vue_type_script_setup_true_lang-DWgq-XoC.mjs";
4
4
  import { a as useServerStore, u as useChannelStore } from "./index.vue_vue_type_style_index_0_lang-Bb0HV9Zp.mjs";
5
5
  import "./index-L_drmZir.mjs";
6
6
  import { useLocalStorage } from "@vueuse/core";
7
- import { L as LoadingOutlined } from "./index.vue_vue_type_style_index_0_lang-DectxdQg.mjs";
7
+ import { L as LoadingOutlined } from "./index.vue_vue_type_style_index_0_lang-Bzp6GXSr.mjs";
8
8
  const _hoisted_1 = { class: "yx-chat-page-container" };
9
9
  const _hoisted_2 = { class: "header-wrap" };
10
10
  const _hoisted_3 = { class: "left" };
@@ -220,19 +220,90 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
220
220
  return props.msg.fromAccount === props.myAccid && nowTime.value - props.msg.time <= REVOCATION_INTERVAL;
221
221
  });
222
222
  const { copy } = useClipboard();
223
+ const imageBlobCache = /* @__PURE__ */ new Map();
224
+ async function fetchImageAsPngBlob(url) {
225
+ const response = await fetch(url);
226
+ const blob = await response.blob();
227
+ if (blob.type === "image/png") {
228
+ return blob;
229
+ }
230
+ return new Promise((resolve, reject) => {
231
+ const objectUrl = URL.createObjectURL(blob);
232
+ const img = document.createElement("img");
233
+ img.crossOrigin = "anonymous";
234
+ img.onload = () => {
235
+ const canvas = document.createElement("canvas");
236
+ canvas.width = img.naturalWidth;
237
+ canvas.height = img.naturalHeight;
238
+ const ctx = canvas.getContext("2d");
239
+ ctx.drawImage(img, 0, 0);
240
+ canvas.toBlob((b) => {
241
+ URL.revokeObjectURL(objectUrl);
242
+ if (b) {
243
+ resolve(b);
244
+ } else {
245
+ reject(new Error("canvas toBlob 失败"));
246
+ }
247
+ }, "image/png");
248
+ };
249
+ img.onerror = () => {
250
+ URL.revokeObjectURL(objectUrl);
251
+ reject(new Error("图片加载失败"));
252
+ };
253
+ img.src = objectUrl;
254
+ });
255
+ }
256
+ function clearImageBlobCache() {
257
+ imageBlobCache.clear();
258
+ }
223
259
  const emit = __emit;
224
260
  const textMsgContextMenuVisible = ref(false);
225
261
  const textMsgContextMenuX = ref(0);
226
262
  const textMsgContextMenuY = ref(0);
227
263
  const textMsgContextMenuRef = ref(null);
228
- function onTextMsgContextMenu(e) {
264
+ const eventSource = ref("");
265
+ function onTextMsgContextMenu(e, p) {
266
+ var _a;
267
+ eventSource.value = p ?? "normal";
229
268
  textMsgContextMenuX.value = e.clientX;
230
269
  textMsgContextMenuY.value = e.clientY;
231
- textMsgContextMenuVisible.value = true;
270
+ if (p === "img") {
271
+ const imgUrl = (_a = props.msg.attach) == null ? void 0 : _a.url;
272
+ if (imgUrl && !imageBlobCache.has(imgUrl)) {
273
+ fetchImageAsPngBlob(imgUrl).then((blob) => {
274
+ imageBlobCache.set(imgUrl, blob);
275
+ }).catch((err) => {
276
+ console.error("预下载图片失败: ", err);
277
+ });
278
+ }
279
+ }
280
+ setTimeout(
281
+ () => {
282
+ textMsgContextMenuVisible.value = true;
283
+ },
284
+ p === "img" ? 150 : 0
285
+ );
232
286
  }
233
287
  function onCopyClick() {
234
- var _a;
288
+ var _a, _b;
235
289
  if (props.msg.type === "text") {
290
+ if (eventSource.value === "img") {
291
+ const imgUrl = (_a = props.msg.attach) == null ? void 0 : _a.url;
292
+ const cachedBlob = imageBlobCache.get(imgUrl);
293
+ if (cachedBlob) {
294
+ navigator.clipboard.write([new ClipboardItem({ "image/png": cachedBlob })]).then(() => {
295
+ }).catch((err) => {
296
+ console.error("copy image error: ", err);
297
+ message.error("复制图片失败");
298
+ }).finally(() => {
299
+ textMsgContextMenuVisible.value = false;
300
+ });
301
+ } else {
302
+ message.warning("图片正在加载中,请稍后再试");
303
+ textMsgContextMenuVisible.value = false;
304
+ }
305
+ return;
306
+ }
236
307
  copy(props.msg.body).then(() => {
237
308
  }).catch((err) => {
238
309
  console.error("copy error: ", err);
@@ -242,13 +313,20 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
242
313
  });
243
314
  }
244
315
  if (props.msg.type === "image") {
245
- copy((_a = props.msg.attach) == null ? void 0 : _a.url).then(() => {
246
- }).catch((err) => {
247
- console.error("copy error: ", err);
248
- message.error("复制失败");
249
- }).finally(() => {
316
+ const imgUrl = (_b = props.msg.attach) == null ? void 0 : _b.url;
317
+ const cachedBlob = imageBlobCache.get(imgUrl);
318
+ if (cachedBlob) {
319
+ navigator.clipboard.write([new ClipboardItem({ "image/png": cachedBlob })]).then(() => {
320
+ }).catch((err) => {
321
+ console.error("copy image error: ", err);
322
+ message.error("复制图片失败");
323
+ }).finally(() => {
324
+ textMsgContextMenuVisible.value = false;
325
+ });
326
+ } else {
327
+ message.warning("图片正在加载中,请稍后再试");
250
328
  textMsgContextMenuVisible.value = false;
251
- });
329
+ }
252
330
  }
253
331
  }
254
332
  function onQuoteClick() {
@@ -317,6 +395,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
317
395
  onBeforeUnmount(() => {
318
396
  document.removeEventListener("click", handleClickOutside, true);
319
397
  document.removeEventListener("contextmenu", handleClickOutside, true);
398
+ clearImageBlobCache();
320
399
  });
321
400
  const myAvatar = computed(() => {
322
401
  var _a, _b, _c, _d;
@@ -478,7 +557,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
478
557
  ], 2)) : __props.msg.type === "image" ? (openBlock(), createElementBlock("div", {
479
558
  key: 2,
480
559
  class: normalizeClass(["chatCardMainContent flash-target", { myMsg: __props.msg.fromAccount === __props.myAccid, otherMsg: __props.msg.fromAccount !== __props.myAccid }]),
481
- onContextmenu: withModifiers(onTextMsgContextMenu, ["prevent"])
560
+ onContextmenu: _cache[0] || (_cache[0] = withModifiers(($event) => onTextMsgContextMenu($event, "img"), ["prevent"]))
482
561
  }, [
483
562
  createVNode(unref(Image), {
484
563
  class: "imgMsg",
@@ -496,24 +575,28 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
496
575
  ], 34)) : (openBlock(), createElementBlock("div", {
497
576
  key: 3,
498
577
  class: normalizeClass(["chatCardMainContent", { myMsg: __props.msg.fromAccount === __props.myAccid, otherMsg: __props.msg.fromAccount !== __props.myAccid }])
499
- }, [..._cache[1] || (_cache[1] = [
578
+ }, [..._cache[3] || (_cache[3] = [
500
579
  createElementVNode("div", { class: "textMsg" }, toDisplayString("暂不支持该消息"), -1)
501
580
  ])], 2)),
502
581
  __props.msg.type === "text" && IMG_EXT.includes(((_j = (_i = __props.msg) == null ? void 0 : _i.attach) == null ? void 0 : _j.ext) || "") ? (openBlock(), createElementBlock("div", _hoisted_14, [
503
- ((_l = (_k = __props.msg) == null ? void 0 : _k.attach) == null ? void 0 : _l.size) && ((_n = (_m = __props.msg) == null ? void 0 : _m.attach) == null ? void 0 : _n.size) < MAX_IMG_PREVIEW_SIZE ? (openBlock(), createBlock(unref(Image), {
582
+ ((_l = (_k = __props.msg) == null ? void 0 : _k.attach) == null ? void 0 : _l.size) && ((_n = (_m = __props.msg) == null ? void 0 : _m.attach) == null ? void 0 : _n.size) < MAX_IMG_PREVIEW_SIZE ? (openBlock(), createElementBlock("div", {
504
583
  key: 0,
505
- class: "imgMsg",
506
- src: (_p = (_o = __props.msg) == null ? void 0 : _o.attach) == null ? void 0 : _p.url,
507
- width: attachImageSize.value.width,
508
- height: attachImageSize.value.height
509
- }, {
510
- previewMask: withCtx(() => [
511
- createElementVNode("div", _hoisted_15, [
512
- createVNode(unref(EyeOutlined))
513
- ])
514
- ]),
515
- _: 1
516
- }, 8, ["src", "width", "height"])) : (openBlock(), createBlock(_sfc_main$1, {
584
+ onContextmenu: _cache[1] || (_cache[1] = withModifiers(($event) => onTextMsgContextMenu($event, "img"), ["prevent"]))
585
+ }, [
586
+ createVNode(unref(Image), {
587
+ class: "imgMsg",
588
+ src: (_p = (_o = __props.msg) == null ? void 0 : _o.attach) == null ? void 0 : _p.url,
589
+ width: attachImageSize.value.width,
590
+ height: attachImageSize.value.height
591
+ }, {
592
+ previewMask: withCtx(() => [
593
+ createElementVNode("div", _hoisted_15, [
594
+ createVNode(unref(EyeOutlined))
595
+ ])
596
+ ]),
597
+ _: 1
598
+ }, 8, ["src", "width", "height"])
599
+ ], 32)) : (openBlock(), createBlock(_sfc_main$1, {
517
600
  key: 1,
518
601
  attach: (_q = __props.msg) == null ? void 0 : _q.attach
519
602
  }, null, 8, ["attach"]))
@@ -540,7 +623,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
540
623
  src: (_s = msgExt.value.quoteMsg.attach) == null ? void 0 : _s.url,
541
624
  width: quoteImageMsgSize.value.width,
542
625
  height: quoteImageMsgSize.value.height,
543
- onClick: _cache[0] || (_cache[0] = withModifiers(() => {
626
+ onClick: _cache[2] || (_cache[2] = withModifiers(() => {
544
627
  }, ["stop", "prevent"]))
545
628
  }, {
546
629
  previewMask: withCtx(() => [
@@ -38905,19 +38905,90 @@ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
38905
38905
  return props.msg.fromAccount === props.myAccid && nowTime.value - props.msg.time <= REVOCATION_INTERVAL;
38906
38906
  });
38907
38907
  const { copy } = useClipboard();
38908
+ const imageBlobCache = /* @__PURE__ */ new Map();
38909
+ async function fetchImageAsPngBlob(url) {
38910
+ const response = await fetch(url);
38911
+ const blob = await response.blob();
38912
+ if (blob.type === "image/png") {
38913
+ return blob;
38914
+ }
38915
+ return new Promise((resolve, reject) => {
38916
+ const objectUrl = URL.createObjectURL(blob);
38917
+ const img = document.createElement("img");
38918
+ img.crossOrigin = "anonymous";
38919
+ img.onload = () => {
38920
+ const canvas = document.createElement("canvas");
38921
+ canvas.width = img.naturalWidth;
38922
+ canvas.height = img.naturalHeight;
38923
+ const ctx = canvas.getContext("2d");
38924
+ ctx.drawImage(img, 0, 0);
38925
+ canvas.toBlob((b) => {
38926
+ URL.revokeObjectURL(objectUrl);
38927
+ if (b) {
38928
+ resolve(b);
38929
+ } else {
38930
+ reject(new Error("canvas toBlob 失败"));
38931
+ }
38932
+ }, "image/png");
38933
+ };
38934
+ img.onerror = () => {
38935
+ URL.revokeObjectURL(objectUrl);
38936
+ reject(new Error("图片加载失败"));
38937
+ };
38938
+ img.src = objectUrl;
38939
+ });
38940
+ }
38941
+ function clearImageBlobCache() {
38942
+ imageBlobCache.clear();
38943
+ }
38908
38944
  const emit = __emit;
38909
38945
  const textMsgContextMenuVisible = ref(false);
38910
38946
  const textMsgContextMenuX = ref(0);
38911
38947
  const textMsgContextMenuY = ref(0);
38912
38948
  const textMsgContextMenuRef = ref(null);
38913
- function onTextMsgContextMenu(e) {
38949
+ const eventSource = ref("");
38950
+ function onTextMsgContextMenu(e, p) {
38951
+ var _a;
38952
+ eventSource.value = p != null ? p : "normal";
38914
38953
  textMsgContextMenuX.value = e.clientX;
38915
38954
  textMsgContextMenuY.value = e.clientY;
38916
- textMsgContextMenuVisible.value = true;
38955
+ if (p === "img") {
38956
+ const imgUrl = (_a = props.msg.attach) == null ? void 0 : _a.url;
38957
+ if (imgUrl && !imageBlobCache.has(imgUrl)) {
38958
+ fetchImageAsPngBlob(imgUrl).then((blob) => {
38959
+ imageBlobCache.set(imgUrl, blob);
38960
+ }).catch((err) => {
38961
+ console.error("预下载图片失败: ", err);
38962
+ });
38963
+ }
38964
+ }
38965
+ setTimeout(
38966
+ () => {
38967
+ textMsgContextMenuVisible.value = true;
38968
+ },
38969
+ p === "img" ? 150 : 0
38970
+ );
38917
38971
  }
38918
38972
  function onCopyClick() {
38919
- var _a;
38973
+ var _a, _b;
38920
38974
  if (props.msg.type === "text") {
38975
+ if (eventSource.value === "img") {
38976
+ const imgUrl = (_a = props.msg.attach) == null ? void 0 : _a.url;
38977
+ const cachedBlob = imageBlobCache.get(imgUrl);
38978
+ if (cachedBlob) {
38979
+ navigator.clipboard.write([new ClipboardItem({ "image/png": cachedBlob })]).then(() => {
38980
+ }).catch((err) => {
38981
+ console.error("copy image error: ", err);
38982
+ message.error("复制图片失败");
38983
+ }).finally(() => {
38984
+ textMsgContextMenuVisible.value = false;
38985
+ });
38986
+ } else {
38987
+ message.warning("图片正在加载中,请稍后再试");
38988
+ textMsgContextMenuVisible.value = false;
38989
+ }
38990
+ return;
38991
+ }
38921
38992
  copy(props.msg.body).then(() => {
38922
38993
  }).catch((err) => {
38923
38994
  console.error("copy error: ", err);
@@ -38927,13 +38998,20 @@ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
38927
38998
  });
38928
38999
  }
38929
39000
  if (props.msg.type === "image") {
38930
- copy((_a = props.msg.attach) == null ? void 0 : _a.url).then(() => {
38931
- }).catch((err) => {
38932
- console.error("copy error: ", err);
38933
- message.error("复制失败");
38934
- }).finally(() => {
39001
+ const imgUrl = (_b = props.msg.attach) == null ? void 0 : _b.url;
39002
+ const cachedBlob = imageBlobCache.get(imgUrl);
39003
+ if (cachedBlob) {
39004
+ navigator.clipboard.write([new ClipboardItem({ "image/png": cachedBlob })]).then(() => {
39005
+ }).catch((err) => {
39006
+ console.error("copy image error: ", err);
39007
+ message.error("复制图片失败");
39008
+ }).finally(() => {
39009
+ textMsgContextMenuVisible.value = false;
39010
+ });
39011
+ } else {
39012
+ message.warning("图片正在加载中,请稍后再试");
38935
39013
  textMsgContextMenuVisible.value = false;
38936
- });
39014
+ }
38937
39015
  }
38938
39016
  }
38939
39017
  function onQuoteClick() {
@@ -39005,6 +39083,7 @@ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
39005
39083
  onBeforeUnmount(() => {
39006
39084
  document.removeEventListener("click", handleClickOutside, true);
39007
39085
  document.removeEventListener("contextmenu", handleClickOutside, true);
39086
+ clearImageBlobCache();
39008
39087
  });
39009
39088
  const myAvatar = computed(() => {
39010
39089
  var _a, _b, _c, _d;
@@ -39166,7 +39245,7 @@ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
39166
39245
  ], 2)) : __props.msg.type === "image" ? (openBlock(), createElementBlock("div", {
39167
39246
  key: 2,
39168
39247
  class: normalizeClass(["chatCardMainContent flash-target", { myMsg: __props.msg.fromAccount === __props.myAccid, otherMsg: __props.msg.fromAccount !== __props.myAccid }]),
39169
- onContextmenu: withModifiers(onTextMsgContextMenu, ["prevent"])
39248
+ onContextmenu: _cache[0] || (_cache[0] = withModifiers(($event) => onTextMsgContextMenu($event, "img"), ["prevent"]))
39170
39249
  }, [
39171
39250
  createVNode(unref(Image), {
39172
39251
  class: "imgMsg",
@@ -39184,24 +39263,28 @@ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
39184
39263
  ], 34)) : (openBlock(), createElementBlock("div", {
39185
39264
  key: 3,
39186
39265
  class: normalizeClass(["chatCardMainContent", { myMsg: __props.msg.fromAccount === __props.myAccid, otherMsg: __props.msg.fromAccount !== __props.myAccid }])
39187
- }, [..._cache[1] || (_cache[1] = [
39266
+ }, [..._cache[3] || (_cache[3] = [
39188
39267
  createElementVNode("div", { class: "textMsg" }, toDisplayString("暂不支持该消息"), -1)
39189
39268
  ])], 2)),
39190
39269
  __props.msg.type === "text" && IMG_EXT.includes(((_j = (_i = __props.msg) == null ? void 0 : _i.attach) == null ? void 0 : _j.ext) || "") ? (openBlock(), createElementBlock("div", _hoisted_14$3, [
39191
- ((_l = (_k = __props.msg) == null ? void 0 : _k.attach) == null ? void 0 : _l.size) && ((_n = (_m = __props.msg) == null ? void 0 : _m.attach) == null ? void 0 : _n.size) < MAX_IMG_PREVIEW_SIZE ? (openBlock(), createBlock(unref(Image), {
39270
+ ((_l = (_k = __props.msg) == null ? void 0 : _k.attach) == null ? void 0 : _l.size) && ((_n = (_m = __props.msg) == null ? void 0 : _m.attach) == null ? void 0 : _n.size) < MAX_IMG_PREVIEW_SIZE ? (openBlock(), createElementBlock("div", {
39192
39271
  key: 0,
39193
- class: "imgMsg",
39194
- src: (_p = (_o = __props.msg) == null ? void 0 : _o.attach) == null ? void 0 : _p.url,
39195
- width: attachImageSize.value.width,
39196
- height: attachImageSize.value.height
39197
- }, {
39198
- previewMask: withCtx(() => [
39199
- createElementVNode("div", _hoisted_15$3, [
39200
- createVNode(unref(EyeOutlined))
39201
- ])
39202
- ]),
39203
- _: 1
39204
- }, 8, ["src", "width", "height"])) : (openBlock(), createBlock(_sfc_main$9, {
39272
+ onContextmenu: _cache[1] || (_cache[1] = withModifiers(($event) => onTextMsgContextMenu($event, "img"), ["prevent"]))
39273
+ }, [
39274
+ createVNode(unref(Image), {
39275
+ class: "imgMsg",
39276
+ src: (_p = (_o = __props.msg) == null ? void 0 : _o.attach) == null ? void 0 : _p.url,
39277
+ width: attachImageSize.value.width,
39278
+ height: attachImageSize.value.height
39279
+ }, {
39280
+ previewMask: withCtx(() => [
39281
+ createElementVNode("div", _hoisted_15$3, [
39282
+ createVNode(unref(EyeOutlined))
39283
+ ])
39284
+ ]),
39285
+ _: 1
39286
+ }, 8, ["src", "width", "height"])
39287
+ ], 32)) : (openBlock(), createBlock(_sfc_main$9, {
39205
39288
  key: 1,
39206
39289
  attach: (_q = __props.msg) == null ? void 0 : _q.attach
39207
39290
  }, null, 8, ["attach"]))
@@ -39228,7 +39311,7 @@ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
39228
39311
  src: (_s = msgExt.value.quoteMsg.attach) == null ? void 0 : _s.url,
39229
39312
  width: quoteImageMsgSize.value.width,
39230
39313
  height: quoteImageMsgSize.value.height,
39231
- onClick: _cache[0] || (_cache[0] = withModifiers(() => {
39314
+ onClick: _cache[2] || (_cache[2] = withModifiers(() => {
39232
39315
  }, ["stop", "prevent"]))
39233
39316
  }, {
39234
39317
  previewMask: withCtx(() => [
@@ -40243,41 +40326,56 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({
40243
40326
  }
40244
40327
  };
40245
40328
  const handlePaste = async (e) => {
40329
+ var _a;
40246
40330
  if (props.sendDisabled || attachmentUploading.value) return;
40247
40331
  const clipboardData = e.clipboardData;
40248
40332
  if (!clipboardData) return;
40249
- const items = clipboardData.items;
40250
- for (let i = 0; i < items.length; i++) {
40251
- const item = items[i];
40252
- if (item.type.startsWith("image/")) {
40253
- const file = item.getAsFile();
40254
- if (file) {
40255
- e.preventDefault();
40256
- if (!isLt(file.size, MAX_IMG_SIZE)) {
40257
- message.error("图片大小最大支持20M");
40258
- return;
40259
- }
40260
- attachmentUploading.value = true;
40261
- try {
40262
- const uploadResult = await channelStore.uploadImage({
40263
- file,
40264
- onUploadStart: () => {
40265
- attachmentUploading.value = true;
40266
- },
40267
- onUploadDone: () => {
40268
- attachmentUploading.value = false;
40269
- }
40270
- });
40271
- attachment.value = uploadResult;
40272
- } catch (error) {
40273
- console.error("上传图片失败:", error);
40274
- message.error("上传图片失败");
40275
- attachmentUploading.value = false;
40276
- }
40333
+ let imageFile = null;
40334
+ if (clipboardData.items && clipboardData.items.length) {
40335
+ for (let i = 0; i < clipboardData.items.length; i++) {
40336
+ const item = clipboardData.items[i];
40337
+ if (item.type.startsWith("image/")) {
40338
+ imageFile = item.getAsFile();
40339
+ break;
40277
40340
  }
40278
- return;
40279
40341
  }
40280
40342
  }
40343
+ if (!imageFile && clipboardData.files && clipboardData.files.length) {
40344
+ for (let i = 0; i < clipboardData.files.length; i++) {
40345
+ if (clipboardData.files[i].type.startsWith("image/")) {
40346
+ imageFile = clipboardData.files[i];
40347
+ break;
40348
+ }
40349
+ }
40350
+ }
40351
+ if (!imageFile) return;
40352
+ e.preventDefault();
40353
+ const existingFile = (_a = attachment.value) == null ? void 0 : _a.__file;
40354
+ if (existingFile && existingFile.size === imageFile.size && existingFile.type === imageFile.type) {
40355
+ return;
40356
+ }
40357
+ if (!isLt(imageFile.size, MAX_IMG_SIZE)) {
40358
+ message.error("图片大小最大支持20M");
40359
+ return;
40360
+ }
40361
+ attachmentUploading.value = true;
40362
+ try {
40363
+ const uploadResult = await channelStore.uploadImage({
40364
+ file: imageFile,
40365
+ onUploadStart: () => {
40366
+ attachmentUploading.value = true;
40367
+ },
40368
+ onUploadDone: () => {
40369
+ attachmentUploading.value = false;
40370
+ }
40371
+ });
40372
+ uploadResult.__file = imageFile;
40373
+ attachment.value = uploadResult;
40374
+ } catch (error) {
40375
+ console.error("上传图片失败:", error);
40376
+ message.error("上传图片失败");
40377
+ attachmentUploading.value = false;
40378
+ }
40281
40379
  };
40282
40380
  const uploadImgHandler = async (file) => {
40283
40381
  if (!file.type.startsWith("image/")) {
@@ -38902,19 +38902,90 @@
38902
38902
  return props.msg.fromAccount === props.myAccid && nowTime.value - props.msg.time <= REVOCATION_INTERVAL;
38903
38903
  });
38904
38904
  const { copy } = core.useClipboard();
38905
+ const imageBlobCache = /* @__PURE__ */ new Map();
38906
+ async function fetchImageAsPngBlob(url) {
38907
+ const response = await fetch(url);
38908
+ const blob = await response.blob();
38909
+ if (blob.type === "image/png") {
38910
+ return blob;
38911
+ }
38912
+ return new Promise((resolve, reject) => {
38913
+ const objectUrl = URL.createObjectURL(blob);
38914
+ const img = document.createElement("img");
38915
+ img.crossOrigin = "anonymous";
38916
+ img.onload = () => {
38917
+ const canvas = document.createElement("canvas");
38918
+ canvas.width = img.naturalWidth;
38919
+ canvas.height = img.naturalHeight;
38920
+ const ctx = canvas.getContext("2d");
38921
+ ctx.drawImage(img, 0, 0);
38922
+ canvas.toBlob((b) => {
38923
+ URL.revokeObjectURL(objectUrl);
38924
+ if (b) {
38925
+ resolve(b);
38926
+ } else {
38927
+ reject(new Error("canvas toBlob 失败"));
38928
+ }
38929
+ }, "image/png");
38930
+ };
38931
+ img.onerror = () => {
38932
+ URL.revokeObjectURL(objectUrl);
38933
+ reject(new Error("图片加载失败"));
38934
+ };
38935
+ img.src = objectUrl;
38936
+ });
38937
+ }
38938
+ function clearImageBlobCache() {
38939
+ imageBlobCache.clear();
38940
+ }
38905
38941
  const emit = __emit;
38906
38942
  const textMsgContextMenuVisible = vue.ref(false);
38907
38943
  const textMsgContextMenuX = vue.ref(0);
38908
38944
  const textMsgContextMenuY = vue.ref(0);
38909
38945
  const textMsgContextMenuRef = vue.ref(null);
38910
- function onTextMsgContextMenu(e) {
38946
+ const eventSource = vue.ref("");
38947
+ function onTextMsgContextMenu(e, p) {
38948
+ var _a;
38949
+ eventSource.value = p != null ? p : "normal";
38911
38950
  textMsgContextMenuX.value = e.clientX;
38912
38951
  textMsgContextMenuY.value = e.clientY;
38913
- textMsgContextMenuVisible.value = true;
38952
+ if (p === "img") {
38953
+ const imgUrl = (_a = props.msg.attach) == null ? void 0 : _a.url;
38954
+ if (imgUrl && !imageBlobCache.has(imgUrl)) {
38955
+ fetchImageAsPngBlob(imgUrl).then((blob) => {
38956
+ imageBlobCache.set(imgUrl, blob);
38957
+ }).catch((err) => {
38958
+ console.error("预下载图片失败: ", err);
38959
+ });
38960
+ }
38961
+ }
38962
+ setTimeout(
38963
+ () => {
38964
+ textMsgContextMenuVisible.value = true;
38965
+ },
38966
+ p === "img" ? 150 : 0
38967
+ );
38914
38968
  }
38915
38969
  function onCopyClick() {
38916
- var _a;
38970
+ var _a, _b;
38917
38971
  if (props.msg.type === "text") {
38972
+ if (eventSource.value === "img") {
38973
+ const imgUrl = (_a = props.msg.attach) == null ? void 0 : _a.url;
38974
+ const cachedBlob = imageBlobCache.get(imgUrl);
38975
+ if (cachedBlob) {
38976
+ navigator.clipboard.write([new ClipboardItem({ "image/png": cachedBlob })]).then(() => {
38977
+ }).catch((err) => {
38978
+ console.error("copy image error: ", err);
38979
+ antDesignVue.message.error("复制图片失败");
38980
+ }).finally(() => {
38981
+ textMsgContextMenuVisible.value = false;
38982
+ });
38983
+ } else {
38984
+ antDesignVue.message.warning("图片正在加载中,请稍后再试");
38985
+ textMsgContextMenuVisible.value = false;
38986
+ }
38987
+ return;
38988
+ }
38918
38989
  copy(props.msg.body).then(() => {
38919
38990
  }).catch((err) => {
38920
38991
  console.error("copy error: ", err);
@@ -38924,13 +38995,20 @@
38924
38995
  });
38925
38996
  }
38926
38997
  if (props.msg.type === "image") {
38927
- copy((_a = props.msg.attach) == null ? void 0 : _a.url).then(() => {
38928
- }).catch((err) => {
38929
- console.error("copy error: ", err);
38930
- antDesignVue.message.error("复制失败");
38931
- }).finally(() => {
38998
+ const imgUrl = (_b = props.msg.attach) == null ? void 0 : _b.url;
38999
+ const cachedBlob = imageBlobCache.get(imgUrl);
39000
+ if (cachedBlob) {
39001
+ navigator.clipboard.write([new ClipboardItem({ "image/png": cachedBlob })]).then(() => {
39002
+ }).catch((err) => {
39003
+ console.error("copy image error: ", err);
39004
+ antDesignVue.message.error("复制图片失败");
39005
+ }).finally(() => {
39006
+ textMsgContextMenuVisible.value = false;
39007
+ });
39008
+ } else {
39009
+ antDesignVue.message.warning("图片正在加载中,请稍后再试");
38932
39010
  textMsgContextMenuVisible.value = false;
38933
- });
39011
+ }
38934
39012
  }
38935
39013
  }
38936
39014
  function onQuoteClick() {
@@ -39002,6 +39080,7 @@
39002
39080
  vue.onBeforeUnmount(() => {
39003
39081
  document.removeEventListener("click", handleClickOutside, true);
39004
39082
  document.removeEventListener("contextmenu", handleClickOutside, true);
39083
+ clearImageBlobCache();
39005
39084
  });
39006
39085
  const myAvatar = vue.computed(() => {
39007
39086
  var _a, _b, _c, _d;
@@ -39163,7 +39242,7 @@
39163
39242
  ], 2)) : __props.msg.type === "image" ? (vue.openBlock(), vue.createElementBlock("div", {
39164
39243
  key: 2,
39165
39244
  class: vue.normalizeClass(["chatCardMainContent flash-target", { myMsg: __props.msg.fromAccount === __props.myAccid, otherMsg: __props.msg.fromAccount !== __props.myAccid }]),
39166
- onContextmenu: vue.withModifiers(onTextMsgContextMenu, ["prevent"])
39245
+ onContextmenu: _cache[0] || (_cache[0] = vue.withModifiers(($event) => onTextMsgContextMenu($event, "img"), ["prevent"]))
39167
39246
  }, [
39168
39247
  vue.createVNode(vue.unref(antDesignVue.Image), {
39169
39248
  class: "imgMsg",
@@ -39181,24 +39260,28 @@
39181
39260
  ], 34)) : (vue.openBlock(), vue.createElementBlock("div", {
39182
39261
  key: 3,
39183
39262
  class: vue.normalizeClass(["chatCardMainContent", { myMsg: __props.msg.fromAccount === __props.myAccid, otherMsg: __props.msg.fromAccount !== __props.myAccid }])
39184
- }, [..._cache[1] || (_cache[1] = [
39263
+ }, [..._cache[3] || (_cache[3] = [
39185
39264
  vue.createElementVNode("div", { class: "textMsg" }, vue.toDisplayString("暂不支持该消息"), -1)
39186
39265
  ])], 2)),
39187
39266
  __props.msg.type === "text" && IMG_EXT.includes(((_j = (_i = __props.msg) == null ? void 0 : _i.attach) == null ? void 0 : _j.ext) || "") ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_14$3, [
39188
- ((_l = (_k = __props.msg) == null ? void 0 : _k.attach) == null ? void 0 : _l.size) && ((_n = (_m = __props.msg) == null ? void 0 : _m.attach) == null ? void 0 : _n.size) < MAX_IMG_PREVIEW_SIZE ? (vue.openBlock(), vue.createBlock(vue.unref(antDesignVue.Image), {
39267
+ ((_l = (_k = __props.msg) == null ? void 0 : _k.attach) == null ? void 0 : _l.size) && ((_n = (_m = __props.msg) == null ? void 0 : _m.attach) == null ? void 0 : _n.size) < MAX_IMG_PREVIEW_SIZE ? (vue.openBlock(), vue.createElementBlock("div", {
39189
39268
  key: 0,
39190
- class: "imgMsg",
39191
- src: (_p = (_o = __props.msg) == null ? void 0 : _o.attach) == null ? void 0 : _p.url,
39192
- width: attachImageSize.value.width,
39193
- height: attachImageSize.value.height
39194
- }, {
39195
- previewMask: vue.withCtx(() => [
39196
- vue.createElementVNode("div", _hoisted_15$3, [
39197
- vue.createVNode(vue.unref(EyeOutlined))
39198
- ])
39199
- ]),
39200
- _: 1
39201
- }, 8, ["src", "width", "height"])) : (vue.openBlock(), vue.createBlock(_sfc_main$9, {
39269
+ onContextmenu: _cache[1] || (_cache[1] = vue.withModifiers(($event) => onTextMsgContextMenu($event, "img"), ["prevent"]))
39270
+ }, [
39271
+ vue.createVNode(vue.unref(antDesignVue.Image), {
39272
+ class: "imgMsg",
39273
+ src: (_p = (_o = __props.msg) == null ? void 0 : _o.attach) == null ? void 0 : _p.url,
39274
+ width: attachImageSize.value.width,
39275
+ height: attachImageSize.value.height
39276
+ }, {
39277
+ previewMask: vue.withCtx(() => [
39278
+ vue.createElementVNode("div", _hoisted_15$3, [
39279
+ vue.createVNode(vue.unref(EyeOutlined))
39280
+ ])
39281
+ ]),
39282
+ _: 1
39283
+ }, 8, ["src", "width", "height"])
39284
+ ], 32)) : (vue.openBlock(), vue.createBlock(_sfc_main$9, {
39202
39285
  key: 1,
39203
39286
  attach: (_q = __props.msg) == null ? void 0 : _q.attach
39204
39287
  }, null, 8, ["attach"]))
@@ -39225,7 +39308,7 @@
39225
39308
  src: (_s = msgExt.value.quoteMsg.attach) == null ? void 0 : _s.url,
39226
39309
  width: quoteImageMsgSize.value.width,
39227
39310
  height: quoteImageMsgSize.value.height,
39228
- onClick: _cache[0] || (_cache[0] = vue.withModifiers(() => {
39311
+ onClick: _cache[2] || (_cache[2] = vue.withModifiers(() => {
39229
39312
  }, ["stop", "prevent"]))
39230
39313
  }, {
39231
39314
  previewMask: vue.withCtx(() => [
@@ -40240,41 +40323,56 @@
40240
40323
  }
40241
40324
  };
40242
40325
  const handlePaste = async (e) => {
40326
+ var _a;
40243
40327
  if (props.sendDisabled || attachmentUploading.value) return;
40244
40328
  const clipboardData = e.clipboardData;
40245
40329
  if (!clipboardData) return;
40246
- const items = clipboardData.items;
40247
- for (let i = 0; i < items.length; i++) {
40248
- const item = items[i];
40249
- if (item.type.startsWith("image/")) {
40250
- const file = item.getAsFile();
40251
- if (file) {
40252
- e.preventDefault();
40253
- if (!isLt(file.size, MAX_IMG_SIZE)) {
40254
- antDesignVue.message.error("图片大小最大支持20M");
40255
- return;
40256
- }
40257
- attachmentUploading.value = true;
40258
- try {
40259
- const uploadResult = await channelStore.uploadImage({
40260
- file,
40261
- onUploadStart: () => {
40262
- attachmentUploading.value = true;
40263
- },
40264
- onUploadDone: () => {
40265
- attachmentUploading.value = false;
40266
- }
40267
- });
40268
- attachment.value = uploadResult;
40269
- } catch (error) {
40270
- console.error("上传图片失败:", error);
40271
- antDesignVue.message.error("上传图片失败");
40272
- attachmentUploading.value = false;
40273
- }
40330
+ let imageFile = null;
40331
+ if (clipboardData.items && clipboardData.items.length) {
40332
+ for (let i = 0; i < clipboardData.items.length; i++) {
40333
+ const item = clipboardData.items[i];
40334
+ if (item.type.startsWith("image/")) {
40335
+ imageFile = item.getAsFile();
40336
+ break;
40274
40337
  }
40275
- return;
40276
40338
  }
40277
40339
  }
40340
+ if (!imageFile && clipboardData.files && clipboardData.files.length) {
40341
+ for (let i = 0; i < clipboardData.files.length; i++) {
40342
+ if (clipboardData.files[i].type.startsWith("image/")) {
40343
+ imageFile = clipboardData.files[i];
40344
+ break;
40345
+ }
40346
+ }
40347
+ }
40348
+ if (!imageFile) return;
40349
+ e.preventDefault();
40350
+ const existingFile = (_a = attachment.value) == null ? void 0 : _a.__file;
40351
+ if (existingFile && existingFile.size === imageFile.size && existingFile.type === imageFile.type) {
40352
+ return;
40353
+ }
40354
+ if (!isLt(imageFile.size, MAX_IMG_SIZE)) {
40355
+ antDesignVue.message.error("图片大小最大支持20M");
40356
+ return;
40357
+ }
40358
+ attachmentUploading.value = true;
40359
+ try {
40360
+ const uploadResult = await channelStore.uploadImage({
40361
+ file: imageFile,
40362
+ onUploadStart: () => {
40363
+ attachmentUploading.value = true;
40364
+ },
40365
+ onUploadDone: () => {
40366
+ attachmentUploading.value = false;
40367
+ }
40368
+ });
40369
+ uploadResult.__file = imageFile;
40370
+ attachment.value = uploadResult;
40371
+ } catch (error) {
40372
+ console.error("上传图片失败:", error);
40373
+ antDesignVue.message.error("上传图片失败");
40374
+ attachmentUploading.value = false;
40375
+ }
40278
40376
  };
40279
40377
  const uploadImgHandler = async (file) => {
40280
40378
  if (!file.type.startsWith("image/")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yx-web-sdk",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "private": false,
5
5
  "main": "dist/yx-web-sdk.umd.js",
6
6
  "module": "dist/yx-web-sdk.es.js",