index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. <template>
  2. <view class="content">
  3. <view class="uni-margin-wrap">
  4. <swiper class="swiper" circular :indicator-dots="true" indicator-color="#DE868F" indicator-active-color="#fff"
  5. :autoplay="true" :interval="1000" :duration="500">
  6. <swiper-item>
  7. <view class="swiper-item">功能介绍1</view>
  8. </swiper-item>
  9. <swiper-item>
  10. <view class="swiper-item">功能介绍2</view>
  11. </swiper-item>
  12. <swiper-item>
  13. <view class="swiper-item">功能介绍3</view>
  14. </swiper-item>
  15. </swiper>
  16. </view>
  17. <form class="form-area">
  18. <view class="form-area-item">
  19. <text class="form-area-item-text">视频名称</text>
  20. <input type="text" :value="videoName" placeholder="请输入视频名称">
  21. </view>
  22. <view class="form-area-item">
  23. <text class="form-area-item-text">选择您的形象</text>
  24. <!-- <lsj-upload ref="lsjUpload" childId="upload1" width="150rpx" height="100%" :option="option" :size="10"
  25. :debug="true" :multiple="false" formats="xlsx" :count="1" :instantly="false" @change="uploadChange"
  26. @uploadEnd="onuploadEnd">
  27. <button class="cu-btn bg-red">选择文件</button>
  28. </lsj-upload>
  29. <button type="primary" @click="handleUploadSubmit"
  30. style="padding: 0rpx 0;margin-top: 20rpx;font-size:25rpx;background-color: #E62F3D;">确认上传</button> -->
  31. <button @click="chooseFile" class="cu-btn bg-red">选择文件</button>
  32. <text class="none-file" v-if="fileInfo.name">{{fileInfo.name}} ({{formatFileSize(fileInfo.size)}})</text>
  33. <text class="none-file" v-else>未选择文件</text>
  34. </view>
  35. <view class="form-area-item">
  36. <text class="form-area-item-text">选择视频背景</text>
  37. <button @click="chooseImage" class="cu-btn bg-red">选择图片</button>
  38. <text class="none-file" v-if="!imagePath">未选择图片</text>
  39. <image :src="imagePath" v-else mode="aspectFit" style="width: 30px; height: 30px;margin-left:10px" />
  40. </view>
  41. <view class="form-area-item">
  42. <text class="form-area-item-text">选择模板</text>
  43. <!-- <picker @change="bindPickerChange" :value="modelIndex" :range="array">
  44. <view class="uni-input">{{array[modelIndex]}}</view>
  45. </picker> -->
  46. <uni-data-select v-model="modelIndex" :localdata="array" @change="bindPickerChange"></uni-data-select>
  47. <CustomModal :visible="isModalVisible" :message="modalMessage" :imageSrc="modalImageSrc" @confirm="onConfirm"
  48. @cancel="onCancel" />
  49. </view>
  50. <view class="form-area-item">
  51. <text class="form-area-item-text">视频内容</text>
  52. <uni-data-checkbox selectedColor="#BD3124" selectedTextColor="#BD3124" v-model="current"
  53. :localdata="videoContent" />
  54. </view>
  55. <view class="form-area-item" v-if="current=='text'">
  56. <textarea placeholder="请输入视频中的文字内容" />
  57. </view>
  58. <view class="form-area-item" v-if="current=='record'">
  59. <button @click="toggleRecording">{{ isRecording ? '停止录音' : '开始录音' }}</button>
  60. <view v-if="audioPath" class="audio-bar" @click="playAudio">
  61. 🎵 录音文件(点击播放)
  62. </view>
  63. </view>
  64. </form>
  65. <view class="btn-area">
  66. <button @click="jumpToList" class="bg-red lg round create-project">生成数字人视频</button>
  67. </view>
  68. </view>
  69. </template>
  70. <script>
  71. import CustomModal from '../components/CustomModal.vue';
  72. const recorderManager = uni.getRecorderManager();
  73. const innerAudioContext = uni.createInnerAudioContext();
  74. innerAudioContext.autoplay = true;
  75. export default {
  76. components: {
  77. CustomModal
  78. },
  79. data() {
  80. return {
  81. videoName: '',
  82. fileInfo: {},
  83. imageInfo: {},
  84. array: [{
  85. value: 'ceshi1',
  86. text: 'demo1'
  87. },
  88. {
  89. value: 'ceshi2',
  90. text: 'demo3'
  91. },
  92. ],
  93. modelIndex: undefined,
  94. isModalVisible: false,
  95. modalMessage: '',
  96. modalImageSrc: '',
  97. videoContent: [{
  98. value: 'text',
  99. text: '文字输入'
  100. },
  101. {
  102. value: 'record',
  103. text: '语音录入'
  104. }
  105. ],
  106. current: 'text',
  107. // 上传组件接口参数
  108. option: {
  109. // 上传服务器地址,需要替换为你的接口地址
  110. url: this.$URL + '/App/selection/xp/uploadExcel',
  111. // 上传附件的key
  112. name: 'file',
  113. // 根据你接口需求自定义请求头,默认不要写content-type,让浏览器自适配
  114. header: {
  115. token: uni.getStorageSync('token'),
  116. },
  117. // 根据你接口需求自定义body参数
  118. formData: {
  119. // 'orderId': 1000
  120. }
  121. },
  122. chooseFile: {},
  123. upload1: 1, // 控件的id
  124. imagePath: '',
  125. isRecording: false,
  126. audioPath: null,
  127. recorderManager: null,
  128. innerAudioContext: null
  129. }
  130. },
  131. onLoad() {
  132. this.recorderManager = uni.getRecorderManager();
  133. this.innerAudioContext = uni.createInnerAudioContext();
  134. // 监听录音停止
  135. this.recorderManager.onStop((res) => {
  136. console.log('录音结束,临时路径:', res.tempFilePath);
  137. this.audioPath = res.tempFilePath; // 替换为新录音路径
  138. this.isRecording = false;
  139. });
  140. // 监听录音错误
  141. this.recorderManager.onError((err) => {
  142. uni.showToast({
  143. title: '录音出错',
  144. icon: 'none'
  145. });
  146. console.error('录音错误:', err);
  147. });
  148. },
  149. methods: {
  150. /* // 上传组件 change
  151. uploadChange(files) {
  152. this.showUploadResult = false
  153. console.log('当前选择的文件列表:', files);
  154. // file 为 Map对象
  155. // 更新选择的文件
  156. // this.files = files;
  157. for (let value of files.values()) {
  158. // 我这里使用的是 单选上传
  159. this.chooseFile = value
  160. console.log(value);
  161. }
  162. },
  163. // 点击上传 按钮
  164. handleUploadSubmit() {
  165. uni.showLoading({
  166. mask: true
  167. })
  168. if (!this.chooseFile.path) {
  169. uni.showToast({
  170. title: '请选择上传文件',
  171. icon: 'none'
  172. })
  173. return
  174. }
  175. console.log('uploadPath', this.chooseFile)
  176. // 上传组件上传
  177. this.$refs['lsjUpload'].upload();
  178. // 上传组件 获取的文件路径path是一个blobURL,如果不想用组件本身的上传方法,需自行处理
  179. },
  180. // 文件上传完毕 事件
  181. onuploadEnd(item) {
  182. uni.hideLoading()
  183. if (item.type !== 'success') {
  184. uni.showToast({
  185. title: '上传文件失败',
  186. icon: 'none'
  187. })
  188. }
  189. console.log(`${item.name}已上传结束,上传状态=${item.type},`, item);
  190. const result = JSON.parse(item.responseText)
  191. // 这里可以做后续操作
  192. }, */
  193. /* async chooseFile() {
  194. try {
  195. const [file] = await uni.chooseFile({
  196. count: 1,
  197. extension: ['.zip', '.doc'],
  198. })
  199. this.fileInfo = {
  200. name: file.name,
  201. size: file.size,
  202. type: file.type,
  203. path: file.path
  204. }
  205. uni.showToast({
  206. title: '文件选择成功',
  207. icon: 'success'
  208. })
  209. // 这里可以处理文件上传或其他操作
  210. console.log('选择的文件:', this.fileInfo)
  211. } catch (error) {
  212. if (error.errMsg !== 'chooseFile:fail cancel') {
  213. uni.showToast({
  214. title: '选择文件失败',
  215. icon: 'none'
  216. })
  217. }
  218. }
  219. }, */
  220. async chooseImage() {
  221. const that = this;
  222. uni.chooseImage({
  223. count: 1, // 默认只能选一张
  224. sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图
  225. sourceType: ['album', 'camera'], // 从相册选择或使用相机
  226. success: function(res) {
  227. // res.tempFilePaths 是图片临时路径数组
  228. console.log('选择成功:', res);
  229. // that.imageInfo.name=res.tempFilePaths[0].split('/')[res.tempFilePaths[0].split('/').length-1]
  230. // 设置选中的图片路径(第一张)
  231. that.imagePath = res.tempFilePaths[0];
  232. },
  233. fail: function(err) {
  234. console.error('图片选择失败:', err);
  235. uni.showToast({
  236. title: '图片选择失败',
  237. icon: 'none'
  238. });
  239. }
  240. });
  241. /* try {
  242. const [file] = await uni.chooseImage({
  243. count: 1,
  244. sizeType: ['original', 'compressed'],
  245. sourceType: ['album'],
  246. success: function(res) {
  247. console.log(JSON.stringify(res.tempFilePaths),res,'=============');
  248. }
  249. });
  250. console.log(file,'----------')
  251. this.imageInfo = {
  252. name: file.name,
  253. size: file.size,
  254. type: file.type,
  255. path: file.path
  256. }
  257. uni.showToast({
  258. title: '文件选择成功',
  259. icon: 'success'
  260. })
  261. // 这里可以处理文件上传或其他操作
  262. console.log('选择的文件:', this.imageInfo)
  263. } catch (error) {
  264. if (error.errMsg !== 'chooseFile:fail cancel') {
  265. uni.showToast({
  266. title: '选择文件失败',
  267. icon: 'none'
  268. })
  269. }
  270. } */
  271. },
  272. // formatFileSize(bytes) {
  273. // if (bytes === 0) return '0 B'
  274. // const k = 1024
  275. // const sizes = ['B', 'KB', 'MB', 'GB']
  276. // const i = Math.floor(Math.log(bytes) / Math.log(k))
  277. // return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
  278. // },
  279. bindPickerChange(e) {
  280. console.log(e)
  281. // 获取选中的值
  282. const tempOption = this.array.find(item => item.value == e).text; // 存储临时选中的值
  283. // 设置弹窗内容
  284. this.modalMessage = `您选择的是: ${tempOption}`;
  285. this.modalImageSrc = '../static/images/xijiao/avator.png'; // 替换为你的图片地址
  286. this.isModalVisible = true; // 显示弹窗
  287. this.modelIndex = e
  288. },
  289. onConfirm() {
  290. // this.modelIndex = e.detail.value; // 更新选中的值
  291. this.isModalVisible = false; // 关闭弹窗
  292. },
  293. onCancel() {
  294. this.isModalVisible = false; // 关闭弹窗
  295. },
  296. radioChange(evt) {
  297. for (let i = 0; i < this.videoContent.length; i++) {
  298. if (this.videoContent[i].value === evt.detail.value) {
  299. this.current = i;
  300. break;
  301. }
  302. }
  303. },
  304. // startRecord() {
  305. // console.log('开始录音');
  306. // recorderManager.start();
  307. // },
  308. // endRecord() {
  309. // console.log('录音结束');
  310. // recorderManager.stop();
  311. // },
  312. // playVoice() {
  313. // console.log('播放录音');
  314. // if (this.voicePath) {
  315. // innerAudioContext.src = this.voicePath;
  316. // innerAudioContext.play();
  317. // }
  318. // },
  319. toggleRecording() {
  320. if (this.isRecording) {
  321. // 停止录音
  322. this.recorderManager.stop();
  323. } else {
  324. // 开始录音前,如果已有录音则清空
  325. if (this.audioPath) {
  326. this.audioPath = null;
  327. }
  328. // 开始录音
  329. this.recorderManager.start({
  330. duration: 60000, // 最大录音时间(毫秒)
  331. sampleRate: 44100,
  332. numberOfChannels: 1,
  333. encodeBitRate: 192000,
  334. format: 'mp3',
  335. frameSize: 50
  336. });
  337. this.isRecording = true;
  338. }
  339. },
  340. playAudio() {
  341. if (!this.audioPath) {
  342. uni.showToast({
  343. title: '没有可播放的录音',
  344. icon: 'none'
  345. });
  346. return;
  347. }
  348. this.innerAudioContext.src = this.audioPath;
  349. this.innerAudioContext.play();
  350. },
  351. jumpToList() {
  352. uni.navigateTo({
  353. url: 'list/index'
  354. });
  355. }
  356. }
  357. }
  358. </script>
  359. <style scoped lang="scss">
  360. .content {
  361. display: flex;
  362. flex-direction: column;
  363. /* align-items: center;
  364. justify-content: center; */
  365. }
  366. .uni-margin-wrap {
  367. width: 690rpx;
  368. width: 100%;
  369. }
  370. .swiper {
  371. height: 300rpx;
  372. }
  373. .swiper-item {
  374. display: block;
  375. height: 300rpx;
  376. line-height: 300rpx;
  377. text-align: center;
  378. background-color: #BD3124;
  379. color: #fff;
  380. }
  381. .form-area {
  382. padding: 20px;
  383. .form-area-item {
  384. display: flex;
  385. flex-direction: row;
  386. margin-top: 30px;
  387. align-items: center;
  388. .form-area-item-text {
  389. width: 120px;
  390. }
  391. /deep/ uni-input {
  392. border: 1px solid #e5e5e5;
  393. border-radius: 4px;
  394. height: 30px;
  395. width: 200px;
  396. }
  397. /deep/ uni-picker {
  398. border-bottom: 1px solid #aaa;
  399. height: 30px;
  400. width: 200px;
  401. display: flex;
  402. align-items: center;
  403. }
  404. /deep/ uni-radio-group,
  405. /deep/ uni-label {
  406. display: flex;
  407. align-items: center;
  408. }
  409. /deep/ uni-label {
  410. margin-right: 10px;
  411. }
  412. /deep/ textarea {
  413. border: 1px solid #aaa;
  414. border-radius: 4px;
  415. width: 320px;
  416. padding: 5px;
  417. margin: 0 auto;
  418. }
  419. .none-file {
  420. margin-left: 10px;
  421. color: #aaa;
  422. }
  423. }
  424. }
  425. .btn-area {
  426. display: flex;
  427. justify-content: center;
  428. margin: 0 auto;
  429. width: 100%;
  430. margin-bottom: 10px;
  431. }
  432. .audio-bar {
  433. background-color: #f0f0f0;
  434. border-radius: 8px;
  435. text-align: center;
  436. }
  437. </style>