opt: optimize styles for invitation page

This commit is contained in:
RockYang 2023-11-23 17:40:15 +08:00
parent 222b1ddbd9
commit e63a30064b
5 changed files with 261 additions and 8 deletions

View File

@ -2,6 +2,7 @@ package handler
import ( import (
"chatplus/core" "chatplus/core"
"chatplus/core/types"
"chatplus/store/model" "chatplus/store/model"
"chatplus/store/vo" "chatplus/store/vo"
"chatplus/utils" "chatplus/utils"
@ -55,7 +56,36 @@ func (h *InviteHandler) Code(c *gin.Context) {
// List Log 用户邀请记录 // List Log 用户邀请记录
func (h *InviteHandler) List(c *gin.Context) { 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 访问邀请码 // Hits 访问邀请码

View File

@ -350,7 +350,7 @@ func main() {
fx.Invoke(func(s *core.AppServer, h *handler.InviteHandler) { fx.Invoke(func(s *core.AppServer, h *handler.InviteHandler) {
group := s.Engine.Group("/api/invite/") group := s.Engine.Group("/api/invite/")
group.GET("code", h.Code) group.GET("code", h.Code)
group.GET("list", h.List) group.POST("list", h.List)
group.GET("hits", h.Hits) group.GET("hits", h.Hits)
}), }),

View 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>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="user-bill" v-loading="loading"> <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 <el-table :data="items" :row-key="row => row.id" table-layout="auto" border
style="--el-table-border-color:#373C47; style="--el-table-border-color:#373C47;
--el-table-tr-bg-color:#2D323B; --el-table-tr-bg-color:#2D323B;
@ -31,7 +31,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-row> </el-row>
<el-empty :image-size="100" v-else/>
<div class="pagination"> <div class="pagination">
<el-pagination v-if="total > 0" background <el-pagination v-if="total > 0" background
layout="total,prev, pager, next" layout="total,prev, pager, next"

View File

@ -4,8 +4,8 @@
<h2>会员推广计划</h2> <h2>会员推广计划</h2>
<div class="share-box"> <div class="share-box">
<div class="info"> <div class="info">
我们非常欢迎您把此应用分享给您身边的朋友分享成功注册后您将获得 {{ inviteChatCalls }} 次对话额度以及 我们非常欢迎您把此应用分享给您身边的朋友分享成功注册后您将获得 <strong>{{ inviteChatCalls }}</strong> 次对话额度以及
{{ inviteImgCalls }} 次AI绘画额度作为奖励 <strong>{{ inviteImgCalls }}</strong> 次AI绘画额度作为奖励
你可以保存下面的二维码或者直接复制分享您的专属推广链接发送给微信好友 你可以保存下面的二维码或者直接复制分享您的专属推广链接发送给微信好友
</div> </div>
@ -19,10 +19,66 @@
</div> </div>
</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> <h2>您推荐用户</h2>
<div class="invite-logs"> <div class="invite-logs">
<el-empty :image-size="100"/> <invite-list/>
</div> </div>
</div> </div>
</div> </div>
@ -34,17 +90,26 @@ import QRCode from "qrcode";
import {httpGet} from "@/utils/http"; import {httpGet} from "@/utils/http";
import {ElMessage} from "element-plus"; import {ElMessage} from "element-plus";
import Clipboard from "clipboard"; import Clipboard from "clipboard";
import InviteList from "@/components/InviteList.vue";
const inviteURL = ref("") const inviteURL = ref("")
const qrImg = ref("") const qrImg = ref("")
const inviteChatCalls = ref(0) const inviteChatCalls = ref(0)
const inviteImgCalls = ref(0) const inviteImgCalls = ref(0)
const users = ref([]) const users = ref([])
const hits = ref(0)
const regNum = ref(0)
const rate = ref(0)
onMounted(() => { onMounted(() => {
httpGet("/api/invite/code").then(res => { httpGet("/api/invite/code").then(res => {
const text = `${location.protocol}//${location.host}/register?invite_code=${res.data.code}` 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) => { QRCode.toDataURL(text, {width: 400, height: 400, margin: 2}, (error, url) => {
if (error) { if (error) {
console.error(error) console.error(error)
@ -57,6 +122,13 @@ onMounted(() => {
ElMessage.error("获取邀请码失败:" + e.message) 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'); const clipboard = new Clipboard('.copy-link');
clipboard.on('success', () => { clipboard.on('success', () => {
@ -77,7 +149,7 @@ onMounted(() => {
height 100vh height 100vh
.inner { .inner {
max-width 800px max-width 1000px
width 100% width 100%
color #e1e1e1 color #e1e1e1
@ -91,6 +163,10 @@ onMounted(() => {
border 1px solid #444444 border 1px solid #444444
border-radius 10px border-radius 10px
padding 10px padding 10px
strong {
color #f56c6c
}
} }
.invite-qrcode { .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
}
}
} }
} }