createSceneH5.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. <template>
  2. <div class=" w-full h-full app-pageContainer" style="overflow: hidden;">
  3. <div class=" w-full py-3 px-10 border-b border-gray-200">
  4. 创建场景
  5. </div>
  6. <!-- 第一步场景基本信息 -->
  7. <div v-show="active == 1" class="px-6 py-8 mx-auto overflow-y-auto w-full"
  8. style="height: calc(100% - 49px - 80px - 60px);">
  9. <div>
  10. <div class="py-4 flex flex-col">
  11. <div class="w-20 h-20">
  12. <div v-if="!character.id" @click="showPopup1 = true"
  13. class="w-20 h-20 bg-gray-100 rounded-full cursor-pointer flex flex-col justify-center items-center text-sm hover:bg-gray-200">
  14. <p>选择</p>
  15. <p>角色</p>
  16. </div>
  17. <img v-else class="w-20 h-20 rounded-full cursor-pointer object-cover shadow" @click="showPopup1 = true"
  18. :src="baseApi + character.picture" alt="">
  19. </div>
  20. <div class="mt-1.5 text-2xl">{{ character.id ? character.characterName : '请选择角色' }}</div>
  21. <div class="mt-1 text-gray-400 text-sm">定义角色所属的上下文,为角色补充创作的故事或设定。</div>
  22. </div>
  23. <div class="py-4">
  24. <div>场景名称</div>
  25. <div class="mt-1 text-gray-400 text-sm">场景的标题</div>
  26. <el-input class="mt-4" v-model="form.sceneName" placeholder="例如:第一次约会" maxlength="10"
  27. show-word-limit></el-input>
  28. </div>
  29. <div class="py-4">
  30. <div>场景描述</div>
  31. <div class="mt-1 text-gray-400 text-sm">
  32. 详细的场景描述,可以为角色补充创作的故事或设定,可以指定互动发生的位置和时间。
  33. </div>
  34. <el-input class="mt-4" v-model="form.sceneDescription" placeholder="例:{char} 成为 {user} 的女朋友已有一段時間了,今天你们决定开始同居。"
  35. type="textarea" maxlength="400" :autosize="{ minRows: 4 }" show-word-limit></el-input>
  36. </div>
  37. <div class="py-4">
  38. <div>欢迎语</div>
  39. <div class="mt-1 text-gray-400 text-sm">
  40. 请填写欢迎语和指定的发言角色,這将会是进入场景时收到的第一句话,将会印象场景的剧情走向
  41. </div>
  42. <el-input class="mt-4" v-model="form.sceneWelcome" placeholder="例如:他的声音里充满了恼怒,问道: “你是我新來的保姆吗?”" type="textarea"
  43. maxlength="400" :autosize="{ minRows: 4 }" show-word-limit></el-input>
  44. </div>
  45. <div class="py-4">
  46. <div>模型</div>
  47. <div class="flex items-start py-3">
  48. <el-radio-group class="radio" v-model="form.modelId">
  49. <el-radio-button v-for="(item, index) in modelList" :key="index" :label="item.id">
  50. {{ item.model }}
  51. </el-radio-button>
  52. </el-radio-group>
  53. </div>
  54. </div>
  55. </div>
  56. </div>
  57. <!-- 第二步背景 -->
  58. <div v-show="active == 2" class="w-full relative" style="height: calc(100% - 49px - 80px - 60px);">
  59. <img v-if="uploadList.length > 0" class=" w-full h-full object-cover" :src="baseApi + uploadList[bgActive].url"
  60. alt="">
  61. <div
  62. class="absolute bg-white p-4 bottom-1/2 right-1/2 transform translate-x-1/2 translate-y-1/2 rounded-lg border border-gray-200"
  63. style="height: 582px;">
  64. <el-tabs v-model="tabActive2">
  65. <el-tab-pane label="上传背景" name="1">
  66. <div class="flex flex-col">
  67. <div>直接使用上传的图像</div>
  68. <div class="mt-1 text-gray-400 text-sm">直接使用上传的图像</div>
  69. <el-upload class=" w-full text-center mt-4" drag :action="uploadUrl" :headers="headers"
  70. :before-upload="handleBeforeUpload" :on-success="handleUploadSuccess" :on-error="handleUploadError"
  71. :show-file-list="false" :limit="10">
  72. <i class="el-icon-upload"></i>
  73. <div class="el-upload__text"><em>点击上传</em></div>
  74. <div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过5M</div>
  75. </el-upload>
  76. <div>背景</div>
  77. <div class="mt-2 grid grid-cols-4 gap-1 gap-y-2">
  78. <div v-for="(item, index) in uploadList" :key="index" @click="bgActive = index">
  79. <img class=" rounded object-cover cursor-pointer border" :class="bgActive === index ? 'bgActive' : ''"
  80. style="width: 120px; height: 90px;" :src="baseApi + item.url" alt="">
  81. </div>
  82. </div>
  83. </div>
  84. </el-tab-pane>
  85. </el-tabs>
  86. </div>
  87. </div>
  88. <div class="bottom flex justify-end items-center pr-6">
  89. <el-button v-if="active == 1" type="primary" :disabled="nextDisabled" @click="next()">下一步</el-button>
  90. <el-button v-if="active == 2" @click="active--">上一步</el-button>
  91. <el-dropdown @command="handleCommand" trigger="click">
  92. <el-button class="mx-2 w-28 " v-show="active == 2">
  93. {{ this.form.type == 0 ? '公开' : '非公开' }}
  94. <i class="el-icon-arrow-up el-icon--right"></i>
  95. </el-button>
  96. <el-dropdown-menu slot="dropdown">
  97. <el-dropdown-item command="0">公开</el-dropdown-item>
  98. <el-dropdown-item command="1">非公开</el-dropdown-item>
  99. </el-dropdown-menu>
  100. </el-dropdown>
  101. <el-button v-if="active == 2" :disabled="uploadList.length == 0" type="primary" @click="submit">发布</el-button>
  102. </div>
  103. <!-- 选择角色对话框 -->
  104. <el-dialog class="dialog" title="选择角色" :visible.sync="showPopup1" fullscreen>
  105. <div>
  106. <span>选择要在哪个角色上新增场景?</span>
  107. <el-tabs v-model="tabActive" @tab-click="handleClickTab">
  108. <el-tab-pane label="我的角色" name="1">
  109. <div class=" grid grid-cols-3 gap-8">
  110. <div v-for="(item, index) in userCharacterList" :key="index" @click="selectCharacter(item)">
  111. <img class="rounded-full object-cover cursor-pointer w-20 h-20" :src="baseApi + item.picture" alt="">
  112. <div class="characterName text-center w-20">{{ item.characterName }}</div>
  113. </div>
  114. </div>
  115. </el-tab-pane>
  116. <el-tab-pane label="热门角色" name="2">
  117. <div class=" grid grid-cols-3 gap-8 justify-items-center">
  118. <div v-for="(item, index) in characterList" :key="index" @click="selectCharacter(item)">
  119. <img class="rounded-full object-cover cursor-pointer w-20 h-20" :src="baseApi + item.picture" alt="">
  120. <div class="characterName text-center w-20">{{ item.characterName }}</div>
  121. </div>
  122. </div>
  123. </el-tab-pane>
  124. </el-tabs>
  125. </div>
  126. </el-dialog>
  127. </div>
  128. </template>
  129. <script>
  130. import { getToken } from "@/utils/auth";
  131. import {
  132. getModelListApi,
  133. } from "@/api/chat.js"
  134. import { characterListApi } from "@/api/home.js"
  135. import { addSceneApi, getSceneDetailApi, editSceneApi } from "@/api/create.js"
  136. import { detailApi } from "@/api/detail.js"
  137. import { queryCharactersApi } from "@/api/profile.js"
  138. export default {
  139. data() {
  140. return {
  141. active: 1,
  142. showPopup1: false,
  143. tabActive: '1',
  144. tabActive2: '1',
  145. form: {
  146. // 角色id
  147. characterId: '',
  148. // 场景名称
  149. sceneName: '',
  150. // 场景描述
  151. sceneDescription: '',
  152. // 欢迎语
  153. sceneWelcome: '',
  154. // 模型
  155. modelId: '',
  156. // 背景图
  157. background: '',
  158. //公开非公开
  159. type: 0
  160. },
  161. // 模型列表
  162. modelList: [],
  163. bagImg: '',
  164. // 用户创建的角色列表
  165. userCharacterList: [],
  166. // 热门角色列表
  167. characterList: [],
  168. // 选中的角色对象
  169. character: {},
  170. // 上传图片相关↓
  171. uploadUrl: process.env.VUE_APP_BASE_API + "/appUser/system/app/upload", // 上传的图片服务器地址
  172. headers: {
  173. UserToken: getToken()
  174. },
  175. uploadList: [],
  176. fileSize: 5,
  177. bgActive: 0
  178. }
  179. },
  180. mounted() {
  181. // 查询用户自己创建的角色
  182. this.queryCharacters()
  183. this.getModelList()
  184. this.getCharacterList()
  185. if (this.$route.query.characterId) {
  186. this.getDetail(this.$route.query.characterId)
  187. // this.character = {
  188. // id: this.$route.query.characterId,
  189. // }
  190. this.form.characterId = this.$route.query.characterId
  191. }
  192. if (this.$route.query.sceneId) {
  193. this.getSceneDetai(this.$route.query.sceneId)
  194. }
  195. },
  196. computed: {
  197. nextDisabled() {
  198. return this.form.sceneName == '' || this.form.sceneDescription == '' || this.form.sceneWelcome == '' || this.form.modelId == ''
  199. }
  200. },
  201. methods: {
  202. queryCharacters() {
  203. let params = {
  204. pageSize: 10,
  205. pageNum: 1
  206. }
  207. queryCharactersApi(params).then(res => {
  208. console.log(res, '人物列表');
  209. this.userCharacterList = res.rows
  210. })
  211. },
  212. getCharacterList(query) {
  213. let params = {
  214. pageSize: 10,
  215. pageNum: 1
  216. }
  217. if (query) {
  218. params = { ...params, ...query }
  219. }
  220. characterListApi(params).then(res => {
  221. // console.log(res, '角色列表');
  222. this.characterList = res.rows
  223. })
  224. },
  225. getModelList() {
  226. getModelListApi().then(res => {
  227. console.log(res, '模型列表');
  228. this.modelList = res.data
  229. })
  230. },
  231. // 获取角色详情
  232. getDetail(id) {
  233. detailApi(id).then(res => {
  234. console.log(res, '角色详情');
  235. this.character = res.data
  236. })
  237. },
  238. // 获取场景详情
  239. getSceneDetai(id) {
  240. getSceneDetailApi(id).then(res => {
  241. console.log(res, '场景详情');
  242. this.form = res.data
  243. let backgroundArr = res.data.background.split('/')
  244. this.uploadList.push({ name: backgroundArr[backgroundArr.length - 1], url: res.data.background });
  245. })
  246. },
  247. // 选择角色
  248. selectCharacter(value) {
  249. console.log(value, 'value');
  250. this.character = value
  251. this.form.characterId = value.id
  252. this.showPopup1 = false
  253. },
  254. handleCommand(value) {
  255. this.form.type = value
  256. },
  257. // 下一步
  258. next() {
  259. this.active++
  260. },
  261. submit() {
  262. this.form.background = this.uploadList[this.bgActive].url
  263. if (this.form.sceneId) {
  264. // 修改
  265. editSceneApi(this.form).then(res => {
  266. console.log(res, 'res>>>>>');
  267. this.$message({
  268. type: 'success',
  269. message: '修改成功!'
  270. });
  271. this.$router.push('/profile')
  272. })
  273. } else {
  274. // 新增
  275. addSceneApi(this.form).then(res => {
  276. console.log(res, 'res>>>>>');
  277. this.$message({
  278. type: 'success',
  279. message: '发布成功!'
  280. });
  281. this.$router.push('/profile')
  282. })
  283. }
  284. },
  285. handleClickTab(tab, event) {
  286. },
  287. // 上传背景相关业务
  288. // 上传前校检格式和大小
  289. handleBeforeUpload(file) {
  290. // 校检文件大小
  291. if (this.fileSize) {
  292. const isLt = file.size / 1024 / 1024 < this.fileSize;
  293. if (!isLt) {
  294. this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
  295. return false;
  296. }
  297. }
  298. return true;
  299. },
  300. // 上传成功回调
  301. handleUploadSuccess(res, file) {
  302. if (res.code === 200) {
  303. this.uploadList.push({ name: res.originalFilename, url: res.fileName });
  304. this.$message({
  305. type: 'success',
  306. message: '上传成功!'
  307. });
  308. } else {
  309. this.$message({
  310. type: 'error',
  311. message: res.msg
  312. });
  313. }
  314. },
  315. // 上传结束处理
  316. uploadedSuccessfully() {
  317. console.log(this.uploadList, 'this.uploadList');
  318. // this.$emit("input", this.listToString(this.fileList));
  319. // this.$modal.closeLoading();
  320. },
  321. // 上传失败
  322. handleUploadError() {
  323. this.$modal.msgError("上传图片失败,请重试");
  324. },
  325. }
  326. }
  327. </script>
  328. <style lang="scss" scoped>
  329. .bottom {
  330. height: 80px;
  331. }
  332. .characterName {
  333. overflow: hidden;
  334. text-overflow: ellipsis;
  335. white-space: nowrap;
  336. }
  337. .bgActive {
  338. border: solid 2px var(--bg-color1);
  339. }
  340. </style>
  341. <style scoped>
  342. .radio>>>.el-radio-button {
  343. margin-bottom: 5px;
  344. margin-right: 10px;
  345. /* border-left: solid 1px #DCDFE6; */
  346. }
  347. .radio>>>.el-radio-button__inner {
  348. border-left: solid 1px #DCDFE6;
  349. border-radius: 4px;
  350. }
  351. .radio>>>.el-radio-button__orig-radio:checked+.el-radio-button__inner {
  352. border-left: solid 1px var(--bg-color1);
  353. background-color: var(--bg-color1);
  354. border-color: var(--bg-color1);
  355. -webkit-box-shadow: -1px 0 0 0 var(--bg-color1);
  356. box-shadow: -1px 0 0 0 var(--bg-color1);
  357. }
  358. </style>