AI-draw.vue 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. <template>
  2. <view class="container" :style="{'top': viewTop + 'px', 'height': viewHeight + 'px'}">
  3. <view class="content">
  4. <scroll-view scroll-y="true" :show-scrollbar="false" :scroll-top="scroll_top" style="position: fixed; top: 70px;width: 80vw; bottom: 210upx;">
  5. <view v-if="recvMsgQueue.length > 0" class="scroll-view-text">
  6. <view style="margin-left: 13%; display: flex; align-items: center;justify-content: center;width: 100%; user-select: text;" v-for="(msgIndex,index) in recvMsgQueue " :key="index">
  7. <!-- ai 的消息结构体 -->
  8. <view v-if="isAItell(recvMsgQueue[index].who_msg)" style="width: 100%;height: 100%;margin-bottom: 1%;">
  9. <text style=" font-size: 10rpx;color: #B34FFF;font-size: 10rpx;margin-left: 2%;background: linear-gradient(90deg, #24BF74 10%, #FAEE07 90%);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">
  10. {{ai_name}}
  11. </text>
  12. <view style="margin-bottom: 3%; margin-top: 1%;margin-left: 1%;padding: 1%; width: 70%;background: #2A2832;border-radius: 10rpx 10rpx 10rpx 10rpx;">
  13. <view @tap="handleClick()" style="width: 100%;background-color: #2A2832;color: white;" class="htmlContent" ref="htmlContent" v-html="returnText(recvMsgQueue[index].msg_text)" >
  14. </view>
  15. <image v-if="getImg(index)!=''" :src="getImg(index)" @click="onclickAiGetImg(index)"></image>
  16. <view @click="stopRecv()" v-if="recvStatus&&recvMsgQueue[index].isFinish==false" style="display: flex;width: 100%;align-items: center;justify-content: center;">
  17. <image src="../../static/closed.png" style="width: 50upx;height: 50upx;align-self: center;"></image>
  18. </view>
  19. </view>
  20. <view v-if="false" style="margin-left: 65%;">
  21. <image src="../../static/msg-opt-3.png" style="margin-left: 10upx;width: 30upx;height: 30upx;"></image>
  22. <image src="../../static/msg-opt-4.png" style="margin-left: 10upx;width: 30upx;height: 30upx;"></image>
  23. </view>
  24. <view style="display: flex; flex-direction: row; width: 500px; align-items: center;">
  25. <view style=" display: flex;flex-wrap: wrap;width: 400px;">
  26. <view style="width: 23%;justify-content: center;align-items: center;display: flex;margin-top: 2%; margin-right: 2%;" v-for="(item,i) in recvMsgQueue[index].actions" :key="i">
  27. <view @click="clickitem(recvMsgQueue[index].actions[i],recvMsgQueue[index].image_id)"
  28. style="background-color: #32B3AA;display: flex; box-sizing: border-box; border-radius: 5px;align-items: center;justify-content: center;width: 100%; padding: 2%; font-size: 16px; word-break: break-all; text-overflow: ellipsis; word-wrap: break-word; white-space: pre-wrap; text-align: center;">{{recvMsgQueue[index].actions[i].text}}</view>
  29. </view>
  30. </view>
  31. <view v-if="recvMsgQueue[index].has_chonghua" style="display: flex; flex-direction: column; width: 40px; margin-left: 18px; align-items: center;"
  32. @click="clickonredraw">
  33. <view style="width: 15px; height: 15px;">
  34. <image style="width: 100%; height: 100%;" src="../../static/img_redraw.png" mode="aspectFit"></image>
  35. </view>
  36. <view style="margin-top: 5px; color: #ffffff; font-size: 13px;">重画</view>
  37. </view>
  38. </view>
  39. </view>
  40. <!-- 我发送 的消息结构体 -->
  41. <view v-if="!isAItell(recvMsgQueue[index].who_msg)" style="width: 100%;height: 100%;margin-top: 1%;">
  42. <view style=" font-size: 10rpx;color: #B34FFF;font-size: 10rpx;margin-left: 2%;">
  43. {{user_name}}
  44. </view>
  45. <view style="margin-bottom: 3%; margin-top: 1%;margin-left: 1%;padding: 1%; width: 70%;background: #2A2832;border-radius: 10rpx 10rpx 10rpx 10rpx;">
  46. <view style="width: 100%;background-color: #2A2832;color: white;" class="htmlContent" ref="htmlContent" >
  47. {{recvMsgQueue[index].msg_text}}
  48. </view>
  49. <image v-if="recvMsgQueue[index].des_img!=''" :src="recvMsgQueue[index].des_img" style="background-color: #111111; margin-top: 1%;" mode="aspectFit" @click="onclickSendImg(index)"></image>
  50. </view>
  51. <view v-if="false" style="margin-left: 65%;">
  52. <image src="../../static/msg-opt-1.png" style="margin-left: 10upx; width: 30upx;height: 30upx;"></image>
  53. <image src="../../static/msg-opt-2.png" style="margin-left: 10upx;width: 30upx;height: 30upx;"></image>
  54. <image src="../../static/msg-opt-3.png" style="margin-left: 10upx;width: 30upx;height: 30upx;"></image>
  55. <image src="../../static/msg-opt-4.png" style="margin-left: 10upx;width: 30upx;height: 30upx;"></image>
  56. </view>
  57. </view>
  58. </view>
  59. </view>
  60. <view v-else style="display:flex; width: 100%; height: 100%; color: #ffffff; justify-content: center; align-items: center;">
  61. <text style="font-size: 20px; font-weight: 400; text-align: center; line-height: 60px;">探索AI世界的奥秘,释放无限的创造潜能。\n开始您的创作吧!</text>
  62. </view>
  63. </scroll-view>
  64. <view class="inputbox" style="display: flex;width: 70vw;height: 90upx;position: fixed;bottom:80upx;">
  65. <view style="display: flex;align-items: center;justify-content: center;width: 5%;position: relative;">
  66. <image @click="onclickchooseImg()" src="../../static/img_upload.png" style="width: 65upx;height: 65upx;"></image>
  67. </view>
  68. <view style="display: flex;width: 90%; margin-left: 5px; border-radius: 25px 25px 25px 25px;opacity: 1;border: 1px solid #32B3AA;" class="textarea-box">
  69. <view style="width: 90%; height: 100%; display: flex;align-items: center;justify-content: center" >
  70. <textarea v-model="inputValue" :cursor-spacing="15" class="textarea" auto-height="true"
  71. @keydown.enter="onKeydown('enter')" :disabled="disabled"
  72. @input="onKeyInput"
  73. placeholder="请输入消息内容(使用 Enter 发送)" :maxlength="-1"
  74. placeholder-class="input-placeholder"></textarea>
  75. </view>
  76. <view v-if="recvStatus==false" style="width: 10%; height: 100%; display: flex; color: aliceblue;align-items: center;justify-content: center" @click="sendMsg">
  77. <image src="../../static/img_send.png" style="width: 29.57upx;height: 37.67upx;" ></image>
  78. </view>
  79. </view>
  80. <view style=" display: flex;align-items: center;justify-content: center;width: 5%;position: relative;">
  81. <image @click="onclickdelete()" src="../../static/img_delete.png" style="width: 50upx;height: 70upx;"></image>
  82. </view>
  83. </view>
  84. </view>
  85. <view style="position: absolute;top: 10%;right: 5%;width: 200upx;display: flex;align-items: center;justify-content: center;">
  86. <view style="color: white;margin-right: 10upx;">开启快速</view>
  87. <image @click="is_open_relax=false" v-show="is_open_relax==true" src="../../static/img-off.png" style="width: 60upx;height: 30upx;"></image>
  88. <image @click="is_open_relax=true" v-show="is_open_relax==false" src="../../static/img-on.png" style="width: 60upx;height: 30upx;"></image>
  89. </view>
  90. <view v-if="false" @click="is_show_import_img_view=true" style="position: absolute;top: 20%;right: 5%;width: 200upx;display: flex;align-items: center;justify-content: center;">
  91. <view style="color: white;margin-right: 10upx;">导入图片</view>
  92. <image src="../../static/import-img.png" style="width: 60upx;height: 60upx;"></image>
  93. </view>
  94. <view v-if="is_show_import_img_view==true" style=" display: flex; height: 100vh; position: absolute;top: 0;width: 500upx;background-color:#24BF74;">
  95. <view style="width: 100%;;">
  96. <view @click="onFileInput" style="display: flex;align-items: center;justify-content: center;margin-top: 5%;center;margin-bottom: 5%;">
  97. <image :src="cur_select_img" style="width: 100upx;height: 100upx;"></image>
  98. </view>
  99. <view style="display: flex;align-items: center;justify-content: center;margin-top: 5%;center;margin-bottom: 5%;">
  100. <textarea style="display: flex;align-items: center;justify-content: center;" v-model="diy_inputValue" :cursor-spacing="15" auto-height="true"
  101. @input="onKeyInput"
  102. placeholder="请输入描述" :maxlength="-1"
  103. placeholder-class="input-placeholder"></textarea>
  104. </view>
  105. <view @click="send_diy_img()" style="display: flex;align-items: center;justify-content: center;margin-top: 5%;center;margin-bottom: 5%;">
  106. <view style="display: flex;align-items: center;justify-content: center;background-color: aqua;;border-radius: 10rpx 10rpx 10rpx 10rpx;width: 50%;">发送</view>
  107. </view>
  108. <view @click="is_show_import_img_view=false" style="display: flex;align-items: center;justify-content: center;margin-top: 5%;center;margin-bottom: 5%;">
  109. <view style="display: flex;align-items: center;justify-content: center;background-color: aqua;;border-radius: 10rpx 10rpx 10rpx 10rpx;width: 50%;">关闭</view>
  110. </view>
  111. <scroll-view scroll-y="true" style="height:500upx;">
  112. <view @click="selectImg(import_list[j])" style="display: flex;align-items: center;justify-content: center;" v-for="(itemx,j) in import_list" :key="j">
  113. <image style="width: 150upx;height: 150upx;margin-top: 5%;" :src="import_list[j]"></image>
  114. </view>
  115. </scroll-view>
  116. </view>
  117. </view>
  118. <view v-if="choose_img_tempFilePath != ''" style="position: fixed; bottom: 190upx; left: 20%; width: 130px; height: 90px;">
  119. <view style="position: absolute; top: 10px; width: 120px; height: 80px; background-color: #111111;">
  120. <image style="width: 100%; height: 100%;" :src="choose_img_tempFilePath" mode="aspectFill" @click="onclickPreviewChooseImg"></image>
  121. </view>
  122. <view style="position: absolute; right: 0; width: 30px; height: 30px;" @click="onclickDeleteChooseImg">
  123. <image style="margin-left: 10px; width: 20px; height: 20px;" src="../../static/img_jian.png" mode="aspectFit"></image>
  124. </view>
  125. </view>
  126. <view v-if="is_up_img_file" style="align-items: center;justify-content: center;display: flex; color: aliceblue; position: absolute; width: 100vw;height:100vh;background-color: black;opacity: 70%;">
  127. 正在上传图片...
  128. </view>
  129. </view>
  130. </template>
  131. <script>
  132. import marked from '../marked/marked.min.js';
  133. import config from '../index/config.js';
  134. import hljs from "../highlight.js/lib/common.js";
  135. import interfaces from '../../utils/interfaces.js'
  136. import "../highlight.js/styles/atom-one-dark.css";
  137. // import '../v-copy.js';
  138. // 键盘的shift键是否被按下
  139. let shiftKeyPressed = false
  140. export default {
  141. components:{
  142. marked
  143. },
  144. data() {
  145. return {
  146. diy_inputValue: '',
  147. is_up_img_file:false,
  148. import_list:[],
  149. is_show_import_img_view:false,
  150. is_open_relax:true,
  151. code:'',
  152. title: 'Hello',
  153. disabled: false,
  154. socketOpen:false,
  155. socketMsgQueue:[],
  156. recvMsgQueue:[],
  157. sendMsgQueue:[],
  158. inputValue: '',
  159. inputOldValue: '',
  160. _socketTask:null,
  161. scroll_text_:"\n",
  162. sendStatus:false,
  163. recvStatus:false,
  164. all_src_list:[],
  165. src_index:0,
  166. scroll_top:0,
  167. user_name:'ZYWZ', //用户名字
  168. ai_name:"SmartAssistant", //ai名字
  169. user_mobile:"18612220000",//用户手机号
  170. ani:'',
  171. ani_style:{
  172. width: "100upx",height: "100upx",rotate: 0,left:"25%",right:"25%",position:"absolute"
  173. },
  174. angle:359,
  175. imageUrl:'',
  176. fileData:null,
  177. cur_select_img:'../../static/import-img.png',
  178. choose_img_tempFilePath: '',
  179. action_type: {
  180. generate: 'generate', // 选择图片和输入框
  181. youhua: 'variation',// 优化
  182. fangda: 'upsample', // 放大
  183. },
  184. current_send: {
  185. msg:'',
  186. image_url: '', // 不为空时,表示用户通过选择图片和输入框内容
  187. image_id: '', // 不为空时,表示是 点'放大'或'优化'
  188. prompt: '',
  189. action: '', // generate:表示用户通过选择图片和输入框内容 variation数字:优化 upsample1数字:放大
  190. }
  191. }
  192. },
  193. mounted(){
  194. this.user_mobile = this.tools.get_user_info().mobile
  195. marked.setOptions({
  196. renderer: new marked.Renderer(),
  197. gfm: true,
  198. tables: true,
  199. breaks: false,
  200. pedantic: false,
  201. sanitize: false,
  202. smartLists: true,
  203. smartypants: false,
  204. highlight: function (code) {
  205. return hljs.highlightAuto(code).value;
  206. },
  207. langPrefix:"hljs language-"
  208. });
  209. // this.recvMsgQueue = uni.getStorageSync('recvMsgQueue')
  210. // console.log('this.recvMsgQueue[0].actions=',this.recvMsgQueue[0].actions)
  211. },
  212. props: {
  213. viewTop: {
  214. type: Number,
  215. default: 0
  216. },
  217. viewHeight: {
  218. type: Number,
  219. default: 0
  220. }
  221. },
  222. methods: {
  223. send_diy_img(){
  224. if(this.cur_select_img==="../../static/import-img.png"){
  225. uni.showToast({
  226. title:"请选择图片"
  227. })
  228. return
  229. }
  230. if(this.diy_inputValue.length>0){
  231. this.is_show_import_img_view = false;
  232. let des_img = this.cur_select_img;
  233. this.sendSocketMessage(this.diy_inputValue,this.cur_select_img+" "+this.diy_inputValue,des_img)
  234. this.diy_inputValue = '';
  235. }else{
  236. uni.showToast({
  237. title:"请添加描述,只支持英文哦"
  238. })
  239. }
  240. },
  241. selectImg(path){
  242. let self = this;
  243. self.cur_select_img = path;
  244. },
  245. onFileInput(event) {
  246. let self = this;
  247. uni.chooseImage({
  248. success: (chooseImageRes) => {
  249. const tempFilePaths = chooseImageRes.tempFilePaths;
  250. self.is_up_img_file = true;
  251. uni.uploadFile({
  252. url: 'https://mlapi.hainanmlwl.com/file/upload_aiimg', //仅为示例,非真实的接口地址
  253. filePath: tempFilePaths[0],
  254. name: 'aiimg_url',
  255. formData: {
  256. // 'aiimg_url': self.cur_select_img
  257. },
  258. success: (uploadFileRes) => {
  259. self.is_up_img_file = false;
  260. let obj = JSON.parse(uploadFileRes.data)
  261. uni.showModal({
  262. content:obj.message,
  263. showCancel:false
  264. })
  265. console.log("self.cur_select_img",self.cur_select_img)
  266. if(obj.code==10000){
  267. let img_url = obj.content.surl
  268. self.import_list.push(img_url)
  269. self.cur_select_img = img_url;
  270. }
  271. console.log("uploadFile",obj);
  272. },
  273. fail() {
  274. uni.showModal({
  275. content:"上传失败",
  276. showCancel:false
  277. })
  278. self.is_up_img_file = false;
  279. }
  280. });
  281. }
  282. });
  283. // console.log("onFileInput",event)
  284. // const file = event.target.files[0] // 获取选择的文件
  285. // const reader = new FileReader() // 创建FileReader对象
  286. // // 当文件被读取时,将其转换为DataURL格式的字符串,并将其作为imageUrl值赋给组件的data属性中
  287. // reader.onload = (e) => {
  288. // this.imageUrl = e.target.result
  289. // // 保存文件对象到data属性中
  290. // this.fileData = file
  291. // }
  292. // reader.readAsDataURL(file) // 读取文件并启动onload回调函数
  293. },
  294. clickitem(action,image_id){
  295. switch(action.id){
  296. case "upsample1":
  297. break;
  298. case "upsample2":
  299. break;
  300. case "upsample3":
  301. break;
  302. case "upsample4":
  303. break;
  304. case "variation1":
  305. break;
  306. case "variation2":
  307. break;
  308. case "variation3":
  309. break;
  310. case "variation4":
  311. break;
  312. }
  313. console.log("clickitem",this.recvStatus,action,image_id)
  314. if(!this.recvStatus){
  315. // this.sendSocketMessage(action,'','',action,image_id)
  316. this.sendSocketMessage(action.text,'','',action.id,image_id)
  317. let self = this
  318. setTimeout(function() {self.showLastMsg()}, 500);
  319. }
  320. },
  321. isRecvMsgFinish(showToast=true) {
  322. if(this.recvMsgQueue.length == 0) {
  323. return true
  324. }
  325. if(showToast) {
  326. uni.showToast({
  327. icon:'none',
  328. title:'正在生成中...',
  329. duration:1500,
  330. })
  331. }
  332. let isFinish = this.recvMsgQueue[this.recvMsgQueue.length - 1].isFinish
  333. return isFinish
  334. },
  335. isHasChonghua(action) {
  336. if(action.includes(this.action_type.generate)) {
  337. // console.log('表示用户通过选择图片和输入框内容')
  338. return true
  339. } else if (action.includes(this.action_type.youhua)) {
  340. // console.log('优化 优化 优化 优化 优化')
  341. return true
  342. } else if (action.includes(this.action_type.fangda)) {
  343. // console.log('放大 放大 放大 放大 放大')
  344. return false
  345. }
  346. return false
  347. },
  348. clickonredraw() {
  349. // console.log('重画')
  350. // console.log('msg=', this.current_send.msg)
  351. // console.log('image_url=', this.current_send.image_url)
  352. // console.log('image_id=', this.current_send.image_id)
  353. // console.log('prompt=', this.current_send.prompt)
  354. // console.log('action=', this.current_send.action)
  355. let self = this
  356. uni.showModal({
  357. content:"是否重画",
  358. confirmText:'确定',
  359. cancelText:'取消',
  360. success:(res)=>{
  361. if(res.confirm){
  362. if(self.isRecvMsgFinish(false) == false) {
  363. return
  364. }
  365. if(self.isHasChonghua(self.current_send.action)) {
  366. self.sendSocketMessage(self.current_send.msg,
  367. self.current_send.prompt,
  368. self.current_send.image_url,
  369. self.current_send.action,
  370. self.current_send.image_id)
  371. setTimeout(function() {self.showLastMsg()}, 500);
  372. } else {
  373. uni.showToast({
  374. icon:'error',
  375. title:'无法重画',
  376. duration:1500,
  377. })
  378. }
  379. }
  380. }
  381. })
  382. },
  383. getImg(index){
  384. return this.recvMsgQueue[index].msg_img
  385. },
  386. onclickdelete(){
  387. if(this.isRecvMsgFinish() == false) {
  388. return
  389. }
  390. let self = this;
  391. if(self.recvMsgQueue.length>0&&self.recvMsgQueue[self.recvMsgQueue.length-1].isFinish){
  392. uni.showModal({
  393. showCancel:true,
  394. content:"是否删除记录",
  395. success:(res)=>{
  396. if(res.confirm){
  397. self.recvMsgQueue = []
  398. }
  399. }
  400. })
  401. }
  402. },
  403. // #ifdef H5 && VUE2
  404. onKeydown(keyname) {
  405. let self = this;
  406. if(self.inputOldValue!=self.inputValue){
  407. self.sendMsg();
  408. }else{
  409. }
  410. },
  411. onKeyup(keyname) {
  412. let self = this;
  413. self.inputOldValue=self.inputValue;
  414. },
  415. // #endif
  416. setCopy(content){
  417. // 使用#ifdef H5和#endif控制各端调用情况
  418. // 该方法不支持h5
  419. //#ifndef H5
  420. uni.setClipboardData({
  421. data: String(content), // 必须字符串
  422. success: function () {
  423. console.log('success');
  424. }
  425. });
  426. //#endif
  427. // h5端赋值方法,使用创建节点
  428. // #ifdef H5
  429. if (!document.queryCommandSupported('copy')) { // 兼容某些浏览器的判断
  430. console.log('该浏览器不支持')
  431. }
  432. let textarea = document.createElement("textarea")
  433. textarea.value = content
  434. textarea.readOnly = "readOnly"
  435. document.body.appendChild(textarea)
  436. textarea.select() // 选择对象
  437. textarea.setSelectionRange(0, content.length) // 核心
  438. let result = document.execCommand("copy") // 执行浏览器复制命令
  439. if (result) {
  440. uni.showToast({
  441. title: '复制成功',
  442. duration: 2000
  443. });
  444. }
  445. textarea.remove()
  446. // #endif
  447. },
  448. updateSrcList(){
  449. let self = this;
  450. let list = this.getIndexList(self.scroll_text_);
  451. for (var i = 0; i < list.length; i+=2) {
  452. let start = list[i];
  453. let end = list[i+1];
  454. self.all_src_list.push(self.scroll_text_.substring(start,end))
  455. }
  456. // console.log("updateSrcList",self.all_src_list,self.scroll_text_)
  457. },
  458. stopRecv(){
  459. let self =this;
  460. self._socketTask.close()
  461. // self.initTcp(true);
  462. self.finish_recv();
  463. },
  464. handleClick(e) {
  465. e = e || window.event;
  466. let target = e.target || e.srcElement;
  467. let name = target.tagName.toLowerCase();
  468. let self =this;
  469. if(name.substring(0,3)=="abc"){
  470. let name_list = name.split('_');
  471. if(name_list.length>0){
  472. let index = parseInt(name_list[1])-1;
  473. console.log("handleClick",index,self.all_src_list)
  474. self.setCopy(self.all_src_list[index])
  475. }
  476. console.log("handleClick",self.all_src_list[index])
  477. console.log(target.tagName.toLowerCase())
  478. }
  479. console.log(name.substring(0,3))
  480. },
  481. copyUpdates(){
  482. let self = this;
  483. let str = self.recvMsgQueue[self.recvMsgQueue.length-1].msg_text
  484. let key = "</code></pre>";
  485. let index = self.all_src_list.length;
  486. while(str.indexOf(key) != -1){
  487. index++;
  488. let s_tag = "</code><abc_"+index+" class=\"copy-button\">Copy</abc_"+index+"></pre>"
  489. console.log("s_tag",s_tag)
  490. str=str.replace("</code></pre>", s_tag)
  491. }
  492. // str=str.replaceAll("</code></pre>", "</code><abc class=\"copy-button\">Copy</abc></pre>")
  493. self.recvMsgQueue[self.recvMsgQueue.length-1].msg_text = str
  494. // console.log("copyUpdates",str)
  495. self.$forceUpdate();
  496. },
  497. getIndexList(box_str){
  498. let box = box_str;
  499. let boxarr=[];
  500. let pos = box.indexOf('```');
  501. while(pos>-1){
  502. boxarr.push(pos);
  503. pos= box.indexOf('```',pos+1);
  504. }
  505. return boxarr
  506. },
  507. returnText(text){
  508. return "<style> body { background-color: black; color: white; } pre {position: relative;}.copy-button { position: absolute; top: 0;right: 0;padding: 4px 8px;background-color: #333;color: #fff;cursor: pointer;user-select: none;} p {line-height: 2;} li{line-height: 1.5;} table {border-collapse: collapse;width: 100%;font-family: Arial, sans-serif;font-size: 14px;}table th,table td {border: 1px solid #ddd;padding: 8px;text-align: left;}</style>" +text
  509. },
  510. // 滚动窗口以显示最新的一条消息
  511. showLastMsg() {
  512. let self = this;
  513. let container = uni.createSelectorQuery().in(this).select(".scroll-view-text");
  514. // 利用uniapp提供的接口获取可视区域的高度和滚动高度
  515. // let query=uni.createSelectorQuery()
  516. // let container=query.select('.box');
  517. container.fields({
  518. // rect:true, //是否返回节点布局位置信息{left,top,right,bottom}
  519. size:true, //是否返回节点尺寸信息{width,height}
  520. scrollOffset:true //是否返回节点滚动信息{scrollLeft,scrollTop}
  521. },(res)=>{
  522. self.scroll_top = res.height;
  523. // console.log(res)
  524. }).exec()
  525. },
  526. isAItell(type){
  527. if(type===config.type_ai){
  528. return true;
  529. }
  530. return false;
  531. },
  532. make_avatar(type){
  533. if(type===config.type_ai){
  534. return "../../static/PubImgs_avatar_avatar7.png";
  535. }
  536. return "../../static/PubImgs_avatar_avatar8.png";
  537. },
  538. isShow(text){
  539. return text!="";
  540. },
  541. initAni(){
  542. let self = this;
  543. if(!self.disabled){
  544. clearTimeout(self.initAni)
  545. return;
  546. }
  547. let loading_title = self.recvMsgQueue[self.recvMsgQueue.length-1].loading_title;
  548. let loading_title_str = ""
  549. for (var i = 0; i < loading_title; i++) {
  550. loading_title_str+="."
  551. }
  552. // console.log("loading_title_str",loading_title_str)
  553. self.recvMsgQueue[self.recvMsgQueue.length-1].msg_text ="正在生成" +loading_title_str;
  554. loading_title = loading_title>=3?0: (loading_title+1)
  555. self.recvMsgQueue[self.recvMsgQueue.length-1].loading_title = loading_title;
  556. setTimeout(self.initAni,1000)
  557. },
  558. async finish_recv(){
  559. let self = this;
  560. self.recvStatus = false;
  561. self.disabled = false;
  562. clearTimeout(self.initAni)
  563. self.scroll_text_+="\n"
  564. // console.log("self.recvMsgQueue",self.scroll_text_)
  565. self.recvMsgQueue[self.recvMsgQueue.length-1].isFinish = true;
  566. self.stopUpdateView();
  567. self.copyUpdates();
  568. self.updateSrcList();
  569. setTimeout(function() {
  570. self.showLastMsg();
  571. }, 200);
  572. },
  573. async sendMsg(){
  574. if(this.choose_img_tempFilePath != '') {
  575. this.sendImageAndTextOperation()
  576. return
  577. }
  578. // 如果内容为空
  579. if (!this.inputValue) {
  580. // 弹出提示框
  581. return uni.showToast({
  582. // 提示内容
  583. title: '内容不能为空',
  584. // 不显示图标
  585. icon: 'none'
  586. });
  587. }
  588. this.sendSocketMessage(this.inputValue)
  589. },
  590. onKeyInput(event) {
  591. // console.log("onKeyInput",event.target.value)
  592. let self = this;
  593. // self.inputOldValue=self.inputValue
  594. },
  595. startUpdateView(){
  596. let self = this;
  597. self.$forceUpdate();
  598. setTimeout(self.startUpdateView,500)
  599. },
  600. stopUpdateView(){
  601. let self = this;
  602. clearTimeout(self.startUpdateView)
  603. },
  604. sendSocketMessage(msg,prompt=msg,des_img='',action='generate',image_id='') {
  605. // console.log("sendSocketMessage send",des_img)
  606. // console.log('msg=',msg, 'prompt=',prompt, 'des_img=',des_img, 'action=',action, 'image_id=',image_id)
  607. let self = this;
  608. let isRelax = self.is_open_relax;
  609. let token = isRelax?"7d995cf958f54d2c9c3264e645a9c4d9":"bdb957b8003e4a46b6b26a7174be5789";
  610. let relax = isRelax?"/relax" :"";
  611. let url = 'https://api.zhishuyun.com/midjourney/imagine' +relax +'?token='+token
  612. self.sendStatus = true;
  613. self.disabled = true;
  614. self.ani_style.rotate = 0;
  615. self.inputValue = '';
  616. self.recvMsgQueue.push({who_msg:config.type_self,msg_type:0,msg_text:msg,isFinish:true,'des_img':des_img})
  617. self.recvMsgQueue.push({who_msg:config.type_ai,msg_type:0,msg_text:"正在生成...",msg_img:"",isFinish:false,actions:[],image_id:"",task_id:"",loading_title:1})
  618. // self.recvMsgQueue[self.recvMsgQueue.length-1].actions=['upsample1', 'upsample2', 'upsample3', 'upsample4', 'variation1', 'variation2', 'variation3', 'variation4']
  619. self.initAni();
  620. if(action.includes(this.action_type.generate)) {
  621. self.current_send.msg = msg
  622. self.current_send.image_url = des_img
  623. self.current_send.image_id = image_id
  624. self.current_send.prompt = prompt
  625. self.current_send.action = action
  626. }
  627. uni.request({
  628. url:url,
  629. timeout:600000,
  630. header: {'Content-Type':'application/json','accept':'application/json'},
  631. method:'POST',
  632. data:{'prompt':prompt,'action':action,'image_id':image_id},
  633. success:(res)=>{
  634. console.log("Ai-view",res)
  635. if(res.statusCode===200){
  636. let html_img = '<!DOCTYPE html> <html><head></head><body> <img src='+res.data.image_url+ '></body></html>'
  637. let recvMsgQueue_lastIndex = self.recvMsgQueue.length-1
  638. self.recvMsgQueue[recvMsgQueue_lastIndex].msg_img = res.data.image_url;
  639. self.recvMsgQueue[recvMsgQueue_lastIndex].msg_text = "";
  640. self.recvMsgQueue[recvMsgQueue_lastIndex].image_id = res.data.image_id;
  641. console.log("res.data.actions",res.data.actions)
  642. self.recvMsgQueue[recvMsgQueue_lastIndex].actions = [];
  643. let list = [];
  644. list = res.data.actions;
  645. // for (var i = 0; i < list.length; i++) {
  646. // self.recvMsgQueue[recvMsgQueue_lastIndex].actions.push(list[i])
  647. // }
  648. // 如果不是放大,需要 'actons数据'和'重画按钮'和'之前有重画按钮隐藏'
  649. let isHasChonghua = self.isHasChonghua(action)
  650. if(isHasChonghua == true) {
  651. for(var recvI = 0; recvI<self.recvMsgQueue.length; recvI++) {
  652. if(self.recvMsgQueue[recvI].has_chonghua == true) {
  653. self.recvMsgQueue[recvI].has_chonghua = false
  654. }
  655. }
  656. }
  657. self.recvMsgQueue[recvMsgQueue_lastIndex].has_chonghua = isHasChonghua
  658. if(isHasChonghua == true) {
  659. for (var i = 0; i < list.length; i++) {
  660. let action = list[i]
  661. let dic = {}
  662. switch (action){
  663. case 'upsample1':
  664. dic = {'id': action, 'text': '放大1'}
  665. break;
  666. case 'upsample2':
  667. dic = {'id': action, 'text': '放大2'}
  668. break;
  669. case 'upsample3':
  670. dic = {'id': action, 'text': '放大3'}
  671. break;
  672. case 'upsample4':
  673. dic = {'id': action, 'text': '放大4'}
  674. break;
  675. case 'variation1':
  676. dic = {'id': action, 'text': '优化1'}
  677. break;
  678. case 'variation2':
  679. dic = {'id': action, 'text': '优化2'}
  680. break;
  681. case 'variation3':
  682. dic = {'id': action, 'text': '优化3'}
  683. break;
  684. case 'variation4':
  685. dic = {'id': action, 'text': '优化4'}
  686. break;
  687. default:
  688. dic = {'id': action, 'text': action}
  689. break;
  690. }
  691. // console.log('action.dic=',dic)
  692. self.recvMsgQueue[recvMsgQueue_lastIndex].actions.push(dic)
  693. }
  694. }
  695. }else{
  696. uni.showModal({
  697. content:res.data.detail+":"+res.statusCode,
  698. showCancel:false
  699. })
  700. self.finish_recv()
  701. self.recvMsgQueue[self.recvMsgQueue.length-1].msg_text = res.data.detail;
  702. }
  703. self.finish_recv()
  704. },
  705. fail: (e) => {
  706. console.log('fail', e)
  707. self.recvMsgQueue[self.recvMsgQueue.length-1].msg_text ="生成失败";
  708. self.finish_recv()
  709. }
  710. })
  711. },
  712. onclickSendImg(index){
  713. let img_url = this.recvMsgQueue[index].des_img
  714. if(img_url) {
  715. uni.previewImage({
  716. urls:[img_url]
  717. })
  718. } else {
  719. uni.showToast({
  720. icon:'error',
  721. title:'预览图片失败',
  722. duration:2000
  723. })
  724. }
  725. },
  726. onclickAiGetImg(index){
  727. let img_url = this.getImg(index)
  728. if(img_url) {
  729. uni.previewImage({
  730. urls:[img_url]
  731. })
  732. } else {
  733. uni.showToast({
  734. icon:'error',
  735. title:'预览图片失败',
  736. duration:2000
  737. })
  738. }
  739. },
  740. onclickPreviewChooseImg() {
  741. let img_url = this.choose_img_tempFilePath
  742. if(img_url) {
  743. uni.previewImage({
  744. urls:[img_url]
  745. })
  746. } else {
  747. uni.showToast({
  748. icon:'error',
  749. title:'预览图片失败',
  750. duration:2000
  751. })
  752. }
  753. },
  754. onclickDeleteChooseImg() {
  755. let self = this
  756. uni.showModal({
  757. showCancel:true,
  758. content:"是否删除",
  759. success:(res)=>{
  760. if(res.confirm){
  761. self.choose_img_tempFilePath = ''
  762. }
  763. }
  764. })
  765. },
  766. onclickchooseImg(event){
  767. if(this.isRecvMsgFinish() == false) {
  768. return
  769. }
  770. let self = this;
  771. uni.chooseImage({
  772. success: (chooseImageRes) => {
  773. const tempFilePaths = chooseImageRes.tempFilePaths;
  774. this.choose_img_tempFilePath = tempFilePaths[0]
  775. if(tempFilePaths.length > 0) {
  776. self.choose_img_tempFilePath = tempFilePaths[0]
  777. } else {
  778. uni.showToast({
  779. icon:'error',
  780. title:'图片选择失败,请重新选择',
  781. duration:2000
  782. })
  783. }
  784. }
  785. });
  786. },
  787. sendImageAndTextOperation() {
  788. if (this.inputValue.length == 0) {
  789. return uni.showToast({
  790. title: '请添加描述哦,只支持英文',
  791. icon: 'none'
  792. });
  793. }
  794. let self = this
  795. self.is_up_img_file = true
  796. // 上传图片+发送图片链接和文本
  797. // 上传图片
  798. uni.uploadFile({
  799. url: 'https://mlapi.hainanmlwl.com/file/upload_aiimg',
  800. filePath: self.choose_img_tempFilePath,
  801. name: 'aiimg_url',
  802. formData: {
  803. // 'aiimg_url': self.cur_select_img
  804. },
  805. success: (uploadFileRes) => {
  806. console.log("uploadFile",uploadFileRes);
  807. self.is_up_img_file = false;
  808. let obj = JSON.parse(uploadFileRes.data)
  809. // uni.showToast({
  810. // title:obj.message,
  811. // duration:1500
  812. // })
  813. if(obj.code == 10000) {
  814. let img_url = obj.content.surl
  815. if(img_url == '') {
  816. uni.showModal({
  817. content:"图片链接为空",
  818. showCancel:false
  819. })
  820. return
  821. }
  822. if(self.choose_img_tempFilePath != '') {
  823. self.choose_img_tempFilePath = ''
  824. }
  825. // 发送
  826. self.sendSocketMessage(self.inputValue, img_url+" "+self.inputValue ,img_url)
  827. } else {
  828. uni.showModal({
  829. showCancel:false,
  830. content:obj.msg
  831. })
  832. }
  833. },
  834. fail(err) {
  835. console.log('上传失败err=',err)
  836. uni.showModal({
  837. content:"上传失败",
  838. showCancel:false
  839. })
  840. self.is_up_img_file = false;
  841. }
  842. });
  843. }
  844. }
  845. }
  846. </script>
  847. <style>
  848. .container{
  849. display: flex;
  850. box-sizing: border-box;
  851. flex-direction: column;
  852. position: fixed;
  853. /* position: relative; */
  854. /* background-color: #2A2832; */
  855. left: 0;
  856. width: 100%;
  857. justify-content: center;
  858. }
  859. .content {
  860. height: 100%;
  861. display: flex;
  862. flex-direction: column;
  863. align-items: center;
  864. justify-content: center;
  865. position: relative;
  866. background-color: #2A2832;
  867. }
  868. .text-area {
  869. display: flex;
  870. justify-content: center;
  871. }
  872. .title {
  873. font-size: 36rpx;
  874. color: #8f8f94;
  875. }
  876. .textarea-box,
  877. .textarea {
  878. width: 100%;
  879. height: 100%;
  880. margin-left: 3%;
  881. },
  882. textarea-box {
  883. width: 100% !important;
  884. }
  885. .textarea-box,
  886. .textarea,
  887. textarea,
  888. textarea-box {
  889. /* height: 120px; */
  890. }
  891. .textarea-box {
  892. background-color: #FFF;
  893. }
  894. uni-scroll-view .uni-scroll-view::-webkit-scrollbar {
  895. display: none;
  896. width: 0 !important;
  897. height: 0 !important;
  898. -webkit-appearance: none;
  899. background: transparent;
  900. color: transparent;
  901. }
  902. </style>