个人登录界面+点击后观看广告界面完整实现
记得切换key,秘钥
个人中心界面
<template>
<view class="login-container">
<!-- 用户信息展示区域 -->
<view class="user-info" v-if="userInfo.nickName">
<image class="avatar" :src="userInfo.avatarUrl" mode="aspectFill"></image>
<view class="user-detail">
<text class="nickname">{{userInfo.nickName}}</text>
<text class="location">{{userInfo.province}} {{userInfo.city}}</text>
</view>
</view>
<!-- 未登录时显示登录按钮 -->
<view class="login-box" v-else>
<image class="logo" src="/static/logo.png" mode="aspectFit"></image>
<!-- 登录成功但未获取用户信息 -->
<button v-if="hasLogin" class="douyin-login-btn" @click="getUserProfile">
<image class="douyin-icon" src="/static/douyin-icon.png" mode="aspectFit"></image>
获取用户信息
</button>
<!-- 未登录 -->
<button v-else class="douyin-login-btn" @click="douyinLogin">
<image class="douyin-icon" src="/static/douyin-icon.png" mode="aspectFit"></image>
抖音一键登录
</button>
</view>
<!-- 底部协议 -->
<view class="agreement">
<text class="agreement-text">登录即代表您已同意</text>
<text class="link">《用户协议》</text>
<text class="agreement-text">和</text>
<text class="link">《隐私政策》</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
hasLogin: false, // 是否已登录但未获取用户信息
userInfo: {
avatarUrl: '',
nickName: '',
gender: 0,
country: '',
province: '',
city: ''
}
}
},
methods: {
// 抖音登录方法
douyinLogin() {
uni.login({
provider: 'toutiao',
success: (loginRes) => {
console.log('登录成功:', loginRes);
this.getSessionInfo(loginRes.code);
},
fail: (err) => {
console.error('登录失败:', err);
uni.showToast({
title: '登录失败',
icon: 'none'
});
}
});
},
// 获取会话信息
getSessionInfo(code) {
const appid = 'apikey';
const secret = 'secret';
const url = `https://developer.toutiao.com/api/apps/jscode2session?appid=${appid}&secret=${secret}&code=${code}`;
uni.request({
url: url,
method: 'GET',
success: (res) => {
console.log('session信息:', res.data);
if (res.data.openid) {
uni.setStorageSync('openid', res.data.openid);
uni.setStorageSync('session_key', res.data.session_key);
// 标记已登录,等待用户点击获取信息
this.hasLogin = true;
uni.showToast({
title: '请点击获取用户信息',
icon: 'none'
});
}
},
fail: (err) => {
console.error('获取session失败:', err);
uni.showToast({
title: '登录失败',
icon: 'none'
});
}
});
},
// 获取用户详细信息 - 直接在点击事件中调用
getUserProfile() {
// 这个方法现在直接被按钮点击事件调用
uni.getUserProfile({
desc: '用于完善用户资料',
success: (res) => {
console.log('用户信息:', res.userInfo);
// 保存用户信息
const userInfo = res.userInfo;
uni.setStorageSync('userInfo', userInfo);
// 更新数据
this.userInfo = {
avatarUrl: userInfo.avatarUrl,
nickName: userInfo.nickName,
gender: userInfo.gender, // 性别 0:未知、1:男、2:女
country: userInfo.country,
province: userInfo.province,
city: userInfo.city
};
// 重置登录状态
this.hasLogin = false;
uni.showToast({
title: '获取信息成功',
icon: 'success'
});
},
fail: (err) => {
console.error('获取用户信息失败:', err);
uni.showToast({
title: '获取用户信息失败',
icon: 'none'
});
}
});
}
}
}
</script>
<style lang="scss" scoped>
.login-container {
min-height: 100vh;
background-color: #ffffff;
position: relative;
}
.top-bg {
height: 400rpx;
background: linear-gradient(to bottom right, #FF0050, #FF4D85);
border-bottom-left-radius: 50% 20%;
border-bottom-right-radius: 50% 20%;
}
.login-box {
position: absolute;
top: 200rpx;
left: 50%;
transform: translateX(-50%);
width: 80%;
padding: 40rpx;
background: #ffffff;
border-radius: 20rpx;
box-shadow: 0 0 20rpx rgba(0,0,0,0.1);
.logo {
width: 120rpx;
height: 120rpx;
margin: 0 auto 30rpx;
display: block;
}
.welcome {
font-size: 36rpx;
color: #333;
text-align: center;
margin-bottom: 60rpx;
font-weight: bold;
}
}
.douyin-login-btn {
background: #000000;
color: #ffffff;
border-radius: 40rpx;
height: 88rpx;
line-height: 88rpx;
margin: 0 30rpx;
display: flex;
align-items: center;
justify-content: center;
.douyin-icon {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
}
.other-login {
margin-top: 60rpx;
.divider {
text-align: center;
color: #999;
font-size: 24rpx;
position: relative;
display: block;
margin: 40rpx 0;
&::before,
&::after {
content: '';
position: absolute;
top: 50%;
width: 80rpx;
height: 1px;
background: #eee;
}
&::before {
left: 30%;
}
&::after {
right: 30%;
}
}
}
.login-icons {
display: flex;
justify-content: center;
margin-top: 30rpx;
.icon-item {
padding: 0 40rpx;
text-align: center;
.iconfont {
font-size: 50rpx;
color: #666;
}
text {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
display: block;
}
}
}
.agreement {
position: absolute;
bottom: 40rpx;
left: 0;
right: 0;
text-align: center;
font-size: 24rpx;
.agreement-text {
color: #999;
}
.link {
color: #FF0050;
}
}
.user-info {
display: flex;
align-items: center;
padding: 30rpx;
background: #fff;
border-radius: 16rpx;
margin-bottom: 30rpx;
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
margin-right: 20rpx;
}
.user-detail {
flex: 1;
.nickname {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
display: block;
}
.location {
font-size: 24rpx;
color: #999;
}
}
}
</style>
主页界面代码,点击后弹出30s广告,不要忘记广告utils,在下方替换你的key
<template>
<view class="container">
<!-- 搜索栏 -->
<view class="search-box">
<input type="text" placeholder="搜索五金知识" class="search-input" />
<text class="iconfont icon-search"></text>
</view>
<!-- 文章列表 -->
<view class="article-list">
<view class="article-item" v-for="(item, index) in articles" :key="index" @tap="goToArticle(item.id)">
<image :src="item.image" mode="aspectFill" class="article-image"></image>
<view class="article-content">
<text class="article-title">{{item.title}}</text>
<text class="article-desc">{{item.description}}</text>
<view class="article-meta">
<text class="article-date">{{item.date}}</text>
<text class="article-category">{{item.category}}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { rewardedVideoAdController } from '../../utils';
export default {
data() {
return {
articles: [
{
id: 1,
title: '电动工具使用安全指南:新手必读',
description: '电动工具虽然便捷高效,但使用不当可能造成安全隐患。本文详细介绍各类电动工具的正确使用方法、注意事项及维护保养技巧,让您安全放心地使用电动工具。',
image: 'https://img.alicdn.com/i2/3962073743/O1CN01gW0ZMN1dWL4DmaAt6_!!3962073743.jpg',
date: '2024-03-20',
category: '安全指南'
},
{
id: 2,
title: '装修必备的20种五金工具详解',
description: '无论是专业装修还是DIY维修,合适的工具都是必不可少的。本文为您精选20种最实用的五金工具,并详细介绍它们的用途、选购要点和使用技巧。',
image: 'https://img.alicdn.com/i4/2206461487758/O1CN01046e8127BDX5h23xE_!!2206461487758.jpg',
date: '2024-03-19',
category: '工具百科'
},
{
id: 3,
title: '五金工具的保养与维护完全攻略',
description: '优质的五金工具价格不菲,想要延长使用寿命,日常保养维护必不可少。本文将分享专业的工具保养方法,包括清洁、防锈、储存等各个环节的注意事项。',
image: 'https://tse3-mm.cn.bing.net/th/id/OIP-C.IRfGMhuz7m4I9oKcjerYfwHaHa?rs=1&pid=ImgDetMain',
date: '2024-03-18',
category: '保养维护'
},
{
id: 4,
title: '新手装修必看:水电安装基础知识',
description: '水电安装是装修中最重要的环节之一,本文将为您详细讲解水电安装的基本流程、常用工具、注意事项以及验收标准,助您轻松掌握装修要领。',
image: 'https://img.alicdn.com/i3/2142756971/TB2e3Lim_cCL1FjSZFPXXXZgpXa_!!2142756971.jpg',
date: '2024-03-17',
category: '装修指南'
},
{
id: 5,
title: '五金工具选购攻略:如何避免踩坑',
description: '市面上五金工具品牌众多,质量参差不齐。本文从专业角度出发,教您如何识别工具品质,选择适合自己的产品,避免购买到劣质工具。',
image: 'https://img.alicdn.com/i2/3962073743/O1CN01gW0ZMN1dWL4DmaAt6_!!3962073743.jpg',
date: '2024-03-16',
category: '购买指南'
},
{
id: 6,
title: '常见五金工具使用技巧大全',
description: '掌握正确的工具使用方法,不仅能提高工作效率,还能延长工具寿命。本文将详细介绍锤子、扳手、螺丝刀等常用工具的专业使用技巧。',
image: 'https://img.alicdn.com/i2/3962073743/O1CN01gW0ZMN1dWL4DmaAt6_!!3962073743.jpg',
date: '2024-03-15',
category: '使用技巧'
},
{
id: 7,
title: '家用五金工具收纳整理方案',
description: '工具收纳不当不仅影响使用效率,还可能造成工具损坏。本文提供多种实用的工具收纳方案,帮助您打造一个井井有条的工具收纳空间。',
image: 'https://img.alicdn.com/i2/3962073743/O1CN01gW0ZMN1dWL4DmaAt6_!!3962073743.jpg',
date: '2024-03-14',
category: '收纳技巧'
},
{
id: 8,
title: '专业级电动工具使用指南',
description: '电钻、角磨机、电锯等专业电动工具的使用需要特别注意安全。本文详细讲解这些工具的使用要领和注意事项,助您安全高效完成工作。',
image: 'https://img.alicdn.com/i1/2206461487758/O1CN01046e8127BDX5h23xE_!!2206461487758.jpg',
date: '2024-03-13',
category: '专业指南'
}
]
}
},
onShow() {
},
methods: {
goToArticle(id) {
uni.showModal({
title: '温馨提示',
content: '观看30秒广告后即可浏览完整内容',
success: async (res) => {
if (res.confirm) {
try {
// 尝试展示广告
console.log(rewardedVideoAdController)
if (rewardedVideoAdController.getCanShow()) {
await rewardedVideoAdController.show(id)
} else {
console.log(rewardedVideoAdController)
if (rewardedVideoAdController.getIsFirstShow()) {
uni.showToast({
title: '请稍后再试',
icon: 'none',
duration: 2000
})
} else {
uni.showToast({
title: '请稍后再试',
icon: 'none',
duration: 2000
})
}
}
} catch (error) {
console.log(error)
console.error('广告展示失败:', error)
uni.showToast({
title: '广告加载失败,请稍后重试',
icon: 'none',
duration: 2000
})
}
}
}
})
}
}
}
</script>
<style>
.container {
padding: 20rpx;
background: linear-gradient(to bottom, #f0f2f5, #e8eaed);
min-height: 100vh;
}
.search-box {
position: relative;
padding: 20rpx;
background: #ffffff;
border-radius: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
border: 2rpx solid rgba(0, 0, 0, 0.05);
}
.search-input {
height: 70rpx;
padding-left: 70rpx;
font-size: 28rpx;
background: #f8f9fa;
border-radius: 35rpx;
}
.article-list {
padding: 10rpx;
}
.article-item {
display: flex;
background: #ffffff;
border-radius: 20rpx;
margin-bottom: 25rpx;
padding: 25rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
border: 2rpx solid rgba(0, 0, 0, 0.03);
transition: all 0.3s ease;
}
.article-item:active {
transform: scale(0.98);
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.article-image {
width: 220rpx;
height: 220rpx;
border-radius: 16rpx;
margin-right: 25rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.article-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.article-title {
font-size: 34rpx;
font-weight: 600;
color: #2c3e50;
margin-bottom: 12rpx;
line-height: 1.4;
}
.article-desc {
font-size: 26rpx;
color: #5c6b7a;
line-height: 1.6;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.article-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20rpx;
}
.article-date {
font-size: 24rpx;
color: #8795a1;
}
.article-category {
font-size: 24rpx;
color: #3498db;
padding: 6rpx 16rpx;
background: rgba(52, 152, 219, 0.1);
border-radius: 20rpx;
font-weight: 500;
}
/* 根据分类显示不同的背景色 */
.article-category:nth-child(4n+1) {
color: #3498db;
background: rgba(52, 152, 219, 0.1);
}
.article-category:nth-child(4n+2) {
color: #e67e22;
background: rgba(230, 126, 34, 0.1);
}
.article-category:nth-child(4n+3) {
color: #27ae60;
background: rgba(39, 174, 96, 0.1);
}
.article-category:nth-child(4n+4) {
color: #8e44ad;
background: rgba(142, 68, 173, 0.1);
}
</style>
广告工具类
// 小程序启动后30秒内才能展示差评广告
const AFTER_LAUNCH_TIME = 3000;
// 距离上次展示广告60秒后,才能展示插屏广告
const NEXT_SHOW_TIME = 6000;
// 是否可以展示广告
let canShow = true;
let timer = null;
// 是否为第一次展示广告
let isFirstShow = true;
// 休眠函数
function sleep(ms) {
if (timer) {
clearTimeout(timer);
}
return new Promise((resolve) => {
timer = setTimeout(() => {
resolve({});
}, ms);
});
}
class AdController {
/**
* @description: 控制广告是否展示
* @return {boolean}
*/
get canShow() {
return canShow;
}
/**
* @description: 判断广告是否第一次展示
* @return {boolean}
*/
get isFirstShow() {
return isFirstShow;
}
}
const controller = new AdController();
class InterstitialAdController {
constructor(adid) {
// 初始化 & 加载插屏广告
this.adid = adid;
this.ad = tt.createInterstitialAd({
adUnitId: this.adid,
});
this.ad.load();
}
/**
* @description: 在app的onLaunch方法中调用
* @return {Promise<void>}
*/
async onAppLaunch() {
// 小程序启动30秒后将canShow置为true,表示可以播放插屏广告
await sleep(AFTER_LAUNCH_TIME);
canShow = true;
}
/**
* @description: 展示插屏广告
* @return {Promise<void>}
*/
show() {
// 如果canShow=false,则不能播放视频
if (!controller.canShow) return;
// 展示插屏广告
this.ad.show();
// 设置广告关闭的回调函数
this.ad.onClose(() => {
console.log('close');
// 休眠60s后把canShow置为true
this.afterNext();
tt.redirectTo({
url: '/pages/index/index',
});
});
canShow = false;
isFirstShow = false;
}
async afterNext() {
// 休眠60s后把canShow置为true
await sleep(NEXT_SHOW_TIME);
canShow = true;
}
/**
* @description: 返回canShow的值
* @return {boolean}
*/
getCanShow() {
return controller.canShow;
}
/**
* @description: 返回isFirstShow的值
* @return {boolean}
*/
getIsFirstShow() {
return controller.isFirstShow;
}
}
class RewardedVideoAdController {
constructor(adid) {
this.ad = tt.createRewardedVideoAd({
adUnitId: adid,
});
// 监听错误
this.ad.onError((err) => {
tt.hideLoading();
switch (err.errCode) {
case 1004:
// 无合适的广告
break;
default:
// 更多请参考错误码文档
}
});
// 监听视频播放完成
this.ad.onClose((data) => {
tt.hideLoading();
console.log(data)
if (data.isEnded) {
console.log('观看了', data.count, '个视频');
} else {
console.log('未观看完视频');
}
// uni.redirectTo({
// url: '/pages/detail/detail',
// });
// 设置计时器,激励视屏广告展示60s内不能展示插屏广告
sleep(NEXT_SHOW_TIME).then(() => {
canShow = true;
});
});
// 预加载资源
this.ad.load();
}
show() {
tt.showLoading({
title: '正在加载广告',
});
// 当激励视频显示时,插屏视频不能展示。
canShow = false;
isFirstShow = false;
this.ad.show();
}
getCanShow() {
return controller.canShow;
}
}
/**
* @description: 初始化一个插屏广告,开发者需要将参数置换为自己小程序的广告id
*/
export const interstitialAdController = new InterstitialAdController(
'vdehpxypin6z1njg51'
);
/**
* @description: 初始化一个激励广告,开发者需要将参数置换为自己小程序的广告id
*/
export const rewardedVideoAdController = new RewardedVideoAdController(
'xpxtwaq6h66269nr5t'
);