myStar.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. <template>
  2. <view class="star-container">
  3. <PageHeader title="" class="PageHeader" v-if="state == 1">
  4. <template slot="center"> </template>
  5. </PageHeader>
  6. <!-- 星灵基因重组仓弹窗 -->
  7. <view class="gender-popup" v-if="state == 0">
  8. <NicknamePopup title="星灵基因重组仓" subtitle="" class="openContentPopUpWindow" ref="openContentPopUpWindow">
  9. <template slot="content">
  10. <uv-textarea v-model="noteContent" maxlength="200" count autoHeight
  11. placeholder="可描述你想要重新赋予Ta的形象、性别、性格、身份、兴趣爱好等(不会展示给其他人,仅你自己知道),示例:有一头波浪状的橙色头发,喜欢运动的阳光男孩..."></uv-textarea>
  12. <view class="btn-box" @tap="confirmGender">创建星灵</view>
  13. </template>
  14. </NicknamePopup>
  15. </view>
  16. <!-- 加载动画区域 -->
  17. <view class="loading-area" v-if="state == 2 || isLoading">
  18. <image src="../../static/me/loadAnimation.gif" mode="widthFix"></image>
  19. </view>
  20. <!-- 角色展示页面 -->
  21. <view class="character-page" v-else-if="state == 1">
  22. <view class="character-container">
  23. <image :src="starImg || 'https://e.zhichao.art/AI_images/b_3_92.png'" mode="widthFix"
  24. class="character-image">
  25. </image>
  26. </view>
  27. <view class="bottom-button" @tap="goToSetProfile"> 设置星灵简介 </view>
  28. </view>
  29. <!-- 表单页面 -->
  30. <view class="form-page" v-else-if="state == 4">
  31. <PageHeader title="设置星灵简介" class="PageHeader">
  32. <template slot="center"> </template>
  33. </PageHeader>
  34. <view class="reserveASeat"></view>
  35. <form @submit="submitForm">
  36. <view class="form-group">
  37. <view class="label">
  38. <text class="required">*</text>
  39. <text>昵称</text>
  40. </view>
  41. <input class="input" v-model="formData.nickname" placeholder="给星灵取个名字"
  42. :class="{ error: showError && !formData.nickname }" />
  43. </view>
  44. <view class="form-group">
  45. <view class="label">性别</view>
  46. <view class="gender-options">
  47. <view class="gender-option" :class="sex == 'male' ? 'selected' : ''"
  48. @tap="selectGender('male')">
  49. <view class="gender-icon male">
  50. <image src="../../static/me/wd_icon_nan.png" mode="aspectFit"></image>
  51. 男性
  52. </view>
  53. </view>
  54. <view class="gender-option" :class="sex == 'female' ? 'selected' : ''"
  55. @tap="selectGender('female')">
  56. <view class="gender-icon female">
  57. <image src="../../static/me/wd_icon_nv.png" mode="aspectFit"></image>
  58. 女性
  59. </view>
  60. </view>
  61. <view class="gender-option" :class="sex == 'other' ? 'selected' : ''"
  62. @tap="selectGender('other')">
  63. <view class="gender-icon other">
  64. <image src="../../static/me/wd_icon_qita.png" mode="aspectFit"></image>
  65. 其它
  66. </view>
  67. </view>
  68. </view>
  69. </view>
  70. <view class="form-group">
  71. <view class="label">
  72. <text class="required">*</text>
  73. <text>人物简介</text>
  74. </view>
  75. <view class="textarea-container">
  76. <textarea class="textarea" v-model="formData.description"
  77. placeholder="填写这个角色的人物介绍,例如性格、身份、背景与历程..." :maxlength="500"
  78. :class="{ error: showError && !formData.description }" autoHeight />
  79. <text class="word-count">{{ formData.description.length }}/500</text>
  80. </view>
  81. </view>
  82. <view class="form-group">
  83. <view class="label">人物标签</view>
  84. <!-- <scroll-view class="tags-scroll" scroll-x="true" show-scrollbar="false">
  85. <view class="tags-container">
  86. <view class="tag" v-for="tag in predefinedTags" :key="tag">
  87. {{ tag }}
  88. </view>
  89. </view>
  90. </scroll-view> -->
  91. <view class="tags-container">
  92. <!-- <view class="tag" :class="{ active: tagSet.has(tag) }" v-for="tag in predefinedTags" :key="tag"
  93. @tap="switchTag(tag)">
  94. {{ tag }}
  95. </view> -->
  96. <uni-data-checkbox mode="tag" multiple v-model="selectTags"
  97. :localdata="predefinedTags"></uni-data-checkbox>
  98. </view>
  99. </view>
  100. </form>
  101. <view class="submit-button" @tap="submitStar"> 确定并提交 </view>
  102. </view>
  103. <!-- 角色信息展示页面 -->
  104. <view class="character-info" v-else-if="state == 5 || state == 6">
  105. <view class="custom-navbar">
  106. <view class="navbar-left" @click="goBack">
  107. <text class="fa fa-angle-left" style="color: #000;"></text>
  108. </view>
  109. <view class="navbar-right" @click="showShare = true">
  110. <text class="fa fa-ellipsis-h"></text>
  111. </view>
  112. </view>
  113. <view class="info-container">
  114. <!-- 角色立绘区域 -->
  115. <view class="character-portrait">
  116. <image :src="starInfo.image" mode="widthFix" class="portrait-image"></image>
  117. </view>
  118. <!-- 角色信息板块 -->
  119. <view class="info-section">
  120. <view class="character-name">
  121. {{ starInfo.nickname }}
  122. <image class="male" v-if="starInfo.sex == 0" src="../../static/me/wd_icon_nan.png"
  123. mode="aspectFit">
  124. </image>
  125. <image class="female" v-else-if="starInfo.sex == 1" src="../../static/me/wd_icon_nv.png"
  126. mode="aspectFit">
  127. </image>
  128. <image class="other" v-else src="../../static/me/wd_icon_qita.png" mode="aspectFit"></image>
  129. </view>
  130. <!-- <view class="section-title">关于Ta</view> -->
  131. <!-- 人物简介 -->
  132. <view class="description-box">
  133. <view class="description-title">
  134. <view>Ta的设定</view>
  135. <view v-if="false" class="edit-button">编辑<text class="fa fa-angle-right"
  136. style="color: #000;"></text></view>
  137. </view>
  138. <view class="description-text"> <text>{{ starInfo.content }}</text> </view>
  139. </view>
  140. <!-- 标签展示 -->
  141. <view class="description-box">
  142. <view class="description-title">
  143. <view>人物标签</view>
  144. </view>
  145. <view class="tags-box">
  146. <view class="tag-item" v-for="tag in starInfo.tags" :key="tag">
  147. {{ tag }}
  148. </view>
  149. </view>
  150. </view>
  151. </view>
  152. </view>
  153. <!-- 底部按钮 -->
  154. <view class="join-button" @tap="handleJoin(1)" v-if="state == 5"> 入驻星球 </view>
  155. <view class="join-button" @tap="handleJoin(0)" v-if="state == 6"> 已驻星球 </view>
  156. </view>
  157. <SharePopup :visible="showShare" :share-url="shareUrl" :share-title="shareTitle" :share-desc="shareDesc"
  158. :share-img="shareImg" @close="showShare = false" />
  159. </view>
  160. </template>
  161. <script>
  162. import tabbarView from "@/components/tabbar/tabbar.vue";
  163. import value from '../../uni_modules/uv-text/components/uv-text/value';
  164. export default {
  165. components: {
  166. tabbarView,
  167. },
  168. data() {
  169. return {
  170. isLoading: false,
  171. selectedGender: null,
  172. tempGender: null,
  173. tabbars: [],
  174. ballColors: [
  175. "#FF6B6B", // 红色
  176. "#4ECDC4", // 青色
  177. "#45B7D1", // 蓝色
  178. "#96CEB4", // 绿色
  179. "#FFEEAD", // 黄色
  180. "#D4A5A5", // 粉色
  181. "#9A8194", // 紫色
  182. "#FF9F1C", // 橙色
  183. ],
  184. showError: false,
  185. formData: {
  186. nickname: "",
  187. sex: "其他",
  188. description: "",
  189. tags: [],
  190. },
  191. predefinedTags: [],
  192. showInfo: false,
  193. starImg: "",
  194. noteContent: "",
  195. starInfo: {},
  196. state: 2, //0 是用户输入星灵基因重组仓的状态 1 是用户已经完成了匹星灵展示页面 2是用户匹配中加载的状态 3是匹配到了待点击进入设置界面 (根据其它字段判断是否失败) 4是用户设置星灵信息的页面 5是用户查看星灵信息的页面 待入驻 6是用户已经已经入驻星球了
  197. sex: "",
  198. selectTags: [],
  199. info: {
  200. "id": 0,
  201. "sso_id": 0,
  202. "image_id": 0,
  203. "image": "",
  204. "nickname": "",
  205. "user_content": "",
  206. "content": "",
  207. "sex_id": 0,
  208. "tags": "",
  209. "status": 0
  210. },
  211. timeoutId: 0,
  212. showShare: false,
  213. shareUrl: "https://your-share-url.com",
  214. shareTitle: "分享标题",
  215. shareDesc: "分享描述",
  216. shareImg: "https://your-share-image.com/image.jpg",
  217. maxRetries: 10, // 最大重试次数
  218. retryCount: 0, // 当前重试次数
  219. pollingInterval: 30000, // 轮询间隔时间(毫秒)
  220. };
  221. },
  222. onLoad() {
  223. this.aIpipeiGetinfo("get");
  224. },
  225. methods: {
  226. // 返回上一页
  227. goBack() {
  228. uni.navigateBack({
  229. delta: 1
  230. });
  231. },
  232. confirmGender() {
  233. // this.selectedGender = this.noteContent;
  234. this.isLoading = true;
  235. this.closeContentPopUpWindow();
  236. this.state = 2
  237. this.apiPeiStar();
  238. },
  239. goToSetProfile() {
  240. this.state = 4;
  241. this.formData.nickname = ''
  242. this.formData.sex = ''
  243. this.formData.tags = ''
  244. },
  245. toggleTag(tag) {
  246. const index = this.formData.tags.indexOf(tag);
  247. if (index > -1) {
  248. this.formData.tags.splice(index, 1);
  249. } else {
  250. this.formData.tags.push(tag);
  251. }
  252. },
  253. handleJoin() {
  254. console.log("入驻星球");
  255. // 处理入驻逻辑
  256. uni.request({
  257. url: this.$apiHost + "/AIpipei/gogogo",
  258. data: {
  259. uuid: getApp().globalData.uuid,
  260. },
  261. header: {
  262. "content-type": "application/x-www-form-urlencoded",
  263. sign: getApp().globalData.headerSign,
  264. },
  265. method: "POST",
  266. // 设置60秒超时
  267. timeout: 60000,
  268. success: (res) => {
  269. this.aIpipeiGetinfo({ polling: false })
  270. },
  271. fail: (err) => {
  272. },
  273. complete: () => {
  274. },
  275. });
  276. },
  277. // 提交用户 开始创建的命令
  278. apiPeiStar() {
  279. if (!this.noteContent) {
  280. uni.showToast({
  281. title: "请输入匹配条件",
  282. icon: "none",
  283. });
  284. return;
  285. }
  286. uni.request({
  287. url: this.$apiHost + "/AIpipei/start",
  288. data: {
  289. uuid: getApp().globalData.uuid,
  290. content: this.noteContent,
  291. },
  292. header: {
  293. "content-type": "application/x-www-form-urlencoded",
  294. sign: getApp().globalData.headerSign,
  295. },
  296. // 设置60秒超时
  297. timeout: 60000,
  298. method: 'POST',
  299. success: (res) => {
  300. setTimeout(() => {
  301. uni.showToast({
  302. title: res.data.str,
  303. icon: "none",
  304. duration: 2000,
  305. });
  306. if (res.data.str == "内容不能为空") {
  307. this.openContentPopUpWindow();
  308. this.state = 0
  309. this.apiPeiStar();
  310. }
  311. if (res.data.str == "开始匹配") {
  312. this.aIpipeiGetinfo({ polling: true })
  313. }
  314. }, 3000);
  315. },
  316. fail: (err) => {
  317. console.error("请求失败:", err);
  318. // 显示错误提示
  319. uni.showToast({
  320. title: "网络请求失败,请重试",
  321. icon: "none",
  322. duration: 2000,
  323. });
  324. // 重置加载状态
  325. },
  326. complete: () => {
  327. },
  328. });
  329. },
  330. // 查询Ai匹配信息
  331. aIpipeiGetinfo({ polling }) {
  332. // 清除之前的定时器
  333. if (this.timeoutId) {
  334. clearTimeout(this.timeoutId);
  335. this.timeoutId = 0;
  336. }
  337. // 检查是否超过最大重试次数
  338. if (polling && this.retryCount >= this.maxRetries) {
  339. uni.showToast({
  340. title: '匹配超时,请重新尝试',
  341. icon: 'none',
  342. duration: 2000
  343. });
  344. this.retryCount = 0;
  345. this.state = 0;
  346. return;
  347. }
  348. // 发起请求
  349. uni.request({
  350. url: this.$apiHost + "/AIpipei/getinfo",
  351. data: {
  352. uuid: getApp().globalData.uuid,
  353. },
  354. header: {
  355. "content-type": "application/json",
  356. sign: getApp().globalData.headerSign,
  357. },
  358. timeout: 60000,
  359. success: (res) => {
  360. console.log("查询到生成信息", res.data);
  361. // 重置重试计数
  362. this.retryCount = 0;
  363. if (res && res.data && res.data.info) {
  364. if (res.data.info && res.data.info.tags != "") {
  365. res.data.info.tags = res.data.info.tags.split(",");
  366. this.predefinedTags = res.data.info.tags.map(tag => { return { text: tag, value: tag } })
  367. console.log(666, res);
  368. } else {
  369. res.data.info.tags = []
  370. }
  371. // 实现状态的判断
  372. // 更改状态为 用户还未匹配过 待输入匹配内容
  373. if (res.data.str == "没有匹配过" && res.data.info) {
  374. this.state = 0
  375. setTimeout(() => {
  376. this.openContentPopUpWindow();
  377. }, 300);
  378. }
  379. // 更改状态为 用户还匹配成功时 待点击设置心灵简介
  380. if (res.data.info.image && res.data.info.status == 2) {
  381. this.state = 1
  382. this.openContentPopUpWindow();
  383. }
  384. if (res.data.info.image && res.data.info.status == 1) {
  385. this.state = 6
  386. }
  387. if (res.data.info.image && res.data.info.status == 3) {
  388. this.state = 5
  389. }
  390. this.starInfo = res.data.info;
  391. if (res.data.info.content) {
  392. this.formData.description = res.data.info.content;
  393. }
  394. }
  395. },
  396. fail: (err) => {
  397. console.error("请求失败:", err);
  398. // 增加重试计数
  399. this.retryCount++;
  400. // 显示错误提示
  401. uni.showToast({
  402. title: `网络请求失败,第${this.retryCount}次重试`,
  403. icon: "none",
  404. duration: 2000,
  405. });
  406. // 如果是网络超时,自动重试
  407. if (err.errMsg.includes("timeout")) {
  408. setTimeout(() => {
  409. console.log("请求超时,正在重试...");
  410. this.aIpipeiGetinfo({ polling: true });
  411. }, 6000);
  412. }
  413. },
  414. complete: () => {
  415. // 如果需要继续轮询,设置下一次请求
  416. if (polling) {
  417. this.timeoutId = setTimeout(() => {
  418. this.aIpipeiGetinfo({ polling: true });
  419. }, this.pollingInterval);
  420. }
  421. }
  422. });
  423. },
  424. // 保存表单信息
  425. submitStar() {
  426. this.formData.tags = this.selectTags.join(",");
  427. let that = this;
  428. uni.showLoading({
  429. mask: true,
  430. });
  431. if (this.formData.sex) {
  432. this.formData.sex = this.genderScreeningId(this.formData.sex);
  433. }
  434. console.log({
  435. uuid: getApp().globalData.uuid,
  436. sex: this.formData.sex,
  437. name: this.formData.nickname,
  438. content: this.formData.description,
  439. tags: this.formData.tags,
  440. }, 2000);
  441. uni.request({
  442. url: this.$apiHost + "/AIpipei/save",
  443. data: {
  444. uuid: getApp().globalData.uuid,
  445. sex: this.formData.sex,
  446. nickname: this.formData.nickname,
  447. content: this.formData.description,
  448. tags: this.formData.tags,
  449. },
  450. header: {
  451. "content-type": "application/x-www-form-urlencoded",
  452. sign: getApp().globalData.headerSign,
  453. },
  454. method: 'POST',
  455. // 设置60秒超时
  456. timeout: 10000,
  457. success: (res) => {
  458. console.log("res.data", res.data);
  459. uni.showToast({
  460. title: res.data.str,
  461. icon: "none",
  462. duration: 2000,
  463. });
  464. if (res.data.success === "yes") {
  465. setTimeout(() => {
  466. that.aIpipeiGetinfo({ polling: false });
  467. }, 300);
  468. }
  469. },
  470. fail: (err) => {
  471. console.error("请求失败:", err);
  472. // 显示错误提示
  473. uni.showToast({
  474. title: "网络请求失败,请重试",
  475. icon: "none",
  476. duration: 2000,
  477. });
  478. },
  479. complete: () => {
  480. uni.hideLoading();
  481. },
  482. });
  483. },
  484. selectGender(option) {
  485. this.formData.sex = option;
  486. this.sex = option;
  487. },
  488. openContentPopUpWindow() {
  489. if (this.$refs.openContentPopUpWindow) {
  490. this.$refs.openContentPopUpWindow.open();
  491. }
  492. },
  493. closeContentPopUpWindow() {
  494. if (this.$refs.openContentPopUpWindow) {
  495. this.$refs.openContentPopUpWindow.close();
  496. }
  497. },
  498. genderScreening(str) {
  499. switch (str) {
  500. case '0':
  501. return '男'
  502. case '1':
  503. return '女'
  504. case '2':
  505. return '其它'
  506. }
  507. },
  508. genderScreeningId(str) {
  509. switch (str) {
  510. case 'male':
  511. return 0
  512. case 'female':
  513. return 1
  514. case 'other':
  515. return 2
  516. }
  517. }
  518. },
  519. };
  520. </script>
  521. <style lang="scss">
  522. @import "./myStar.scss";
  523. .openContentPopUpWindow {
  524. ::v-deep.uv-textarea {
  525. width: 694rpx !important;
  526. border-radius: 20rpx !important;
  527. border: 1rpx solid #000000 !important;
  528. margin: 0 auto;
  529. margin-bottom: 44rpx;
  530. min-height: 300rpx;
  531. padding-bottom: 40rpx;
  532. .uv-textarea__field {
  533. min-height: 200rpx !important;
  534. font-weight: 400;
  535. font-size: 28rpx;
  536. color: #1f1f1f;
  537. }
  538. }
  539. }
  540. .textarea-container {
  541. .textarea {
  542. background: #f2f6f2 !important;
  543. min-height: 100rpx;
  544. }
  545. }
  546. .tags-container {
  547. ::v-deep.checklist-box {
  548. border-radius: 16rpx !important;
  549. border: 2rpx solid #1f1f1f !important;
  550. background-color: #fff !important;
  551. display: flex;
  552. align-items: center;
  553. justify-content: center;
  554. .checklist-text {
  555. font-size: 28rpx;
  556. color: #1f1f1f;
  557. font-family: "PingFang SC-Bold" !important;
  558. }
  559. &.is-checked {
  560. background: #f7ffea !important;
  561. border-color: #7ebc00 !important;
  562. .checklist-text {
  563. color: #1f1f1f !important;
  564. }
  565. }
  566. }
  567. }
  568. .star-container {
  569. /* 自定义导航栏样式 */
  570. .custom-navbar {
  571. display: flex;
  572. flex-direction: row;
  573. align-items: center;
  574. justify-content: space-between;
  575. width: 100%;
  576. height: 90rpx;
  577. padding: 0 20rpx;
  578. padding-top: var(--status-bar-height);
  579. background-color: transparent;
  580. position: fixed;
  581. top: 0;
  582. left: 0;
  583. z-index: 100;
  584. background: transparent;
  585. &::before {
  586. content: '';
  587. position: absolute;
  588. top: 0;
  589. left: 0;
  590. width: 100%;
  591. height: var(--status-bar-height);
  592. background-color: #fff;
  593. z-index: -1;
  594. }
  595. .navbar-left {
  596. width: 80rpx;
  597. height: 80rpx;
  598. display: flex;
  599. align-items: center;
  600. justify-content: center;
  601. .fa-angle-left {
  602. font-size: 48rpx;
  603. color: #333;
  604. }
  605. }
  606. .navbar-right {
  607. width: 80rpx;
  608. height: 80rpx;
  609. display: flex;
  610. justify-content: center;
  611. align-items: center;
  612. .fa-ellipsis-h {
  613. font-size: 36rpx;
  614. color: #333;
  615. }
  616. }
  617. }
  618. }
  619. </style>