BasicList.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. <template>
  2. <div class="invite-page">
  3. <a-page-header title="我的邀请" class="page-header" />
  4. <div class="invite-content">
  5. <!-- 佣金概览卡片 -->
  6. <a-card class="commission-card" :bordered="false">
  7. <div class="commission-overview">
  8. <div class="commission-amount">
  9. <div class="amount-label">当前剩余佣金</div>
  10. <div class="amount-value">{{ currencySymbol }} {{ (info.commission_balance / 100).toFixed(2) }}</div>
  11. </div>
  12. <div class="commission-actions">
  13. <a-button type="primary" icon="swap" class="action-btn" @click="showTransferModal"> 划转 </a-button>
  14. <a-button type="default" icon="dollar" class="action-btn" @click="showWithdrawModal">
  15. 推广佣金提现
  16. </a-button>
  17. </div>
  18. </div>
  19. </a-card>
  20. <!-- 统计信息 -->
  21. <div class="stats-grid">
  22. <a-card class="stat-card">
  23. <div class="stat-item">
  24. <div class="stat-label">已注册用户数</div>
  25. <div class="stat-value">{{ stat[0] }}人</div>
  26. </div>
  27. </a-card>
  28. <a-card class="stat-card">
  29. <div class="stat-item">
  30. <div class="stat-label">佣金比例</div>
  31. <div class="stat-value">{{ stat[3] }}%</div>
  32. </div>
  33. </a-card>
  34. <a-card class="stat-card">
  35. <div class="stat-item">
  36. <div class="stat-label">
  37. <span>确认中的佣金</span>
  38. <a-tooltip title="等待确认的佣金金额">
  39. <a-icon type="question-circle" class="help-icon" />
  40. </a-tooltip>
  41. </div>
  42. <div class="stat-value">{{ currencySymbol }} {{ (stat[2] / 100).toFixed(2) }}</div>
  43. </div>
  44. </a-card>
  45. <a-card class="stat-card">
  46. <div class="stat-item">
  47. <div class="stat-label">累计获得佣金</div>
  48. <div class="stat-value">{{ currencySymbol }} {{ (stat[1] / 100).toFixed(2) }}</div>
  49. </div>
  50. </a-card>
  51. </div>
  52. <!-- 邀请码管理 - 列表形式 -->
  53. <a-card class="invite-code-card" title="邀请码管理">
  54. <div class="invite-code-header">
  55. <a-button type="primary" @click="generateInviteCode" icon="plus"> 生成邀请码 </a-button>
  56. </div>
  57. <div v-if="inviteCodes.length === 0" class="empty-state">
  58. <a-empty description="暂无数据" />
  59. </div>
  60. <div v-else class="invite-code-list">
  61. <div v-for="(code, index) in inviteCodes" :key="code.id" class="invite-code-item">
  62. <div class="code-info">
  63. <div class="code-section">
  64. <div class="code-label">邀请码</div>
  65. <div class="code-value">
  66. <span class="code-text">{{ code.code }}</span>
  67. <a-button type="link" @click="copyInviteLink(code.code)" class="copy-btn"> 复制链接 </a-button>
  68. </div>
  69. </div>
  70. <div class="time-section">
  71. <div class="code-label">创建时间</div>
  72. <div class="code-time">{{ getTime(code.created_at) }}</div>
  73. </div>
  74. </div>
  75. <a-divider v-if="index < inviteCodes.length - 1" />
  76. </div>
  77. </div>
  78. </a-card>
  79. <!-- 佣金发放记录 -->
  80. <a-card class="commission-record-card" title="佣金发放记录">
  81. <a-table
  82. :columns="commissionColumns"
  83. :data-source="commissionData"
  84. :pagination="false"
  85. class="commission-table"
  86. >
  87. <template slot="emptyText">
  88. <a-empty description="暂无数据" />
  89. </template>
  90. </a-table>
  91. </a-card>
  92. </div>
  93. <!-- 划转账弹窗 -->
  94. <a-modal
  95. v-model="transferModalVisible"
  96. title="推广佣金划转至余额"
  97. ok-text="确认"
  98. cancel-text="取消"
  99. @ok="handleTransfer"
  100. @cancel="handleCancelTransfer"
  101. >
  102. <div class="transfer-modal-content">
  103. <p class="transfer-notice">划转后的余额仅用于加速乐消费使用</p>
  104. <div class="current-balance">
  105. <div class="balance-label">当前推广佣金余额</div>
  106. <div class="balance-amount">{{ currencySymbol }} {{ (info.commission_balance / 100).toFixed(2) }}</div>
  107. </div>
  108. <a-form :form="transferForm" layout="vertical">
  109. <a-form-item label="划转金额">
  110. <a-input
  111. v-decorator="[
  112. 'transferAmount'
  113. ]"
  114. placeholder="请输入需要划转到余额的金额"
  115. type="number"
  116. />
  117. </a-form-item>
  118. </a-form>
  119. </div>
  120. </a-modal>
  121. <!-- 提现弹窗 -->
  122. <a-modal
  123. v-model="withdrawModalVisible"
  124. title="申请提现"
  125. ok-text="确认"
  126. cancel-text="取消"
  127. @ok="handleWithdraw"
  128. @cancel="handleCancelWithdraw"
  129. >
  130. <a-form :form="withdrawForm" layout="vertical">
  131. <a-form-item label="提现方式">
  132. <a-select
  133. v-decorator="['withdrawMethod', { rules: [{ required: true, message: '请选择提现方式' }] }]"
  134. placeholder="请选择提现方式"
  135. >
  136. <a-select-option v-for="(item,index) in withdrawMethods" :key="index" :value="item">{{ item }}</a-select-option>
  137. </a-select>
  138. </a-form-item>
  139. <a-form-item label="提现账号">
  140. <a-input
  141. v-decorator="['withdrawAccount', { rules: [{ required: true, message: '请输入提现账号' }] }]"
  142. placeholder="请输入提现账号"
  143. />
  144. </a-form-item>
  145. <!-- <a-form-item label="提现金额">
  146. <a-input
  147. v-decorator="[
  148. 'withdrawAmount',
  149. {
  150. rules: [{ required: true, message: '请输入提现金额' }, { validator: validateWithdrawAmount }],
  151. },
  152. ]"
  153. placeholder="请输入提现金额"
  154. type="number"
  155. />
  156. </a-form-item> -->
  157. </a-form>
  158. </a-modal>
  159. </div>
  160. </template>
  161. <script>
  162. export default {
  163. name: 'InvitePage',
  164. data() {
  165. return {
  166. transferModalVisible: false,
  167. withdrawModalVisible: false,
  168. transferForm: this.$form.createForm(this),
  169. withdrawForm: this.$form.createForm(this),
  170. commissionColumns: [
  171. {
  172. title: '发放时间',
  173. dataIndex: 'created_at',
  174. key: 'created_at',
  175. align: 'center',
  176. width: '50%',
  177. customRender: (text) => `${this.getTime(text)}`,
  178. },
  179. {
  180. title: '佣金',
  181. dataIndex: 'get_amount',
  182. key: 'get_amount',
  183. align: 'center',
  184. width: '50%',
  185. customRender: (text) => `${this.currencySymbol} ${(text / 100).toFixed(2)}`,
  186. },
  187. ],
  188. commissionData: [], // 空数据,显示暂无数据
  189. inviteCodes: [],
  190. info: '',
  191. currencySymbol: '',
  192. stat: [],
  193. load: false,
  194. withdrawMethods:[],
  195. loading:false,
  196. }
  197. },
  198. created() {
  199. this.getInfo()
  200. this.getConfig()
  201. this.getInviteList()
  202. this.getInviteCode()
  203. },
  204. methods: {
  205. getTime(time) {
  206. const targetTimestamp = time * 1000 // 转换为毫秒
  207. const targetDate = new Date(targetTimestamp)
  208. const expired = targetDate.toLocaleString('zh-CN')
  209. return expired
  210. },
  211. getInviteCode() {
  212. this.$http.get('/user/invite/fetch').then((res) => {
  213. console.log('res', res)
  214. this.inviteCodes = res?.data?.codes ?? []
  215. this.stat = res?.data?.stat ?? []
  216. })
  217. },
  218. getInviteList() {
  219. this.$http.get('/user/invite/details').then((res) => {
  220. console.log('res', res)
  221. this.commissionData = res?.data ?? []
  222. })
  223. },
  224. getInfo() {
  225. this.$http.get('/user/info').then((res) => {
  226. console.log('res', res)
  227. this.info = res?.data ?? ''
  228. })
  229. },
  230. getConfig(id) {
  231. this.$http
  232. .get(`/user/comm/config`)
  233. .then((res) => {
  234. this.currencySymbol = res?.data?.currency_symbol ?? ''
  235. this.withdrawMethods = res?.data?.withdraw_methods??[]
  236. })
  237. .catch((error) => {
  238. console.error('获取失败:', error)
  239. })
  240. .finally(() => {})
  241. },
  242. // 显示划转弹窗
  243. showTransferModal() {
  244. this.transferModalVisible = true
  245. this.$nextTick(() => {
  246. this.transferForm.resetFields()
  247. })
  248. },
  249. // 显示提现弹窗
  250. showWithdrawModal() {
  251. this.withdrawModalVisible = true
  252. this.$nextTick(() => {
  253. this.withdrawForm.resetFields()
  254. })
  255. },
  256. // 处理划转
  257. handleTransfer() {
  258. this.transferForm.validateFields((err, values) => {
  259. if (!err) {
  260. console.log('划转信息:', values)
  261. if(this.load) return false
  262. this.load = true
  263. this.$http
  264. .post(`/user/transfer`, {
  265. transfer_amount: values.transferAmount * 100,
  266. })
  267. .then((res) => {
  268. if(res.data){
  269. this.$message.success('划转成功')
  270. this.transferModalVisible = false
  271. this.getInfo()
  272. }
  273. })
  274. .catch((error) => {
  275. console.error('失败:', error)
  276. })
  277. .finally(() => {
  278. this.load = false
  279. })
  280. }
  281. })
  282. },
  283. // 处理提现
  284. handleWithdraw() {
  285. this.withdrawForm.validateFields((err, values) => {
  286. if (!err) {
  287. console.log('提现信息:', values)
  288. if(this.loading) return false
  289. this.loading = true
  290. this.$http
  291. .post(`/user/ticket/withdraw`, {
  292. withdraw_account: values.withdrawAccount,
  293. withdraw_method: values.withdrawMethod
  294. })
  295. .then((res) => {
  296. if(res.data){
  297. this.transferModalVisible = false
  298. this.getInfo()
  299. }
  300. })
  301. .catch((error) => {
  302. console.log('失败:', error)
  303. })
  304. .finally(() => {
  305. this.loading = false
  306. })
  307. this.$message.success('提现申请已提交')
  308. this.withdrawModalVisible = false
  309. }
  310. })
  311. },
  312. // 取消划转
  313. handleCancelTransfer() {
  314. this.transferModalVisible = false
  315. },
  316. // 取消提现
  317. handleCancelWithdraw() {
  318. this.withdrawModalVisible = false
  319. },
  320. // 生成邀请码
  321. generateInviteCode() {
  322. this.$http
  323. .get(`/user/invite/save`)
  324. .then((res) => {
  325. this.this.getInviteCode()
  326. this.$message.success('新邀请码已生成')
  327. })
  328. .catch((error) => {
  329. console.error('获取失败:', error)
  330. })
  331. .finally(() => {
  332. this.load = false
  333. })
  334. this.$message.success('新邀请码已生成')
  335. },
  336. // 复制邀请链接
  337. copyInviteLink(code) {
  338. const inviteLink = `${window.location.origin}/register?code=${code}`
  339. const textArea = document.createElement('textarea')
  340. textArea.value = inviteLink
  341. document.body.appendChild(textArea)
  342. textArea.select()
  343. document.execCommand('copy')
  344. document.body.removeChild(textArea)
  345. this.$message.success('邀请链接已复制到剪贴板')
  346. },
  347. // 验证划转金额
  348. validateTransferAmount(rule, value, callback) {
  349. if (value && value <= 0) {
  350. callback('划转金额必须大于0')
  351. } else {
  352. callback()
  353. }
  354. },
  355. // 验证提现金额
  356. validateWithdrawAmount(rule, value, callback) {
  357. if (value && value <= 0) {
  358. callback('提现金额必须大于0')
  359. } else if (value && value > 0) {
  360. callback()
  361. } else {
  362. callback()
  363. }
  364. },
  365. },
  366. }
  367. </script>
  368. <style scoped>
  369. .invite-page {
  370. padding: 24px;
  371. background: #f0f2f5;
  372. min-height: 100vh;
  373. }
  374. .page-header {
  375. background: #fff;
  376. margin-bottom: 24px;
  377. border-radius: 8px;
  378. padding: 16px 24px;
  379. }
  380. .commission-card {
  381. margin-bottom: 24px;
  382. border-radius: 8px;
  383. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  384. }
  385. .commission-overview {
  386. display: flex;
  387. justify-content: space-between;
  388. align-items: center;
  389. padding: 20px;
  390. }
  391. .commission-amount {
  392. text-align: left;
  393. }
  394. .amount-label {
  395. font-size: 14px;
  396. color: #666;
  397. margin-bottom: 8px;
  398. }
  399. .amount-value {
  400. font-size: 32px;
  401. font-weight: 700;
  402. color: #1890ff;
  403. }
  404. .commission-actions {
  405. display: flex;
  406. gap: 12px;
  407. }
  408. .action-btn {
  409. height: 40px;
  410. padding: 0 20px;
  411. border-radius: 6px;
  412. font-weight: 500;
  413. }
  414. /* 统计信息网格 */
  415. .stats-grid {
  416. display: grid;
  417. grid-template-columns: repeat(4, 1fr);
  418. gap: 16px;
  419. margin-bottom: 24px;
  420. }
  421. .stat-card {
  422. border-radius: 8px;
  423. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  424. }
  425. .stat-item {
  426. padding: 16px;
  427. text-align: center;
  428. }
  429. .stat-label {
  430. font-size: 14px;
  431. color: #666;
  432. margin-bottom: 8px;
  433. display: flex;
  434. align-items: center;
  435. justify-content: center;
  436. gap: 4px;
  437. }
  438. .stat-value {
  439. font-size: 18px;
  440. font-weight: 600;
  441. color: #262626;
  442. }
  443. .help-icon {
  444. color: #1890ff;
  445. cursor: pointer;
  446. }
  447. /* 邀请码管理和佣金发放记录卡片 */
  448. .invite-code-card,
  449. .commission-record-card {
  450. border-radius: 8px;
  451. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  452. margin-bottom: 24px;
  453. }
  454. .invite-code-header {
  455. margin-bottom: 16px;
  456. }
  457. .empty-state {
  458. padding: 40px 0;
  459. }
  460. .invite-code-list {
  461. padding: 8px 0;
  462. }
  463. .invite-code-item {
  464. padding: 8px 0;
  465. }
  466. .code-info {
  467. display: flex;
  468. justify-content: space-between;
  469. align-items: flex-start;
  470. }
  471. .code-section,
  472. .time-section {
  473. flex: 1;
  474. }
  475. .code-label {
  476. font-size: 14px;
  477. color: #666;
  478. margin-bottom: 8px;
  479. }
  480. .code-value {
  481. display: flex;
  482. align-items: center;
  483. gap: 12px;
  484. }
  485. .code-text {
  486. font-family: 'Courier New', monospace;
  487. font-weight: 600;
  488. color: #1890ff;
  489. font-size: 16px;
  490. }
  491. .copy-btn {
  492. padding: 0;
  493. height: auto;
  494. font-size: 14px;
  495. }
  496. .code-time {
  497. color: #666;
  498. font-size: 14px;
  499. }
  500. /* 佣金发放记录表格 */
  501. .commission-table {
  502. margin-top: 8px;
  503. }
  504. .commission-table >>> .ant-table-thead > tr > th {
  505. background-color: #fafafa;
  506. font-weight: 600;
  507. color: #262626;
  508. border-bottom: 1px solid #e8e8e8;
  509. }
  510. .commission-table >>> .ant-table-tbody > tr > td {
  511. border-bottom: 1px solid #e8e8e8;
  512. padding: 12px 8px;
  513. }
  514. .commission-table >>> .ant-table-tbody > tr:hover > td {
  515. background-color: #fafafa;
  516. }
  517. /* 弹窗内容样式 */
  518. .transfer-modal-content {
  519. padding: 8px 0;
  520. }
  521. .transfer-notice {
  522. color: #666;
  523. margin-bottom: 20px;
  524. font-size: 14px;
  525. }
  526. .current-balance {
  527. background: #f5f5f5;
  528. padding: 16px;
  529. border-radius: 6px;
  530. margin-bottom: 20px;
  531. }
  532. .balance-label {
  533. font-size: 14px;
  534. color: #666;
  535. margin-bottom: 4px;
  536. }
  537. .balance-amount {
  538. font-size: 20px;
  539. font-weight: 600;
  540. color: #1890ff;
  541. }
  542. /* 响应式设计 */
  543. @media (max-width: 768px) {
  544. .stats-grid {
  545. grid-template-columns: repeat(2, 1fr);
  546. }
  547. .commission-overview {
  548. flex-direction: column;
  549. gap: 16px;
  550. text-align: center;
  551. }
  552. .commission-actions {
  553. width: 100%;
  554. justify-content: center;
  555. }
  556. .code-info {
  557. flex-direction: column;
  558. gap: 12px;
  559. }
  560. .code-section,
  561. .time-section {
  562. width: 100%;
  563. }
  564. }
  565. </style>