四零语境前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

612 lines
16 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <view class="promote-page">
  3. <uv-status-bar></uv-status-bar>
  4. <!-- 主要内容区域 -->
  5. <view class="content">
  6. <!-- 推广官图片 -->
  7. <view class="promote-image-container">
  8. <uv-icon name="arrow-left" size="20" color="black" @click="goBack"></uv-icon>
  9. <view :style="{flex: 1, justifyContent: 'center', display: 'flex'}">
  10. <image
  11. class="promote-image"
  12. src="/static/promoter.png"
  13. mode="widthFix"
  14. ></image>
  15. </view>
  16. </view>
  17. <!-- 推广标语图片 -->
  18. <view class="slogan-container">
  19. <image
  20. class="slogan-image"
  21. src="/static/promotion-slogan.png"
  22. mode="widthFix"
  23. ></image>
  24. </view>
  25. </view>
  26. <!-- 外部大容器 -->
  27. <view class="main-container">
  28. <!-- 个人信息容器 -->
  29. <view class="profile-container">
  30. <image class="profile-avatar" :src="userInfo.avatar" mode="aspectFill"></image>
  31. <view class="profile-info">
  32. <text class="profile-name">{{ userInfo.name }}</text>
  33. <!-- <text class="profile-id">ID: {{ userInfo.id }}</text> -->
  34. </view>
  35. <view class="profile-stats">
  36. <view class="stat-item">
  37. <text class="stat-number">{{ num }}</text>
  38. <text class="stat-label">推广人数</text>
  39. </view>
  40. <view class="stat-item">
  41. <text class="stat-number">{{ userInfo.price || 0 }}</text>
  42. <text class="stat-label">总佣金</text>
  43. </view>
  44. </view>
  45. </view>
  46. <!-- 功能按钮区域 -->
  47. <view class="function-buttons">
  48. <view class="function-item" @click="goTeam">
  49. <image class="function-icon" src="/static/team-icon.png" mode="aspectFit"></image>
  50. <text class="function-text">我的团队</text>
  51. </view>
  52. <view class="function-item" @click="goQrcode">
  53. <image class="function-icon" src="/static/qrcode-icon.png" mode="aspectFit"></image>
  54. <text class="function-text">推广二维码</text>
  55. </view>
  56. <view class="function-item" @click="goCash">
  57. <image class="function-icon" src="/static/withdraw-icon.png" mode="aspectFit"></image>
  58. <text class="function-text">提现</text>
  59. </view>
  60. </view>
  61. <!-- 余额显示 -->
  62. <view class="balance-section">
  63. <text class="balance-label">余额</text>
  64. <text class="balance-amount">¥{{ userInfo.commission || 0 }}</text>
  65. <!-- <uv-icon name="arrow-right" size="16" color="#999"></uv-icon> -->
  66. </view>
  67. <!-- 转账记录容器 -->
  68. <view class="record-container">
  69. <view class="record-list">
  70. <view v-if="hasMore && !list.length">
  71. <uv-loading-icon text="加载中" ></uv-loading-icon>
  72. </view>
  73. <view v-else-if="!list.length">
  74. <uv-empty ></uv-empty>
  75. </view>
  76. <view v-else class="record-item" v-for="(item, index) in list" :key="index">
  77. <view class="record-avatar">
  78. <image class="avatar" :src="item.userInfo.avatar" mode="aspectFill"></image>
  79. <text class="avatar-name">{{ item.user_dictText }}</text>
  80. </view>
  81. <view class="record-info">
  82. <text class="record-title">{{ item.title }}</text>
  83. <text class="record-date">{{ item.createTime }}</text>
  84. </view>
  85. <view class="record-amount">
  86. <text class="amount">{{ item.type === '1' ? '+' + item.money : '-' + item.money }}</text>
  87. <!-- 领取按钮 - 当status=1且为微信提现时显示 -->
  88. <uv-button
  89. v-if="item.withdrawal && item.withdrawal.status === '1' && item.withdrawal.withdrawStatus === '0' && item.withdrawal.method === '0'"
  90. :custom-style="{
  91. height: '60rpx',
  92. borderRadius: '30rpx',
  93. background: '#06DADC',
  94. border: '1rpx solid #06DADC',
  95. lineHeight: '60rpx',
  96. fontSize: '24rpx',
  97. padding: '0 20rpx'
  98. }"
  99. type="primary"
  100. size="mini"
  101. @click="requestMerchantTransfer(item.withdrawal)">
  102. 领取
  103. </uv-button>
  104. <text class="status"
  105. :class="item.withdrawal.status === '1'
  106. && item.withdrawal.withdrawStatus === '0' ? 'status-available' : 'status-received'"
  107. v-else-if="item.withdrawal ">{{ getText(item.withdrawal) }}</text>
  108. </view>
  109. </view>
  110. </view>
  111. </view>
  112. </view>
  113. <!-- 底部固定按钮 -->
  114. <view class="bottom-button-container">
  115. <uv-button :custom-style="{
  116. height: '82rpx',
  117. borderRadius: '198rpx',
  118. background: '#06DADC',
  119. border: '2rpx solid #06DADC',
  120. lineHeight: '82rpx',
  121. fontSize: '36rpx'
  122. }" type="primary" @click="goQrcode">分享</uv-button>
  123. <uv-safe-bottom></uv-safe-bottom>
  124. </view>
  125. </view>
  126. </template>
  127. <script>
  128. import MixinList from '@/mixins/list'
  129. import uvLoadingIcon from '../../uni_modules/uv-loading-icon/components/uv-loading-icon/uv-loading-icon.vue'
  130. export default {
  131. components: { uvLoadingIcon },
  132. mixins: [MixinList],
  133. data() {
  134. return {
  135. mixinListApi: 'promotion.water',
  136. num: 0,
  137. userInfo: {},
  138. }
  139. },
  140. methods: {
  141. goBack() {
  142. uni.navigateBack()
  143. },
  144. goTeam() {
  145. uni.navigateTo({
  146. url: '/subPages/user/team'
  147. })
  148. },
  149. goCash() {
  150. uni.navigateTo({
  151. url: '/subPages/user/cash'
  152. })
  153. },
  154. getText(widhdraw) {
  155. // 如果已经领取了
  156. if (widhdraw.withdrawStatus !== '0') {
  157. return '已领取,' + (widhdraw.method === '0' ? '微信打款' : '线下付款')
  158. }else if (widhdraw.status === '0') {
  159. return '提现待审核中'
  160. }else if(widhdraw.status === '2'){
  161. return '审核失败!'
  162. }
  163. },
  164. // 获取推广统计
  165. async getStatistics() {
  166. const res = await this.$api.promotion.statistics()
  167. if (res.code === 200) {
  168. this.num = res.result.num
  169. }
  170. },
  171. // 获取用户信息
  172. async getUserInfo() {
  173. const res = await this.$api.login.getUserInfo()
  174. if (res.code === 200) {
  175. this.userInfo = res.result
  176. }
  177. },
  178. // 提现接口
  179. requestMerchantTransfer(item) {
  180. if (!wx.canIUse('requestMerchantTransfer')) {
  181. wx.showModal({
  182. content: '你的微信版本过低,请更新至最新版本。',
  183. showCancel: false,
  184. });
  185. return
  186. }
  187. // 提现
  188. // #ifdef H5
  189. // H5环境下检查微信环境
  190. if (!this.isInWechat()) {
  191. uni.showToast({
  192. title: '请在微信中打开',
  193. icon: 'none'
  194. })
  195. return
  196. }
  197. // H5环境下使用专门的H5提现方法
  198. this.requestMerchantTransferH5(item.packageInfo, async () => {
  199. const withdrawRes = await this.$api.promotion.withdrawSuccess({
  200. id : item.id
  201. })
  202. if (withdrawRes.code === 200) {
  203. uni.showToast({
  204. title: '提现成功',
  205. icon: 'success'
  206. })
  207. }
  208. })
  209. return
  210. // #endif
  211. // #ifdef MP-WEIXIN
  212. wx.requestMerchantTransfer({
  213. mchId: '1724993508',//
  214. appId: wx.getAccountInfoSync().miniProgram.appId,
  215. package: item.packageInfo,
  216. success: async (res) => {
  217. // res.err_msg将在页面展示成功后返回应用时返回ok,并不代表提款成功
  218. console.log('success:', res);
  219. // this.getData()
  220. const withdrawRes = await this.$api.promotion.withdrawSuccess({
  221. id : item.id
  222. })
  223. if (withdrawRes.code === 200) {
  224. uni.showToast({
  225. title: '提现成功',
  226. icon: 'success'
  227. })
  228. }
  229. },
  230. fail: (res) => {
  231. console.log('fail:', res);
  232. // this.getData()
  233. },
  234. });
  235. // #endif
  236. },
  237. // #ifdef H5
  238. // H5环境下的微信收款确认
  239. requestMerchantTransferH5(packageInfo, callback) {
  240. // 使用Vue原型上的jWeixin
  241. const jWeixin = this.$jWeixin
  242. if (!jWeixin) {
  243. console.error('jWeixin未初始化')
  244. uni.showToast({
  245. title: '微信环境异常',
  246. icon: 'none'
  247. })
  248. return
  249. }
  250. jWeixin.ready(() => {
  251. jWeixin.checkJsApi({
  252. jsApiList: ['requestMerchantTransfer'],
  253. success: (res) => {
  254. if (res.checkResult['requestMerchantTransfer']) {
  255. // H5环境下使用WeixinJSBridge
  256. if (typeof WeixinJSBridge !== 'undefined') {
  257. WeixinJSBridge.invoke('requestMerchantTransfer', {
  258. mchId: '1724993508',
  259. appId: wx.getAccountInfoSync().miniProgram.appId,
  260. package: packageInfo,
  261. }, (res) => {
  262. if (res.err_msg === 'requestMerchantTransfer:ok') {
  263. callback && callback()
  264. } else {
  265. console.log('提现失败:', res)
  266. uni.showToast({
  267. title: '提现失败,请稍后再试',
  268. icon: 'none'
  269. })
  270. }
  271. })
  272. } else {
  273. console.error('WeixinJSBridge未找到')
  274. uni.showToast({
  275. title: '请在微信中打开',
  276. icon: 'none'
  277. })
  278. }
  279. } else {
  280. uni.showToast({
  281. title: '你的微信版本过低,请更新至最新版本',
  282. icon: 'none'
  283. })
  284. }
  285. },
  286. fail: (error) => {
  287. console.error('checkJsApi失败:', error)
  288. uni.showToast({
  289. title: '微信接口检查失败',
  290. icon: 'none'
  291. })
  292. }
  293. })
  294. })
  295. jWeixin.error((res) => {
  296. console.error('微信配置失败:', res)
  297. uni.showToast({
  298. title: '微信配置失败',
  299. icon: 'none'
  300. })
  301. })
  302. },
  303. // #endif
  304. // 去二维码
  305. goQrcode() {
  306. uni.navigateTo({
  307. url: '/subPages/user/share'
  308. })
  309. },
  310. },
  311. async onShow() {
  312. Promise.all([
  313. this.getUserInfo(),
  314. this.getStatistics()
  315. ])
  316. },
  317. }
  318. </script>
  319. <style lang="scss" scoped>
  320. .promote-page {
  321. min-height: 100vh;
  322. background: linear-gradient(124deg, #22F2EB 0%, #24B0F6 30%);
  323. // background: linear-gradient(124deg, #22F2EB 0%, #24B0F6 100%);
  324. }
  325. .content {
  326. padding: 0rpx 40rpx 0;
  327. .promote-image-container {
  328. display: flex;
  329. justify-content: center;
  330. margin-bottom: 30rpx;
  331. align-items: center;
  332. .promote-image {
  333. margin: 0 auto;
  334. width: 168rpx;
  335. }
  336. }
  337. .slogan-container {
  338. display: flex;
  339. justify-content: center;
  340. margin-bottom: 80rpx;
  341. .slogan-image {
  342. width: 670rpx;
  343. }
  344. }
  345. }
  346. // 外部大容器
  347. .main-container {
  348. width: 100%;
  349. border-radius: 48rpx;
  350. border: 2rpx solid #06DADC12;
  351. box-sizing: border-box;
  352. padding: 40rpx;
  353. padding-bottom: 200rpx;
  354. background: linear-gradient(180deg, #DEFFFF 0%, #FBFEFF 22.65%, #F0FBFF 100%);
  355. // 个人信息容器
  356. .profile-container {
  357. background: #fff;
  358. margin-bottom: 30rpx;
  359. display: flex;
  360. align-items: center;
  361. border-radius: 48rpx;
  362. border-width: 2rpx;
  363. justify-content: space-between;
  364. background: linear-gradient(180deg, #DEFFFF 0%, #FBFEFF 22.65%, #F0FBFF 100%);
  365. // background: red;
  366. border: 2rpx solid #06DADC12;
  367. // padding-top: 32rpx;
  368. padding-right: 40rpx;
  369. // padding-bottom: 32rpx;
  370. // padding-left: 40rpx;
  371. height: 200rpx;
  372. .profile-avatar {
  373. width: 128rpx;
  374. height: 128rpx;
  375. border-radius: 50%;
  376. margin-right: 24rpx;
  377. }
  378. .profile-info {
  379. flex: 1;
  380. .profile-name {
  381. display: block;
  382. font-size: 32rpx;
  383. font-weight: 600;
  384. color: #333;
  385. margin-bottom: 8rpx;
  386. }
  387. .profile-id {
  388. font-size: 24rpx;
  389. color: #999;
  390. }
  391. }
  392. .profile-stats {
  393. display: flex;
  394. .stat-item {
  395. text-align: center;
  396. margin-left: 100rpx;
  397. .stat-number {
  398. display: block;
  399. font-size: 32rpx;
  400. font-weight: bold;
  401. color: #333;
  402. margin-bottom: 8rpx;
  403. }
  404. .stat-label {
  405. font-size: 24rpx;
  406. color: #999;
  407. }
  408. }
  409. }
  410. }
  411. // 功能按钮区域
  412. .function-buttons {
  413. display: flex;
  414. justify-content: space-around;
  415. align-items: center;
  416. margin-bottom: 48rpx;
  417. .function-item {
  418. display: flex;
  419. // flex-direction: column;
  420. align-items: center;
  421. gap: 16rpx;
  422. border-right: 1px solid #06DADC;
  423. padding-right: 30rpx;
  424. &:nth-child(3) {
  425. border-right: none;
  426. }
  427. .function-icon {
  428. width: 46rpx;
  429. height: 46rpx;
  430. // margin-bottom: 16rpx;
  431. }
  432. .function-text {
  433. font-size: 24rpx;
  434. color: #181818;
  435. }
  436. }
  437. }
  438. // 余额显示
  439. .balance-section {
  440. display: flex;
  441. align-items: center;
  442. justify-content: space-between;
  443. margin-bottom: 40rpx;
  444. .balance-label {
  445. font-size: 32rpx;
  446. color: #191919;
  447. font-weight: 500;
  448. }
  449. .balance-amount {
  450. font-size: 32rpx;
  451. font-weight: 500;
  452. color: #191919;
  453. flex: 1;
  454. margin-left: 20rpx;
  455. }
  456. }
  457. // 转账记录容器
  458. .record-container {
  459. .record-list {
  460. min-height: 40vh;
  461. border: 1px solid #F0F0F0;
  462. border-radius: 24rpx;
  463. // background: red;
  464. .record-item {
  465. height: 116rpx;
  466. background: #fff;
  467. // border-radius: 16rpx;
  468. padding: 16rpx 32rpx;
  469. // margin-bottom: 20rpx;
  470. display: flex;
  471. align-items: center;
  472. border-bottom: 2rpx solid #F1F1F1 ;
  473. // margin-right: 24rpx;
  474. // box-sizing: border-box;
  475. .record-avatar{
  476. display: flex;
  477. align-items: center;
  478. flex-direction: column;
  479. gap: 12rpx;
  480. .avatar-name {
  481. font-size: 24rpx;
  482. color: #999;
  483. }
  484. .avatar {
  485. width: 50rpx;
  486. height: 50rpx;
  487. border-radius: 50%;
  488. // margin-right: 24rpx;
  489. }
  490. }
  491. .record-info {
  492. flex: 3;
  493. text-align: center;
  494. .record-title {
  495. display: block;
  496. font-size: 28rpx;
  497. color: #333;
  498. margin-bottom: 8rpx;
  499. }
  500. .record-date {
  501. font-size: 26rpx;
  502. color: #A3A3A3;
  503. }
  504. }
  505. .record-amount {
  506. flex: 2;
  507. text-align: center;
  508. display: flex;
  509. flex-direction: column;
  510. align-items: center;
  511. gap: 8rpx;
  512. .amount {
  513. display: block;
  514. font-size: 28rpx;
  515. font-weight: bold;
  516. color: #333;
  517. }
  518. .status {
  519. font-size: 26rpx;
  520. // padding: 8rpx 16rpx;
  521. border-radius: 20rpx;
  522. &.status-received {
  523. // background: #f0f0f0;
  524. color: #A3A3A3;
  525. }
  526. &.status-available {
  527. // background: #22F2EB;
  528. padding: 3rpx 16rpx;
  529. color: $primary-color;
  530. border: 2rpx solid $primary-color;
  531. }
  532. }
  533. }
  534. }
  535. }
  536. }
  537. }
  538. // 底部固定按钮
  539. .bottom-button-container {
  540. position: fixed;
  541. bottom: 0;
  542. left: 0;
  543. right: 0;
  544. padding: 30rpx 40rpx;
  545. background: rgba(255, 255, 255, 0.95);
  546. border-top: 1px solid #F1F1F1;
  547. // height: 143rpx;
  548. // backdrop-filter: blur(10rpx);
  549. }
  550. </style>