liuyiran vor 1 Woche
Ursprung
Commit
ae4a7a4122

+ 9 - 0
App.vue

@@ -8,7 +8,16 @@
 			uni.removeStorageSync('fileName');
       this.initApp()
     },
+		mounted() {
+			this.$root.$wsEventBus.$on('websocket-message', this.handleMessage);
+		},
     methods: {
+			handleMessage(data) {
+				uni.showToast({
+					title: '加载中',
+					
+				})
+			},
       // 初始化应用
       initApp() {
         // 初始化应用配置

+ 10 - 5
api/login.js

@@ -6,15 +6,20 @@ export function login(username, password, code, uuid) {
     username,
     password,
     code,
-    uuid
+    uuid,
+		tenantId: '0',
+		clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
+		grantType: 'password'
   }
   return request({
-    'url': '/login',
+    'url': '/auth/login',
     headers: {
-      isToken: false
+      isToken: false,
+			isClientId:true,
+			isEncrypt:true,
     },
     'method': 'post',
-    'data': data
+    'data': data,
   })
 }
 
@@ -51,7 +56,7 @@ export function getCodeImg() {
   return request({
     'url': '/captchaImage',
     headers: {
-      isToken: false
+      isToken: false,
     },
     method: 'get',
     timeout: 20000

+ 8 - 0
api/system/user.js

@@ -39,3 +39,11 @@ export function uploadAvatar(data) {
     filePath: data.filePath
   })
 }
+
+// 用户照片上传
+export function uploadPerson(data) {
+  return upload({
+    url: '/common/upload',
+		file:data.file
+  })
+}

+ 2 - 1
config.js

@@ -1,7 +1,8 @@
 // 应用全局配置
 module.exports = {
-  baseUrl: 'https://vue.ruoyi.vip/prod-api',
+  // baseUrl: 'https://vue.ruoyi.vip/prod-api',
   // baseUrl: 'http://localhost:8080',
+	baseUrl:"http://192.168.0.3:8848/api",
   // 应用信息
   appInfo: {
     // 应用名称

+ 5 - 1
main.js

@@ -4,16 +4,20 @@ import store from './store' // store
 import plugins from './plugins' // plugins
 import './permission' // permission
 import { getDicts } from "@/api/system/dict/data"
+import { connectWebSocket, eventBus } from '@/utils/websocket'
 
 Vue.use(plugins)
 
 Vue.config.productionTip = false
 Vue.prototype.$store = store
 Vue.prototype.getDicts = getDicts
-
+Vue.prototype.$wsEventBus = eventBus;
 App.mpType = 'app'
 
 const app = new Vue({
+	onLaunch: () => {
+		connectWebSocket(); // 应用启动时连接 WebSocket
+	},
   ...App
 })
 

+ 6 - 0
package.json

@@ -0,0 +1,6 @@
+{
+  "dependencies": {
+    "crypto-js": "^4.2.0",
+    "jsencrypt": "^3.3.2"
+  }
+}

+ 17 - 17
pages.json

@@ -16,12 +16,12 @@
 				//   "navigationStyle": "custom"
 			}
 		},
-		{
-			"path": "pages/work/index",
-			"style": {
-				"navigationBarTitleText": "列表"
-			}
-		},
+		// {
+		// 	"path": "pages/work/index",
+		// 	"style": {
+		// 		"navigationBarTitleText": "列表"
+		// 	}
+		// },
 		{
 			"path": "pages/list/index",
 			"style": {
@@ -109,18 +109,18 @@
 				"selectedIconPath": "static/images/tabbar/home_.png",
 				"text": "工作台"
 			},
-			// {
-			//      "pagePath": "pages/work/index",
-			//      "iconPath": "static/images/tabbar/work.png",
-			//      "selectedIconPath": "static/images/tabbar/work_.png",
-			//      "text": "列表"
-			//    }, 
 			{
-				"pagePath": "pages/mine/index",
-				"iconPath": "static/images/tabbar/mine.png",
-				"selectedIconPath": "static/images/tabbar/mine_.png",
-				"text": "我的"
-			}
+			     "pagePath": "pages/list/index",
+					 "iconPath": "static/images/tabbar/work.png",
+					 "selectedIconPath": "static/images/tabbar/work_.png",
+			     "text": "列表"
+			   } 
+			// {
+			// 	"pagePath": "pages/mine/index",
+			// 	"iconPath": "static/images/tabbar/mine.png",
+			// 	"selectedIconPath": "static/images/tabbar/mine_.png",
+			// 	"text": "我的"
+			// }
 		]
 	},
 	"globalStyle": {

+ 90 - 63
pages/index.vue

@@ -19,24 +19,24 @@
 				<text class="form-area-item-text">视频名称</text>
 				<input type="text" v-model="videoName" placeholder="请输入视频名称">
 			</view>
-			<view class="form-area-item" :key="timeStamp">
+			<!-- 			<view class="form-area-item" :key="timeStamp">
 				<text class="form-area-item-text">选择您的形象</text>
 				<button @click="chooseFile" class="cu-btn bg-red">选择文件</button>
 				<text class="file-name" v-if="fileInfo.name">{{fileInfo.name}}</text>
 				<text class="none-file" v-if="!showPerson">未选择文件</text>
-			</view>
+			</view> -->
 			<view class="form-area-item">
-				<text class="form-area-item-text">选择视频背景</text>
+				<text class="form-area-item-text">选择您的形象</text>
 				<button @click="chooseImage" class="cu-btn bg-red">选择图片</button>
 				<text class="none-file" v-if="!imagePath">未选择图片</text>
 				<image :src="imagePath" v-else mode="aspectFit" style="width: 30px; height: 30px;margin-left:10px" />
 			</view>
-			<view class="form-area-item">
+			<!-- 			<view class="form-area-item">
 				<text class="form-area-item-text">选择模板</text>
 				<uni-data-select v-model="modelIndex" :localdata="array" @change="bindPickerChange"></uni-data-select>
 				<CustomModal :visible="isModalVisible" :message="modalMessage" :imageSrc="modalImageSrc" @confirm="onConfirm"
 					@cancel="onCancel" />
-			</view>
+			</view> -->
 			<view class="form-area-item">
 				<text class="form-area-item-text">视频内容</text>
 				<uni-data-checkbox selectedColor="#BD3124" selectedTextColor="#BD3124" v-model="current"
@@ -45,6 +45,11 @@
 			<view class="form-area-item" v-if="current=='text'">
 				<textarea v-model="videoText" placeholder="请输入视频中的文字内容" />
 			</view>
+			<view class="form-area-item" v-if="current=='text'">
+				<text class="form-area-item-text">音色选择</text>
+				<uni-data-checkbox selectedColor="#BD3124" selectedTextColor="#BD3124" v-model="timbre"
+					:localdata="timbreContent" />
+			</view>
 			<view class="form-area-item" v-if="current=='record'">
 				<button @click="toggleRecording">{{ isRecording ? '停止录音' : '开始录音' }}</button>
 				<!-- 				<view v-if="audioPath" class="audio-bar" @click="playAudio">
@@ -57,38 +62,41 @@
 		<view class="btn-area">
 			<button @click="jumpToList" class="bg-red lg round create-project">生成数字人视频</button>
 			<uni-popup ref="alertDialog" type="dialog">
-				<uni-popup-dialog type="warning" cancelText="关闭" title="提示" :content="missingContent"
+				<uni-popup-dialog type="warning" cancelText="确定" title="提示" :content="missingContent"
 					@close="dialogClose"></uni-popup-dialog>
-				</uni-popup>
+			</uni-popup>
 		</view>
 	</view>
 </template>
 <script>
-	import CustomModal from '../components/CustomModal.vue';
+	// import CustomModal from '../components/CustomModal.vue';
+	import {
+		uploadPerson
+	} from "@/api/system/user"
 	const recorderManager = uni.getRecorderManager();
 	const innerAudioContext = uni.createInnerAudioContext();
 	innerAudioContext.autoplay = true;
 	export default {
-		components: {
-			CustomModal
-		},
+		// components: {
+		// 	CustomModal
+		// },
 		data() {
 			return {
 				videoName: '',
 				fileInfo: {},
-				array: [{
-						value: 'ceshi1',
-						text: 'demo1'
-					},
-					{
-						value: 'ceshi2',
-						text: 'demo3'
-					},
-				],
-				modelIndex: undefined,
-				isModalVisible: false,
-				modalMessage: '',
-				modalImageSrc: '',
+				// array: [{
+				// 		value: 'ceshi1',
+				// 		text: 'demo1'
+				// 	},
+				// 	{
+				// 		value: 'ceshi2',
+				// 		text: 'demo3'
+				// 	},
+				// ],
+				// modelIndex: undefined,
+				// isModalVisible: false,
+				// modalMessage: '',
+				// modalImageSrc: '',
 				videoContent: [{
 						value: 'text',
 						text: '文字输入'
@@ -110,7 +118,17 @@
 					method: 'pause'
 				},
 				videoText: '',
-				missingContent: []
+				missingContent: [],
+				timbre:'a',
+				timbreContent: [{
+						value: 'a',
+						text: 'timbre1'
+					},
+					{
+						value: 'b',
+						text: 'timbre2'
+					}
+				],
 			}
 		},
 		onLoad() {
@@ -140,11 +158,11 @@
 			if (this.fileInfo.name) this.showPerson = true
 		},
 		methods: {
-			chooseFile() {
+			/* chooseFile() {
 				uni.navigateTo({
 					url: "/pages/root-filelist/root-filelist",
 				});
-			},
+			}, */
 			async chooseImage() {
 				const that = this;
 				uni.chooseImage({
@@ -163,8 +181,7 @@
 					}
 				});
 			},
-			bindPickerChange(e) {
-				console.log(e)
+/* 			bindPickerChange(e) {
 				const tempOption = this.array.find(item => item.value == e).text;
 				this.modalMessage = `您选择的是: ${tempOption}`;
 				this.modalImageSrc = '../static/images/xijiao/avator.png';
@@ -176,7 +193,7 @@
 			},
 			onCancel() {
 				this.isModalVisible = false;
-			},
+			}, */
 			radioChange(evt) {
 				for (let i = 0; i < this.videoContent.length; i++) {
 					if (this.videoContent[i].value === evt.detail.value) {
@@ -200,8 +217,8 @@
 						sampleRate: 44100,
 						numberOfChannels: 1,
 						encodeBitRate: 192000,
-						format: 'mp3',
-						frameSize: 50
+						format: 'wav',
+						// frameSize: 50
 					});
 					this.isRecording = true;
 				}
@@ -217,37 +234,45 @@
 			// 	this.innerAudioContext.src = this.audioPath;
 			// 	this.innerAudioContext.play();
 			// },
-
-			jumpToList() {
-				let missingFields = [];
-				if (!this.videoName.trim()) {
-					missingFields.push('视频名称');
-				}
-				if (!this.fileInfo.path) {
-					missingFields.push('人物形象');
-				}
-				if (!this.imagePath) {
-					missingFields.push('视频背景');
+			uploadImagePath() {
+				let data = {
+					// name: 'personimage',
+					// filePath: this.imagePath,
+					file: this.imagePath
 				}
-				if (!this.modelIndex) {
-					missingFields.push('视频模板');
-				}
-				const hasContent = this.videoText.trim() !== '';
-				const hasVoice = this.audioPath;
-				if (!hasContent && !hasVoice) {
-					missingFields.push('文字内容或语音输入');
-				}
-				if (missingFields.length > 0) {
-					this.missingContent = `请确定:${missingFields.join('、')}是否填写完整!`
-					this.$refs.alertDialog.open()
-					// return;
-				}
-				uni.navigateTo({
-					url: 'list/index'
-				});
+				uploadPerson(data).then(response => {
+					store.commit('SET_PERSON', baseUrl + response.data.url)
+					uni.navigateBack()
+				})
 			},
-			dialogConfirm() {
-				console.log('点击确认')
+			jumpToList() {
+					let missingFields = [];
+					if (!this.videoName.trim()) {
+						missingFields.push('视频名称');
+					}
+					// if (!this.fileInfo.path) {
+					// 	missingFields.push('人物形象');
+					// }
+					if (!this.imagePath) {
+						missingFields.push('人物形象');
+					}
+					// if (!this.modelIndex) {
+					// 	missingFields.push('视频模板');
+					// }
+					const hasContent = this.videoText.trim() !== '' && this.timbre;
+					const hasVoice = this.audioPath;
+					if (!hasContent && !hasVoice) {
+						missingFields.push('文字内容或语音输入');
+					}
+					if (missingFields.length > 0) {
+						this.missingContent = `请确定:${missingFields.join('、')}是否填写完整!`
+						this.$refs.alertDialog.open()
+						return;
+					}
+					// this.uploadImagePath()
+					uni.navigateTo({
+						url: 'list/creatingVideo/creatingVideo',
+					});
 			},
 			dialogClose() {
 				console.log('点击关闭')
@@ -345,9 +370,8 @@
 	.btn-area {
 		display: flex;
 		justify-content: center;
-		margin: 0 auto;
+		margin: 20px auto;
 		width: 100%;
-		margin-bottom: 10px;
 	}
 
 	.audio-bar {
@@ -355,4 +379,7 @@
 		display: block;
 		margin-top: 10px;
 	}
+	/deep/ .uni-border-left{
+		display:none;
+	}
 </style>

+ 0 - 27
pages/list/playVideo/playVideo.vue

@@ -71,33 +71,6 @@
 						}
 					}
 				});
-				/* uni.downloadFile({
-					url: "https://samplelib.com/lib/preview/mp4/sample-5s.mp4",
-					success: (res) => {
-						if (res.statusCode === 200) {
-							uni.saveVideoToPhotosAlbum({
-								filePath: res.tempFilePath
-							})
-							// 提示用户下载成功
-							uni.showToast({
-								title: "下载成功",
-								icon: "success"
-							});
-						}
-						else {
-							uni.showToast({
-								title: "资源格式错误,请联系管理员"
-							});
-						}
-					},
-					fail: (err) => {
-						uni.hideLoading();
-						uni.showToast({
-							title: "下载失败",
-							icon:'error'
-						})
-					}
-				}) */
 			},
 			shareToggle() {
 				this.$refs.share.open()

+ 13 - 9
pages/login.vue

@@ -51,10 +51,13 @@
         register: true,
         globalConfig: getApp().globalData.config,
         loginForm: {
-          username: "admin",
+          username: "superadmin",
           password: "admin123",
           code: "",
-          uuid: ""
+          uuid: "",
+					tenantId: '0',
+					clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
+					grantType: 'password'
         }
       }
     },
@@ -79,10 +82,10 @@
       // 获取图形验证码
       getCode() {
         getCodeImg().then(res => {
-          this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled
+          this.captchaEnabled = res.data.captchaEnabled === undefined ? true : res.data.captchaEnabled
           if (this.captchaEnabled) {
-            this.codeUrl = 'data:image/gif;base64,' + res.img
-            this.loginForm.uuid = res.uuid
+            this.codeUrl = 'data:image/gif;base64,' + res.data.img
+            this.loginForm.uuid = res.data.uuid
           }
         })
       },
@@ -112,10 +115,11 @@
       },
       // 登录成功后,处理函数
       loginSuccess(result) {
-        // 设置用户信息
-        this.$store.dispatch('GetInfo').then(res => {
-          this.$tab.reLaunch('/pages/index')
-        })
+				this.$tab.reLaunch('/pages/index')
+      //   // 设置用户信息
+      //   this.$store.dispatch('GetInfo').then(res => {
+      //     this.$tab.reLaunch('/pages/index')
+      //   })
       }
     }
   }

+ 0 - 1
pages/root-filelist/root-filelist.vue

@@ -63,7 +63,6 @@
 					this.fileList = [];
 					this.getPrivateDir(item.path);
 				} else {
-					console.log('选中的文件', item);
 					this.fileInfo.name = item.name
 					this.fileInfo.path = item.path
 					uni.setStorageSync('filePath', item.path);

BIN
static/images/tabbar/work.png


BIN
static/images/tabbar/work_.png


+ 8 - 3
store/modules/user.js

@@ -41,7 +41,11 @@ const user = {
     SET_PERMISSIONS: (state, permissions) => {
       state.permissions = permissions
       storage.set(constant.permissions, permissions)
-    }
+    },
+		SET_PERSON: (state, person) => {
+		  state.person = person
+		  storage.set(constant.person, person)
+		},
   },
 
   actions: {
@@ -53,8 +57,9 @@ const user = {
       const uuid = userInfo.uuid
       return new Promise((resolve, reject) => {
         login(username, password, code, uuid).then(res => {
-          setToken(res.token)
-          commit('SET_TOKEN', res.token)
+					console.log(res,'resresres')
+          setToken(res.data.access_token)
+          commit('SET_TOKEN', res.data.access_token)
           resolve()
         }).catch(error => {
           reject(error)

+ 66 - 0
utils/crypto.js

@@ -0,0 +1,66 @@
+import CryptoJS from 'crypto-js'
+
+/**
+ * 随机生成32位的字符串
+ * @returns {string}
+ */
+const generateRandomString = () => {
+  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
+  let result = ''
+  const charactersLength = characters.length
+  for (let i = 0; i < 32; i++) {
+    result += characters.charAt(Math.floor(Math.random() * charactersLength))
+  }
+  return result
+}
+
+/**
+ * 随机生成aes 密钥
+ * @returns {string}
+ */
+export const generateAesKey = () => {
+  return CryptoJS.enc.Utf8.parse(generateRandomString())
+}
+
+/**
+ * 加密base64
+ * @returns {string}
+ */
+export const encryptBase64 = str => {
+  return CryptoJS.enc.Base64.stringify(str)
+}
+
+/**
+ * 解密base64
+ */
+export const decryptBase64 = str => {
+  return CryptoJS.enc.Base64.parse(str)
+}
+
+/**
+ * 使用密钥对数据进行加密
+ * @param message
+ * @param aesKey
+ * @returns {string}
+ */
+export const encryptWithAes = (message, aesKey) => {
+  const encrypted = CryptoJS.AES.encrypt(message, aesKey, {
+    mode: CryptoJS.mode.ECB,
+    padding: CryptoJS.pad.Pkcs7
+  })
+  return encrypted.toString()
+}
+
+/**
+ * 使用密钥对数据进行解密
+ * @param message
+ * @param aesKey
+ * @returns {string}
+ */
+export const decryptWithAes = (message, aesKey) => {
+  const decrypted = CryptoJS.AES.decrypt(message, aesKey, {
+    mode: CryptoJS.mode.ECB,
+    padding: CryptoJS.pad.Pkcs7
+  })
+  return decrypted.toString(CryptoJS.enc.Utf8)
+}

+ 22 - 0
utils/jsEncrypt.js

@@ -0,0 +1,22 @@
+// import { JSEncrypt } from 'JSEncrypt'
+import JSEncrypt from '@/node_modules/jsencrypt/bin/jsencrypt.js'
+
+
+// 密钥对生成 http://web.chacuo.net/netrsakeypair
+const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
+// 前端不建议存放私钥 不建议解密数据 因为都是透明的意义不大
+const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
+
+// 加密
+export const encrypt = (txt) => {
+  const encrypt = new JSEncrypt()
+  encrypt.setPublicKey(publicKey) // 设置公钥
+  return encrypt.encrypt(txt) // 对数据进行加密
+}
+
+// 解密
+export const decrypt = (txt) => {
+  const encrypt = new JSEncrypt()
+  encrypt.setPrivateKey(privateKey) // 设置私钥
+  return encrypt.decrypt(txt) // 对数据进行解密
+}

+ 19 - 1
utils/request.js

@@ -3,17 +3,35 @@ import config from '@/config'
 import { getToken } from '@/utils/auth'
 import errorCode from '@/utils/errorCode'
 import { toast, showConfirm, tansParams } from '@/utils/common'
-
+import { encryptBase64, encryptWithAes, generateAesKey, decryptWithAes, decryptBase64 } from '@/utils/crypto'
+import { encrypt, decrypt } from '@/utils/jsEncrypt'
 let timeout = 10000
 const baseUrl = config.baseUrl
 
 const request = config => {
+	
   // 是否需要设置 token
   const isToken = (config.headers || {}).isToken === false
+	// 是否需要加密
+	const isEncrypt = (config.headers || {}).isEncrypt === true
+	// 是否需要ClientId
+	const isClientId = (config.headers || {}).isClientId === true
   config.header = config.header || {}
+	config.header['Content-Type'] = 'application/json;charset=utf-8'
   if (getToken() && !isToken) {
     config.header['Authorization'] = 'Bearer ' + getToken()
   }
+	if (isClientId) {
+		config.header['clientId'] = 'e5cd7e4891bf95d1d19206ce24a7b32e'
+	}
+	// 当开启参数加密
+	if (isEncrypt && (config.method === 'post' || config.method === 'put')) {
+		// 生成一个 AES 密钥
+		const aesKey = generateAesKey()
+		config.header['Encrypt-Key'] = encrypt(encryptBase64(aesKey))
+		config.data = typeof config.data === 'object' ? 
+		encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey)
+	}
   // get请求映射params参数
   if (config.params) {
     let url = config.url + '?' + tansParams(config.params)

+ 10 - 2
utils/upload.js

@@ -24,10 +24,18 @@ const upload = config => {
       uni.uploadFile({
         timeout: config.timeout || timeout,
         url: baseUrl + config.url,
-        filePath: config.filePath,
+        filePath: config.file,
         name: config.name || 'file',
         header: config.header,
-        formData: config.formData,
+        // formData: config.formData,
+				formData:{
+					'tenantId': '0',
+					'clientId': 'e5cd7e4891bf95d1d19206ce24a7b32e',
+					'grantType': 'password'
+				},
+				// tenantId: '0',
+				// clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
+				// grantType: 'password',
         success: (res) => {
           let result = JSON.parse(res.data)
           const code = result.code || 200

+ 28 - 0
utils/websocket.js

@@ -0,0 +1,28 @@
+import Vue from 'vue'
+let socket = null;
+const eventBus = new Vue(); // 使用一个小型事件总线通知页面
+
+export function connectWebSocket() {
+  const url = '你的WebSocket地址'; // 替换为你的后端地址
+  socket = uni.connectSocket({
+    url: url,
+    success: () => console.log('连接建立成功')
+  });
+
+  socket.onMessage(res => {
+    const data = JSON.parse(res.data); // 假设返回的是 JSON 格式数据
+    if (data.type === 'completed') {
+      eventBus.$emit('websocket-message', data);
+    }
+  });
+
+  socket.onError(err => {
+    console.error('WebSocket错误:', err);
+  });
+
+  socket.onClose(() => {
+    console.log('WebSocket已关闭');
+  });
+}
+
+export { eventBus };