Browse Source

新增 智能生成音乐模块

XSXS 4 weeks ago
parent
commit
bba37da5f8

+ 9 - 1
common/websocket.js

@@ -9,6 +9,7 @@ class WebSocketClient {
 		this.onMessageCallback = null;
 		this.onErrorCallback = null;
 		this.onCloseCallback = null;
+		this.onOpenCallback = null;
 		this.url = null;
 		this.heartbeatTimer = null;
 		this.heartbeatInterval = 20000;
@@ -57,10 +58,13 @@ class WebSocketClient {
 						this.send(message);
 					}
 					resolve();
+					if (this.onOpenCallback) {
+						this.onOpenCallback();
+					}
 				});
 
 				this.ws.onMessage((res) => {
-					if (res.data === 'PONG') {
+					if (res.data === 'PONG'||res.data === 'PING') {
 						this.lastHeartbeatTime = Date.now();
 						return;
 					}
@@ -196,6 +200,10 @@ class WebSocketClient {
 	onClose(callback) {
 		this.onCloseCallback = callback;
 	}
+
+	onOpen(callback) { 
+		this.onOpenCallback = callback;
+	}
 }
 
 export default new WebSocketClient();

+ 245 - 0
components/MultiSelectPopup/MultiSelectPopup.vue

@@ -0,0 +1,245 @@
+<template>
+	<view v-if="visible" class="popup-overlay" @click.self="closePopup">
+		<view class="popup-content">
+			<view class="popup-header">
+				<text class="popup-action popup-cancel" @click="closePopup">取消</text>
+				<text class="popup-action popup-confirm" @click="confirmSelection">确定</text>
+			</view>
+			<view class="picker-container">
+				<scroll-view scroll-y class="column parent-column">
+					<view v-for="(parent, index) in options" :key="index" 
+					      class="column-item parent-item"
+					      :class="{ 'active': activeParentIndex === index }"
+					      @click="setActiveParent(index)">
+						{{ parent.label }}
+					</view>
+				</scroll-view>
+				<scroll-view scroll-y class="column child-column">
+					<view v-if="activeParentIndex !== null && currentChildOptions.length > 0">
+						<view v-for="(child, index) in currentChildOptions" :key="index"
+						      class="column-item child-item"
+						      :class="{ 'selected': selectedChildValues.includes(child.value) }"
+						      @click="selectChild(child)">
+							{{ child.label }}
+							<view v-if="selectedChildValues.includes(child.value)" class="checkmark">✓</view>
+						</view>
+					</view>
+					<view v-else-if="activeParentIndex !== null && currentChildOptions.length === 0" class="no-children-text">
+						暂无选项
+					</view>
+				</scroll-view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'MultiSelectPopup',
+	props: {
+		initialOptions: {
+			type: Array,
+			default: () => [] 
+		},
+		initialSelectedValues: { // Optional: to pre-select items
+			type: Array,
+			default: () => []
+		}
+	},
+	data() {
+		return {
+			visible: false,
+			options: [],
+			activeParentIndex: null,
+			selectedChildValues: [] // Changed to array for multi-select
+		};
+	},
+	computed: {
+		currentChildOptions() {
+			if (this.activeParentIndex !== null && this.options[this.activeParentIndex] && this.options[this.activeParentIndex].children) {
+				return this.options[this.activeParentIndex].children;
+			}
+			return [];
+		}
+	},
+	watch: {
+		initialOptions: {
+			immediate: true,
+			handler(newVal) {
+				this.options = JSON.parse(JSON.stringify(newVal));
+				this.selectedChildValues = [...this.initialSelectedValues];
+				
+				if (this.options.length > 0) {
+					let preselectedParentIndex = 0; 
+					for (let i = 0; i < this.options.length; i++) {
+						const parent = this.options[i];
+						if (parent.children && parent.children.some(c => this.initialSelectedValues.includes(c.value))) {
+							preselectedParentIndex = i;
+							break;
+						}
+					}
+					this.activeParentIndex = preselectedParentIndex;
+				} else {
+					this.activeParentIndex = null;
+				}
+			}
+		}
+	},
+	methods: {
+		openPopup() {
+			this.options = JSON.parse(JSON.stringify(this.initialOptions));
+			this.selectedChildValues = [...this.initialSelectedValues];
+
+			if (this.options.length > 0) {
+				let preselectedParentIndex = 0;
+				for (let i = 0; i < this.options.length; i++) {
+					const parent = this.options[i];
+					if (parent.children && parent.children.some(c => this.selectedChildValues.includes(c.value))) {
+						preselectedParentIndex = i;
+						break;
+					}
+				}
+				this.activeParentIndex = (this.options.length > 0) ? preselectedParentIndex : null;
+			} else {
+				this.activeParentIndex = null;
+			}
+			this.visible = true;
+		},
+		closePopup() {
+			this.visible = false;
+			this.$emit('popup-closed');
+		},
+		setActiveParent(parentIndex) {
+			this.activeParentIndex = parentIndex;
+		},
+		selectChild(child) {
+			const index = this.selectedChildValues.indexOf(child.value);
+			if (index > -1) {
+				this.selectedChildValues.splice(index, 1);
+			} else {
+				this.selectedChildValues.push(child.value);
+			}
+		},
+		confirmSelection() {
+			if (this.selectedChildValues.length > 0) {
+				this.$emit('selection-confirmed', [...this.selectedChildValues]);
+				this.closePopup();
+			} else {
+				uni.showToast({
+					title: '请至少选择一个选项',
+					icon: 'none'
+				});
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.popup-overlay {
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	background-color: rgba(0, 0, 0, 0.5);
+	display: flex;
+	align-items: flex-end;
+	justify-content: center;
+	z-index: 1000;
+}
+
+.popup-content {
+	background-color: white;
+	width: 100%;
+	max-height: 50vh; /* Adjusted max height */
+	border-top-left-radius: 20rpx;
+	border-top-right-radius: 20rpx;
+	display: flex;
+	flex-direction: column;
+	animation: slideUp 0.3s ease-out;
+}
+
+@keyframes slideUp {
+	from {
+		transform: translateY(100%);
+	}
+	to {
+		transform: translateY(0);
+	}
+}
+
+.popup-header {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	padding: 25rpx 30rpx;
+	border-bottom: 1rpx solid #eee;
+}
+
+.popup-action {
+	font-size: 30rpx;
+	cursor: pointer;
+}
+
+.popup-cancel {
+	color: #666;
+}
+
+.popup-confirm {
+	color: #007aff; // Blue color for confirm
+}
+
+.picker-container {
+	display: flex;
+	flex: 1;
+	height: calc(50vh - 81rpx); /* Max height minus header height */
+	overflow: hidden; /* Prevent overall container from scrolling */
+}
+
+.column {
+	height: 100%;
+	overflow-y: auto;
+	padding: 10rpx 0;
+}
+
+.parent-column {
+	flex: 1;
+	background-color: #f8f8f8;
+	border-right: 1rpx solid #eee;
+}
+
+.child-column {
+	flex: 1.5; // Child column can be wider
+	background-color: #fff;
+}
+
+.column-item {
+	padding: 25rpx 30rpx;
+	font-size: 28rpx;
+	cursor: pointer;
+	text-align: center;
+	border-bottom: 1rpx solid #f0f0f0; /* Light border for items */
+}
+
+.parent-item.active {
+	background-color: #fff; 
+	color: #007aff; 
+	font-weight: bold;
+	border-right: 4rpx solid #007aff; /* Indicate active parent */
+	margin-right: -1rpx; /* Overlap border */
+}
+
+.child-item.selected {
+	color: #007aff;
+	font-weight: bold;
+	background-color: #e6f2ff; /* Light blue background for selected child */
+}
+
+.no-children-text {
+	padding: 40rpx;
+	text-align: center;
+	color: #999;
+	font-size: 26rpx;
+}
+</style>

+ 3 - 3
pages.json

@@ -388,7 +388,7 @@
 			}
 		},
 		{
-			"path" : "pages/makedetail/dialogGeneration",
+			"path" : "pages/makedetail/intelligentLifeChart",
 			"style" : 
 			{
 				"navigationBarTitleText" : "",
@@ -396,13 +396,13 @@
 			}
 		},
 		{
-			"path" : "pages/my/dome/dome",
+			"path" : "pages/makedetail/intelligentMusicProduction",
 			"style" : 
 			{
 				"navigationBarTitleText" : "",
 				"navigationStyle": "custom"
 			}
-		}
+		} 
 	],
 	"globalStyle": {
 		"navigationBarTextStyle": "black",

+ 3 - 2
pages/make/index.vue

@@ -179,12 +179,13 @@ export default {
 				if (type == 'cube') {
 					uni.navigateTo({
 						// url: '/pages/makedetail/makeImgDetail'
-						url: '/pages/makedetail/dialogGeneration'
+						url: '/pages/makedetail/intelligentLifeChart'
 						// url: '/pages/my/dome/dome'
 					})
 				} else if (type == 'music') {
 					uni.navigateTo({
-						url: '/pages/makedetail/makeMusicDetail'
+						//url: '/pages/makedetail/makeMusicDetail'
+						url: '/pages/makedetail/intelligentMusicProduction'
 					})
 				} else if (type == 'character') {
 					uni.navigateTo({

+ 0 - 0
pages/makedetail/dialogGeneration.scss → pages/makedetail/intelligentLifeChart.scss


+ 3 - 4
pages/makedetail/dialogGeneration.vue → pages/makedetail/intelligentLifeChart.vue

@@ -72,7 +72,7 @@
 				<view class="bom-box-bg">
 					<c-lottie ref="cLottieRef" :src='"/static/lottie/xiaomeng.json"' class="icon-img" height="108rpx"
 						width="112rpx" :loop="true" :autoPlay="false"></c-lottie>
-
+						
 				</view>
 				<!-- 底部输入区 -->
 				<view class="input-bar">
@@ -426,8 +426,7 @@ export default {
 					animation: 0,
 				})
 				.then(() => {
-					websocket.send('clear');
-					this.retrieveHistoricalRecords()
+					this.goPage('/pages/makedetail/makeImgDetail')
 				});
 
 		}
@@ -467,7 +466,7 @@ export default {
 </script>
 
 <style lang="scss">
-@import './dialogGeneration.scss';
+@import './intelligentLifeChart.scss';
 
 .to-bottom-btn {
 	position: fixed;

+ 232 - 0
pages/makedetail/intelligentMusicProduction.scss

@@ -0,0 +1,232 @@
+page {
+  padding: 0;
+  margin: 0;
+}
+.dialog-generation {
+  height: 100vh;
+  background: linear-gradient(180deg, #181c3a 0%, #2a1c3a 100%);
+  padding: 0;
+  margin: 0;
+  position: relative;
+  font-family: "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
+  background: url("../../static/makedetail/dialog-generation-bg.jpg") top
+    center/100% 100% no-repeat;
+  box-sizing: border-box;
+  padding-top: var(--status-bar-height);
+  padding-bottom: var(--window-bottom);
+  box-sizing: border-box;
+}
+.subject-matter-ofText {
+  width: 100vw;
+  height: 100%;
+  box-sizing: border-box;
+}
+
+.navbar {
+  width: calc(100vw - 60rpx);
+  height: 160rpx;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 0 30rpx;
+  // z-index: 99;
+  // padding-top: var(--status-bar-height);
+  .navbar-left {
+    width: 435rpx;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    .back {
+      width: 32rpx;
+      height: 32rpx;
+      opacity: 0.9;
+    }
+    .elf-name {
+      color: #fff;
+      font-family: "CustomFont";
+      font-size: 36rpx;
+      transform: skewX(-5deg);
+      opacity: 0.9;
+    }
+    .deepseek {
+      width: 168rpx;
+      height: 36rpx;
+    }
+  }
+  .navbar-right {
+    width: 130rpx;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    .primary {
+      width: 48rpx;
+      height: 48rpx;
+      opacity: 0.9;
+    }
+    .createChat {
+      width: 48rpx;
+      height: 48rpx;
+      opacity: 0.9;
+    }
+  }
+}
+.navbar-reserveASeat {
+  width: 100vw;
+  height: calc(160rpx + var(--status-bar-height));
+}
+
+.chat-content {
+  flex: 1;
+  // overflow-y: auto;
+  background: transparent;
+  height: calc(100% - 380rpx);
+  width: 100vw;
+  box-sizing: border-box;
+  padding: 0 28rpx;
+}
+
+.chat-bubble {
+  font-size: 32rpx;
+  word-break: break-all;
+  margin: 28rpx 0;
+}
+.chat-bubble.user {
+  margin-left: calc(100% - 516rpx);
+  width: 514rpx;
+  color: rgba(255, 255, 255, 0.7);
+  background: rgba(255, 255, 255, 0.1);
+  border: rgba(255, 255, 255, 0.1) solid 2rpx; 
+  border-radius: 36rpx 36rpx 12rpx 36rpx; 
+  padding: 22rpx 32rpx 20rpx 34rpx;
+  backdrop-filter: blur(10px);
+}
+.chat-bubble.ai {
+  color: rgba(255, 255, 255, 0.7);
+  line-height: 1.5;
+}
+.chat-bubble .chat-bubble span {
+  color: #fff;
+}
+.bom-reserveASeat {
+  width: 100vw;
+  height: calc(160rpx + var(--window-bottom));
+}
+.bom-box {
+  width: 100vw;
+  height: calc(160rpx + var(--window-bottom));
+  // position: fixed;
+  bottom: 0;
+  left: 0;
+  padding-bottom: var(--window-bottom);
+  box-sizing: border-box;
+  position: relative;
+  left: 0;
+  top: 0;
+  background: url("../../static/makedetail/bom-box-top-bg.png") top center/100% auto no-repeat,#040503;
+
+  .bom-box-bg{
+    width: 100vw;height: 178rpx;
+    position: absolute;
+    left: 0;
+    top: -178rpx;
+    background: url("../../static/makedetail/bom-box-bg.png") no-repeat top center /
+      100% 100%;
+      .icon-img {
+        height: 108rpx;
+        width: 112rpx;
+        position: absolute;
+        bottom: 0;
+        left: 20rpx;
+      }
+  }
+  // padding-top: 30rpx;
+  .input-bar {
+    display: flex;
+    min-height: 90rpx;
+    width: 700rpx;
+    background: rgba(255, 255, 255, 0.15);
+    border-radius: 46rpx;
+    border: 2rpx solid rgba(255, 255, 255, 0.15);
+    margin: 0 25rpx;
+    align-items: center;
+    padding-left: 116rpx;
+    padding-right: 22rpx;
+    position: relative;
+    left: 0;
+    top: 0;
+    .input-box {
+      flex: 1;
+      height: 60rpx;
+      border: none;
+      outline: none;
+      background: transparent;
+      color: #fff;
+      font-size: 30rpx;
+      padding-left: 10rpx;
+    }
+    .send-btn {
+      margin-left: 18rpx;
+      background: linear-gradient(90deg, #ffb86c 0%, #ff6e7f 100%);
+      color: #fff;
+      border: none;
+      border-radius: 32rpx;
+      font-size: 30rpx;
+      padding: 0 38rpx;
+      height: 60rpx;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+    .stop,
+    .keyboard,
+    .send {
+      width: 52rpx;
+      height: 52rpx;
+      opacity: 0.9;
+    }
+    .send {
+      opacity: 1;
+    }
+  
+  }
+  .footer-tip {
+    text-align: center;
+    color: rgba(255, 255, 255, 0.5);
+    font-size: 24rpx;
+    width: 100vw;
+    padding: 20rpx 0;
+  }
+}
+
+.chat-content-empty {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  image {
+    width: 182rpx;
+    height: 182rpx;
+  }
+  .chat-content-empty-title {
+    font-family: "PingFang SC-Bold";
+    font-weight: 400;
+    font-size: 36rpx;
+    color: rgba(255, 255, 255, 0.9);
+    padding-top: 8rpx;
+    padding-bottom: 12rpx;
+  }
+  .chat-content-empty-desc {
+    font-family: "PingFang SC-Medium";
+    font-size: 28rpx;
+    color: rgba(255, 255, 255, 0.5);
+  }
+}
+.btn-box {
+  padding: 6rpx 22rpx;
+  border-radius: 392rpx;
+  border: 2rpx solid rgba(255, 255, 255, 0.9);
+  display: inline-block;
+  margin-top: 16rpx;
+}

+ 817 - 0
pages/makedetail/intelligentMusicProduction.vue

@@ -0,0 +1,817 @@
+<template>
+	<view class="dialog-generation">
+		<view class="subject-matter-ofText" :style="{ paddingBottom: keyboardHeight + 'px' }">
+			<!-- 顶部导航栏 -->
+			<div class="navbar">
+				<view class="navbar-left">
+					<image @click="goBack()" class="back" src="../../static/vip/hy_icon_jiantou.png"></image>
+					<view class="elf-name">精灵·小萌</view>
+					<image class="deepseek" src="../../static/makedetail/deepseek-logo.png"></image>
+				</view>
+				<view class="navbar-right">
+					<image @click="switchToNormal()" class="primary" src="../../static/makedetail/primary.png"></image>
+					<image @click="newChar()" class="createChat" src="../../static/makedetail/createChat.png"></image>
+				</view>
+			</div>
+
+			<!-- <view class="navbar-reserveASeat"> </view>  -->
+			<!-- 聊天内容区 -->
+			<scroll-view class="chat-content" :scroll-into-view="toView" scroll-y
+				:style="{ height: stateType === 3 ? 'calc(100% - 180rpx)' : `calc(100% - ${370 + textareaHeight}rpx)` }"
+				@scroll="onChatScroll">
+				<template v-if="messages && messages.length > 0">
+					<!-- <scroll-view class="chat-content" scroll-y> -->
+					<view v-for="(msg, idx) in messages" :key="idx" :class="['chat-bubble', msg.role]">
+						<template v-if="msg.role === 'user'">
+							{{ msg.content }}
+						</template>
+						<template v-else-if="msg.role === 'ai'">
+							<view class="ai-bubble-row">
+								<view class="ai-avatar">
+									<image src="../../static/makedetail/characterProfilePicture.png" mode="aspectFill">
+									</image>
+								</view>
+								<!-- 第一句话 -->
+								<view v-if="msg.type == 0" class="ai-bubble-content">
+									<view>欢迎来到萌创星球AI写歌🎉🎉🎉!这里能满足你创作音乐🎸、释放灵感🎵,创造独属于你的音乐旋律!
+									</view>
+									<br>
+									<view>
+										可以根据自己的喜好点击下方的选项,快速开始创作🎼,
+									</view>
+									<div @click="onCateSent('纯音乐')" class="btn-box"> 纯音乐
+									</div>
+									<div @click="onCateSent('AI生成歌词')" class="btn-box" style="margin:0 20rpx"> AI生成歌词
+									</div>
+									<div @click="onCateSent('自定义歌词')" class="btn-box"> 自定义歌词
+									</div>
+								</view>
+								<!-- 第二句话 -->
+								<view class="ai-bubble-content" v-else-if="msg.type == 1">
+									<text>{{ msg.content }}</text>
+								</view>
+								<!-- 第三句话 -->
+								<view class="ai-bubble-content" v-else-if="msg.type == 2">
+									<!-- <text>{{ msg.content }}</text> -->
+									<textarea v-model="lyricData" class="lyric-editor" auto-height
+										:adjust-position="false" placeholder="修改歌词..." maxlength="-1" />
+									<div @click="onLyricSent()" class="btn-box"> 保存歌词
+									</div>
+								</view>
+								<view class="ai-bubble-content" v-else-if="msg.type == 4">
+									<view>请输入音乐的风格</view> 
+									<br>
+									<view>
+										也可以根据自己的喜好点击下方的选项,快速开始创作🎼,
+									</view>
+									<div @click="openTagPopup()" class="btn-box"> 
+										选择标签
+									</div>
+								</view>
+
+								<!-- <view class="ai-bubble-content" v-else-if="msg.type == 1">
+									<template v-if="idx === lastAiIndex && isLoading">
+										<text>{{ displayText }}</text>
+										<text v-if="isLoading" class="loading-dot">...</text>
+									</template>
+<template v-else>
+										<text>{{ msg.content }}</text>
+									</template>
+</view> -->
+
+								<view v-else class="ai-bubble-content">
+									<text>OK!~小萌正在根据你的描述生成形象中</text><br>
+									<div v-if="msg.isStartGenerating && msg.startGeneratingId == 0" class="btn-box">
+										点击查看
+										({{ countdown }}) s</div>
+									<div v-else
+										@click="goPage(`/pages/makedetail/makeMusicDetail?id=${msg.startGeneratingId}`)"
+										class="btn-box"> 点击查看
+									</div>
+								</view>
+							</view>
+						</template>
+					</view>
+					<view id="bottom-anchor"></view>
+					<view v-if="error" class="chat-bubble ai error">{{ error }}</view>
+					<view style="height: 200rpx; width:100vw;"></view>
+					<image v-if="showToBottomBtn && keyboardHeight === 0" class="to-bottom-btn" @click="scrollToBottom"
+						src="../../static/makedetail/toBottomBtn.png"></image>
+				</template>
+				<template v-else>
+					<view class="chat-content-empty">
+						<image src="../../static/makedetail/characterProfilePicture.png" mode="aspectFill"></image>
+						<view class="chat-content-empty-title">嗨!我是创梦精灵</view>
+						<view class="chat-content-empty-desc">与我聊聊你想要生成的角色吧!</view>
+					</view>
+				</template>
+			</scroll-view>
+			<!-- <view class="bom-reserveASeat"></view> -->
+
+			<view v-if="stateType !== 3" class="bom-box"
+				:style="{ bottom: 0 + 'px', height: `${190 + textareaHeight}rpx` }">
+				<view class="bom-box-bg">
+					<c-lottie ref="cLottieRef" :src='"/static/lottie/xiaomeng.json"' class="icon-img" height="108rpx"
+						width="112rpx" :loop="true" :autoPlay="false"></c-lottie>
+
+				</view>
+				<!-- 底部输入区 -->
+				<view class="input-bar">
+					<!-- <image class="icon-img" src="../../static/makedetail/characterProfilePicture.png" mode="aspectFill">
+					</image> -->
+
+
+					<textarea :autoHeight="true" class="input-box" :adjust-position="false" v-model="question"
+						:disabled="isLoading" placeholder="给我发送消息吧..." maxlength="400" @input="onTextareaInput"
+						confirm-type="search" @confirm="onSend" rows="1"
+						style="overflow-y:auto;max-height:176rpx;min-height:44rpx;" />
+					<!-- <button class="send-btn" :disabled="isLoading || !question.trim()" @click="onSend">{{ isLoading ?
+						'生成中...' :
+						'发送' }}</button> -->
+					<image v-if="isLoading" class="stop" src="../../static/makedetail/stop.png" mode=""
+						@click="stopStreamAnswer" />
+					<image v-else-if="!isLoading && !question.trim() && keyboardHeight === 0" class="keyboard"
+						src="../../static/makedetail/keyboard.png" mode="" />
+
+					<image v-else-if="!isLoading && question.trim() && keyboardHeight !== 0" class="send"
+						src="../../static/makedetail/send.png" mode="" @click="onSend" />
+				</view>
+				<!-- 底部提示 -->
+				<view class="footer-tip">内容由AI生成,禁用相关功能请联系管理员。</view>
+			</view>
+		</view>
+		 
+		<DialogBox ref="DialogBox"></DialogBox>
+		<multi-select-popup ref="tagPopup" :initial-options="tagOptions" @selection-confirmed="handleTagSelection" @popup-closed="handlePopupClosed"></multi-select-popup>
+	</view>
+</template>
+<script>
+import websocket from '@/common/websocket.js';
+import MultiSelectPopup from '@/components/MultiSelectPopup/MultiSelectPopup.vue';
+
+export default {
+	components: {
+		MultiSelectPopup
+	},
+	data() {
+		return {
+			question: '',
+			answer: '',
+			displayText: '',
+			isComplete: false,
+			isLoading: false,
+			loadingText: '正在生成歌词,请稍候',
+			loadingDots: '...',
+			loadingTimer: null,
+			error: null,
+			retryCount: 0,
+			maxRetries: 3,
+			timer: null,
+			lastResponseTime: 0,
+			timeout: 30000,
+			typingSpeed: 50,
+			messages: [],
+			lastAiIndex: -1,
+			keyboardHeight: 0,
+			statusBarHeight: 0,
+			windowBottom: 0,
+			toView: '',
+			showToBottomBtn: false,
+			textareaHeight: 0,
+			isConnected: false,
+			isStartGenerating: false,
+			countdown: 0,
+			stateType: 1,
+			stateTypeArray: ['clear', 'cate', 'getLyrics', 'setLyrics', 'getTags', 'setTags', 'setContent','','','','','setContent'],
+			content: '',
+			musicGenre: '',
+			lyricData: '',
+			tagsData: '', 
+			tagOptions: [], // 用于存储多选弹窗的选项 
+			// type = clear   清除|cate 第一句话选择 |getLyrics 获取歌词(需要上传描述)|setLyrics 修改歌词(再去判断是否有* 有重复没有下一步 没有展示标签)|setTags(修改标签) (下发一个static 失败有提示 成功就正常生成)|getTags 获取标签
+		};
+	},
+	methods: {
+		// /Work/streamAnswerMusicLast
+		// /Work/streamAnswerMusic
+		// {type:'',content:''}
+		// type = clear  清除|cate 第一句话选择 |getLyrics 获取歌词(需要上传描述)|setLyrics 修改歌词(再去判断是否有* 有重复没有下一步 没有展示标签)|setTags(修改标签) (下发一个static 失败有提示 成功就正常生成)|getTags 获取标签
+
+		scrollToBottom() {
+			this.toView = '';
+			this.$nextTick(() => {
+				this.toView = 'bottom-anchor';
+			});
+		},
+		async initWebSocket() {
+			if (this.isConnected) return;
+
+			try {
+				// 发送初始化消息
+				websocket.onOpen(() => {
+					setTimeout(()  => {
+						websocket.send(JSON.stringify({ type: 'getTags', content: '' }));
+					})
+				},1000);
+
+				await websocket.connect('wss://e.zhichao.art/Gapi/Work/streamAnswerMusic', {
+					uuid: getApp().globalData.uuid
+				});
+
+				this.isConnected = true;
+				// 设置消息处理回调
+				websocket.onMessage((data) => {
+					console.log("data:", data);
+					if (data && data.includes('{')) {
+						var data = JSON.parse(data);
+					}
+					var type = data && data.type || '';
+					var content = data && data.content || '';
+
+					// type = error,
+					// content=no_cate(首次分类没有设置)
+					// content="歌词不合规,请重新输入"|"歌词中有违规禁词,请修改!"
+					// type='lyrics',content=具体歌词
+					// type=success,content='OKOKOK':提交成功
+					// type=result,content=ID
+
+
+					
+					// 111111111111111111111111111111111
+					// if (type === 'success' && content === 'OKOKOK') {
+					// 	// 音乐生成开始,AI消息加载中提示
+					// 	const aiMsg = this.messages[this.lastAiIndex];
+					// 	if (aiMsg) {
+					// 		aiMsg.isStartGenerating = true;
+					// 		aiMsg.content = '音乐生成中,请稍候...';
+					// 	}
+					// 	this.isStartGenerating = true;
+					// 	this.countdownFun(20);
+					// }
+					// if (type === 'result' && content) {
+					// 	// 音乐生成中,显示跳转到音乐详情页按钮
+					// 	const aiMsg = this.messages[this.lastAiIndex];
+					// 	if (aiMsg) {
+					// 		aiMsg.isStartGenerating = false;
+					// 		aiMsg.startGeneratingId = content;
+					// 		aiMsg.content = '音乐已生成,点击跳转到详情页';
+					// 	}
+					// }
+					if (this.isLoading) {
+						if (type == 'cate' && content == 'success') {
+							// {"type":"setContent","content":content}
+							// 此时 第第一步选择完成
+							if (this.musicGenre == '纯音乐') {
+								// 跳过获取 修改 歌词
+								this.stateType = 11;
+								this.messages.push({ role: 'ai', type: 1, content: '输入描述生成音乐' }); 
+							} else {
+								this.stateType = 2;
+							}
+
+							if (this.musicGenre == 'AI生成歌词') {
+								this.messages.push({ role: 'ai', type: 1, content: '请描述歌词' });
+								this.stateType = 2;
+							}
+
+							console.log(type, content);
+
+							// {"type":"lyrics","content":"《古风之约》\n\n青山绿水间 桃花映人面\n清风拂衣袖 相思绕指尖\n亭台楼阁畔 琴声悠扬传\n往事如烟云 飘散在天边\n\n红墙绿瓦下 谁在等归雁\n明月照窗前 孤影难入眠\n一纸素笺上 写满了思念\n岁月如流水 匆匆又一年\n\n我与你共赴 这一场古风之约\n看那春花秋月 浪漫又缠绵\n执手相看泪眼 无语凝噎\n只愿与你相伴 直到永远\n\n我与你共赴 这一场古风之约\n听那琵琶弦上 倾诉着哀怨\n举杯对饮成三人 醉在花间\n只愿与你相守 岁岁年年"}
+						}
+						// if (data.includes('[DONE]')) {
+						// 	// 结束生成
+						// 	this.completeAnswer();
+						// }  else if (data.includes('ID:')) {
+						// 	// 提取ID
+						// 	const aiMsg = this.messages[this.lastAiIndex];
+						// 	if (aiMsg) {
+						// 		aiMsg.startGeneratingId = data.split(':')[1];
+						// 	}
+						// }  else if (data.includes('OKOKOK')) {
+						// 	const aiMsg = this.messages[this.lastAiIndex];
+						// 	if (aiMsg) {
+						// 		aiMsg.isStartGenerating = true;
+						// 	}
+						// 	this.isStartGenerating = true;
+						// 	this.countdownFun(20);
+						// } else {
+						// 	const aiMsg = this.messages[this.lastAiIndex];
+						// 	if (aiMsg) {
+						// 		aiMsg.isStartGenerating = false;
+						// 		this.displayText += data;
+						// 		this.answer += data;
+						// 		aiMsg.content += data;
+						// 		this.retryCount = 0;
+						// 		this.lastResponseTime = Date.now();
+						// 		this.scrollToBottom();
+						// 	}
+						// }
+						this.isLoading = false;
+					}
+
+					if (type == 'lyrics' && content) {
+						console.log('获取到歌词', content);
+						// 替换加载中的消息
+						const lastMessageIndex = this.messages.length -1;
+						// 替换加载中的消息
+						this.stopLoadingAnimation();
+						// Find and remove the loading message
+						for (let i = this.messages.length - 1; i >= 0; i--) {
+							if (this.messages[i].isGeneratingLyrics) {
+								this.messages.splice(i, 1);
+								break;
+							}
+						}
+						this.messages.push({ role: 'ai', type: 2, content: content });
+						this.stateType = 3;
+						this.lyricData = content;
+						console.log(this.messages, 24);
+
+					}
+					if (type == 'tags' && content  ) { 
+						// 此时歌词合法 下一步获取标签
+						this.stateType = 4; 
+						this.messages.push({ role: 'ai', type: 4, content: '请输入标签' });
+					}
+					if(type == 'getTags' && content && !this.tagsData ){ //确保只处理一次 
+						// 获取标签成功
+						this.tagsData = JSON.parse(content);
+						console.log('获取到标签', this.tagsData);
+						// 将获取到的标签数据转换为 MultiSelectPopup 需要的格式
+						this.tagOptions = this.formatTagOptions(this.tagsData); 
+					}
+					this.scrollToBottom();
+				});
+
+				// 设置错误处理回调
+				websocket.onError((error) => {
+					console.error('WebSocket错误:', error);
+					this.isConnected = false;
+					this.handleError(error);
+				});
+
+				// 设置关闭处理回调
+				websocket.onClose(() => {
+					console.log('WebSocket已关闭');
+					this.isConnected = false;
+					this.isLoading = false;
+				});
+
+
+			} catch (error) {
+				console.error('WebSocket初始化失败:', error);
+				this.isConnected = false;
+				this.handleError(error);
+			}
+		},
+		async startStreamAnswer(content) {
+			if (!content.trim()) {
+				uni.showToast({
+					title: '请输入问题',
+					icon: 'none'
+				});
+				return;
+			}
+
+			// 检查连接状态,如果断开则重连
+			if (!this.isConnected) {
+				try {
+					await this.initWebSocket();
+				} catch (error) {
+					console.error('重连失败:', error);
+					uni.showToast({
+						title: '连接已断开,请重试',
+						icon: 'none'
+					});
+					return;
+				}
+			}
+			console.log('发送消息:', content);
+
+			this.resetState();
+			this.messages.push({ role: 'user', content });
+
+			try {
+				this.isLoading = true;
+
+				if (false) {
+					let aiMsg = {
+						role: 'ai',
+						content: '',
+						isStartGenerating: false,
+						startGeneratingId: 0
+					};
+					this.messages.push(aiMsg);
+					this.lastAiIndex = this.messages.length - 1;
+					this.content = content;
+				}
+				// 发送消息
+				const messageType = this.stateTypeArray[this.stateType];
+				
+				let messageToSend = { type: messageType, content: content };
+
+				// 特殊处理 stateType 11 (纯音乐描述)
+				if (this.stateType === 11) {
+					messageToSend = { type: 'setContent', content: content };
+				}
+
+				websocket.send(JSON.stringify(messageToSend));
+
+				// 如果是获取歌词,则显示加载中
+				if (messageType === 'getLyrics') {
+					this.messages.push({ role: 'ai', type: 1, content: this.loadingText + this.loadingDots, isGeneratingLyrics: true });
+					this.startLoadingAnimation();
+					this.scrollToBottom();
+				}
+
+			} catch (error) {
+				console.error('发送消息失败:', error);
+				this.handleError(error);
+			}
+		},
+		onSend() {
+			if (!this.question.trim() || this.isLoading) return;
+
+			if (this.stateType === 4) { // 当前状态是等待输入标签
+				const userTagInput = this.question.trim();
+				this.messages.push({ role: 'user', content: userTagInput });
+				websocket.send(JSON.stringify({ type: 'setTags', content: userTagInput }));
+				this.messages.push({ role: 'ai', type: 1, content: `好的,已选择标签:${userTagInput},小萌开始生成音乐啦!` });
+				this.question = '';
+				this.scrollToBottom();
+				return; // 阻止后续的 startStreamAnswer 调用
+			}
+
+			this.startStreamAnswer(this.question);
+			this.question = '';
+		},
+		// 发送第一步指令
+		onCateSent(content) {
+			if (this.isLoading || this.musicGenre) return;
+			this.startStreamAnswer(content);
+			this.musicGenre = content;
+		},
+		// 发送歌词逻辑
+		onLyricSent() {
+			if (this.isLoading) return;
+			if (this.lyricData.includes('*')) {
+				uni.showToast({
+					title: '歌词有"*",请修改后再保存',
+					icon: 'none'
+				});
+			}
+			this.startStreamAnswer(this.lyricData);
+		},
+		// 打开标签选择弹窗
+		openTagPopup() {
+			this.$refs.tagPopup.openPopup();
+		},
+		formatTagOptions(tagsData) {
+			console.log('格式化标签选项', tagsData);
+			// tagsData 的预期格式: 
+			// [
+			//   { "name": "情感", "children": [ { "name": "欢快", "children": [] }, ... ] },
+			//   ...
+			// ]
+			// 需要转换为 MultiSelectPopup 需要的格式:
+			// [
+			//   { label: '情感', expanded: true, children: [{ label: '欢快', value: '欢快', selected: false }, ...] },
+			//   ...
+			// ]
+			if (!Array.isArray(tagsData)) return [];
+			return tagsData.map(parentTag => ({
+				label: parentTag.name,
+				expanded: true, // 默认展开父选项
+				children: parentTag.children.map(childTag => ({
+					label: childTag.name,
+					value: childTag.name, // 通常value和label相同,或根据实际情况设置
+					selected: false
+				}))
+			}));
+		},
+		handleTagSelection(selectedValues) {
+			console.log('选中的标签:', selectedValues);
+			// 处理选中的标签,例如发送到后端
+			// 示例:将选中的标签数组转换为字符串发送
+			const tagsString = selectedValues.join(','); 
+			websocket.send(JSON.stringify({ type: 'setTags', content: tagsString }));
+			// 可以在这里添加AI回复,告知用户标签已选择,正在生成音乐等
+			this.messages.push({ role: 'ai', type: 1, content: `好的,已选择标签:${tagsString},小萌开始生成音乐啦!` });
+			this.scrollToBottom();
+		},
+		handlePopupClosed() {
+			// 如果用户关闭了弹窗但没有选择任何标签,可以提供一个默认行为或提示
+			// 例如,如果没有选择标签,可以发送一个空数组或特定指令
+		 
+		},
+		startLoadingAnimation() {
+			let dotCount = 1;
+			this.loadingTimer = setInterval(() => {
+				dotCount++;
+				if (dotCount > 3) {
+					dotCount = 1;
+				}
+				this.loadingDots = '.'.repeat(dotCount);
+				// Update the loading message content if it exists
+				const loadingMessage = this.messages.find(msg => msg.isGeneratingLyrics);
+				if (loadingMessage) {
+					loadingMessage.content = this.loadingText + this.loadingDots;
+				}
+			}, 500); // 更新频率,例如每500毫秒
+		},
+		stopLoadingAnimation() {
+			if (this.loadingTimer) {
+				clearInterval(this.loadingTimer);
+				this.loadingTimer = null;
+			}
+			this.loadingDots = '...'; // Reset to default
+		},
+		resetState() {
+			this.answer = '';
+			this.displayText = '';
+			this.isComplete = false;
+			this.error = null;
+			this.retryCount = 0;
+			this.lastResponseTime = Date.now();
+		},
+		handleError(error) {
+			if (this.retryCount < this.maxRetries) {
+				this.retryCount++;
+				this.retryRequest();
+			} else {
+				this.error = '请求失败,请稍后重试';
+				this.isLoading = false;
+				this.stopLoadingAnimation();
+				uni.showToast({
+					title: this.error,
+					icon: 'none'
+				});
+			}
+		},
+		retryRequest() {
+			uni.showToast({
+				title: `正在重试 (${this.retryCount}/${this.maxRetries})`,
+				icon: 'none'
+			});
+			setTimeout(() => {
+				this.startStreamAnswer(this.question);
+			}, 1000 * this.retryCount);
+		},
+		completeAnswer() {
+			this.isComplete = true;
+			this.isLoading = false;
+			this.stopLoadingAnimation();
+		},
+		stopStreamAnswer() {
+			if (this.timer) {
+				clearTimeout(this.timer);
+				this.timer = null;
+			}
+			this.isLoading = false;
+			this.stopLoadingAnimation();
+		},
+		checkTimeout() {
+			if (Date.now() - this.lastResponseTime > this.timeout) {
+				this.handleError(new Error('请求超时'));
+			}
+		},
+
+		goBack() {
+			uni.navigateBack({
+				delta: 1
+			});
+		},
+		onChatScroll(e) {
+			const threshold = 600; // 离底部1.0417rem以内不显示按钮
+			const {
+				scrollHeight,
+				scrollTop
+			} = e.detail;
+			const clientHeight = e.detail.clientHeight || e.detail.height || 0;
+			if (scrollHeight - scrollTop - clientHeight > threshold) {
+				this.showToBottomBtn = true;
+			} else {
+				this.showToBottomBtn = false;
+			}
+		},
+		onTextareaInput(e) {
+			console.log(e.detail);
+
+			// 获取textarea的实际高度
+			const query = uni.createSelectorQuery().in(this);
+			query.select('.input-box').boundingClientRect(data => {
+				if (data) {
+					// 将px转换为rpx (假设设计稿是750rpx宽度)
+					const height = (data.height * 750) / uni.getSystemInfoSync().windowWidth;
+					// 减去基础高度90rpx,得到额外增加的高度
+					this.textareaHeight = Math.max(0, height - 90);
+					// 滚动到底部
+					this.scrollToBottom();
+				}
+			}).exec();
+		},
+		retrieveHistoricalRecords() {
+			uni.request({
+				url: this.$apiHost + '/Work/streamAnswerMusicLast',
+				method: 'GET',
+				header: {
+					'content-type': 'application/json',
+					'sign': getApp().globalData.headerSign
+				},
+				data: {
+					uuid: getApp().globalData.uuid,
+					task_type: 2
+				},
+				success: (res) => {
+					// step  0 未开始 1 已选择过第一次的选项  2 获取到歌词   
+					//  cate  选择类型    lyrics 历史记录 
+
+					console.log("获取历史记录:", res.data);
+					if (res.data.success === "yes") {
+						this.messages = [];
+						this.musicGenre = res.data.cate;
+
+						// 未开始
+						this.messages.push({ role: 'ai', type: 0, content: ' ' });
+
+						if (res.data.step >= 1) {
+							// 获取到歌词
+							this.messages.push({ role: 'user', content: res.data.cate });
+
+						}
+						if (res.data.cate == 'AI生成歌词') {
+							this.messages.push({ role: 'ai', type: 1, content: '请描述歌词' });
+							this.stateType = 2;
+						}
+						if (res.data.content) {
+							this.messages.push({ role: 'user', type: 1, content: res.data.content });
+						}
+						// AI生成歌词逻辑
+						if (res.data.lyrics) {
+							this.messages.push({ role: 'ai', type: 2, content: res.data.lyrics });
+							this.stateType = 3;
+							this.lyricData = res.data.lyrics;
+						}
+
+					} else {
+						this.messages = []
+					}
+
+					console.log("获取历史记录:", this.messages);
+
+				},
+				fail: (err) => {
+					console.log('获取历史记录失败', err);
+					uni.showToast({
+						title: '获取历史记录失败',
+						icon: 'none'
+					});
+				}
+			})
+		},
+		// 重新计算元素高度
+		recalculateHeights() {
+			// 重新计算textarea高度
+			const query = uni.createSelectorQuery().in(this);
+			query.select('.input-box').boundingClientRect(data => {
+				if (data) {
+					const height = (data.height * 750) / uni.getSystemInfoSync().windowWidth;
+					this.textareaHeight = Math.max(0, height - 90);
+				}
+			}).exec();
+		},
+		countdownFun(n) {
+			if (this.timer) {
+				clearInterval(this.timer);
+			}
+			this.countdown = n;
+			// 倒计时
+			this.timer = setInterval(() => {
+				this.countdown--;
+				if (this.countdown <= 0) {
+					clearInterval(this.timer);
+				}
+			}, 1000);
+		},
+		goPage(page) {
+			uni.navigateTo({
+				url: page,
+			});
+		},
+		newChar() {
+			this.$refs["DialogBox"]
+				.confirm({
+					title: "是否创建新对话",
+					content: "立即结束当前对话内容,开启新的对话?",
+					DialogType: "inquiry",
+					btn1: "取消",
+					btn2: "确定",
+					animation: 0,
+				})
+				.then(() => {
+					websocket.send(JSON.stringify({ type: 'clear', content: '' }));
+					this.retrieveHistoricalRecords()
+				});
+
+		},
+		switchToNormal() {
+			this.$refs["DialogBox"]
+				.confirm({
+					title: "是否切回常规模式",
+					content: "切换至普通常规模式进行创作?",
+					DialogType: "inquiry",
+					btn1: "取消",
+					btn2: "确定",
+					animation: 0,
+				})
+				.then(() => {
+					this.goPage('/pages/makedetail/makeMusicDetail')
+				});
+
+		}
+	},
+	async created() {
+		await this.initWebSocket();
+		this.retrieveHistoricalRecords();
+		this.timer = setInterval(() => {
+			if (this.isLoading) {
+				this.checkTimeout();
+			}
+		}, 1000);
+
+		uni.onKeyboardHeightChange(res => {
+			this.keyboardHeight = res.height;
+			if (res.height === 0) {
+				this.$refs.cLottieRef.call('stop')
+			} else {
+				this.$refs.cLottieRef.call('play')
+
+			}
+			this.$nextTick(() => {
+				this.recalculateHeights();
+			});
+		});
+
+		const systemInfo = uni.getSystemInfoSync();
+		this.statusBarHeight = systemInfo.statusBarHeight;
+		this.windowBottom = systemInfo.safeAreaInsets ? systemInfo.safeAreaInsets.bottom : 0;
+	},
+
+	beforeDestroy() {
+		websocket.close();
+		this.stopStreamAnswer();
+	}
+}
+</script>
+
+<style lang="scss">
+@import './intelligentLifeChart.scss';
+
+.lyric-editor {
+	width: 100%;
+	background-color: transparent;
+	border: none;
+	padding: 0;
+	margin-top: 10rpx;
+	color: rgba(255, 255, 255, 0.7);
+	font-size: 32rpx;
+}
+
+.to-bottom-btn {
+	position: fixed;
+	right: 50%;
+	transform: translateX(50%);
+	bottom: 20rpx;
+	width: 60rpx;
+	height: 60rpx;
+	bottom: 220rpx;
+	z-index: 999;
+	opacity: .75;
+	transition: opacity 0.2s;
+}
+
+.ai-bubble-row {
+	display: flex;
+	align-items: flex-start;
+}
+
+.ai-avatar {
+	width: 60rpx;
+	height: 60rpx;
+	border-radius: 50%;
+	margin-right: 20rpx;
+	border: solid 2rpx rgb(238, 238, 238, .3);
+	display: flex;
+	align-items: flex-end;
+	justify-content: center;
+
+	image {
+		width: 52rpx;
+		height: 52rpx;
+	}
+}
+
+.ai-bubble-content {
+	flex: 1;
+}
+</style>

+ 0 - 77
pages/my/dome/dome.vue

@@ -1,77 +0,0 @@
-<template>
-    <view>
-        <c-lottie
-            ref="cLottieRef"
-            :src='src'
-            @LoopComplete="onLoopComplete" 
-            width="750rpx" height='750rpx' :loop="true"></c-lottie>
-        <view class="content">
-            <view>切换图像</view> 
-            <view class="btnBox">
-                <button @click="src='/static/lottie/xiaomeng.json'" size="mini">热销</button>
-                <button @click="src='/static/lottie/data.json'" size="mini">热销</button>
-                <button @click="src='https://mp-eeab6da6-80cd-4e80-844a-66b2a7203834.cdn.bspapp.com/cloudstorage/c42b5f05-06b9-43e7-8436-c1029eee610a.json'" size="mini">字母</button>
-            </view>
-            <view>播放暂停</view>
-            <view class="btnBox">
-                <button @click="this.$refs.cLottieRef.call('play')" size="mini">播放</button>
-                <button @click="this.$refs.cLottieRef.call('setDirection',-1)" size="mini">反向播放</button>
-                <button @click="this.$refs.cLottieRef.call('pause')" size="mini">暂停播放</button>
-                <button @click="this.$refs.cLottieRef.call('stop')" size="mini">停止播放</button>
-            </view>
-            <view>播放速度</view>
-            <view class="btnBox">
-                <button @click="this.$refs.cLottieRef.call('setSpeed',1)" size="mini">播放速度1x</button>
-                <button @click="this.$refs.cLottieRef.call('setSpeed',2)" size="mini">播放速度2x</button>
-            </view>
-            <view>播放其它设置</view>
-            <view class="btnBox">
-                <button @click="this.$refs.cLottieRef.call('goToAndStop',[2000,false])" size="mini">跳转到2s并暂停</button>
-                <button @click="this.$refs.cLottieRef.call('goToAndPlay',[2000,false])" size="mini">跳转到2s并播放</button>
-            </view>
-            <view class="btnBox">
-                <button @click="this.$refs.cLottieRef.call('goToAndStop',[2,true])" size="mini">跳转到第2帧并暂停</button>
-                <button @click="this.$refs.cLottieRef.call('goToAndPlay',[2,true])" size="mini">跳转到第2帧并播放</button>
-            </view>
-            <view class="btnBox">
-                <button @click="this.$refs.cLottieRef.call('playSegments',[[10,20],false])" size="mini">播放完之前的片段,播放10-20帧</button>
-            </view>
-            <view class="btnBox">
-                <button @click="this.$refs.cLottieRef.call('playSegments',[[[0,5],[10,18]],true])" size="mini">直接播放0-5帧和10-18帧</button>
-            </view>
-
-        </view>
-    </view>
-</template>
-
-<script>
-    export default {
-        data() {
-            return {
-                src: 'https://mp-eeab6da6-80cd-4e80-844a-66b2a7203834.cdn.bspapp.com/cloudstorage/7b538fb7-d2d5-4524-bf21-6c20e3b5ce6f.json'
-            }
-        },
-        methods: {
-            onLoopComplete(val) {
-                // console.log('当前循环播放完成',val);
-            }
-        },
-    }
-</script>
-
-<style lang="scss">
-    .page{
-        width: 100vw;
-        overflow-x: hidden;
-    }
-    .content{
-        padding: 20rpx;
-        font-size: 28rpx;
-    }
-    .btnBox{
-        width: 100%;
-        display: flex;align-items: center;
-        margin-top: 20rpx;
-        margin-bottom: 30rpx;
-    }
-</style>

+ 0 - 552
pages/my/step copy.vue

@@ -1,552 +0,0 @@
-<template>
-	<view class="page">
-		<view class="list_info">
-			<block v-if="step == 1">
-				<view class="name">请问你的性别是:</view>
-				<view class="itemSex">
-					<view class="sex2" :class="sex == 1 ? 'active' : ''" style="margin-right: 10rpx" @click="chkSex(1)">
-						<image class="photo" src="../../static/me/sex_1.png" mode="widthFix" />
-						<text>男性</text>
-					</view>
-					<view class="sex2" :class="sex == 2 ? 'active' : ''" style="margin-left: 10rpx" @click="chkSex(2)">
-						<image class="photo" src="../../static/me/sex_2.png" mode="widthFix" />
-						<text>女性</text>
-					</view>
-				</view>
-				<view class="name">给自己取个名字吧:</view>
-				<view class="item">
-					<input type="text" class="input" placeholder="请输入昵称" v-model="nickname" maxlength="10" />
-				</view>
-				<view class="name">你的微信号是:</view>
-				<view class="item">
-					<input type="text" class="input" placeholder="请输入微信号" v-model="wechat" maxlength="32" />
-				</view>
-				<view class="name">选一张你的真实照片作为封面:</view>
-				<view class="bcenter">
-					<view class="avator" @click="upload">
-						<image :src="avator" mode="aspectFill" class="img" />
-						<image class="photo" src="../../static/me/photo.png" mode="widthFix" />
-					</view>
-				</view>
-			</block>
-			<block v-if="step == 2">
-				<view class="name">给自己写一个个性签名:</view>
-				<view class="item">
-					<input type="text" class="input" placeholder="请输入个性签名" v-model="qianmin" maxlength="64" />
-				</view>
-				<view class="name">职业:</view>
-				<view class="item">
-					<input type="text" class="input" placeholder="请输入职业" v-model="ziye" maxlength="50" />
-				</view>
-				<view class="name">年龄:</view>
-				<view class="itemSingle">
-					<slider class="slider" :value="age" @change="sliderChange1" activeColor="#FF2A95"
-						background-color="#282828" block-color="#FF2A95" block-size="12" min="10" max="100"
-						show-value />
-				</view>
-				<view class="name">身高(CM):</view>
-				<view class="itemSingle">
-					<slider class="slider" :value="height" @change="sliderChange2" activeColor="#FF2A95"
-						background-color="#282828" block-color="#FF2A95" block-size="12" min="150" max="220"
-						show-value />
-				</view>
-				<view class="name">体重(KG):</view>
-				<view class="itemSingle">
-					<slider class="slider" :value="weight" @change="sliderChange3" activeColor="#FF2A95"
-						background-color="#282828" block-color="#FF2A95" block-size="12" min="30" max="100"
-						show-value />
-				</view>
-			</block>
-			<block v-if="step == 3">
-				<view class="name">学历:</view>
-				<view class="item" @click="SetXueli">
-					<input type="text" class="input" placeholder="请选择" v-model="xueli_sel" disabled="true" />
-					<image class="arrow" src="../../static/me/arrow_right_gray.png" mode="widthFix" />
-				</view>
-				<view class="name">您的星座是:</view>
-				<view class="item" @click="SetXinzuo">
-					<input type="text" class="input" placeholder="请选择" v-model="xinzuo_sel" disabled="true" />
-					<image class="arrow" src="../../static/me/arrow_right_gray.png" mode="widthFix" />
-				</view>
-			</block>
-			<block v-if="step == 4">
-				<view class="name">兴趣爱好:
-					<view class="right" @click="showPop = true">添加+</view>
-				</view>
-				<view class="item_tag">
-					<view class="tag" v-for="(item, index) in sel_tags" :key="index">
-						{{ item }}
-						<image class="close" src="../../static/me/close.png" mode="widthFix" @click="delTag(item)" />
-					</view>
-				</view>
-			</block>
-
-			<view class="blankHeight"></view>
-		</view>
-
-		<view class="btn_submit" v-if="step == 1" @click="step++">下一步</view>
-		<view class="btn_list" v-if="step == 2 || step == 3">
-			<view class="btn_submit1" @click="step--">上一步</view>
-			<view class="btn_submit2" @click="step++">下一步</view>
-		</view>
-		<view class="btn_list" v-if="step == 4">
-			<view class="btn_submit1" @click="step--">上一步</view>
-			<view class="btn_submit2" @click="submitData()">完成</view>
-		</view>
-		<view class="popSel" v-if="showPop" @tap.stop="onPreview">
-			<view class="list_info">
-				<view class="name">你的兴趣爱好是?</view>
-				<view class="desc">提示:最多选择10个兴趣爱好标签</view>
-				<view class="item_tag">
-					<view @tap.stop="chkTag(item)" class="tag" :class="selTags(item) ? 'active' : ''"
-						v-for="(item, index) in list_tag" :key="index">
-						{{ item }}
-					</view>
-				</view>
-			</view>
-			<view class="btn_submit" @click="showPop = false">提交选择</view>
-		</view>
-
-		<view class="tag_all_select">
-			<view class="tag_items">
-				<view v-for="(item, index) in tagList" :key="index" class="tag_item"
-					:class="[item.size, { active: selectedTags.includes(index) }]" @click="selectTag(index)">
-					{{ item.name }}
-				</view>
-			</view>
-		</view>
-		<view class="blankHeight"></view>
-		<view class="blankHeight"></view>
-		<view class="blankHeight"></view>
-
-		<ToastW3 ref="ToastW3"></ToastW3>
-		<DialogBox ref="DialogBox"></DialogBox>
-
-		<view v-if="showRights" style="
-        width: 100%;
-        height: 300rpx;
-        background-color: rgba(255, 255, 255, 0.9);
-        position: fixed;
-        top: 0;
-        display: flex;
-        flex-direction: column;
-        justify-content: center;
-        align-items: center;
-      ">
-			<text style="
-          width: 90%;
-          color: #000000;
-          font-size: 38rpx;
-          text-align: left;
-          padding: 10rpx 20rpx;
-          padding-top: 10rpx;
-        ">正在获取相机、存储权限</text>
-			<text style="
-          width: 90%;
-          color: #666666;
-          font-size: 28rpx;
-          text-align: left;
-          padding: 10rpx 20rpx;
-        ">该权限用于获取设备拍摄或获取本地应用相册,进行头像或图片上传。</text>
-		</view>
-	</view>
-</template>
-
-<script>
-import {
-	requestAndroidPermission,
-	gotoAppPermissionSetting,
-} from "../index/permission.js";
-export default {
-	components: {},
-	data() {
-		return {
-			showRights: false,
-			title: "",
-			step: 1,
-			sel: 1,
-			info: {},
-			showPop: false,
-			nickname: "",
-			wechat: "",
-			sex: 1,
-			age: 18,
-			height: 160,
-			weight: 50,
-			avator: "../../static/me/avator.png",
-			ziye: "",
-			qianmin: "",
-
-			xueli_sel: "",
-			xueli: [
-				"初中",
-				"初中",
-				"中专",
-				"高中",
-				"专科",
-				"本科",
-				"研究生",
-				"硕士",
-				"博士",
-			],
-			xinzuo_sel: "",
-			xinzuo: [
-				"水瓶座",
-				"双鱼座",
-				"白羊座",
-				"金牛座",
-				"双子座",
-				"巨蟹座",
-				"狮子座",
-				"处女座",
-				"天秤座",
-				"天蝎座",
-				"射手座",
-				"摩羯座",
-			],
-
-			list_tag: ["篮球", "排球", "足球", "羽毛球", "健身达人", "美食达人"],
-			sel_tags: [],
-			selectedTags: [],
-			tagList: [
-				{ name: "游戏", size: "large" },
-				{ name: "音乐", size: "medium" },
-				{ name: "盲盒", size: "large" },
-				{ name: "小可爱", size: "small" },
-				{ name: "娃娃人", size: "medium" },
-				{ name: "纹身", size: "large" },
-				{ name: "洛丽塔", size: "small" },
-				{ name: "女装", size: "medium" },
-				{ name: "萌球", size: "small" },
-			],
-		};
-	},
-	onLoad() {
-		this.step = 1;
-		let tagStr =
-			"篮球、羽毛球、兵乓球、足球、滑板、滑旱冰、跑步、跳绳、举重、听音乐、看电影、绘画、写小说、看书、做弹弓玩、做木剑玩、做橡皮枪玩、积木、用麻将搭金字塔、拼图、拆装、扑克牌、小汽车、手表、鞋之类的、弹吉他、钢琴、萨克斯、葫芦丝、大号、小号、折纸、剪纸、品茶、涂鸦、英雄联盟、qq堂、cs、cf、地下城勇士、桌面游戏";
-		this.list_tag = tagStr.split("、");
-		this.getInfoData();
-	},
-	onShow() { },
-	methods: {
-		onBack() { },
-		chkSel() {
-			if (this.sel == 1) {
-				this.sel = 0;
-			} else {
-				this.sel = 1;
-			}
-		},
-		chkSex(sex) {
-			this.sex = sex;
-		},
-		delTag(tg) {
-			let list_tag2 = [];
-			for (let i = 0; i < this.sel_tags.length; i++) {
-				if (this.sel_tags[i] != tg && this.sel_tags[i] != "") {
-					list_tag2.push(this.sel_tags[i]);
-				}
-			}
-			this.sel_tags = list_tag2;
-		},
-		selTags(itm) {
-			let that = this;
-			let isIn = false;
-			for (let entry of this.sel_tags) {
-				// console.log(entry); // 1, "string", false
-				if (entry == itm) {
-					isIn = true;
-					break;
-				}
-			}
-			return isIn;
-		},
-		chkTag(itm) {
-			if (this.selTags(itm)) {
-				let tmpTags = [];
-				for (let entry of this.sel_tags) {
-					// console.log(entry); // 1, "string", false
-					if (entry != itm && entry != "") {
-						tmpTags.push(entry);
-					}
-				}
-				this.sel_tags = tmpTags;
-			} else {
-				if (this.sel_tags.length >= 10) {
-					this.$refs["ToastW3"].showToast({
-						title: "最多选择10个标签",
-						animation: 0,
-					});
-					return;
-				}
-				this.sel_tags.push(itm);
-			}
-		},
-		sliderChange1(e) {
-			this.age = e.detail.value;
-		},
-		sliderChange2(e) {
-			this.height = e.detail.value;
-		},
-		sliderChange3(e) {
-			this.weight = e.detail.value;
-		},
-		SetXueli() {
-			let that = this;
-			uni.showActionSheet({
-				itemColor: "#000000",
-				itemList: this.xueli,
-				success: function (res) {
-					that.xueli_sel = that.xueli[res.tapIndex];
-					// console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
-				},
-				fail: function (res) {
-					console.log(res.errMsg);
-				},
-			});
-		},
-		SetXinzuo() {
-			let that = this;
-			uni.showActionSheet({
-				itemColor: "#000000",
-				itemList: this.xinzuo,
-				success: function (res) {
-					that.xinzuo_sel = that.xinzuo[res.tapIndex];
-					// console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
-				},
-				fail: function (res) {
-					console.log(res.errMsg);
-				},
-			});
-		},
-		getInfoData() {
-			console.log(this.$apiHost + "/Member/getinfoData");
-			uni.request({
-				url: this.$apiHost + "/Member/getinfoData", //仅为示例,并非真实接口地址。
-				data: {
-					uuid: getApp().globalData.uuid,
-				},
-				header: {
-					"content-type": "application/json", //自定义请求头信息
-				},
-				success: (res) => {
-					console.log("res", res.data);
-					this.nickname = res.data.nickname;
-					this.wechat = res.data.wechat;
-					this.sex = res.data.sex || 2;
-					this.age = res.data.age || 18;
-					this.height = res.data.height || 160;
-					this.weight = res.data.weight || 50;
-					this.xueli_sel = res.data.xueli;
-					this.xinzuo_sel = res.data.xinzuo;
-					this.ziye = res.data.ziye;
-					this.qianmin = res.data.qianmin;
-					if (res.data.avator != "") {
-						this.avator = res.data.avator;
-					}
-					if (res.data.aihao != null && res.data.aihao != undefined) {
-						if (res.data.aihao.length > 0) {
-							this.sel_tags = res.data.aihao.split(",");
-						}
-					}
-				},
-			});
-		},
-
-		submitData() {
-			let aihao = this.sel_tags.join(",");
-			let obj2 = {
-				uuid: getApp().globalData.uuid,
-				avator: this.avator,
-				nickname: this.nickname,
-				wechat: this.wechat,
-				sex: this.sex,
-				age: this.age,
-				height: this.height,
-				weight: this.weight,
-				xueli: this.xueli_sel,
-				xinzuo: this.xinzuo_sel,
-				ziye: this.ziye,
-				qianmin: this.qianmin,
-				aihao: aihao,
-			};
-			// console.log("obj2", obj2);
-			uni.request({
-				url: this.$apiHost + "/Member/setinfoData", //仅为示例,并非真实接口地址。
-				data: obj2,
-				method: "POST",
-				header: {
-					"Content-Type": "application/x-www-form-urlencoded",
-					sign: getApp().globalData.headerSign,
-				},
-				dataType: "json",
-				// header: {
-				// 	'content-type': 'application/json', //自定义请求头信息
-				// 	'Access-Control-Allow-Origin': '*'
-				// },
-				success: (res) => {
-					console.log("res", res.data);
-					this.$refs["ToastW3"].showToast({
-						title: res.data.str,
-						animation: 0,
-					});
-					if (res.data.success == "yes") {
-						uni.switchTab({
-							url: "/pages/index/index",
-						});
-					}
-				},
-			});
-		},
-		// upload() {
-		// 	let that = this;
-		// 	this.$refs['DialogBox'].confirm({
-		// 		title: '提示',
-		// 		content: '该权限用于获取设备拍摄或获取本地应用相册,进行头像的图片上传。',
-		// 		DialogType: 'inquiry',
-		// 		btn1: '拒绝',
-		// 		btn2: '同意',
-		// 		animation: 0
-		// 	}).then((res) => {
-		// 		that.upload2();
-		// 	})
-		// },
-		upload() {
-			this.checkRights();
-			console.log("----upload");
-			var _self = this;
-			uni.chooseImage({
-				count: 1,
-				sizeType: ["compressed"], //可以指定是原图还是压缩图,默认二者都有
-				sourceType: ["album", "camera"], //从相册、相机选择
-				extension: [".png", ".jpeg", ".jpg"],
-				success: function (res) {
-					console.log("res:", res);
-					_self.imglocal = res.tempFilePaths[0];
-					const tempFilePaths = res.tempFilePaths[0];
-					console.log("tempFilePaths:", tempFilePaths);
-					// 图片上传
-					const uploadTask = uni.uploadFile({
-						url: _self.$apiHost + "/Xweb/upload_img?skey=" + _self.skey, // post请求地址
-						filePath: res.tempFilePaths[0],
-						name: "file", // 待确认
-						success: function (uploadFileRes) {
-							let resdata = JSON.parse(uploadFileRes.data);
-							console.log("Success11:", uploadFileRes);
-							console.log("Success21:", resdata);
-							if (resdata.success == "yes") {
-								_self.avator = resdata.url;
-							}
-						},
-						fail: function (uploadFileFail) {
-							console.log("Error:", uploadFileFail.data);
-						},
-						complete: () => {
-							console.log("Complete:");
-						},
-					});
-				},
-				error: function (e) {
-					console.log(e);
-				},
-			});
-		},
-		checkRights() {
-			let that = this;
-			that.showRights = true;
-			requestAndroidPermission("android.permission.CAMERA")
-				.then((result) => {
-					that.showRights = false;
-					// 根据返回的结果进行处理
-					if (result === 1) {
-					} else if (result === 0) {
-						console.log("权限被拒绝");
-					} else if (result === -1) {
-						console.log("权限被永久拒绝");
-					}
-				})
-				.catch((error) => {
-					that.showRights = true;
-					console.log("权限申请失败:", error);
-				});
-		},
-		selectTag(index) {
-			const tagIndex = this.selectedTags.indexOf(index);
-			if (tagIndex === -1) {
-				// 如果未选中,则添加到选中数组
-				this.selectedTags.push(index);
-			} else {
-				// 如果已选中,则从数组中移除
-				this.selectedTags.splice(tagIndex, 1);
-			}
-		},
-	},
-};
-</script>
-
-<style scoped lang="scss">
-@import "normal.scss";
-
-.popSel {
-	position: fixed;
-	z-index: 999999;
-	top: 0;
-	left: 0;
-	background-color: #121212;
-	width: 100vh;
-	height: 100vh;
-}
-
-.tag_all_select {
-	padding: 20rpx;
-
-	.tag_items {
-		display: flex;
-		flex-wrap: wrap;
-		gap: 30rpx;
-		justify-content: center;
-		align-items: center;
-
-		.tag_item {
-			display: flex;
-			align-items: center;
-			justify-content: center;
-			border-radius: 50%;
-			background: rgba(255, 255, 255, 0.1);
-			color: #fff;
-			font-size: 26rpx;
-			transition: all 0.3s ease;
-			cursor: pointer;
-
-			&.small {
-				width: 120rpx;
-				height: 120rpx;
-			}
-
-			&.medium {
-				width: 160rpx;
-				height: 160rpx;
-				font-size: 28rpx;
-			}
-
-			&.large {
-				width: 200rpx;
-				height: 200rpx;
-				font-size: 32rpx;
-			}
-
-			&.active {
-				transform: scale(1.2);
-				background: rgba(255, 255, 255, 0.2);
-				box-shadow: 0 0 20rpx rgba(255, 255, 255, 0.3);
-				z-index: 1;
-			}
-
-			&:not(.active):hover {
-				transform: scale(1.05);
-			}
-		}
-	}
-}
-</style>