partner-home.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. <template>
  2. <view class="partner-home">
  3. <!-- 头部信息 -->
  4. <view class="header">
  5. <image class="avatar" src="https://shuhi.oss-cn-qingdao.aliyuncs.com/mini/logo.png" mode="aspectFit">
  6. </image>
  7. <view class="header-text">
  8. <text class="title">您好,合伙人</text>
  9. <text class="subtitle">这里是合伙人数据中心</text>
  10. </view>
  11. <navigator url="/pages-mine/pages/partner/partner-rule" class="rule-btn">合伙人规则</navigator>
  12. </view>
  13. <view class="content" style="margin-top: -180rpx">
  14. <!-- 收入统计 -->
  15. <view class="income-section">
  16. <view class="section-header">
  17. <text class="section-title">收入统计</text>
  18. <text class="tip">每月20号到账上月结算收益</text>
  19. </view>
  20. <view class="income-grid">
  21. <view class="income-item">
  22. <text class="amount">¥{{ detail.totalIncome || "0.00" }}</text>
  23. <text class="label">累计收入</text>
  24. </view>
  25. <view class="income-item">
  26. <text class="amount">¥{{ detail.todayEstimateIncome || "0.00" }}</text>
  27. <text class="label">今日预估</text>
  28. </view>
  29. <view class="income-item">
  30. <text class="amount">¥{{ detail.lastMontyEstimateIncome || "0.00" }}</text>
  31. <text class="label">上月预估</text>
  32. </view>
  33. <view class="income-item">
  34. <text class="amount">¥{{ detail.thisMontyEstimateIncome || "0.00" }}</text>
  35. <text class="label">本月预估</text>
  36. </view>
  37. <view class="income-item">
  38. <text class="amount">¥{{ detail.lastMontySettlement || "0.00" }}</text>
  39. <text class="label">上月结算</text>
  40. </view>
  41. <view class="income-item">
  42. <text class="amount">¥{{ detail.waitAccount || "0.00" }}</text>
  43. <text class="label">待到账</text>
  44. </view>
  45. </view>
  46. </view>
  47. <!-- 实用工具 -->
  48. <view class="tools-section">
  49. <text class="section-title">实用工具</text>
  50. <view class="tools-grid">
  51. <navigator url="/pages-mine/pages/partner/income-detail" class="tool-item">
  52. <image src="/pages-mine/static/partner1.png" mode="aspectFit"></image>
  53. <text>收入明细</text>
  54. </navigator>
  55. <navigator url="/pages-mine/pages/partner/order-detail" class="tool-item">
  56. <image src="/pages-mine/static/partner2.png" mode="aspectFit"></image>
  57. <text>订单明细</text>
  58. </navigator>
  59. </view>
  60. </view>
  61. <!-- 生成海报按钮 -->
  62. <button class="generate-poster" @click="generatePoster" :loading="loading">生成专属二维码海报</button>
  63. <!-- 添加canvas元素 -->
  64. <canvas canvas-id="posterCanvas" id="posterCanvas"
  65. style="width: 750px; height: 1334px; position: fixed; left: -9999px"></canvas>
  66. <!-- 添加海报弹窗 -->
  67. <u-popup v-model="showPoster" mode="center" border-radius="16" :custom-style="posterStyle">
  68. <view class="poster-container">
  69. <image class="poster-image" :src="posterInfo.tempFilePath || posterInfo.background" mode="aspectFit"
  70. @longpress="saveImage"></image>
  71. <view class="poster-tip">长按图片保存至相册分享</view>
  72. </view>
  73. </u-popup>
  74. </view>
  75. </view>
  76. </template>
  77. <script>
  78. export default {
  79. data() {
  80. return {
  81. detail: {
  82. totalIncome: 0,
  83. todayEstimateIncome: 0,
  84. lastMontyEstimateIncome: 0,
  85. thisMontyEstimateIncome: 0,
  86. lastMontySettlement: 0,
  87. waitAccount: 0,
  88. },
  89. showPoster: false,
  90. posterStyle: {
  91. backgroundColor: "transparent",
  92. },
  93. posterInfo: {},
  94. loading: false,
  95. };
  96. },
  97. onLoad() {
  98. this.getPartnerInfo();
  99. },
  100. methods: {
  101. getPartnerInfo() {
  102. uni.$u.get("/token/getUserPartnerInfo").then((res) => {
  103. if (res.code == 200) {
  104. this.detail = res.data;
  105. }
  106. });
  107. },
  108. //生成海报
  109. generatePoster() {
  110. this.loading = true;
  111. this.getPosterInfo().then((data) => {
  112. console.log("海报数据:", data);
  113. let userInfo = uni.getStorageSync("userInfo");
  114. // 下载背景图片
  115. uni.downloadFile({
  116. url: data.background,
  117. success: (downloadRes) => {
  118. const ctx = uni.createCanvasContext("posterCanvas");
  119. const dpr = uni.getSystemInfoSync().pixelRatio || 1;
  120. // 设置canvas尺寸为原图3倍,提高清晰度
  121. const canvasWidth = 750;
  122. const canvasHeight = 1334;
  123. // 绘制背景图
  124. ctx.drawImage(downloadRes.tempFilePath, 0, 0, canvasWidth, canvasHeight);
  125. // 下载并绘制二维码
  126. uni.downloadFile({
  127. url: data.inviteUrl,
  128. success: (qrRes) => {
  129. // 根据比例调整坐标
  130. const scale = canvasWidth / 250;
  131. const adjustX = (x) => x * scale;
  132. const adjustY = (y) => y * scale;
  133. const adjustSize = (size) => size * scale;
  134. let avatar =
  135. userInfo.imgPath || "https://shuhi.oss-cn-qingdao.aliyuncs.com/mini/t11.png";
  136. uni.downloadFile({
  137. url: avatar,
  138. success: (avatarRes) => {
  139. console.log("头像下载成功:", avatarRes);
  140. // 绘制微信头像
  141. ctx.save();
  142. ctx.beginPath();
  143. // Adjust the position and size for the circular clipping path
  144. const avatarRadius = adjustSize(15);
  145. const avatarX = adjustX(data.headImgPosX);
  146. const avatarY = adjustY(data.headImgPosY);
  147. ctx.arc(
  148. avatarX + avatarRadius,
  149. avatarY + avatarRadius,
  150. avatarRadius,
  151. 0,
  152. 2 * Math.PI
  153. );
  154. ctx.clip();
  155. ctx.drawImage(
  156. avatarRes.tempFilePath,
  157. avatarX,
  158. avatarY,
  159. adjustSize(30), // Ensure the size matches the clipping path
  160. adjustSize(30)
  161. );
  162. ctx.restore();
  163. // 绘制微信昵称
  164. ctx.setFontSize(adjustSize(14));
  165. ctx.setFillStyle("#333333");
  166. let nickName = userInfo.nickName || "书嗨";
  167. let canvasStr = `${nickName}邀请你加入`;
  168. let firstLine = canvasStr.slice(0, 16);
  169. let secondLine = canvasStr.slice(16);
  170. ctx.fillText(firstLine, adjustX(data.nickNamePosX), adjustY(data.nickNamePosY + 12));
  171. ctx.fillText(secondLine, adjustX(data.nickNamePosX), adjustY(data.nickNamePosY + 30));
  172. // 绘制二维码
  173. ctx.save();
  174. ctx.beginPath();
  175. ctx.arc(
  176. adjustX(data.qrCodePosX + data.qrCodeWidth / 2),
  177. adjustY(data.qrCodePosY + data.qrCodeHeight / 2),
  178. adjustSize(data.qrCodeWidth / 2),
  179. 0,
  180. 2 * Math.PI
  181. );
  182. ctx.clip();
  183. ctx.drawImage(
  184. qrRes.tempFilePath,
  185. adjustX(data.qrCodePosX),
  186. adjustY(data.qrCodePosY),
  187. adjustSize(data.qrCodeWidth),
  188. adjustSize(data.qrCodeHeight)
  189. );
  190. ctx.restore();
  191. // 执行最终绘制
  192. ctx.draw(false, () => {
  193. setTimeout(() => {
  194. uni.canvasToTempFilePath({
  195. canvasId: "posterCanvas",
  196. width: canvasWidth,
  197. height: canvasHeight,
  198. destWidth: canvasWidth * dpr,
  199. destHeight: canvasHeight * dpr,
  200. quality: 1,
  201. success: (res) => {
  202. this.loading = false;
  203. this.showPoster = true;
  204. this.posterInfo.tempFilePath = res.tempFilePath;
  205. },
  206. fail: (err) => {
  207. console.error("生成图片失败:", err);
  208. uni.showToast({
  209. title: "海报生成失败",
  210. icon: "none"
  211. });
  212. this.loading = false;
  213. }
  214. });
  215. }, 100);
  216. });
  217. },
  218. fail: (err) => {
  219. console.error("头像下载失败:", err);
  220. this.loading = false;
  221. uni.showToast({
  222. title: "头像加载失败",
  223. icon: "none"
  224. });
  225. }
  226. });
  227. },
  228. fail: (err) => {
  229. console.error("二维码下载失败:", err);
  230. this.loading = false;
  231. uni.showToast({
  232. title: "二维码加载失败",
  233. icon: "none"
  234. });
  235. }
  236. });
  237. },
  238. fail: (err) => {
  239. console.error("背景图下载失败:", err);
  240. this.loading = false;
  241. uni.showToast({
  242. title: "背景图加载失败",
  243. icon: "none"
  244. });
  245. }
  246. });
  247. });
  248. },
  249. //获取海报信息
  250. getPosterInfo() {
  251. return new Promise((resolve, reject) => {
  252. uni.$u.get("/token/getUserPartnerPic").then((res) => {
  253. if (res.code == 200) {
  254. this.posterInfo = res.data;
  255. resolve(res.data);
  256. } else {
  257. reject(res.msg);
  258. }
  259. });
  260. });
  261. },
  262. saveImage() {
  263. // #ifdef MP-WEIXIN
  264. uni.getSetting({
  265. success: (res) => {
  266. if (!res.authSetting["scope.writePhotosAlbum"]) {
  267. uni.authorize({
  268. scope: "scope.writePhotosAlbum",
  269. success: () => {
  270. this.saveImageToAlbum();
  271. },
  272. fail: () => {
  273. uni.showToast({
  274. title: "请授权保存图片到相册",
  275. icon: "none",
  276. });
  277. },
  278. });
  279. } else {
  280. this.saveImageToAlbum();
  281. }
  282. },
  283. });
  284. // #endif
  285. // #ifdef MP-ALIPAY
  286. // 支付宝小程序直接调用保存,会自动触发授权
  287. my.saveImage({
  288. url: this.posterInfo.tempFilePath,
  289. showActionSheet: false,
  290. success: () => {
  291. uni.showToast({
  292. title: "保存成功",
  293. icon: "success",
  294. });
  295. },
  296. fail: (err) => {
  297. console.error("保存失败:", err);
  298. uni.showToast({
  299. title: "保存失败",
  300. icon: "none",
  301. });
  302. }
  303. });
  304. // #endif
  305. },
  306. saveImageToAlbum() {
  307. // #ifdef MP-WEIXIN
  308. uni.saveImageToPhotosAlbum({
  309. filePath: this.posterInfo.tempFilePath,
  310. success: () => {
  311. uni.showToast({
  312. title: "保存成功",
  313. icon: "success",
  314. });
  315. },
  316. fail: () => {
  317. uni.showToast({
  318. title: "保存失败",
  319. icon: "none",
  320. });
  321. },
  322. });
  323. // #endif
  324. },
  325. },
  326. };
  327. </script>
  328. <style></style>
  329. <style lang="scss" scoped>
  330. .partner-home {
  331. min-height: 100vh;
  332. background-color: #f5f5f5;
  333. .section-title {
  334. font-size: 32rpx;
  335. font-weight: bold;
  336. margin-bottom: 20rpx;
  337. display: block;
  338. }
  339. .header {
  340. display: flex;
  341. align-items: flex-start;
  342. background-color: #4caf50;
  343. padding: 30rpx;
  344. padding-top: 45rpx;
  345. border-radius: 16rpx;
  346. margin-bottom: 20rpx;
  347. height: 322rpx;
  348. border-radius: 0rpx 0rpx 161rpx 161rpx;
  349. .avatar {
  350. width: 80rpx;
  351. height: 80rpx;
  352. border-radius: 50%;
  353. margin-right: 20rpx;
  354. }
  355. .header-text {
  356. flex: 1;
  357. color: #fff;
  358. .title {
  359. font-size: 32rpx;
  360. font-weight: bold;
  361. display: block;
  362. }
  363. .subtitle {
  364. font-size: 24rpx;
  365. opacity: 0.8;
  366. }
  367. }
  368. .rule-btn {
  369. padding: 10rpx 20rpx;
  370. background: rgba(255, 255, 255, 0.2);
  371. border-radius: 30rpx;
  372. color: #fff;
  373. font-size: 24rpx;
  374. }
  375. }
  376. .income-section {
  377. background-color: #fff;
  378. border-radius: 16rpx;
  379. padding: 30rpx;
  380. margin: 20rpx auto;
  381. width: calc(100% - 40rpx);
  382. .section-header {
  383. display: flex;
  384. justify-content: space-between;
  385. align-items: center;
  386. margin-bottom: 20rpx;
  387. .tip {
  388. font-size: 24rpx;
  389. color: #999;
  390. }
  391. }
  392. .income-grid {
  393. display: grid;
  394. grid-template-columns: repeat(2, 1fr);
  395. gap: 20rpx;
  396. .income-item {
  397. background-color: #f8f8f8;
  398. padding: 20rpx;
  399. border-radius: 12rpx;
  400. text-align: center;
  401. .amount {
  402. color: #ff4d4f;
  403. font-size: 32rpx;
  404. font-weight: bold;
  405. display: block;
  406. }
  407. .label {
  408. font-size: 24rpx;
  409. color: #666;
  410. margin-top: 8rpx;
  411. }
  412. }
  413. }
  414. }
  415. .tools-section {
  416. background-color: #fff;
  417. border-radius: 16rpx;
  418. padding: 30rpx;
  419. margin: 20rpx auto;
  420. width: calc(100% - 40rpx);
  421. .tools-grid {
  422. display: flex;
  423. gap: 30rpx;
  424. .tool-item {
  425. flex: 1;
  426. display: flex;
  427. align-items: center;
  428. padding: 20rpx;
  429. border-radius: 12rpx;
  430. image {
  431. width: 90rpx;
  432. height: 90rpx;
  433. margin-right: 10rpx;
  434. }
  435. text {
  436. font-size: 26rpx;
  437. color: #333;
  438. }
  439. }
  440. }
  441. }
  442. .generate-poster {
  443. width: calc(100% - 60rpx);
  444. height: 88rpx;
  445. line-height: 88rpx;
  446. background-color: #4caf50;
  447. color: #fff;
  448. border-radius: 10rpx;
  449. font-size: 28rpx;
  450. margin: 40rpx auto;
  451. }
  452. .poster-container {
  453. display: flex;
  454. flex-direction: column;
  455. align-items: center;
  456. .poster-image {
  457. width: 250px;
  458. height: 445px;
  459. }
  460. .poster-tip {
  461. font-size: 32rpx;
  462. color: #fff;
  463. text-align: center;
  464. background-color: #333;
  465. text-justify: space-between;
  466. width: 250px;
  467. line-height: 54rpx;
  468. position: relative;
  469. top: -2rpx;
  470. }
  471. }
  472. ::v-deep .u-mode-center-box {
  473. background-color: transparent !important;
  474. }
  475. }
  476. </style>