小说小程序前端代码仓库(小程序)
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.

496 lines
9.8 KiB

  1. <template>
  2. <!-- 豆豆充值页面 -->
  3. <view class="recharge-page">
  4. <!-- 顶部导航栏 -->
  5. <navbar title="豆豆充值" leftClick @leftClick="$utils.navigateBack" />
  6. <!-- 当前余额卡片 -->
  7. <view class="card balance-card">
  8. <view class="card-title">当前余额</view>
  9. <view class="current-balance">
  10. <text class="balance-amount">{{ userInfo.integerPrice || 0 }}</text>
  11. <text class="balance-unit">豆豆</text>
  12. </view>
  13. </view>
  14. <!-- 充值套餐选择 -->
  15. <view class="card package-card">
  16. <view class="card-title">选择充值套餐</view>
  17. <view class="package-grid">
  18. <view
  19. v-for="(item, index) in rechargePackages"
  20. :key="index"
  21. :class="['package-item', { selected: selectedPackage === index }]"
  22. @click="selectPackage(index)"
  23. >
  24. <view class="package-beans">{{ item.num }}豆豆</view>
  25. <view class="package-price">¥{{ item.money }}</view>
  26. <view v-if="item.giveNum" class="package-bonus">{{ item.giveNum }}豆豆</view>
  27. </view>
  28. </view>
  29. </view>
  30. <!-- 订单信息卡片 -->
  31. <view class="card order-card" v-if="totalPrice > 0">
  32. <view class="order-title">订单信息</view>
  33. <view class="order-item">
  34. <text class="order-label">充值金额</text>
  35. <text class="order-value">¥{{ totalPrice.toFixed(2) }}</text>
  36. </view>
  37. <view class="order-item">
  38. <text class="order-label">获得豆豆</text>
  39. <text class="order-value">{{ totalBeans }}豆豆</text>
  40. </view>
  41. <view class="order-divider"></view>
  42. <view class="order-item total-row">
  43. <view class="order-total-label">合计</view>
  44. <view class="order-total">
  45. <text class="order-total-highlight">¥{{ totalPrice.toFixed(2) }}</text>
  46. </view>
  47. </view>
  48. </view>
  49. <!-- 支付方式 -->
  50. <!-- <view class="card payment-card">
  51. <view class="card-title">支付方式</view>
  52. <view class="payment-methods">
  53. <view
  54. v-for="(method, index) in paymentMethods"
  55. :key="index"
  56. :class="['payment-item', { selected: selectedPayment === index }]"
  57. @click="selectPayment(index)"
  58. >
  59. <view class="payment-icon">
  60. <uv-icon :name="method.icon" size="40rpx" color="#223a7a" />
  61. </view>
  62. <text class="payment-name">{{ method.name }}</text>
  63. <view class="payment-radio">
  64. <view v-if="selectedPayment === index" class="radio-checked"></view>
  65. </view>
  66. </view>
  67. </view>
  68. </view> -->
  69. <!-- 提示信息 -->
  70. <view class="tip-text">
  71. 请仔细核查并确认相关信息因用户个人疏忽导致的充值错误需由用户自行承担一旦完成充值概不退换
  72. </view>
  73. <!-- 底部充值按钮 -->
  74. <view class="footer-bar">
  75. <button class="recharge-btn" @click="handleRecharge" :disabled="totalPrice <= 0">
  76. 立即充值 ¥{{ totalPrice.toFixed(2) }}
  77. </button>
  78. </view>
  79. </view>
  80. </template>
  81. <script>
  82. export default {
  83. data() {
  84. return {
  85. selectedPackage: null,
  86. customAmount: '',
  87. selectedPayment: 0,
  88. rechargePackages: [
  89. ],
  90. paymentMethods: [
  91. { name: '微信支付', icon: 'weixin-fill' },
  92. ]
  93. }
  94. },
  95. computed: {
  96. customBeans() {
  97. const amount = parseFloat(this.customAmount)
  98. return isNaN(amount) ? 0 : Math.floor(amount * 10)
  99. },
  100. totalPrice() {
  101. if (this.selectedPackage !== null) {
  102. return this.rechargePackages[this.selectedPackage].money
  103. } else if (this.customAmount) {
  104. return parseFloat(this.customAmount) || 0
  105. }
  106. return 0
  107. },
  108. totalBeans() {
  109. if (this.selectedPackage !== null) {
  110. const pkg = this.rechargePackages[this.selectedPackage]
  111. return pkg.num + (pkg.giveNum || 0)
  112. } else if (this.customAmount) {
  113. return this.customBeans
  114. }
  115. return 0
  116. }
  117. },
  118. onLoad(query) {
  119. // 可以通过参数预设充值金额
  120. if (query.amount) {
  121. this.customAmount = query.amount
  122. }
  123. },
  124. onShow() {
  125. this.$store.commit('getUserInfo')
  126. this.getPayPackageList()
  127. },
  128. methods: {
  129. getPayPackageList(){
  130. this.$api('getPayPackageList')
  131. .then(res => {
  132. if(res.code == 200){
  133. this.rechargePackages = res.result
  134. }
  135. })
  136. },
  137. // 选择充值套餐
  138. selectPackage(index) {
  139. this.selectedPackage = index
  140. this.customAmount = ''
  141. },
  142. // 选择支付方式
  143. selectPayment(index) {
  144. this.selectedPayment = index
  145. },
  146. // 自定义金额输入变化
  147. onCustomAmountChange() {
  148. if (this.customAmount) {
  149. this.selectedPackage = null
  150. }
  151. },
  152. // 处理充值
  153. async handleRecharge() {
  154. if (this.totalPrice <= 0) {
  155. uni.showToast({
  156. title: '请选择充值套餐或输入充值金额',
  157. icon: 'none'
  158. })
  159. return
  160. }
  161. if (this.selectedPayment === null) {
  162. uni.showToast({
  163. title: '请选择支付方式',
  164. icon: 'none'
  165. })
  166. return
  167. }
  168. try {
  169. // 这里应该调用充值接口
  170. const result = await this.$fetch('createPayPackageOrder', {
  171. packageId : this.rechargePackages[this.selectedPackage].id
  172. })
  173. await uni.requestPaymentWxPay({result})
  174. uni.showToast({
  175. title: `充值成功,获得${this.totalBeans}豆豆`,
  176. icon: 'success'
  177. })
  178. // 更新用户信息
  179. this.$store.commit('getUserInfo')
  180. setTimeout(() => {
  181. uni.navigateBack()
  182. }, 1500)
  183. } catch (error) {
  184. uni.showToast({
  185. title: '充值失败,请重试',
  186. icon: 'none'
  187. })
  188. }
  189. }
  190. }
  191. }
  192. </script>
  193. <style scoped lang="scss">
  194. .recharge-page {
  195. min-height: 100vh;
  196. background: #f8f8f8;
  197. padding-bottom: 120rpx;
  198. }
  199. .card {
  200. background: #fff;
  201. border-radius: 20rpx;
  202. margin: 24rpx 16rpx 0 16rpx;
  203. padding: 24rpx;
  204. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
  205. }
  206. .card-title {
  207. font-size: 32rpx;
  208. font-weight: bold;
  209. color: #222;
  210. margin-bottom: 24rpx;
  211. }
  212. // 当前余额卡片
  213. .balance-card {
  214. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  215. color: #fff;
  216. text-align: center;
  217. .card-title {
  218. color: #fff;
  219. opacity: 0.9;
  220. }
  221. .current-balance {
  222. display: flex;
  223. align-items: baseline;
  224. justify-content: center;
  225. gap: 8rpx;
  226. margin-top: 16rpx;
  227. .balance-amount {
  228. font-size: 56rpx;
  229. font-weight: bold;
  230. }
  231. .balance-unit {
  232. font-size: 28rpx;
  233. opacity: 0.8;
  234. }
  235. }
  236. }
  237. // 充值套餐
  238. .package-grid {
  239. display: flex;
  240. flex-wrap: wrap;
  241. gap: 16rpx;
  242. }
  243. .package-item {
  244. width: calc(50% - 8rpx);
  245. background: #f8f9ff;
  246. border: 2rpx solid #f0f0f0;
  247. border-radius: 16rpx;
  248. padding: 24rpx 16rpx;
  249. text-align: center;
  250. transition: all 0.2s;
  251. position: relative;
  252. box-sizing: border-box;
  253. &.selected {
  254. border-color: #223a7a;
  255. background: rgba(34, 58, 122, 0.05);
  256. }
  257. .package-beans {
  258. font-size: 32rpx;
  259. font-weight: bold;
  260. color: #222;
  261. margin-bottom: 8rpx;
  262. }
  263. .package-price {
  264. font-size: 28rpx;
  265. color: #223a7a;
  266. font-weight: 500;
  267. margin-bottom: 4rpx;
  268. }
  269. .package-bonus {
  270. font-size: 20rpx;
  271. color: #e94f7a;
  272. background: rgba(233, 79, 122, 0.1);
  273. padding: 2rpx 8rpx;
  274. border-radius: 8rpx;
  275. display: inline-block;
  276. }
  277. }
  278. // 表单样式
  279. .form-row {
  280. display: flex;
  281. flex-direction: column;
  282. align-items: flex-start;
  283. margin-bottom: 24rpx;
  284. }
  285. .form-label {
  286. color: #888;
  287. font-size: 28rpx;
  288. margin-bottom: 12rpx;
  289. font-weight: 400;
  290. &.required {
  291. color: #222;
  292. font-weight: 500;
  293. display: flex;
  294. align-items: center;
  295. }
  296. }
  297. .star {
  298. color: #e94f7a;
  299. margin-right: 4rpx;
  300. font-size: 28rpx;
  301. }
  302. .form-value {
  303. color: #222;
  304. font-size: 28rpx;
  305. font-weight: 600;
  306. }
  307. .form-input {
  308. width: 100%;
  309. border: 1rpx solid #e5e5e5;
  310. border-radius: 12rpx;
  311. font-size: 28rpx;
  312. padding: 16rpx;
  313. background: #fafafa;
  314. color: #222;
  315. box-sizing: border-box;
  316. }
  317. .divider {
  318. height: 1rpx;
  319. background: #f2f2f2;
  320. margin: 16rpx 0;
  321. width: 100%;
  322. }
  323. // 订单信息
  324. .order-card {
  325. .order-item {
  326. display: flex;
  327. justify-content: space-between;
  328. align-items: center;
  329. margin-bottom: 16rpx;
  330. .order-label {
  331. font-size: 28rpx;
  332. color: #666;
  333. }
  334. .order-value {
  335. font-size: 28rpx;
  336. color: #222;
  337. font-weight: 500;
  338. }
  339. &.total-row {
  340. margin-bottom: 0;
  341. margin-top: 16rpx;
  342. .order-total-label {
  343. font-size: 32rpx;
  344. color: #222;
  345. font-weight: 600;
  346. }
  347. .order-total-highlight {
  348. font-size: 32rpx;
  349. color: #223a7a;
  350. font-weight: bold;
  351. }
  352. }
  353. }
  354. .order-divider {
  355. height: 1rpx;
  356. background: #f2f2f2;
  357. margin: 16rpx 0;
  358. }
  359. }
  360. // 支付方式
  361. .payment-methods {
  362. display: flex;
  363. flex-direction: column;
  364. gap: 16rpx;
  365. }
  366. .payment-item {
  367. display: flex;
  368. align-items: center;
  369. padding: 20rpx;
  370. border: 2rpx solid #f0f0f0;
  371. border-radius: 12rpx;
  372. transition: all 0.2s;
  373. &.selected {
  374. border-color: #223a7a;
  375. background: rgba(34, 58, 122, 0.05);
  376. }
  377. .payment-icon {
  378. width: 48rpx;
  379. height: 48rpx;
  380. margin-right: 16rpx;
  381. image {
  382. width: 100%;
  383. height: 100%;
  384. }
  385. }
  386. .payment-name {
  387. flex: 1;
  388. font-size: 28rpx;
  389. color: #222;
  390. }
  391. .payment-radio {
  392. width: 32rpx;
  393. height: 32rpx;
  394. border: 2rpx solid #ddd;
  395. border-radius: 50%;
  396. position: relative;
  397. .radio-checked {
  398. width: 16rpx;
  399. height: 16rpx;
  400. background: #223a7a;
  401. border-radius: 50%;
  402. position: absolute;
  403. top: 50%;
  404. left: 50%;
  405. transform: translate(-50%, -50%);
  406. }
  407. }
  408. }
  409. .tip-text {
  410. color: #bbb;
  411. font-size: 22rpx;
  412. margin: 32rpx 32rpx 0 32rpx;
  413. line-height: 1.6;
  414. }
  415. .footer-bar {
  416. // position: fixed;
  417. // left: 0;
  418. // right: 0;
  419. // bottom: 90rpx;
  420. background: #fff;
  421. padding: 24rpx 32rpx 32rpx 32rpx;
  422. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  423. z-index: 10;
  424. }
  425. .recharge-btn {
  426. width: 100%;
  427. background: #223a7a;
  428. color: #fff;
  429. font-size: 32rpx;
  430. border-radius: 32rpx;
  431. height: 88rpx;
  432. line-height: 88rpx;
  433. border: none;
  434. font-weight: 500;
  435. &[disabled] {
  436. background: #ccc;
  437. color: #999;
  438. }
  439. }
  440. </style>