partner-home.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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. // 下载背景图片
  119. uni.downloadFile({
  120. url: data.background,
  121. success: (downloadRes) => {
  122. const ctx = uni.createCanvasContext("posterCanvas");
  123. const dpr = uni.getSystemInfoSync().pixelRatio || 1;
  124. // 设置canvas尺寸为原图3倍,提高清晰度
  125. const canvasWidth = 750;
  126. const canvasHeight = 1334;
  127. // 绘制背景图
  128. ctx.drawImage(downloadRes.tempFilePath, 0, 0, canvasWidth, canvasHeight);
  129. // 下载并绘制二维码
  130. uni.downloadFile({
  131. url: data.inviteUrl,
  132. success: (qrRes) => {
  133. // 根据比例调整坐标
  134. const scale = canvasWidth / 250;
  135. const adjustX = (x) => x * scale;
  136. const adjustY = (y) => y * scale;
  137. const adjustSize = (size) => size * scale;
  138. // 绘制微信头像
  139. ctx.save();
  140. ctx.beginPath();
  141. ctx.arc(
  142. adjustX(data.headImgPosX),
  143. adjustY(data.headImgPosY),
  144. adjustSize(20),
  145. 0,
  146. 2 * Math.PI
  147. );
  148. ctx.clip();
  149. ctx.drawImage(
  150. "/static/img/logo.png",
  151. adjustX(data.headImgPosX),
  152. adjustY(data.headImgPosY),
  153. adjustSize(30),
  154. adjustSize(30)
  155. );
  156. ctx.restore();
  157. // 绘制微信昵称
  158. ctx.setFontSize(adjustSize(14));
  159. ctx.setFillStyle("#333333");
  160. ctx.fillText("合伙人", adjustX(data.nickNamePosX), adjustY(data.nickNamePosY));
  161. // 绘制二维码
  162. ctx.save(); // Save the current canvas state
  163. ctx.beginPath();
  164. // Create a circular clipping path for QR code
  165. ctx.arc(
  166. adjustX(data.qrCodePosX + data.qrCodeWidth/2),
  167. adjustY(data.qrCodePosY + data.qrCodeHeight/2),
  168. adjustSize(data.qrCodeWidth/2),
  169. 0,
  170. 2 * Math.PI
  171. );
  172. ctx.clip(); // Apply the clipping path
  173. ctx.drawImage(
  174. qrRes.tempFilePath,
  175. adjustX(data.qrCodePosX),
  176. adjustY(data.qrCodePosY),
  177. adjustSize(data.qrCodeWidth),
  178. adjustSize(data.qrCodeHeight)
  179. );
  180. ctx.restore(); // Restore the canvas state
  181. // 执行绘制
  182. ctx.draw(false, () => {
  183. setTimeout(() => {
  184. // 将画布内容保存为图片
  185. uni.canvasToTempFilePath({
  186. canvasId: "posterCanvas",
  187. width: canvasWidth,
  188. height: canvasHeight,
  189. destWidth: canvasWidth * dpr,
  190. destHeight: canvasHeight * dpr,
  191. quality: 1,
  192. success: (res) => {
  193. this.loading = false;
  194. this.showPoster = true;
  195. this.posterInfo.tempFilePath = res.tempFilePath;
  196. },
  197. fail: (err) => {
  198. console.error('生成图片失败:', err);
  199. uni.showToast({
  200. title: "海报生成失败",
  201. icon: "none",
  202. });
  203. this.loading = false;
  204. },
  205. });
  206. }, 100);
  207. });
  208. },
  209. fail: (err) => {
  210. this.loading = false;
  211. console.error('二维码下载失败:', err);
  212. uni.showToast({
  213. title: "二维码加载失败",
  214. icon: "none",
  215. });
  216. }
  217. });
  218. },
  219. fail: (err) => {
  220. console.error('背景图下载失败:', err);
  221. uni.showToast({
  222. title: "背景图加载失败",
  223. icon: "none",
  224. });
  225. }
  226. });
  227. });
  228. },
  229. //获取海报信息
  230. getPosterInfo() {
  231. return new Promise((resolve, reject) => {
  232. uni.$u.get("/token/getUserPartnerPic").then((res) => {
  233. if (res.code == 200) {
  234. this.posterInfo = res.data;
  235. resolve(res.data);
  236. } else {
  237. reject(res.msg);
  238. }
  239. });
  240. });
  241. },
  242. saveImage() {
  243. uni.getSetting({
  244. success: (res) => {
  245. if (!res.authSetting["scope.writePhotosAlbum"]) {
  246. uni.authorize({
  247. scope: "scope.writePhotosAlbum",
  248. success: () => {
  249. this.saveImageToAlbum();
  250. },
  251. fail: () => {
  252. uni.showToast({
  253. title: "请授权保存图片到相册",
  254. icon: "none",
  255. });
  256. },
  257. });
  258. } else {
  259. this.saveImageToAlbum();
  260. }
  261. },
  262. });
  263. },
  264. saveImageToAlbum() {
  265. uni.saveImageToPhotosAlbum({
  266. filePath: this.posterInfo.tempFilePath,
  267. success: () => {
  268. uni.showToast({
  269. title: "保存成功",
  270. icon: "success",
  271. });
  272. },
  273. fail: () => {
  274. uni.showToast({
  275. title: "保存失败",
  276. icon: "none",
  277. });
  278. },
  279. });
  280. },
  281. },
  282. };
  283. </script>
  284. <style></style>
  285. <style lang="scss" scoped>
  286. .partner-home {
  287. min-height: 100vh;
  288. background-color: #f5f5f5;
  289. .section-title {
  290. font-size: 32rpx;
  291. font-weight: bold;
  292. margin-bottom: 20rpx;
  293. display: block;
  294. }
  295. .header {
  296. display: flex;
  297. align-items: flex-start;
  298. background-color: #4caf50;
  299. padding: 30rpx;
  300. padding-top: 45rpx;
  301. border-radius: 16rpx;
  302. margin-bottom: 20rpx;
  303. height: 322rpx;
  304. border-radius: 0rpx 0rpx 161rpx 161rpx;
  305. .avatar {
  306. width: 80rpx;
  307. height: 80rpx;
  308. border-radius: 50%;
  309. margin-right: 20rpx;
  310. }
  311. .header-text {
  312. flex: 1;
  313. color: #fff;
  314. .title {
  315. font-size: 32rpx;
  316. font-weight: bold;
  317. display: block;
  318. }
  319. .subtitle {
  320. font-size: 24rpx;
  321. opacity: 0.8;
  322. }
  323. }
  324. .rule-btn {
  325. padding: 10rpx 20rpx;
  326. background: rgba(255, 255, 255, 0.2);
  327. border-radius: 30rpx;
  328. color: #fff;
  329. font-size: 24rpx;
  330. }
  331. }
  332. .income-section {
  333. background-color: #fff;
  334. border-radius: 16rpx;
  335. padding: 30rpx;
  336. margin: 20rpx auto;
  337. width: calc(100% - 40rpx);
  338. .section-header {
  339. display: flex;
  340. justify-content: space-between;
  341. align-items: center;
  342. margin-bottom: 20rpx;
  343. .tip {
  344. font-size: 24rpx;
  345. color: #999;
  346. }
  347. }
  348. .income-grid {
  349. display: grid;
  350. grid-template-columns: repeat(2, 1fr);
  351. gap: 20rpx;
  352. .income-item {
  353. background-color: #f8f8f8;
  354. padding: 20rpx;
  355. border-radius: 12rpx;
  356. text-align: center;
  357. .amount {
  358. color: #ff4d4f;
  359. font-size: 32rpx;
  360. font-weight: bold;
  361. display: block;
  362. }
  363. .label {
  364. font-size: 24rpx;
  365. color: #666;
  366. margin-top: 8rpx;
  367. }
  368. }
  369. }
  370. }
  371. .tools-section {
  372. background-color: #fff;
  373. border-radius: 16rpx;
  374. padding: 30rpx;
  375. margin: 20rpx auto;
  376. width: calc(100% - 40rpx);
  377. .tools-grid {
  378. display: flex;
  379. gap: 30rpx;
  380. .tool-item {
  381. flex: 1;
  382. display: flex;
  383. align-items: center;
  384. padding: 20rpx;
  385. border-radius: 12rpx;
  386. image {
  387. width: 90rpx;
  388. height: 90rpx;
  389. margin-right: 10rpx;
  390. }
  391. text {
  392. font-size: 26rpx;
  393. color: #333;
  394. }
  395. }
  396. }
  397. }
  398. .generate-poster {
  399. width: calc(100% - 60rpx);
  400. height: 88rpx;
  401. line-height: 88rpx;
  402. background-color: #4caf50;
  403. color: #fff;
  404. border-radius: 10rpx;
  405. font-size: 28rpx;
  406. margin: 40rpx auto;
  407. }
  408. .poster-container {
  409. display: flex;
  410. flex-direction: column;
  411. align-items: center;
  412. .poster-image {
  413. width: 250px;
  414. height: 445px;
  415. }
  416. .poster-tip {
  417. font-size: 32rpx;
  418. color: #fff;
  419. text-align: center;
  420. background-color: #333;
  421. text-justify: space-between;
  422. width: 250px;
  423. line-height: 54rpx;
  424. position: relative;
  425. top: -2rpx;
  426. }
  427. }
  428. ::v-deep .u-mode-center-box {
  429. background-color: transparent !important;
  430. }
  431. }
  432. </style>