partner-home.vue 18 KB

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