抖音小程序登录+广告实现代码完整实现

个人登录界面+点击后观看广告界面完整实现

记得切换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'
);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值