样式:增加tab栏切换

This commit is contained in:
xiao12feng8 2026-02-01 10:56:35 +08:00
parent 1c52847a02
commit b6a36d2ff4
9 changed files with 14557 additions and 119 deletions

View File

@ -1,70 +0,0 @@
"""测试后端服务状态"""
import requests
import json
print("=" * 60)
print("后端服务状态检查")
print("=" * 60)
# 测试 PHP 后端
print("\n1. 测试 PHP 后端 (http://127.0.0.1:8080)")
print("-" * 60)
try:
response = requests.get("http://127.0.0.1:8080", timeout=3)
print(f"✓ PHP 后端正常运行")
print(f" 状态码: {response.status_code}")
except requests.exceptions.ConnectionError:
print("✗ PHP 后端未启动或无法连接")
print(" 请使用 phpstudy 启动 Apache 服务")
except Exception as e:
print(f"✗ 连接错误: {e}")
# 测试 Python 后端
print("\n2. 测试 Python 后端 (http://127.0.0.1:8000)")
print("-" * 60)
try:
response = requests.get("http://127.0.0.1:8000/docs", timeout=3)
print(f"✓ Python 后端正常运行")
print(f" 状态码: {response.status_code}")
except requests.exceptions.ConnectionError:
print("✗ Python 后端未启动")
print(" 请运行: python -m uvicorn lover.main:app --reload --host 127.0.0.1 --port 8000")
except Exception as e:
print(f"✗ 连接错误: {e}")
# 测试登录接口
print("\n3. 测试登录接口")
print("-" * 60)
test_data = {
"mobile": "13758924481",
"password": "123456",
"captcha": "223344"
}
print(f"测试数据: {json.dumps(test_data, ensure_ascii=False)}")
try:
response = requests.post(
"http://127.0.0.1:8080/api/user/mobilelogin",
json=test_data,
headers={
"Content-Type": "application/json",
"sid": "2"
},
timeout=5
)
print(f"状态码: {response.status_code}")
print(f"响应: {json.dumps(response.json(), ensure_ascii=False, indent=2)}")
except requests.exceptions.ConnectionError:
print("✗ 无法连接到登录接口")
print(" PHP 后端可能未正确配置")
except Exception as e:
print(f"✗ 请求错误: {e}")
print("\n" + "=" * 60)
print("检查完成")
print("=" * 60)
print("\n建议:")
print("1. 如果 PHP 后端未启动,请使用 phpstudy 启动 Apache")
print("2. 如果登录接口返回错误,检查数据库中是否有该用户")
print("3. 如果需要创建测试用户,可以在数据库中手动添加")

View File

@ -23,9 +23,10 @@
"type" : "uni-app:app-ios" "type" : "uni-app:app-ios"
}, },
{ {
"customPlaygroundType" : "local", "customPlaygroundType" : "device",
"packageName" : "uni.app.UNI1DDC3A9", "localRepoPath" : "C:/Users/Administrator/Desktop/Project/AI_GirlFriend/xuniYou",
"playground" : "custom", "packageName" : "uni.app.UNIF098CA6",
"playground" : "standard",
"type" : "uni-app:app-android" "type" : "uni-app:app-android"
}, },
{ {

View File

@ -1,6 +1,6 @@
{ {
"name" : "虚拟女友", "name" : "虚拟女友",
"appid" : "__UNI__1F3C178", "appid" : "__UNI__F098CA6",
"description" : "", "description" : "",
"versionName" : "1.0.0", "versionName" : "1.0.0",
"versionCode" : 110, "versionCode" : 110,
@ -64,38 +64,50 @@
} }
} }
}, },
"screenOrientation" : ["portrait-primary","portrait-secondary","landscape-primary","landscape-secondary"] "screenOrientation" : [
"portrait-primary",
"portrait-secondary",
"landscape-primary",
"landscape-secondary"
]
}, },
"nativePlugins" : { /* -
"Agora-RTC" : { * 使
"__plugin_info__" : { * Agora-RTC https://ext.dcloud.net.cn/plugin?id=3720
"name" : "Agora音视频插件", *
"description" : "Agora官方维护的音视频插件并且在GitHub上开源欢迎大家积极参与问题反馈和代码贡献", * 使
"platforms" : "Android,iOS", * "nativePlugins" : {
"url" : "", * "Agora-RTC" : {
"android_package_name" : "", * "__plugin_info__" : {
"ios_bundle_id" : "", * "name" : "Agora音视频插件",
"isCloud" : false, * "description" : "Agora官方维护的音视频插件并且在GitHub上开源欢迎大家积极参与问题反馈和代码贡献",
"bought" : -1, * "platforms" : "Android,iOS",
"pid" : "", * "url" : "",
"parameters" : {} * "android_package_name" : "",
} * "ios_bundle_id" : "",
}, * "isCloud" : false,
"AudioRecode" : { * "bought" : -1,
"__plugin_info__" : { * "pid" : "",
"name" : "AudioRecode", * "parameters" : {}
"description" : "录音插件", * }
"platforms" : "Android", * },
"url" : "", * "AudioRecode" : {
"android_package_name" : "", * "__plugin_info__" : {
"ios_bundle_id" : "", * "name" : "AudioRecode",
"isCloud" : false, * "description" : "录音插件",
"bought" : -1, * "platforms" : "Android",
"pid" : "", * "url" : "",
"parameters" : {} * "android_package_name" : "",
} * "ios_bundle_id" : "",
} * "isCloud" : false,
} * "bought" : -1,
* "pid" : "",
* "parameters" : {}
* }
* }
* }
*/
"nativePlugins" : {}
}, },
/* */ /* */
"quickapp" : {}, "quickapp" : {},

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,34 @@
</view> </view>
</view> </view>
<view v-show="getBobbiesList.reg_step == 4"> <view v-show="getBobbiesList.reg_step == 4">
<uni-nav-bar fixed statusBar background-color="transparent" :border="false"></uni-nav-bar> <!-- Tab - 固定在顶部 -->
<view class="tab-container" v-if="getBobbiesList.reg_step == 4">
<scroll-view class="tab-scroll" scroll-x="true" show-scrollbar="false">
<view class="tab-list">
<view
v-for="(tab, index) in tabs"
:key="index"
class="tab-item"
:class="{ 'active': currentTab === index }"
@click="switchTab(index)">
<text class="tab-name">{{ tab.name }}</text>
</view>
</view>
</scroll-view>
</view>
<!-- Swiper 内容区域 - 自定义细线指示器 -->
<swiper
v-if="getBobbiesList.reg_step == 4"
class="swiper-container"
:current="currentTab"
@change="onSwiperChange"
:duration="300"
:indicator-dots="false">
<!-- 首页内容 -->
<swiper-item>
<scroll-view class="swiper-scroll" scroll-y="true">
<view class="backA"></view> <view class="backA"></view>
<view class="bodyA"> <view class="bodyA">
<view class="listA"> <view class="listA">
@ -66,7 +93,7 @@
<view class="listA_progress" @click="toties"> <view class="listA_progress" @click="toties">
<view> <view>
<view class="listA_charts"> <view class="listA_charts">
<qiun-data-charts type="arcbar" :opts="opts" :chartData="chartData" /> <qiun-data-charts v-if="chartData.series" type="arcbar" :opts="opts" :chartData="chartData" />
<view class="listA_number faj"> <view class="listA_number faj">
{{ getBobbiesList.bond_today ? getBobbiesList.bond_today : 0 }}<text>/{{ {{ getBobbiesList.bond_today ? getBobbiesList.bond_today : 0 }}<text>/{{
getBobbiesList.bond_today_all ? getBobbiesList.bond_today_all : 0 getBobbiesList.bond_today_all ? getBobbiesList.bond_today_all : 0
@ -109,15 +136,137 @@
</image> </image>
<view class="bannerA_text faj">装束设置</view> <view class="bannerA_text faj">装束设置</view>
</view> </view>
<!-- <view class="bannerA" style="margin-bottom: 40rpx;">
<image @click="toreplacement1"
src="https://nvlovers.oss-cn-qingdao.aliyuncs.com/uploads/20251226/1b394cf9d5e731d8f34fc267ed59ac3d.png"
mode="aspectFill">
</image>
<view class="bannerA_text faj">装束设置</view>
</view> -->
</view> </view>
<!-- <view class="promptA faj"><text class="faj">达到Lv.4才可以解锁换装</text></view> --> </scroll-view>
</swiper-item>
<!-- 聊天页面 -->
<swiper-item>
<view class="swiper-content" @click="tochat">
<view class="feature-page">
<view class="feature-icon">💬</view>
<view class="feature-title">聊天</view>
<view class="feature-desc">与她开始对话</view>
<view class="feature-btn">进入聊天</view>
</view>
</view>
</swiper-item>
<!-- 唱歌页面 -->
<swiper-item>
<view class="swiper-content feature-page">
<view class="feature-icon">🎤</view>
<view class="feature-title">唱歌功能</view>
<view class="feature-desc">让她为你唱一首歌</view>
<scroll-view class="feature-scroll" scroll-y="true">
<view class="song-list">
<view class="song-item" v-for="(item,index) in singSongsList" :key="index" @click="selectSongDirect(item)">
<view class="song-info">
<text class="song-title">{{item.title}}</text>
</view>
<image class="song-icon" src="/static/images/chat_a6.png" mode="widthFix"></image>
</view>
</view>
</scroll-view>
</view>
</swiper-item>
<!-- 跳舞页面 -->
<swiper-item>
<view class="swiper-content feature-page">
<view class="feature-icon">💃</view>
<view class="feature-title">跳舞功能</view>
<view class="feature-desc">让她为你跳一支舞</view>
<view class="dance-form">
<textarea
class="dance-input"
v-model="dancePrompt"
placeholder="描述你想看的舞蹈动作"
maxlength="200" />
<view class="dance-btn" @click="requestDance">生成舞蹈视频</view>
</view>
</view>
</swiper-item>
<!-- 换服装页面 -->
<swiper-item>
<view class="swiper-content" @click="toreplacement">
<view class="feature-page">
<view class="feature-icon">👗</view>
<view class="feature-title">换服装</view>
<view class="feature-desc">为她挑选不同的服装</view>
<view class="feature-btn">进入换装</view>
</view>
</view>
</swiper-item>
<!-- 刷礼物页面 -->
<swiper-item>
<view class="swiper-content" @click="togift">
<view class="feature-page">
<view class="feature-icon">🎁</view>
<view class="feature-title">送礼物</view>
<view class="feature-desc">送她一份心意</view>
<view class="feature-btn">打开礼物匣</view>
</view>
</view>
</swiper-item>
<!-- 商城页面 -->
<swiper-item>
<view class="swiper-content feature-page">
<view class="feature-icon">🛒</view>
<view class="feature-title">商城</view>
<view class="feature-desc">购买更多功能和道具</view>
<scroll-view class="feature-scroll" scroll-y="true">
<view class="shop-list">
<view class="shop-item" v-for="i in 5" :key="i">
<view class="shop-info">
<text class="shop-title">套餐 {{i}}</text>
<text class="shop-desc">包含多种功能和道具</text>
</view>
<view class="shop-price">
<text class="price-value">¥{{i * 30}}</text>
<view class="shop-buy-btn">购买</view>
</view>
</view>
</view>
</scroll-view>
</view>
</swiper-item>
<!-- 短剧页面 -->
<swiper-item>
<view class="swiper-content feature-page">
<view class="feature-icon">🎬</view>
<view class="feature-title">短剧</view>
<view class="feature-desc">观看精彩短剧内容</view>
<scroll-view class="feature-scroll" scroll-y="true">
<view class="drama-list">
<view class="drama-item" v-for="i in 4" :key="i">
<image class="drama-cover" src="/static/images/avatar.png" mode="aspectFill"></image>
<view class="drama-info">
<text class="drama-title">短剧标题 {{i}}</text>
<text class="drama-desc">精彩剧情简介...</text>
<view class="drama-play-btn">立即播放</view>
</view>
</view>
</view>
</scroll-view>
</view>
</swiper-item>
</swiper>
<!-- 自定义细线指示器 -->
<view class="swiper-indicator" v-if="getBobbiesList.reg_step == 4">
<view
v-for="(tab, index) in tabs"
:key="index"
class="indicator-line"
:class="{ 'active': currentTab === index }">
</view>
</view>
</view> </view>
</view> </view>
<!-- 青少年模式弹框 --> <!-- 青少年模式弹框 -->
@ -135,7 +284,11 @@
import { import {
GetUserBasic, GetUserBasic,
LoverInit, LoverInit,
LoverBasic LoverBasic,
SingSongs,
SingGenerate,
SingGenerateTask,
DanceGenerate
} from '@/utils/api.js' } from '@/utils/api.js'
import notHave from '@/components/not-have.vue'; import notHave from '@/components/not-have.vue';
import topSafety from '@/components/top-safety.vue'; import topSafety from '@/components/top-safety.vue';
@ -154,6 +307,21 @@
}, },
data() { data() {
return { return {
// Tab
currentTab: 0,
tabs: [
{ name: '首页' },
{ name: '聊天' },
{ name: '唱歌' },
{ name: '跳舞' },
{ name: '换服装' },
{ name: '刷礼物' },
{ name: '商城' },
{ name: '短剧' }
],
dancePrompt: '',
singSongsList: [],
songId: 0,
statusBarHeight: uni.getWindowInfo().statusBarHeight, statusBarHeight: uni.getWindowInfo().statusBarHeight,
currentStep: 0, currentStep: 0,
chartData: {}, chartData: {},
@ -207,8 +375,99 @@
this.getUserBasic() this.getUserBasic()
this.loverBasic() this.loverBasic()
} }
//
this.getSingSongs();
}, },
methods: { methods: {
// Tab
switchTab(index) {
this.currentTab = index;
},
onSwiperChange(e) {
this.currentTab = e.detail.current;
},
//
selectSongDirect(song) {
this.songId = song.id;
uni.showLoading({ title: '生成中...' });
SingGenerate({ song_id: song.id }).then(res => {
if (res.code == 1) {
this.getSingGenerateTask(res.data.task_id);
} else {
uni.hideLoading();
uni.showToast({ title: res.msg, icon: 'none' });
}
});
},
//
requestDance() {
if (!this.dancePrompt || !this.dancePrompt.trim()) {
uni.showToast({ title: '请输入舞蹈描述', icon: 'none' });
return;
}
uni.showLoading({ title: '生成中...' });
DanceGenerate({ prompt: this.dancePrompt }).then(res => {
if (res.code == 1) {
uni.hideLoading();
uni.showToast({ title: '生成成功', icon: 'success' });
} else {
uni.hideLoading();
uni.showToast({ title: res.msg, icon: 'none' });
}
});
},
//
getSingSongs() {
SingSongs().then(res => {
if (res.code == 1) {
this.singSongsList = res.data.list || [];
}
});
},
//
getSingSongs() {
SingSongs().then(res => {
if (res.code == 1) {
this.singSongsList = res.data.songs || [];
}
});
},
//
getSingGenerateTask(task_id) {
const that = this;
let attempts = 0;
const maxAttempts = 20;
const doPoll = () => {
attempts++;
if (attempts > maxAttempts) {
uni.hideLoading();
uni.showToast({ title: '处理超时,请稍后重试', icon: 'none' });
return;
}
SingGenerateTask(task_id).then(res => {
if (res.code == 1) {
const data = res.data;
if (data.status == 'succeeded') {
uni.hideLoading();
uni.showToast({ title: '生成成功', icon: 'success' });
} else if (data.status == 'failed') {
uni.hideLoading();
uni.showToast({ title: data.error_msg || '生成失败', icon: 'none' });
} else {
setTimeout(doPoll, 4000);
}
} else {
uni.hideLoading();
uni.showToast({ title: res.msg, icon: 'none' });
}
});
};
doPoll();
},
// //
handleUnderAgeStatusChange(status) { handleUnderAgeStatusChange(status) {
this.underAgeEnabled = status; this.underAgeEnabled = status;
@ -844,4 +1103,296 @@
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.5);
border-radius: 20rpx; border-radius: 20rpx;
} }
/* Tab 栏样式 - 固定在顶部 */
.tab-container {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
background: rgba(255, 255, 255, 0.95);
padding: 10rpx 0;
padding-top: calc(10rpx + var(--status-bar-height));
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.tab-scroll {
white-space: nowrap;
}
.tab-list {
display: inline-flex;
padding: 0 20rpx;
}
.tab-item {
display: inline-block;
padding: 15rpx 30rpx;
margin: 0 10rpx;
font-size: 28rpx;
color: #666;
border-radius: 30rpx;
transition: all 0.3s;
}
.tab-item.active {
color: #fff;
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%);
font-weight: bold;
}
.tab-name {
white-space: nowrap;
}
/* Swiper 容器样式 */
.swiper-container {
flex: 1;
height: calc(100vh - 120rpx);
margin-top: calc(80rpx + var(--status-bar-height));
}
.swiper-scroll {
height: 100%;
}
.swiper-content {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
/* 功能页面样式 */
.feature-page {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40rpx;
box-sizing: border-box;
}
.feature-icon {
font-size: 120rpx;
margin-bottom: 30rpx;
}
.feature-title {
font-size: 40rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.feature-desc {
font-size: 28rpx;
color: #666;
margin-bottom: 40rpx;
text-align: center;
}
.feature-btn {
padding: 20rpx 60rpx;
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%);
color: #fff;
border-radius: 50rpx;
font-size: 30rpx;
}
.feature-scroll {
width: 100%;
max-height: 60vh;
margin-top: 20rpx;
}
/* 歌曲列表样式 */
.song-list {
width: 100%;
padding: 20rpx;
}
.song-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 25rpx;
margin-bottom: 20rpx;
background: #f5f5f5;
border-radius: 15rpx;
}
.song-info {
flex: 1;
}
.song-title {
font-size: 30rpx;
color: #333;
}
.song-icon {
width: 40rpx;
height: 40rpx;
}
/* 跳舞表单样式 */
.dance-form {
width: 100%;
padding: 20rpx;
}
.dance-input {
width: 100%;
min-height: 200rpx;
padding: 20rpx;
background: #f5f5f5;
border-radius: 15rpx;
font-size: 28rpx;
margin-bottom: 30rpx;
box-sizing: border-box;
}
.dance-btn {
width: 100%;
padding: 25rpx;
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%);
color: #fff;
text-align: center;
border-radius: 50rpx;
font-size: 30rpx;
}
/* 商城列表样式 */
.shop-list {
width: 100%;
padding: 20rpx;
}
.shop-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
margin-bottom: 20rpx;
background: #f5f5f5;
border-radius: 15rpx;
}
.shop-info {
flex: 1;
}
.shop-title {
font-size: 32rpx;
color: #333;
font-weight: bold;
margin-bottom: 10rpx;
}
.shop-desc {
font-size: 24rpx;
color: #999;
}
.shop-price {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.price-value {
font-size: 36rpx;
color: #ff4444;
font-weight: bold;
margin-bottom: 10rpx;
}
.shop-buy-btn {
padding: 10rpx 30rpx;
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%);
color: #fff;
border-radius: 30rpx;
font-size: 24rpx;
}
/* 短剧列表样式 */
.drama-list {
width: 100%;
padding: 20rpx;
}
.drama-item {
display: flex;
margin-bottom: 30rpx;
background: #f5f5f5;
border-radius: 15rpx;
overflow: hidden;
}
.drama-cover {
width: 200rpx;
height: 150rpx;
flex-shrink: 0;
}
.drama-info {
flex: 1;
padding: 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.drama-title {
font-size: 30rpx;
color: #333;
font-weight: bold;
margin-bottom: 10rpx;
}
.drama-desc {
font-size: 24rpx;
color: #999;
margin-bottom: 15rpx;
}
.drama-play-btn {
align-self: flex-start;
padding: 8rpx 25rpx;
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%);
color: #fff;
border-radius: 20rpx;
font-size: 24rpx;
}
/* 自定义细线指示器样式 */
.swiper-indicator {
position: fixed;
bottom: 20rpx;
left: 0;
right: 0;
display: flex;
justify-content: center;
align-items: center;
gap: 8rpx;
z-index: 100;
padding: 0 20rpx;
}
.indicator-line {
flex: 1;
height: 3rpx;
background: rgba(0, 0, 0, 0.1);
border-radius: 2rpx;
transition: all 0.3s ease;
}
.indicator-line.active {
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%);
height: 4rpx;
}
</style> </style>

View File

@ -1,6 +1,10 @@
// 本地开发 - 电脑浏览器调试使用
export const baseURL = 'http://127.0.0.1:8080' export const baseURL = 'http://127.0.0.1:8080'
export const baseURLPy = 'http://127.0.0.1:8000' export const baseURLPy = 'http://127.0.0.1:8000'
// export const baseURL = 'http://192.168.1.178:8000'
// 开发环境 - 手机端调试使用局域网IP需要时取消注释
// export const baseURL = 'http://192.168.1.164:8080'
// export const baseURLPy = 'http://192.168.1.164:8000'
export const sid = 2 export const sid = 2
@ -54,8 +58,14 @@ export const request = (options, url_type = 1, isShowLoad = true) => {
} }
}, },
fail: (err) => { fail: (err) => {
console.log('接口错误',err) uni.hideLoading(); // 添加这行确保请求失败时也隐藏loading
reject(err) console.log('接口错误',err);
uni.showToast({
icon: "none",
duration: 3000,
title: "网络请求失败,请检查网络连接"
});
reject(err);
} }
}) })
}) })

File diff suppressed because it is too large Load Diff

View File

@ -1 +1,2 @@
1. 将密码校验删除,因为无法生成模型,用最简单的方法来尝试这些内容。 1. 将密码校验删除,因为无法生成模型,用最简单的方法来尝试这些内容。
2. 将Hbuilder的AppId更换成自己的原本的保留(__UNI__1F3C178)。还是无法正常编译将下面的插件注释掉不用Agora-RTC音视频插件和AudioRecode录音插件。