toutiao.ts 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399
  1. // import { Notifications } from "../../notification/notifications";
  2. //2021年1月7日版本
  3. import TyqEventMgr from "../../tyq-event-mgr";
  4. import { Size, sys, Vec3, view } from 'cc';
  5. // TODO: 录制视频成功,并且打印出视频所在位置,分享视频的时候显示shareVideo: fail parse share info fail; 把功能移植到特工上面是可以分享的,不知道是不是录制内容问题
  6. class toutiao {
  7. ////////////////////////////
  8. // 类成员
  9. ///////////////////////////
  10. public static readonly _instance: toutiao = new toutiao();
  11. /** 系统信息 */
  12. private systemInfo: any = null;
  13. /** banner广告对象 */
  14. private bannerAd: any = null;
  15. /** 奖励视频广告对象 */
  16. private rewardedVideoAd: any = null;
  17. /** 全局唯一的录屏管理器 */
  18. private GameRecorderManager: any = null;
  19. /**客服按钮对象 */
  20. public ContactButton: any = null;
  21. /**更多游戏按钮 */
  22. public MoregameButton: any = null;
  23. /** 录屏开始事件 */
  24. private startListener: Function = null;
  25. /** 监听录屏继续事件 */
  26. private resumeListener: Function = null;
  27. /** 录屏暂停事件 */
  28. private pauseListener: Function = null;
  29. /** 录屏结束事件 */
  30. private stopListener: Function = null;
  31. /** 录屏错误事件 */
  32. private errorListener: Function = null;
  33. /** 录屏中断开始事件 */
  34. private interrupBeginListener: Function = null;
  35. /** 录屏中断结束事件 */
  36. private interrupEndListener: Function = null;
  37. /** banner广告id */
  38. private _bannerAdUnitId = '20bfdbajjibh1jjdd6';
  39. private _bannerAdRefreshTimeOutId: number = 0;
  40. /** 奖励视频广告id */
  41. private _rewardedVideoAdUnitId = '17fiaf9in9n22630nb';
  42. private _closeRewardVideoListener: Function = null;
  43. /**插屏广告ID */
  44. private __interstitialAdUnitId = "aiikioo1db8aj0bffc";
  45. /**插屏广告AD实例 */
  46. private interstitialAd: any = null;
  47. ////////////////////////////
  48. // get、set访问器
  49. ///////////////////////////
  50. public set bannerAdUnitId(adUnitId: string) {
  51. this._bannerAdUnitId = adUnitId;
  52. }
  53. public set rewardedVideoAdUnitId(adUnitId: string) {
  54. this._rewardedVideoAdUnitId = adUnitId;
  55. }
  56. public set closeRewardVideoListener(cb: Function) {
  57. this._closeRewardVideoListener = cb;
  58. }
  59. ////////////////////////////
  60. // 构造器
  61. ///////////////////////////
  62. protected constructor() {
  63. if (window.tt) {
  64. this.registerRecordScreenEvent();
  65. //this.handle();
  66. }
  67. }
  68. ////////////////////////////
  69. // 登录模块
  70. ///////////////////////////
  71. public toutiaoLogin() {
  72. return new Promise((resolve, reject) => {
  73. this.getSystemInfoSync();
  74. let loginData = {
  75. code: '',
  76. anonymousCode: '',
  77. name: null,
  78. photo: null
  79. };
  80. this.login()
  81. .then((res: { code: string; anonymousCode: string; isLogin: boolean }) => {
  82. loginData.code = res.code;
  83. loginData.anonymousCode = res.anonymousCode;
  84. if (res.isLogin) {
  85. this.getUserInfo()
  86. .then((data: { userInfo: any; rawData: string; signature?: string; encryptedData?: string; iv?: string }) => {
  87. loginData.name = data.userInfo.nickName;
  88. loginData.photo = data.userInfo.avatarUrl;
  89. resolve(loginData);
  90. })
  91. .catch(() => {
  92. reject(loginData);
  93. });
  94. } else {
  95. resolve(null);
  96. }
  97. })
  98. .catch(() => {
  99. reject(null);
  100. });
  101. });
  102. }
  103. /**
  104. * @description 获取临时登录凭证
  105. */
  106. private login() {
  107. return new Promise((resolve, reject) => {
  108. tt.login({
  109. /** 未登录时,是否强制调起登录框 */
  110. force: false,
  111. success: (res: { code: string; anonymousCode: string; isLogin: boolean }) => {
  112. console.log(`login调用成功${res.code} ${res.anonymousCode}`);
  113. resolve(res);
  114. },
  115. fail: (res: any) => {
  116. console.log('login调用失败');
  117. reject();
  118. }
  119. });
  120. });
  121. }
  122. /**
  123. * @description 获取已登录用户的基本信息或特殊信息
  124. * @tips 本 API 依赖于login,请确保调用前已经调用了该API
  125. */
  126. public getUserInfo() {
  127. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  128. return new Promise((resolve, reject) => {
  129. tt.getUserInfo({
  130. /** 是否需要返回敏感数据 */
  131. withCredentials: true,
  132. success: (res: { userInfo: object; rawData: string; signature?: string; encryptedData?: string; iv?: string }) => {
  133. console.log(`getUserInfo调用成功${res.userInfo}`);
  134. resolve(res);
  135. },
  136. fail: (res: any) => {
  137. console.log('getUserInfo调用失败: ', res);
  138. reject();
  139. }
  140. });
  141. });
  142. }
  143. ////////////////////////////
  144. // banner广告
  145. ///////////////////////////
  146. /**
  147. * @description 显示banner广告
  148. * @param refreshTime 自动刷新时间,默认10秒,0为不刷新;
  149. */
  150. public showBannerAd(refreshTime: number = 10) {
  151. console.log("创建banner广告");
  152. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  153. if (!toutiao.isSupportedAPI(tt.createBannerAd)) {
  154. console.log('不支持banner广告');
  155. return;
  156. }
  157. this.hideBannerAd();
  158. this.createBannerAd();
  159. this.bannerAd.onLoad(() => {
  160. console.log('=====> @framework, banner广告加载成功');
  161. this.bannerAd.show().then((args: any) => {
  162. console.log("----> =====> @framework, banner广告 show 成功", args);
  163. }).catch((args: any) => {
  164. console.log("----> =====> @framework, banner广告 show 失败", args);
  165. });
  166. // 移除setTimeout
  167. if (this._bannerAdRefreshTimeOutId != 0) {
  168. clearTimeout(this._bannerAdRefreshTimeOutId);
  169. this._bannerAdRefreshTimeOutId = 0;
  170. }
  171. // 延时刷新banner
  172. if (refreshTime > 0) {
  173. this._bannerAdRefreshTimeOutId = setTimeout(() => {
  174. if (this.bannerAd)
  175. this.showBannerAd(refreshTime);
  176. }, refreshTime * 1000);
  177. }
  178. });
  179. }
  180. /**
  181. * @description 隐藏banner广告
  182. */
  183. public hideBannerAd() {
  184. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  185. console.log("----> hideBannerAd", this.bannerAd);
  186. if (this.bannerAd) {
  187. this.bannerAd.hide();
  188. this.bannerAd.destroy();
  189. this.bannerAd = null;
  190. }
  191. }
  192. /**
  193. * @description 创建banner广告
  194. * @tips 每个广告实例只会与一条固定的广告素材绑定。开发者如果想要展示另一条广告,需要创建一个新的bannerAd实例。
  195. * Banner广告一般的比例为16:9,最小宽度是128(设备像素),最大宽度是208(设备像素)。开发者可以在这之间自由指定广告宽度。广告组件会自动等比例缩放素材。
  196. */
  197. private createBannerAd() {
  198. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  199. console.log('12342365476572354r52345')
  200. console.log(this._bannerAdUnitId)
  201. if (this.bannerAd) {
  202. return;
  203. }
  204. let targetBannerAdWidth = 208;
  205. this.bannerAd = tt.createBannerAd({
  206. // 广告单元 id
  207. adUnitId: this._bannerAdUnitId,
  208. style: {
  209. // 根据系统约定尺寸计算出广告高度
  210. top: this.systemInfo.windowHeight - (targetBannerAdWidth / 16 * 9),
  211. left: (this.systemInfo.windowWidth - targetBannerAdWidth) / 2,
  212. width: targetBannerAdWidth
  213. }
  214. });
  215. this.bannerAd.onError((res) => {
  216. console.log('=====> @framework, banner广告加载失败:', res);
  217. });
  218. this.bannerAd.onResize((res: { width: number; height: number }) => {
  219. if (this.bannerAd && targetBannerAdWidth !== res.width) {
  220. this.bannerAd.style.top = this.systemInfo.windowHeight - res.height;
  221. this.bannerAd.style.left = (this.systemInfo.windowWidth - res.width) / 2;
  222. this.bannerAd.style.width = res.width;
  223. }
  224. });
  225. }
  226. /**
  227. * 调起抖音看广告视频
  228. * @param str 为什么看视频
  229. */
  230. public showRewardVideoAd(str: string) {
  231. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  232. return new Promise((resolve, reject) => {
  233. this.createRewardedVideoAd().then(() => {
  234. TyqEventMgr.ins.onAdLoad();
  235. this.rewardedVideoAd.show().then(() => {
  236. TyqEventMgr.ins.onAdShow();
  237. }).catch(() => {
  238. this.rewardedVideoAd.load().then(() => {
  239. TyqEventMgr.ins.onAdLoad();
  240. TyqEventMgr.ins.onAdShow();
  241. this.rewardedVideoAd.show();
  242. })
  243. });
  244. }, (err?) => {
  245. console.log('=====> @framework, 奖励视频对象为不存在');
  246. /* 观看视频回调失败之后的回调 */
  247. reject();
  248. return null;
  249. });
  250. //关闭视频回调。
  251. let closeListener = (res) => {
  252. //用户完整的看完了视频
  253. if (res.isEnded) {
  254. TyqEventMgr.ins.onAdSuccess();
  255. // resolve();
  256. //看完广告获得薯片
  257. } else {
  258. /* 观看广告半路退出时的回调 */
  259. TyqEventMgr.ins.onAdCancel();
  260. // reject();
  261. }
  262. resolve(res.isEnded);
  263. this.closeRewardVideo();
  264. this.rewardedVideoAd.offClose(closeListener);
  265. };
  266. //当用户点击了 Video 广告上的关闭按钮时,会触发 close 事件的监听器。
  267. this.rewardedVideoAd.onClose(closeListener);
  268. let errorListener = (res) => {
  269. console.log('=====> @framework, 加载奖励视频错误:', res);
  270. var msg = "观看广告失败:未知原因"
  271. switch (res.errCode) {
  272. case 1000:
  273. msg = "观看广告失败:后端错误调用失败"
  274. break;
  275. case 1001:
  276. msg = "观看广告失败:参数错误"
  277. break;
  278. case 1002:
  279. msg = "观看广告失败:广告单元无效"
  280. break;
  281. case 1003:
  282. msg = "观看广告失败:内部错误"
  283. break;
  284. case 1004:
  285. msg = "观看广告失败:无适合的广告"
  286. break;
  287. case 1005:
  288. msg = "观看广告失败:广告组件审核中"
  289. break;
  290. case 1006:
  291. msg = "观看广告失败:广告组件被驳回"
  292. break;
  293. case 1007:
  294. msg = "观看广告失败:广告能力被禁用"
  295. break;
  296. case 1008:
  297. msg = "观看广告失败:广告单元已关闭"
  298. break;
  299. case 20002:
  300. msg = "观看广告失败:不支持 banner 广告 API"
  301. break;
  302. case 120002:
  303. msg = "观看广告失败:今日观看广告次数已达上限"
  304. break;
  305. default:
  306. break;
  307. }
  308. reject(msg);
  309. this.rewardedVideoAd.offError(errorListener);
  310. };
  311. this.rewardedVideoAd.onError(errorListener);
  312. });
  313. }
  314. /**
  315. * 展示插屏广告
  316. */
  317. public showInterstitialAd() {
  318. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  319. console.log("插屏广告");
  320. this.creatorInterstitialAd().then(() => {
  321. this.interstitialAd.show().then(() => {
  322. console.log("插屏广告显示成功");
  323. }).catch((err) => {
  324. console.log(err);
  325. console.log("插屏广告显示失败");
  326. //失败就再请求一次
  327. this.interstitialAd.load().then(() => {
  328. this.interstitialAd.show();
  329. })
  330. });
  331. })
  332. }
  333. /**创建插屏广告 */
  334. private creatorInterstitialAd() {
  335. return new Promise<void>((resolve, reject) => {
  336. if (!tt.createInterstitialAd) {
  337. console.log("不支持插屏广告");
  338. reject();
  339. return;
  340. }
  341. if (this.interstitialAd) {
  342. console.log("插屏广告已经创建");
  343. resolve();
  344. return;
  345. }
  346. this.interstitialAd = tt.createInterstitialAd({
  347. adUnitId: this.__interstitialAdUnitId
  348. })
  349. this.interstitialAd.load().then(() => {
  350. resolve();
  351. }, (err) => {
  352. console.log("插屏广告出错啦");
  353. console.log(err);
  354. }).catch((err) => {
  355. console.log("插屏广告出错");
  356. console.log(err);
  357. reject();
  358. });
  359. });
  360. }
  361. /**
  362. * 调起抖音看广告视频
  363. * @param str 为什么看视频
  364. */
  365. public showRewardVideoAd1(str: string) {
  366. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  367. //友盟+ 统计
  368. // if(window.tt.uma){
  369. // }
  370. return new Promise<void>((resolve, reject) => {
  371. this.createRewardedVideoAd();
  372. if (!this.rewardedVideoAd) {
  373. console.log('=====> @framework, 奖励视频对象为不存在');
  374. tt.uma.trackEvent("touchVideo", { source: str, state: "失败" });
  375. /* 当观看广告半路失败之后的回调 */
  376. reject();
  377. return null;
  378. }
  379. this.rewardedVideoAd.load()
  380. .then(() => {
  381. this.rewardedVideoAd.show();
  382. }).catch((err: any) => {
  383. console.log("----> =====> @framework, 奖励视频show失败", err);
  384. /* 当观看广告半路失败之后的回调 */
  385. reject();
  386. tt.uma.trackEvent("touchVideo", { source: str, state: "失败" });
  387. });
  388. //关闭视频回调。
  389. let closeListener = (res) => {
  390. //用户完整的看完了视频
  391. if (res.isEnded) {
  392. resolve();
  393. //看完广告获得薯片
  394. // this.getChips(0);
  395. tt.uma.trackEvent("touchVideo", { source: str, state: "成功" });
  396. } else {
  397. reject();
  398. tt.uma.trackEvent("touchVideo", { source: str, state: "失败" });
  399. }
  400. this.closeRewardVideo();
  401. this.rewardedVideoAd.offClose(closeListener);
  402. };
  403. //当用户点击了 Video 广告上的关闭按钮时,会触发 close 事件的监听器。
  404. this.rewardedVideoAd.onClose(closeListener);
  405. let errorListener = (res) => {
  406. console.log('=====> @framework, 加载奖励视频错误:', res);
  407. var msg = "观看广告失败:未知原因"
  408. switch (res.errCode) {
  409. case 1000:
  410. msg = "观看广告失败:后端错误调用失败"
  411. break;
  412. case 1001:
  413. msg = "观看广告失败:参数错误"
  414. break;
  415. case 1002:
  416. msg = "观看广告失败:广告单元无效"
  417. break;
  418. case 1003:
  419. msg = "观看广告失败:内部错误"
  420. break;
  421. case 1004:
  422. msg = "观看广告失败:无适合的广告"
  423. break;
  424. case 1005:
  425. msg = "观看广告失败:广告组件审核中"
  426. break;
  427. case 1006:
  428. msg = "观看广告失败:广告组件被驳回"
  429. break;
  430. case 1007:
  431. msg = "观看广告失败:广告能力被禁用"
  432. break;
  433. case 1008:
  434. msg = "观看广告失败:广告单元已关闭"
  435. break;
  436. case 20002:
  437. msg = "观看广告失败:不支持 banner 广告 API"
  438. break;
  439. case 120002:
  440. msg = "观看广告失败:今日观看广告次数已达上限"
  441. break;
  442. default:
  443. break;
  444. }
  445. reject(msg);
  446. tt.uma.trackEvent("touchVideo", { source: str, state: "失败" });
  447. this.rewardedVideoAd.offError(errorListener);
  448. };
  449. this.rewardedVideoAd.onError(errorListener);
  450. });
  451. }
  452. /**
  453. * @description 创建视频广告单例(小游戏端是全局单例)
  454. * @tips 全局只有一个videoAd实例,重复创建没有用
  455. */
  456. private createRewardedVideoAd() {
  457. return new Promise<void>((resolve, reject) => {
  458. if (!tt.createRewardedVideoAd) {
  459. console.log('=====> @framework, 当前客户端版本过低,无法使用奖励视频功能,请升级到最新客户端版本后重试');
  460. reject();
  461. return;
  462. }
  463. if (this.rewardedVideoAd) {
  464. resolve();
  465. return;
  466. }
  467. this.rewardedVideoAd = tt.createRewardedVideoAd({
  468. adUnitId: this._rewardedVideoAdUnitId
  469. });
  470. this.rewardedVideoAd.load().then(() => {
  471. resolve();
  472. }, () => {
  473. console.log("加载失败");
  474. }).catch((err: any) => {
  475. console.log("----> =====> @framework, 奖励视频show失败", err);
  476. reject(err);
  477. });
  478. });
  479. }
  480. private closeRewardVideo() {
  481. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  482. if (this._closeRewardVideoListener) {
  483. this._closeRewardVideoListener();
  484. }
  485. }
  486. ////////////////////////////
  487. // 分享
  488. ///////////////////////////
  489. /**
  490. * @description 主动拉起转发界面
  491. * @param _title 转发标题,不传则默认使用当前小游戏的名称。
  492. * @param _imageUrl 转发显示图片的链接,可以是网络图片路径或本地图片文件路径或相对代码包根目录的图片文件路径,显示图片长宽比推荐 5:4
  493. * @param _query 查询字符串,必须是 key1=val1&key2=val2 的格式。从这条转发消息进入后,可通过 tt.getLaunchOptionSync() 或 tt.onShow() 获取启动参数中的 query。
  494. */
  495. public shareAppMessage(_title: string, _videoUrl: string, _extra: Array<string>, _query?: string) {
  496. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  497. return new Promise<any>((resolve, reject) => {
  498. tt.shareAppMessage({
  499. // article 发布图文内容
  500. // video 发布视频内容
  501. // token 口令分享,生成一串特定的字符串文本,仅头条APP支持
  502. channel: 'video', //
  503. title: _title,
  504. // imageUrl: _videoUrl,
  505. //query: _query,
  506. /** 附加信息 */
  507. extra: {
  508. videoTag: _extra[0],
  509. videoPath: _videoUrl,
  510. hashtag_list: _extra,
  511. videoTopics: _extra,
  512. withVideoId: true,
  513. },
  514. success: (res) => {
  515. //分享录屏 获得薯片
  516. // this.getChips(1);
  517. // this.getVideoInfo(res.videoId, _videoUrl)
  518. resolve(res.videoId);
  519. },
  520. fail: (e) => {
  521. console.error(e);
  522. reject();
  523. }
  524. });
  525. });
  526. }
  527. /**获取视频分享的信息 */
  528. public getVideoInfo(id, url) {
  529. console.log("获取视频分享的信息");
  530. console.log(id);
  531. tt.request({
  532. url: url,
  533. method: "POST",
  534. data: {
  535. alias_ids: [id],
  536. },
  537. success: (res) => {
  538. console.log(res);
  539. if (res.data.data[0].video_info.cover_url) {
  540. console.log(res.data.data[0].video_info); // 包含 cover_url,还有其它字段
  541. } else {
  542. setTimeout(() => {
  543. this.getVideoInfo(id, url);
  544. }, 5000);
  545. }
  546. },
  547. });
  548. }
  549. //屏幕内截图
  550. public screenshot() {
  551. const canvas = tt.createCanvas();
  552. canvas.getContext("2d");
  553. // // console.log("开始屏幕内截图");
  554. // // oTempFilePath 方法
  555. // canvas.toTempFilePath({
  556. // x: 200,
  557. // y: 200,
  558. // width: 200,
  559. // height: 150,
  560. // destWidth: 200,
  561. // destHeight: 150,
  562. // success: (res) => {
  563. // console.log(res.tempFilePath);
  564. // },
  565. // });
  566. return new Promise<any>((resolve, reject) => {
  567. canvas.toTempFilePath({
  568. x: 200,
  569. y: 200,
  570. width: 200,
  571. height: 150,
  572. destWidth: 200,
  573. destHeight: 150,
  574. success: (res) => {
  575. console.log("开始屏幕内截图");
  576. resolve(res)
  577. },
  578. fail: (e) => {
  579. console.error(e);
  580. reject();
  581. }
  582. });
  583. });
  584. }
  585. /* 创建图片 */
  586. public createImage(tempFilePath) {
  587. console.log("开始绘制图片");
  588. const image = tt.createImage();
  589. image.src = tempFilePath;
  590. image.width = 200;
  591. image.height = 200;
  592. image.addEventListener("load", (res) => {
  593. console.log("加载成功");
  594. });
  595. image.addEventListener("error", (res) => {
  596. console.log("加载失败");
  597. });
  598. }
  599. /* 返回当前app的名字 */
  600. public returnAppName(): string {
  601. return this.systemInfo.appName.toUpperCase();
  602. }
  603. /**跳转视频播放页
  604. * @param videoId 传入的视频id
  605. */
  606. public navigateToVideoView(videoId) {
  607. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  608. return new Promise<void>((resolve, reject) => {
  609. tt.navigateToVideoView({
  610. videoId: videoId,
  611. success: (res) => {
  612. resolve(res.videoId);
  613. /* res结构: {errMsg: string } */
  614. },
  615. fail: (e) => {
  616. if (e.errCode === 1006) {
  617. tt.showToast({
  618. title: "something wrong with your network",
  619. });
  620. }
  621. reject();
  622. }
  623. });
  624. });
  625. }
  626. /**获取视频点赞数,封面图
  627. * @param videoId 传入的视频id//可以是数组形式
  628. */
  629. public request(videoId) {
  630. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  631. return new Promise<void>((resolve, reject) => {
  632. tt.request({
  633. url: "https://gate.snssdk.com/developer/api/get_video_info",
  634. method: "POST",
  635. data: {
  636. alias_ids: [videoId],
  637. },
  638. success: (res) => {
  639. resolve(res);
  640. },
  641. fail: (e) => {
  642. reject();
  643. }
  644. });
  645. });
  646. }
  647. /**
  648. * @description 注册GameRecorderManager
  649. */
  650. private registerRecordScreenEvent() {
  651. this.getSystemInfoSync();
  652. if (!toutiao.isSupportedAPI(tt.getGameRecorderManager)) {
  653. console.log('=====> @framework,当前客户端版过低,无法使用奖励视频功能,请升级到最新客户端版本后重试');
  654. return;
  655. }
  656. this.GameRecorderManager = tt.getGameRecorderManager();
  657. // 监听录屏开始事件
  658. this.GameRecorderManager.onStart((res: any) => {
  659. if (this.startListener) {
  660. this.startListener();
  661. }
  662. });
  663. // 监听录屏继续事件
  664. this.GameRecorderManager.onResume(() => {
  665. if (this.resumeListener) {
  666. this.resumeListener();
  667. }
  668. });
  669. // 监听录屏暂停事件
  670. this.GameRecorderManager.onPause(() => {
  671. if (this.pauseListener) {
  672. this.pauseListener();
  673. }
  674. });
  675. // 监听录屏结束事件。可以通过 onStop 接口监听录屏结束事件,获得录屏地址
  676. this.GameRecorderManager.onStop((res: { videoPath: string }) => {
  677. if (this.stopListener) {
  678. this.stopListener(res.videoPath, res);
  679. }
  680. });
  681. // 监听录屏错误事件
  682. this.GameRecorderManager.onError((res: { errMsg: string }) => {
  683. console.log('=====> @framework,录屏错误:', res.errMsg);
  684. if (this.errorListener) {
  685. this.errorListener();
  686. }
  687. });
  688. // 监听录屏中断开始
  689. this.GameRecorderManager.onInterruptionBegin(() => {
  690. if (this.interrupBeginListener) {
  691. this.interrupBeginListener();
  692. }
  693. });
  694. // 监听录屏中断结束
  695. this.GameRecorderManager.onInterruptionEnd(() => {
  696. if (this.interrupEndListener) {
  697. this.interrupEndListener();
  698. }
  699. });
  700. //监听用户分享
  701. tt.onShareAppMessage(function (res) {
  702. return {
  703. templateId: "3hlt8qdiwq012h84b8",
  704. success() {
  705. // console.log('转发发布器已调起');
  706. },
  707. fail() {
  708. // console.log('转发发布器调起失败');
  709. //console.log(res);
  710. }
  711. }
  712. })
  713. }
  714. /**
  715. * @description 开始录屏
  716. * @param _duration 录屏的时长,单位 s,必须大于3s,最大值 120(2 分钟)
  717. * @param callback 录制开始事件回调
  718. */
  719. public startRecordScreen(_duration: number, callback?: Function) {
  720. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  721. this.startListener = callback;
  722. this.GameRecorderManager.start({
  723. duration: _duration
  724. });
  725. }
  726. /**
  727. * @description 暂停录屏
  728. */
  729. public pauseRecordScreen(callback?: Function) {
  730. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  731. this.pauseListener = callback;
  732. this.GameRecorderManager.pause();
  733. }
  734. public resumeRecordScreen(callback?: Function) {
  735. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  736. this.resumeListener = callback;
  737. this.GameRecorderManager.resume();
  738. }
  739. /**
  740. * @description 暂停录屏
  741. * @param callback
  742. */
  743. public stopRecordScreen(callback?: Function) {
  744. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  745. this.stopListener = callback;
  746. this.GameRecorderManager.stop();
  747. }
  748. ////////////////////////////
  749. // 其他能力
  750. ///////////////////////////
  751. /**
  752. * @description 检查用户当前的 session 状态是否有效。
  753. * @tips 只有成功调用 tt.login 才会生成 session,checkSession 才会进入 success 回调当用户退出登录会清除 session
  754. */
  755. private checkSession() {
  756. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  757. return new Promise<void>((resolve, reject) => {
  758. tt.checkSession({
  759. success: () => {
  760. resolve();
  761. console.log('session未过期');
  762. },
  763. fail: () => {
  764. reject();
  765. console.log('session已过期,需要重新登录');
  766. }
  767. });
  768. });
  769. }
  770. // /**显示客服管理按钮 */
  771. // public showContactButton(url, top, left, success?: Function, fail?: Function) {
  772. // const Framesize = cc.view.getFrameSize()
  773. // if (!this.ContactButton) {
  774. // this.ContactButton = tt.createContactButton({
  775. // type: "image", //
  776. // image: url,
  777. // /** 附加信息 */
  778. // style: {
  779. // left: Framesize.width * left,
  780. // top: (Framesize.height / (Framesize.width / 375) / 2 - 667 / 2 + 535) * (Framesize.width / 375),
  781. // width: 50,
  782. // height: 50,
  783. // borderRadius: 50
  784. // //borderWidth: 0,
  785. // // textAlign:"center"
  786. // },
  787. // success: (res) => {
  788. // console.log("客服创建成功");
  789. // if (success) {
  790. // success();
  791. // }
  792. // //this.ContactButton.show();
  793. // },
  794. // fail: (e) => {
  795. // console.log("客服创建失败");
  796. // if (fail) {
  797. // fail();
  798. // }
  799. // }
  800. // });
  801. // }
  802. // else {
  803. // this.ContactButton.show();
  804. // }
  805. // }
  806. /**显示客服管理按钮 */
  807. public showContactButton(url, pos: Vec3, size: Size) {
  808. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  809. const pixelSize = view.getVisibleSize();
  810. const Framesize = view.getFrameSize();
  811. const scalX = Framesize.width / pixelSize.width;
  812. const scalY = Framesize.height / pixelSize.height;
  813. if (!this.ContactButton) {
  814. this.ContactButton = tt.createContactButton({
  815. type: "image", //
  816. image: url,
  817. /** 附加信息 */
  818. style: {
  819. left: Framesize.width / 2 + Math.abs(pos.x) * scalX,
  820. top: Framesize.height / 2 + Math.abs(pos.y) * scalY,
  821. width: size.width * scalX,
  822. height: size.height * scalY,
  823. borderRadius: 0,
  824. borderWidth: 0,
  825. //lineHeight: 40,
  826. },
  827. success: (res) => {
  828. console.log("客服创建成功");
  829. },
  830. fail: (e) => {
  831. console.log("客服创建失败");
  832. }
  833. });
  834. } else {
  835. this.ContactButton.show();
  836. }
  837. }
  838. /**客服按钮隐藏 */
  839. public hideContactButton() {
  840. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  841. if (!this.ContactButton) return;
  842. this.ContactButton.hide();
  843. }
  844. /**添加更多游戏的按钮 */
  845. // public createMoreGamesButton(url, pos: cc.Vec3, size: cc.Size) {
  846. // if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  847. // const pixelSize = cc.director.getWinSizeInPixels();
  848. // const Framesize = cc.view.getFrameSize();
  849. // const scalX = Framesize.width / pixelSize.width;
  850. // const scalY = Framesize.height / pixelSize.height;
  851. // if (!this.ContactButton) {
  852. // this.ContactButton = tt.createMoreGamesButton({
  853. // type: "image", //
  854. // image: url,
  855. // /** 附加信息 */
  856. // style: {
  857. // left: Framesize.width / 2 + Math.abs(pos.x) * scalX,
  858. // top: Framesize.height / 2 + Math.abs(pos.y) * scalY,
  859. // width: size.width * scalX,
  860. // height: size.height * scalY,
  861. // borderRadius: 0,
  862. // borderWidth: 0,
  863. // //lineHeight: 40,
  864. // },
  865. // actionType: "box",
  866. // appLaunchOptions: [
  867. // {
  868. // appId: "tta930df8d360b7e6d",
  869. // // query: "gameName=假如学生喜爱写作业&pos=" + pos,
  870. // query: "",
  871. // extraData: {}
  872. // },
  873. // {
  874. // appId: "ttbc61cadc235aa747",
  875. // // query: "gameName=假如学生爱写作业&pos=" + pos,
  876. // query: "",
  877. // extraData: {}
  878. // },
  879. // {
  880. // appId: "ttcf37b98a500cd748",
  881. // // query: "gameName=赛几探险记&pos=" + pos,
  882. // query: "",
  883. // extraData: {}
  884. // },
  885. // {
  886. // appId: "ttefcd997070f6f7bc",
  887. // // query: "gameName=烧脑吧赛几&pos=" + pos,
  888. // query: "",
  889. // extraData: {}
  890. // },
  891. // ],
  892. // onNavigateToMiniGameBox(res) {
  893. // console.log("跳转到小游戏盒子", res);
  894. // },
  895. // success: (res) => {
  896. // console.log("更多游戏创建成功");
  897. // this.ContactButton.show();
  898. // },
  899. // fail: (e) => {
  900. // console.log("更多游戏创建失败");
  901. // console.log(e);
  902. // }
  903. // });
  904. // }
  905. // }
  906. /**
  907. * @description 获取用户已经授权过的配置。结果中只会包含小程序向用户请求过的权限
  908. */
  909. public getSetting() {
  910. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  911. return new Promise((resolve, reject) => {
  912. tt.getSetting({
  913. success: (res: any) => {
  914. resolve(res.authSetting);
  915. },
  916. fail: () => {
  917. reject();
  918. console.log('session已过期,需要重新登录');
  919. }
  920. });
  921. });
  922. }
  923. /**调用方法会提前向用户发出授权请求。该方法不会调用对应接口,只会弹框咨询用户是否授权或者获取用户信息。如果用户之前有授权,该接口直接返回成功,不会跟用户产生交互 */
  924. public authorize() {
  925. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  926. return new Promise((resolve, reject) => {
  927. tt.authorize({
  928. authorize: 'scope.userInfo',
  929. success: (res: any) => {
  930. resolve(res.authSetting);
  931. },
  932. fail: () => {
  933. reject();
  934. console.log('session已过期,需要重新登录');
  935. }
  936. });
  937. });
  938. }
  939. /**
  940. * @description 返回小游戏启动参数(好友邀请中获取query)
  941. */
  942. private getLaunchOptionsSync() {
  943. return tt.getLaunchOptionsSync();
  944. }
  945. /**
  946. * 打开设置页面,返回用户设置过的授权结果。
  947. */
  948. public openSetting() {
  949. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  950. return new Promise((resolve, reject) => {
  951. tt.openSetting({
  952. success: (res: {}) => {
  953. resolve(res);
  954. },
  955. fail: (res: any) => {
  956. reject();
  957. }
  958. })
  959. });
  960. };
  961. ////////////////////////////
  962. // 震动
  963. ///////////////////////////
  964. /**
  965. * 使手机发生较长时间的振动。
  966. * @param callback 回调 (isSuccess, res)
  967. */
  968. public vibrateLong(callback?: Function): void {
  969. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  970. tt.vibrateLong({
  971. success(res) {
  972. if (callback)
  973. callback(true, res);
  974. },
  975. fail(res) {
  976. if (callback)
  977. callback(false, res);
  978. }
  979. });
  980. };
  981. /**
  982. * 使手机发生较短时间的振动
  983. * @param cb 回调 (isSuccess, res)
  984. */
  985. public vibrateShort(cb?: Function): void {
  986. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  987. tt.vibrateShort({
  988. success(res) {
  989. if (cb)
  990. cb(true, res);
  991. },
  992. fail(res) {
  993. if (cb)
  994. cb(false, res);
  995. }
  996. });
  997. };
  998. ////////////////////////////
  999. // 通用
  1000. ///////////////////////////
  1001. /**
  1002. * @description 获取系统信息
  1003. */
  1004. private getSystemInfoSync() {
  1005. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1006. try {
  1007. this.systemInfo = tt.getSystemInfoSync();
  1008. console.log('this.systemInfo', this.systemInfo)
  1009. } catch (e) {
  1010. console.log('获取系统信息失败');
  1011. }
  1012. }
  1013. /**
  1014. * @description 是否支持改API
  1015. * @param api
  1016. */
  1017. private static isSupportedAPI(api: any): boolean {
  1018. return !!api;
  1019. }
  1020. /**
  1021. * 关闭小程序
  1022. * @param 成功回调
  1023. */
  1024. public exitMiniProgram(cb: Function): void {
  1025. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1026. tt.exitMiniProgram({
  1027. success(res) {
  1028. if (cb)
  1029. cb(true, res);
  1030. },
  1031. fail(res) {
  1032. if (cb)
  1033. cb(false, res);
  1034. }
  1035. });
  1036. };
  1037. /**
  1038. * 关注抖音号
  1039. */
  1040. public openAwemeUserProfile() {
  1041. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1042. tt.openAwemeUserProfile();
  1043. }
  1044. /**
  1045. * 显示灰色背景的消息提示框
  1046. * @param title 内容
  1047. * @param duration 提示框停留时间,单位ms
  1048. * @param cb 成功后回调
  1049. */
  1050. public showToast(title: string, duration: number = 3000, cb?: Function): void {
  1051. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1052. tt.showToast({
  1053. title: title,
  1054. duration: duration,
  1055. success: () => {
  1056. if (cb) cb();
  1057. }
  1058. });
  1059. };
  1060. /**
  1061. * 获取系统信息
  1062. * @param callback 成功后回调
  1063. */
  1064. public getSystemInfo(callback?: Function): void {
  1065. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1066. tt.getSystemInfo({
  1067. success: (res) => {
  1068. if (callback) callback(res);
  1069. }
  1070. });
  1071. };
  1072. /**监听用户点击右上角菜单中的“转发”按钮时触发的事件
  1073. * Tip: 该方法可以监听用户通过右上角菜单中触发的分享操作,在不同宿主端具体的操作会有所差别,在抖音中包括分享和拍抖音,在头条包括分享和发头条,以此类推。
  1074. Tip: 该方法的实际调用链路为
  1075. 开发者注册此事件后
  1076. 用户点击小游戏菜单中的分享或者拍抖音等按钮
  1077. 自动调用开发者通过 tt.onShareAppMessage 定义的函数,并传入带有 channel 参数的对象,执行得到该函数的返回对象,
  1078. 接着调用 tt.shareAppMessage ,将上一步返回的对象传入其中,拉起分享。
  1079. Tip:该方法可以用于监听用户在小游戏右上角菜单中的分享操作,开发者可以监听此事件并自定义分享行为。
  1080. */
  1081. public onShareAppMessage(callback?: Function): void {
  1082. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1083. tt.onShareAppMessage(function (res) {
  1084. // console.log(res);
  1085. if (callback) callback(res);
  1086. })
  1087. }
  1088. /**
  1089. *
  1090. * @param time 生成最后几秒的视频
  1091. * @param videoPath 视频地址,在结束录频时会返回,传入这里
  1092. */
  1093. public clipVideo(time: number, videoPath: any, callback?: Function) {
  1094. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1095. const recorder = this.GameRecorderManager;
  1096. recorder.clipVideo({
  1097. path: videoPath,
  1098. timeRange: [time, 0],
  1099. success(res) {
  1100. //如果成功,并且有回调
  1101. if (callback) {
  1102. callback(res);
  1103. }
  1104. // console.log(res.videoPath); // 生成最后time秒的视频
  1105. },
  1106. fail(e) {
  1107. console.error(e);
  1108. },
  1109. });
  1110. }
  1111. /**
  1112. * 显示分享按钮
  1113. */
  1114. public showShareMenu() {
  1115. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1116. tt.showShareMenu({
  1117. success(res) {
  1118. console.log("已成功显示转发按钮");
  1119. },
  1120. fail(err) {
  1121. console.log("showShareMenu 调用失败", err.errMsg);
  1122. },
  1123. complete(res) {
  1124. console.log("showShareMenu 调用完成");
  1125. },
  1126. })
  1127. }
  1128. /**
  1129. * 新的登录接口 获取code
  1130. *
  1131. * @param bool 是否强制拉取登录框
  1132. */
  1133. public TTLogin(cb: Function, fail?: Function, bool = false) {
  1134. const name = Toutiao.returnAppName();
  1135. let bools = null
  1136. if (!bool) {
  1137. bools = false;
  1138. if (name == 'DOUYIN' || name == 'douyin_lite') {
  1139. bools = true;
  1140. }
  1141. } else {
  1142. bools = true;
  1143. }
  1144. tt.login({
  1145. /** 未登录时,是否强制调起登录框 */
  1146. force: bools,
  1147. success: (res: { errMsg: string; code: string; anonymousCode: string; isLogin: boolean }) => {
  1148. if (cb) {
  1149. cb(res);
  1150. }
  1151. },
  1152. fail: (res: any) => {
  1153. if (fail) {
  1154. fail(res);
  1155. }
  1156. }
  1157. })
  1158. }
  1159. /**
  1160. * 获取用户敏感信息
  1161. */
  1162. public TTGetUserInfo(cb: Function, fail?: Function) {
  1163. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1164. tt.getUserInfo({
  1165. withCredentials: true,//获取用户敏感信息
  1166. success(res) {
  1167. cb(res);
  1168. },
  1169. fail(res) {
  1170. if (fail) {
  1171. fail(res);
  1172. }
  1173. },
  1174. })
  1175. }
  1176. // // /**添加游戏关注 */
  1177. public showFavoriteGuide() {
  1178. tt.showFavoriteGuide({
  1179. type: "bar",
  1180. content: "一键添加到我的小程序",
  1181. position: "bottom",
  1182. success(res) {
  1183. console.log("引导组件展示成功");
  1184. },
  1185. fail(res) {
  1186. console.log("引导组件展示失败");
  1187. },
  1188. });
  1189. }
  1190. // public
  1191. /* 添加更多游戏 */
  1192. public showMoreGamesModal() {
  1193. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1194. tt.setMoreGamesInfo({
  1195. appLaunchOptions: [
  1196. {
  1197. appId: "tta930df8d360b7e6d",
  1198. // query: "gameName=假如学生喜爱写作业&pos=" + pos,
  1199. query: "foo=bar&baz=qux",
  1200. extraData: {}
  1201. },
  1202. {
  1203. appId: "ttbc61cadc235aa747",
  1204. // query: "gameName=假如学生爱写作业&pos=" + pos,
  1205. query: "foo=bar&baz=qux",
  1206. extraData: {}
  1207. },
  1208. {
  1209. appId: "ttcf37b98a500cd748",
  1210. // query: "gameName=赛几探险记&pos=" + pos,
  1211. query: "foo=bar&baz=qux",
  1212. extraData: {}
  1213. },
  1214. {
  1215. appId: "ttefcd997070f6f7bc",
  1216. // query: "gameName=烧脑吧赛几&pos=" + pos,
  1217. query: "foo=bar&baz=qux",
  1218. extraData: {}
  1219. },
  1220. ],
  1221. });
  1222. tt.showMoreGamesModal({
  1223. appLaunchOptions: [
  1224. {
  1225. appId: "tta930df8d360b7e6d",
  1226. // query: "gameName=假如学生喜爱写作业&pos=" + pos,
  1227. query: "foo=bar&baz=qux",
  1228. extraData: {}
  1229. },
  1230. {
  1231. appId: "ttbc61cadc235aa747",
  1232. // query: "gameName=假如学生爱写作业&pos=" + pos,
  1233. query: "foo=bar&baz=qux",
  1234. extraData: {}
  1235. },
  1236. {
  1237. appId: "ttcf37b98a500cd748",
  1238. // query: "gameName=赛几探险记&pos=" + pos,
  1239. query: "foo=bar&baz=qux",
  1240. extraData: {}
  1241. },
  1242. {
  1243. appId: "ttefcd997070f6f7bc",
  1244. // query: "gameName=烧脑吧赛几&pos=" + pos,
  1245. query: "foo=bar&baz=qux",
  1246. extraData: {}
  1247. },
  1248. ],
  1249. success(res) {
  1250. console.log('success', res.errMsg)
  1251. },
  1252. fail(res) {
  1253. console.log('fail', res.errMsg)
  1254. },
  1255. })
  1256. // tt.onNavigateToMiniProgram(function (res) {
  1257. // console.log("open other games", res);
  1258. // if (res.errCode != 2) {
  1259. // // adLogic.reportAnalytics('clickMoreSuccess', {
  1260. // // name: pos,
  1261. // // errcode: res.errCode,
  1262. // // });
  1263. // }
  1264. // })
  1265. }
  1266. /**
  1267. * 获取抖音头像
  1268. */
  1269. getHeadImage(url: string, cb?: Function, fail?: Function) {
  1270. if (sys.platform != sys.Platform.BYTEDANCE_MINI_GAME) return;
  1271. const image = tt.createImage();
  1272. image.src = url;
  1273. image.width = 60;
  1274. image.height = 60;
  1275. image.addEventListener("load", (res) => {
  1276. console.log("加载成功");
  1277. console.log(image);
  1278. if (cb) {
  1279. cb(res);
  1280. }
  1281. });
  1282. image.addEventListener("error", (res) => {
  1283. if (fail) {
  1284. fail(res);
  1285. }
  1286. console.log("加载失败");
  1287. });
  1288. }
  1289. }
  1290. export const Toutiao = toutiao._instance;