mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 08:13:43 +08:00 
			
		
		
		
	opt: optimize styles for invitation page
This commit is contained in:
		@@ -2,6 +2,7 @@ package handler
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core"
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	"chatplus/store/model"
 | 
			
		||||
	"chatplus/store/vo"
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
@@ -55,7 +56,36 @@ func (h *InviteHandler) Code(c *gin.Context) {
 | 
			
		||||
// List Log 用户邀请记录
 | 
			
		||||
func (h *InviteHandler) List(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
	resp.SUCCESS(c)
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Page     int `json:"page"`
 | 
			
		||||
		PageSize int `json:"page_size"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	userId := h.GetLoginUserId(c)
 | 
			
		||||
	session := h.db.Session(&gorm.Session{}).Where("inviter_id = ?", userId)
 | 
			
		||||
	var total int64
 | 
			
		||||
	session.Model(&model.InviteLog{}).Count(&total)
 | 
			
		||||
	var items []model.InviteLog
 | 
			
		||||
	var list = make([]vo.InviteLog, 0)
 | 
			
		||||
	offset := (data.Page - 1) * data.PageSize
 | 
			
		||||
	res := session.Order("id DESC").Offset(offset).Limit(data.PageSize).Find(&items)
 | 
			
		||||
	if res.Error == nil {
 | 
			
		||||
		for _, item := range items {
 | 
			
		||||
			var v vo.InviteLog
 | 
			
		||||
			err := utils.CopyObject(item, &v)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				v.Id = item.Id
 | 
			
		||||
				v.CreatedAt = item.CreatedAt.Unix()
 | 
			
		||||
				list = append(list, v)
 | 
			
		||||
			} else {
 | 
			
		||||
				logger.Error(err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	resp.SUCCESS(c, vo.NewPage(total, data.Page, data.PageSize, list))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Hits 访问邀请码
 | 
			
		||||
 
 | 
			
		||||
@@ -350,7 +350,7 @@ func main() {
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, h *handler.InviteHandler) {
 | 
			
		||||
			group := s.Engine.Group("/api/invite/")
 | 
			
		||||
			group.GET("code", h.Code)
 | 
			
		||||
			group.GET("list", h.List)
 | 
			
		||||
			group.POST("list", h.List)
 | 
			
		||||
			group.GET("hits", h.Hits)
 | 
			
		||||
		}),
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										99
									
								
								web/src/components/InviteList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								web/src/components/InviteList.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="invite-list" v-loading="loading">
 | 
			
		||||
    <el-row v-if="items.length > 0">
 | 
			
		||||
      <el-table :data="items" :row-key="row => row.id" table-layout="auto" border
 | 
			
		||||
                style="--el-table-border-color:#373C47;
 | 
			
		||||
                --el-table-tr-bg-color:#2D323B;
 | 
			
		||||
                --el-table-row-hover-bg-color:#373C47;
 | 
			
		||||
                --el-table-header-bg-color:#474E5C;
 | 
			
		||||
                --el-table-text-color:#d1d1d1">
 | 
			
		||||
        <el-table-column prop="username" label="用户"/>
 | 
			
		||||
        <el-table-column prop="invite_code" label="邀请码"/>
 | 
			
		||||
        <el-table-column label="邀请奖励">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
            <span>对话:{{ scope.row['reward']['chat_calls'] }}次</span>,
 | 
			
		||||
            <span>绘图:{{ scope.row['reward']['chat_calls'] }}次</span>
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
 | 
			
		||||
        <el-table-column label="注册时间">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
            <span>{{ dateFormat(scope.row['created_at']) }}</span>
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
      </el-table>
 | 
			
		||||
    </el-row>
 | 
			
		||||
    <el-empty :image-size="100" v-else/>
 | 
			
		||||
    <div class="pagination">
 | 
			
		||||
      <el-pagination v-if="total > 0" background
 | 
			
		||||
                     layout="total,prev, pager, next"
 | 
			
		||||
                     :hide-on-single-page="true"
 | 
			
		||||
                     v-model:current-page="page"
 | 
			
		||||
                     v-model:page-size="pageSize"
 | 
			
		||||
                     @current-change="fetchData()"
 | 
			
		||||
                     :total="total"/>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {onMounted, ref, watch} from "vue";
 | 
			
		||||
import {httpGet, httpPost} from "@/utils/http";
 | 
			
		||||
import {ElMessage} from "element-plus";
 | 
			
		||||
import {dateFormat} from "@/utils/libs";
 | 
			
		||||
import {DocumentCopy} from "@element-plus/icons-vue";
 | 
			
		||||
import Clipboard from "clipboard";
 | 
			
		||||
 | 
			
		||||
const items = ref([])
 | 
			
		||||
const total = ref(0)
 | 
			
		||||
const page = ref(1)
 | 
			
		||||
const pageSize = ref(10)
 | 
			
		||||
const loading = ref(true)
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  fetchData()
 | 
			
		||||
  const clipboard = new Clipboard('.copy-order-no');
 | 
			
		||||
  clipboard.on('success', () => {
 | 
			
		||||
    ElMessage.success("复制成功!");
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  clipboard.on('error', () => {
 | 
			
		||||
    ElMessage.error('复制失败!');
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// 获取数据
 | 
			
		||||
const fetchData = () => {
 | 
			
		||||
  httpPost('/api/invite/list', {page: page.value, page_size: pageSize.value}).then((res) => {
 | 
			
		||||
    if (res.data) {
 | 
			
		||||
      items.value = res.data.items
 | 
			
		||||
      total.value = res.data.total
 | 
			
		||||
      page.value = res.data.page
 | 
			
		||||
      pageSize.value = res.data.page_size
 | 
			
		||||
    }
 | 
			
		||||
    loading.value = false
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    ElMessage.error("获取数据失败:" + e.message);
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="stylus">
 | 
			
		||||
.invite-list {
 | 
			
		||||
  .pagination {
 | 
			
		||||
    margin: 20px 0 0 0;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .copy-order-no {
 | 
			
		||||
    cursor pointer
 | 
			
		||||
    position relative
 | 
			
		||||
    left 6px
 | 
			
		||||
    top 2px
 | 
			
		||||
    color #20a0ff
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="user-bill" v-loading="loading">
 | 
			
		||||
    <el-row>
 | 
			
		||||
    <el-row v-if="items.length > 0">
 | 
			
		||||
      <el-table :data="items" :row-key="row => row.id" table-layout="auto" border
 | 
			
		||||
                style="--el-table-border-color:#373C47;
 | 
			
		||||
                --el-table-tr-bg-color:#2D323B;
 | 
			
		||||
@@ -31,7 +31,7 @@
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
      </el-table>
 | 
			
		||||
    </el-row>
 | 
			
		||||
 | 
			
		||||
    <el-empty :image-size="100" v-else/>
 | 
			
		||||
    <div class="pagination">
 | 
			
		||||
      <el-pagination v-if="total > 0" background
 | 
			
		||||
                     layout="total,prev, pager, next"
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,8 @@
 | 
			
		||||
      <h2>会员推广计划</h2>
 | 
			
		||||
      <div class="share-box">
 | 
			
		||||
        <div class="info">
 | 
			
		||||
          我们非常欢迎您把此应用分享给您身边的朋友,分享成功注册后您将获得 {{ inviteChatCalls }} 次对话额度以及
 | 
			
		||||
          {{ inviteImgCalls }} 次AI绘画额度作为奖励。
 | 
			
		||||
          我们非常欢迎您把此应用分享给您身边的朋友,分享成功注册后您将获得 <strong>{{ inviteChatCalls }}</strong> 次对话额度以及
 | 
			
		||||
          <strong>{{ inviteImgCalls }}</strong> 次AI绘画额度作为奖励。
 | 
			
		||||
          你可以保存下面的二维码或者直接复制分享您的专属推广链接发送给微信好友。
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
@@ -19,10 +19,66 @@
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="invite-stats">
 | 
			
		||||
        <el-row :gutter="20">
 | 
			
		||||
          <el-col :span="8">
 | 
			
		||||
            <div class="item-box yellow">
 | 
			
		||||
              <el-row :gutter="10">
 | 
			
		||||
                <el-col :span="10">
 | 
			
		||||
                  <div class="item-icon">
 | 
			
		||||
                    <i class="iconfont icon-role"></i>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
                <el-col :span="14">
 | 
			
		||||
                  <div class="item-info">
 | 
			
		||||
                    <div class="num">{{ hits }}</div>
 | 
			
		||||
                    <div class="text">点击量</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
              </el-row>
 | 
			
		||||
            </div>
 | 
			
		||||
          </el-col>
 | 
			
		||||
          <el-col :span="8">
 | 
			
		||||
            <div class="item-box blue">
 | 
			
		||||
              <el-row :gutter="10">
 | 
			
		||||
                <el-col :span="10">
 | 
			
		||||
                  <div class="item-icon">
 | 
			
		||||
                    <i class="iconfont icon-order"></i>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
                <el-col :span="14">
 | 
			
		||||
                  <div class="item-info">
 | 
			
		||||
                    <div class="num">{{ regNum }}</div>
 | 
			
		||||
                    <div class="text">注册量</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
              </el-row>
 | 
			
		||||
            </div>
 | 
			
		||||
          </el-col>
 | 
			
		||||
          <el-col :span="8">
 | 
			
		||||
            <div class="item-box green">
 | 
			
		||||
              <el-row :gutter="10">
 | 
			
		||||
                <el-col :span="10">
 | 
			
		||||
                  <div class="item-icon">
 | 
			
		||||
                    <i class="iconfont icon-chart"></i>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
                <el-col :span="14">
 | 
			
		||||
                  <div class="item-info">
 | 
			
		||||
                    <div class="num">{{ rate }}%</div>
 | 
			
		||||
                    <div class="text">转化率</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
              </el-row>
 | 
			
		||||
            </div>
 | 
			
		||||
          </el-col>
 | 
			
		||||
        </el-row>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <h2>您推荐用户</h2>
 | 
			
		||||
 | 
			
		||||
      <div class="invite-logs">
 | 
			
		||||
        <el-empty :image-size="100"/>
 | 
			
		||||
        <invite-list/>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -34,17 +90,26 @@ import QRCode from "qrcode";
 | 
			
		||||
import {httpGet} from "@/utils/http";
 | 
			
		||||
import {ElMessage} from "element-plus";
 | 
			
		||||
import Clipboard from "clipboard";
 | 
			
		||||
import InviteList from "@/components/InviteList.vue";
 | 
			
		||||
 | 
			
		||||
const inviteURL = ref("")
 | 
			
		||||
const qrImg = ref("")
 | 
			
		||||
const inviteChatCalls = ref(0)
 | 
			
		||||
const inviteImgCalls = ref(0)
 | 
			
		||||
const users = ref([])
 | 
			
		||||
const hits = ref(0)
 | 
			
		||||
const regNum = ref(0)
 | 
			
		||||
const rate = ref(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  httpGet("/api/invite/code").then(res => {
 | 
			
		||||
    const text = `${location.protocol}//${location.host}/register?invite_code=${res.data.code}`
 | 
			
		||||
    hits.value = res.data["hits"]
 | 
			
		||||
    regNum.value = res.data["reg_num"]
 | 
			
		||||
    if (hits.value > 0) {
 | 
			
		||||
      rate.value = ((regNum.value / hits.value) * 100).toFixed(2)
 | 
			
		||||
    }
 | 
			
		||||
    QRCode.toDataURL(text, {width: 400, height: 400, margin: 2}, (error, url) => {
 | 
			
		||||
      if (error) {
 | 
			
		||||
        console.error(error)
 | 
			
		||||
@@ -57,6 +122,13 @@ onMounted(() => {
 | 
			
		||||
    ElMessage.error("获取邀请码失败:" + e.message)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  httpGet("/api/admin/config/get?key=system").then(res => {
 | 
			
		||||
    inviteChatCalls.value = res.data["invite_chat_calls"]
 | 
			
		||||
    inviteImgCalls.value = res.data["invite_img_calls"]
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    ElMessage.error("获取系统配置失败:" + e.message)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // 复制链接
 | 
			
		||||
  const clipboard = new Clipboard('.copy-link');
 | 
			
		||||
  clipboard.on('success', () => {
 | 
			
		||||
@@ -77,7 +149,7 @@ onMounted(() => {
 | 
			
		||||
  height 100vh
 | 
			
		||||
 | 
			
		||||
  .inner {
 | 
			
		||||
    max-width 800px
 | 
			
		||||
    max-width 1000px
 | 
			
		||||
    width 100%
 | 
			
		||||
    color #e1e1e1
 | 
			
		||||
 | 
			
		||||
@@ -91,6 +163,10 @@ onMounted(() => {
 | 
			
		||||
        border 1px solid #444444
 | 
			
		||||
        border-radius 10px
 | 
			
		||||
        padding 10px
 | 
			
		||||
 | 
			
		||||
        strong {
 | 
			
		||||
          color #f56c6c
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .invite-qrcode {
 | 
			
		||||
@@ -113,6 +189,54 @@ onMounted(() => {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .invite-stats {
 | 
			
		||||
      padding 30px 10px
 | 
			
		||||
 | 
			
		||||
      .item-box {
 | 
			
		||||
        border-radius 10px
 | 
			
		||||
        padding 0 10px
 | 
			
		||||
 | 
			
		||||
        .el-col {
 | 
			
		||||
          height 140px
 | 
			
		||||
          display flex
 | 
			
		||||
          align-items center
 | 
			
		||||
          justify-content center
 | 
			
		||||
 | 
			
		||||
          .iconfont {
 | 
			
		||||
            font-size 60px
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          .item-info {
 | 
			
		||||
            font-size 18px
 | 
			
		||||
 | 
			
		||||
            .text, .num {
 | 
			
		||||
              padding 3px 0
 | 
			
		||||
              text-align center
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .num {
 | 
			
		||||
              font-size 40px
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .yellow {
 | 
			
		||||
        background-color #ffeecc
 | 
			
		||||
        color #D68F00
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .blue {
 | 
			
		||||
        background-color #D6E4FF
 | 
			
		||||
        color #1062FE
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .green {
 | 
			
		||||
        background-color #E7F8EB
 | 
			
		||||
        color #2D9F46
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user