index.uts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. import { UIActivityViewController, UIImage } from "UIKit";
  2. import { URL, Data } from "Foundation";
  3. import { ShareWithSystemOptions } from "../interface.uts";
  4. /**
  5. * 分享类型枚举
  6. */
  7. const ShareType = {
  8. TEXT: "text", // 纯文本分享
  9. IMAGE: "image", // 图片分享
  10. VIDEO: "video", // 视频分享
  11. AUDIO: "audio", // 音频分享
  12. FILE: "file", // 文件分享
  13. LINK: "link" // 链接分享
  14. };
  15. /**
  16. * 判断是否为网络 URL
  17. * @param url 地址
  18. * @returns 是否为网络 URL
  19. */
  20. function isNetworkUrl(url: string): boolean {
  21. return url.startsWith("http://") || url.startsWith("https://");
  22. }
  23. /**
  24. * 下载网络文件到本地缓存
  25. * @param url 网络地址
  26. * @param success 成功回调,返回本地文件路径
  27. * @param fail 失败回调
  28. */
  29. function downloadNetworkFile(
  30. url: string,
  31. success: (localPath: string) => void,
  32. fail: (error: string) => void
  33. ): void {
  34. uni.downloadFile({
  35. url: url,
  36. success: (res) => {
  37. if (res.statusCode == 200) {
  38. success(res.tempFilePath);
  39. } else {
  40. fail(`下载失败,状态码: ${res.statusCode}`);
  41. }
  42. },
  43. fail: (err) => {
  44. fail(`下载失败: ${err.errMsg ?? "未知错误"}`);
  45. }
  46. });
  47. }
  48. /**
  49. * 处理文件路径并加载数据
  50. * @param filePath 文件路径
  51. * @returns Data 对象或 null
  52. */
  53. function loadFileData(filePath: string): Data | null {
  54. const absolutePath = UTSiOS.convert2AbsFullPath(filePath);
  55. const fileURL = new URL((fileURLWithPath = absolutePath));
  56. const data = UTSiOS.try(new Data((contentsOf = fileURL)), "?");
  57. return data;
  58. }
  59. /**
  60. * 创建文本分享内容
  61. * @param title 标题
  62. * @param summary 描述
  63. * @param url 链接
  64. * @returns 分享内容数组
  65. */
  66. function createTextShareItems(title: string, summary: string, url: string): any[] {
  67. // 组合分享内容
  68. let content = "";
  69. if (title != "") {
  70. content = title;
  71. }
  72. if (summary != "") {
  73. content = content == "" ? summary : content + "\n" + summary;
  74. }
  75. if (url != "") {
  76. content = content == "" ? url : content + "\n" + url;
  77. }
  78. // 如果内容为空,使用默认文本
  79. if (content == "") {
  80. content = "分享内容";
  81. }
  82. return [content];
  83. }
  84. /**
  85. * 创建图片分享内容
  86. * @param url 图片路径
  87. * @param success 成功回调
  88. * @param fail 失败回调
  89. */
  90. function createImageShareItems(
  91. url: string,
  92. success: (items: any[]) => void,
  93. fail: (error: string) => void
  94. ): void {
  95. if (url == "") {
  96. fail("图片路径不能为空");
  97. return;
  98. }
  99. const handleImagePath = (localPath: string) => {
  100. const absolutePath = UTSiOS.convert2AbsFullPath(localPath);
  101. const image = new UIImage((contentsOfFile = absolutePath));
  102. if (image == null) {
  103. fail("图片加载失败");
  104. return;
  105. }
  106. success([image]);
  107. };
  108. if (isNetworkUrl(url)) {
  109. // 网络图片先下载
  110. downloadNetworkFile(url, handleImagePath, fail);
  111. } else {
  112. // 本地图片直接处理
  113. handleImagePath(url);
  114. }
  115. }
  116. /**
  117. * 创建文件分享内容(视频、音频、文件)
  118. * @param filePath 文件路径
  119. * @param success 成功回调
  120. * @param fail 失败回调
  121. */
  122. function createFileShareItems(
  123. filePath: string,
  124. success: (items: any[]) => void,
  125. fail: (error: string) => void
  126. ): void {
  127. if (filePath == "") {
  128. fail("文件路径不能为空");
  129. return;
  130. }
  131. const handleFilePath = (localPath: string) => {
  132. const data = loadFileData(localPath);
  133. if (data == null) {
  134. fail("文件加载失败");
  135. return;
  136. }
  137. const absolutePath = UTSiOS.convert2AbsFullPath(localPath);
  138. const fileURL = new URL.init((fileURLWithPath = absolutePath));
  139. success([data, fileURL]);
  140. };
  141. if (isNetworkUrl(filePath)) {
  142. // 网络文件先下载
  143. downloadNetworkFile(filePath, handleFilePath, fail);
  144. } else {
  145. // 本地文件直接处理
  146. handleFilePath(filePath);
  147. }
  148. }
  149. /**
  150. * 启动系统分享界面
  151. * @param items 分享内容数组
  152. * @param success 成功回调
  153. * @param fail 失败回调
  154. */
  155. function presentShareController(
  156. items: any[],
  157. success: () => void,
  158. fail: (error: string) => void
  159. ): void {
  160. DispatchQueue.main.async(
  161. (execute = (): void => {
  162. const activityViewController = new UIActivityViewController(
  163. (activityItems = items),
  164. (applicationActivities = nil)
  165. );
  166. const currentVC = UTSiOS.getCurrentViewController();
  167. if (currentVC == null) {
  168. fail("无法获取当前视图控制器");
  169. return;
  170. }
  171. currentVC.present(
  172. activityViewController,
  173. (animated = true),
  174. (completion = () => {
  175. success();
  176. })
  177. );
  178. })
  179. );
  180. }
  181. /**
  182. * 系统分享功能
  183. * @param options 分享参数
  184. * @param options.type 分享类型: text(文本) | image(图片) | video(视频) | audio(音频) | file(文件) | link(链接)
  185. * @param options.title 分享标题
  186. * @param options.summary 分享描述/内容
  187. * @param options.url 资源路径(图片/视频/音频/文件路径或链接地址,支持本地路径和网络 URL)
  188. * @param options.success 成功回调
  189. * @param options.fail 失败回调
  190. */
  191. export function shareWithSystem(options: ShareWithSystemOptions): void {
  192. const type = options.type;
  193. const title = options.title ?? "";
  194. const summary = options.summary ?? "";
  195. const url = options.url ?? "";
  196. // 根据分享类型创建对应的内容
  197. if (type == (ShareType["TEXT"] as string) || type == (ShareType["LINK"] as string)) {
  198. // 文本或链接分享
  199. const items = createTextShareItems(title, summary, url);
  200. presentShareController(
  201. items,
  202. () => {
  203. options.success?.();
  204. },
  205. (error: string) => {
  206. options.fail?.(error);
  207. }
  208. );
  209. } else if (type == (ShareType["IMAGE"] as string)) {
  210. // 图片分享
  211. createImageShareItems(
  212. url,
  213. (items: any[]) => {
  214. presentShareController(
  215. items,
  216. () => {
  217. options.success?.();
  218. },
  219. (error: string) => {
  220. options.fail?.(error);
  221. }
  222. );
  223. },
  224. (error: string) => {
  225. options.fail?.(error);
  226. }
  227. );
  228. } else if (
  229. type == (ShareType["VIDEO"] as string) ||
  230. type == (ShareType["AUDIO"] as string) ||
  231. type == (ShareType["FILE"] as string)
  232. ) {
  233. // 视频、音频、文件分享
  234. createFileShareItems(
  235. url,
  236. (items: any[]) => {
  237. presentShareController(
  238. items,
  239. () => {
  240. options.success?.();
  241. },
  242. (error: string) => {
  243. options.fail?.(error);
  244. }
  245. );
  246. },
  247. (error: string) => {
  248. options.fail?.(error);
  249. }
  250. );
  251. } else {
  252. // 未知类型,默认使用文本分享
  253. const items = createTextShareItems(title, summary, url);
  254. presentShareController(
  255. items,
  256. () => {
  257. options.success?.();
  258. },
  259. (error: string) => {
  260. options.fail?.(error);
  261. }
  262. );
  263. }
  264. }