diff --git a/app.js b/app.js index 56eef3a..e509415 100644 --- a/app.js +++ b/app.js @@ -70,29 +70,6 @@ App({ } ]; }, - onLaunch(option){ - const { query } = option; - // 获取渠道值 - this.globalData.cid = query.cid || ''; - // 挂载 request - const { domain, appid, cid } = this.globalData; - this.request = createRequest({ domain, appid, cid }); - - // 获取系统信息,计算安全区域 - const windowInfo = wx.getWindowInfo ? wx.getWindowInfo() : wx.getSystemInfoSync(); - const { statusBarHeight, safeArea, windowHeight, screenHeight } = windowInfo; - this.globalData.statusBarHeight = statusBarHeight; - this.globalData.safeAreaTop = safeArea.top; - this.globalData.safeAreaBottom = screenHeight - safeArea.bottom; - // 右上角胶囊位置信息 - const menuButtonInfo = wx.getMenuButtonBoundingClientRect(); - // 导航栏高度,是胶囊以及胶囊的上下边距所包含的高度,通常是 32+4+4 - this.globalData.navBarHeight = menuButtonInfo.height + (menuButtonInfo.top - statusBarHeight) * 2; - this.globalData.totalNavHeight = statusBarHeight + this.globalData.navBarHeight; - this.globalData.contentHeight = windowHeight - this.globalData.totalNavHeight; - this.globalData.totalTabBarHeight = this.globalData.tabBarHeight + this.globalData.safeAreaBottom; - this.globalData.contentWithoutTabbarHeight = this.globalData.contentHeight - this.globalData.totalTabBarHeight; - }, // TTS接口请求方法 async requestTTS(text, voice) { return new Promise((resolve, reject) => { @@ -165,5 +142,34 @@ App({ } return list; } + }, + onLaunch(option){ + const { query } = option; + // 获取渠道值 + this.globalData.cid = query.cid || ''; + // 挂载 request + const { domain, appid, cid } = this.globalData; + this.request = createRequest({ domain, appid, cid }); + + // 获取系统信息,计算安全区域 + const windowInfo = wx.getWindowInfo ? wx.getWindowInfo() : wx.getSystemInfoSync(); + const { statusBarHeight, safeArea, windowHeight, screenHeight } = windowInfo; + this.globalData.statusBarHeight = statusBarHeight; + this.globalData.safeAreaTop = safeArea.top; + this.globalData.safeAreaBottom = screenHeight - safeArea.bottom; + // 右上角胶囊位置信息 + const menuButtonInfo = wx.getMenuButtonBoundingClientRect(); + // 导航栏高度,是胶囊以及胶囊的上下边距所包含的高度,通常是 32+4+4 + this.globalData.navBarHeight = menuButtonInfo.height + (menuButtonInfo.top - statusBarHeight) * 2; + this.globalData.totalNavHeight = statusBarHeight + this.globalData.navBarHeight; + this.globalData.contentHeight = windowHeight - this.globalData.totalNavHeight; + this.globalData.totalTabBarHeight = this.globalData.tabBarHeight + this.globalData.safeAreaBottom; + this.globalData.contentWithoutTabbarHeight = this.globalData.contentHeight - this.globalData.totalTabBarHeight; + + // 移除 2022-12-05 之前的本地存储,因为可能有敏感词存在 + const timestamp = new Date('2022-12-05').getTime(); + const audioList = this.globalData.getAudioList().filter(v => v.time > timestamp); + const storageKey = 'audioList'; + wx.setStorageSync(storageKey, audioList); } }); diff --git a/app.json b/app.json index cf3e0b4..f08005d 100644 --- a/app.json +++ b/app.json @@ -1,14 +1,15 @@ { "pages": [ "pages/index/index", - "pages/result/result" + "pages/text-to-audio/text-to-audio", + "pages/text-to-audio-result/text-to-audio-result" ], "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "文字转语音", - "navigationBarTextStyle": "black", + "navigationBarTextStyle": "white", "navigationStyle": "custom" }, "sitemapLocation": "sitemap.json" -} \ No newline at end of file +} diff --git a/app.wxss b/app.wxss index 9105d95..89c2b87 100644 --- a/app.wxss +++ b/app.wxss @@ -48,13 +48,9 @@ page { padding-bottom: env(safe-area-inset-bottom); } -/* 保留原有的 container 类以兼容其他页面 */ -.container { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-between; - box-sizing: border-box; -} +::-webkit-scrollbar { + display: none; + width: 0; + height: 0; + color: transparent; +} \ No newline at end of file diff --git a/components/custom-nav-bar/index.js b/components/custom-nav-bar/index.js new file mode 100644 index 0000000..22045c0 --- /dev/null +++ b/components/custom-nav-bar/index.js @@ -0,0 +1,94 @@ +Component({ + properties: { + // 导航栏标题 + title: { + type: String, + value: '' + }, + // 背景颜色 + background: { + type: String, + value: 'transparent' + }, + // 标题颜色 + color: { + type: String, + value: '#333333' + }, + // 是否显示返回按钮 + showBack: { + type: Boolean, + value: true + }, + // 返回按钮图标颜色 + backIconColor: { + type: String, + value: '#ffffff' + }, + // 是否显示首页按钮 + showHome: { + type: Boolean, + value: false + }, + // 特殊模式,不占高度 + noPlaceholder: { + type: Boolean, + value: false + } + }, + + data: { + statusBarHeight: 0, + navBarHeight: 44, + totalHeight: 0 + }, + + lifetimes: { + attached() { + const app = getApp() + + // 直接使用 app.globalData 中已经计算好的数据 + this.setData({ + statusBarHeight: app.globalData.statusBarHeight, + navBarHeight: app.globalData.navBarHeight, + totalHeight: app.globalData.totalNavHeight + }) + } + }, + + methods: { + // 返回上一页 + onBack() { + const pages = getCurrentPages() + if (pages.length > 1) { + wx.navigateBack({ + delta: 1 + }) + } else { + // 如果是第一个页面,尝试返回首页 + wx.switchTab({ + url: '/pages/index/index', + fail: () => { + wx.reLaunch({ + url: '/pages/index/index' + }) + } + }) + } + this.triggerEvent('back') + }, + + // 返回首页 + onHome() { + wx.switchTab({ + url: '/pages/index/index', + fail: () => { + wx.reLaunch({ + url: '/pages/index/index' + }) + } + }) + this.triggerEvent('home') + } + } +}) diff --git a/components/custom-nav-bar/index.json b/components/custom-nav-bar/index.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/components/custom-nav-bar/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/components/custom-nav-bar/index.wxml b/components/custom-nav-bar/index.wxml new file mode 100644 index 0000000..f054dea --- /dev/null +++ b/components/custom-nav-bar/index.wxml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + {{title}} + + + + + + + + + + + diff --git a/components/custom-nav-bar/index.wxss b/components/custom-nav-bar/index.wxss new file mode 100644 index 0000000..78df547 --- /dev/null +++ b/components/custom-nav-bar/index.wxss @@ -0,0 +1,89 @@ +.nav-bar { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 9999; + background: transparent; +} + +.status-bar { + width: 100%; +} + +.nav-bar-content { + display: flex; + align-items: center; + justify-content: space-between; + position: relative; +} + +.nav-bar-left { + display: flex; + align-items: center; + flex-shrink: 0; + z-index: 2; +} + +.nav-bar-btn { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + margin-left: 8px; + margin-right: 4px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.2); + backdrop-filter: blur(10px); + transition: all 0.3s ease; +} + +.nav-bar-btn:active { + background: rgba(255, 255, 255, 0.4); + transform: scale(0.95); +} + +/* 返回按钮箭头 */ +.back-icon { + width: 10px; + height: 10px; + margin-left: 4px; + border-left: 1.5px solid #fff; + border-bottom: 1.5px solid #fff; + transform: rotate(45deg); +} + +/* 首页图标 */ +.home-icon { + font-size: 20px; + font-weight: 600; + line-height: 1; +} + +.nav-bar-title { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + font-size: 16px; + font-weight: bold; + max-width: 60%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + z-index: 1; +} + +.nav-bar-right { + display: flex; + align-items: center; + flex-shrink: 0; + min-width: 32px; + justify-content: flex-end; + z-index: 2; +} + +.nav-bar-placeholder { + width: 100%; +} diff --git a/pages/index/index.js b/pages/index/index.js index 59c4c69..c49a680 100644 --- a/pages/index/index.js +++ b/pages/index/index.js @@ -1,290 +1,7 @@ -//index.js -//获取应用实例 -const app = getApp(); -const typeList = app.globalData.getTypeList(); -const selectedIndex = typeList.length > 1 ? app.globalData.getSelectedTypeIndex() : 0; - -let audio = null; -let videoAd = null; - +// index.js Page({ - data: { - placeholder: '点击此处输入文字', - text: '', - typeList, - selectedIndex: String(selectedIndex) ? selectedIndex : 0, - isAudioPlaying: false, - currentAudioText: '' - }, - onLoad(){ + data: {}, + onLoad() { wx.showShareMenu(); - this.initVideoAd(); - }, - - // 初始化激励视频广告 - initVideoAd() { - // 若在开发者工具中无法预览广告,请切换开发者工具中的基础库版本 - if (wx.createRewardedVideoAd) { - videoAd = wx.createRewardedVideoAd({ - adUnitId: 'adunit-aedf0933f69de926' - }); - - videoAd.onLoad(() => { - console.log('激励视频广告加载成功'); - }); - - videoAd.onError((err) => { - console.error('激励视频广告加载失败', err); - }); - - videoAd.onClose((res) => { - if (res && res.isEnded) { - // 用户看完广告,重置使用次数 - this.resetTtsCount(); - } else { - // 用户中途退出 - wx.showToast({ - title: '请看完广告获取次数', - icon: 'none' - }); - } - }); - } - }, - - // 显示激励视频广告 - showVideoAd() { - if (videoAd) { - videoAd.show().catch(() => { - // 失败重试 - videoAd.load() - .then(() => videoAd.show()) - .catch(err => { - console.error('激励视频广告显示失败', err); - wx.showToast({ - title: '广告加载失败,请稍后重试', - icon: 'none' - }); - }); - }); - } else { - wx.showToast({ - title: '广告功能暂不可用', - icon: 'none' - }); - } - }, - - // 重置TTS使用次数 - async resetTtsCount() { - wx.showLoading({ - title: '正在重置次数...', - mask: true - }); - - try { - const result = await new Promise((resolve, reject) => { - app.request({ - url: `${app.globalData.domain}/open-api/wx330e54aa6000516d/tts-count-reset`, - success(res) { - resolve(res.data); - }, - fail(err) { - reject(err); - } - }); - }); - - wx.hideLoading(); - - if (result.code === 2000000) { - wx.showToast({ - title: '获得10次使用机会!', - icon: 'success' - }); - } else { - wx.showToast({ - title: result.msg || '重置失败', - icon: 'none' - }); - } - } catch (error) { - wx.hideLoading(); - wx.showToast({ - title: '网络错误', - icon: 'none' - }); - } - }, - - // 显示次数用完弹窗 - showLimitDialog() { - wx.showModal({ - title: '使用次数已用完', - content: '今日免费次数已用完,观看广告可获得额外10次使用机会', - confirmText: '观看广告', - cancelText: '明日再试', - success: (res) => { - if (res.confirm) { - this.showVideoAd(); - } - } - }); - }, - inputText(e){ - this.data.text = e.detail.value; - }, - async play(){ - const text = this.data.text || this.data.placeholder; - const selectedType = this.data.typeList[this.data.selectedIndex]; - const type = selectedType.desc; - - this.resetAudio(); - audio = wx.createInnerAudioContext({useWebAudioImplement: true}); - audio.autoplay = true; - - // 显示播放蒙层 - this.setData({ - isAudioPlaying: true, - currentAudioText: text - }); - - // 初始化蒙层组件的音频监听 - setTimeout(() => { - const overlayComponent = this.selectComponent('audio-overlay'); - if (overlayComponent) { - overlayComponent.initAudio(audio); - } - }, 100); - - // 检查是否播放过 - const audioList = app.globalData.getAudioList(); - let played = audioList.find(v => v.text === text && v.type === type); - - // 播放过,直接使用本地文件,无需检查合规性 - if(played){ - played.time = Date.now(); - app.globalData.setAudioList('delete', played.path); - app.globalData.setAudioList('add', played); - // 播放失败要移除缓存内容 - audio.onError(err => { - app.globalData.setAudioList('delete', played.path); - this.setData({ isAudioPlaying: false }); - wx.showModal({ - title: '提示', - content: '该语音播放过但缓存失效,点击确定重新播放', - showCancel: false, - success: res => { - this.play(); - } - }); - }); - return audio.src = played.path; - } - - // 未播放过,先检查文本是否合规 - wx.showLoading({ - title: '正在合成语音...', - mask: true - }); - const result = await new Promise((resolve, reject) => { - app.request({ - url: `${app.globalData.domain}/open-api/mp/msg-sec-check`, - data: { - appid: 'wx330e54aa6000516d', - content: text, - openid: wx.getStorageSync('openid') - }, - success(res){ - resolve(JSON.parse(res.data?.data)?.result); - }, - fail(err){ - reject(err); - } - }); - }); - // 不合规结束 - if(result.suggest !== 'pass'){ - wx.hideLoading(); - this.setData({ isAudioPlaying: false }); - return wx.showToast({ - title: '内容不合规', - icon: 'error', - mask: true, - duration: 2000 - }); - } - // 未播放过,调用TTS接口生成语音 - try { - const url = await selectedType.createUrl(text); - // 下载并播放 - wx.downloadFile({ - url, - success (res) { - wx.hideLoading(); - audio.src = res.tempFilePath; - app.globalData.setAudioList('add', { - // 音频文本 - text, - // 音频音效 - type, - // 音频网络地址 - url, - // 音频本地地址 - path: res.tempFilePath, - // 音频创建时间 - time: Date.now() - }); - }, - fail(err) { - console.log(err); - wx.hideLoading(); - this.setData({ isAudioPlaying: false }); - wx.showToast({ - title: '下载失败', - icon: 'none' - }); - } - }); - } catch (error) { - wx.hideLoading(); - this.setData({ isAudioPlaying: false }); - // TTS接口调用失败的错误已经在app.js中处理了 - } - }, - clear(){ - this.resetAudio(); - this.setData({ - text: '' - }); - }, - resetAudio(){ - if(audio){ - audio.destroy(); - audio = null; - } - this.setData({ - isAudioPlaying: false, - currentAudioText: '' - }); - }, - - // 音频停止播放的回调 - onAudioStop() { - this.resetAudio(); - }, - selectIndex(e){ - const { index } = e.target.dataset; - this.setData({ - selectedIndex: index - }, () => { - this.play(); - }); - app.globalData.setSelectedTypeIndex(index); - }, - gotoAudioList(){ - wx.navigateTo({ - url: '/pages/result/result' - }); } }); diff --git a/pages/index/index.wxml b/pages/index/index.wxml index 7c6a0c7..1d44a93 100644 --- a/pages/index/index.wxml +++ b/pages/index/index.wxml @@ -5,41 +5,12 @@ - -