result.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  1. <template>
  2. <div class="resout-container AI-Design-container">
  3. <div class="header">
  4. <van-nav-bar title="生成结果" left-arrow @click-left="returnPage" @click-right="toHome">
  5. <template #right>
  6. <van-icon name="wap-home-o" color="#333" size="26" />
  7. </template>
  8. </van-nav-bar>
  9. </div>
  10. <div class="container">
  11. <!-- 房屋效果图 -->
  12. <div class="image-container">
  13. <div v-if="imageUrl" class="comparisonWrapper">
  14. <div class="imgbox-t">
  15. <div>
  16. <p class="tit">原图</p>
  17. </div>
  18. <div>
  19. <p class="tit">AI效果图</p>
  20. </div>
  21. </div>
  22. <div class="imgbox-b">
  23. <div class="image-wrapper" @click="imgClick(UserFilePathUrl)">
  24. <img :src="UserFilePathUrl" alt="房屋效果图" class="house-image" />
  25. <div></div>
  26. </div>
  27. <div class="image-wrapper" @click="imgClick(imageUrl)">
  28. <img :src="imageUrlSmall" alt="房屋效果图" class="house-image" />
  29. <div></div>
  30. </div>
  31. </div>
  32. </div>
  33. <div v-else class="loading-state">
  34. <img v-if="StateCode != 3 && StateCode != 4" src="@/assets/AIDesign/loding.gif" style="width: 100px;">
  35. <p class="loading-text">{{ type || StateCode ? (StateCode == 3 ? '生成失败' : (StateCode == 4 ?
  36. '生成失败,\n' + StateInfo :
  37. '正在为您加速生成中,先喝杯茶吧~\n退出本页不影响生成')) : '加载中...' }}</p>
  38. </div>
  39. </div>
  40. <!-- 保存按钮 -->
  41. <!-- <button class="save-button" :disabled="!imageUrl" :class="{ 'save-button-disabled': !imageUrl }"
  42. @click="saveImageToAlbum">保存图片</button> -->
  43. <!-- 功能按钮组 -->
  44. <div class="button-group">
  45. <!--<button class="action-button" :disabled="!imageUrl" :class="{ 'save-button-disabled': !imageUrl }"-->
  46. <button class="action-button" :disabled="regenerateDisable"
  47. :class="regenerateDisable == true ? 'save-button-disabled' : ''" @click="regenerate">
  48. <van-icon class="icon" name="replay" />
  49. <span class="text">重新生成</span>
  50. </button>
  51. <button class="action-button" @click="viewHistory">
  52. <van-icon class="icon" name="clock-o" />
  53. <span class="text">历史生图</span>
  54. <span v-if="!readState" class="badge-dot"></span>
  55. </button>
  56. <!-- 服务商随身邦和部分经销商展示转人工 -->
  57. <button class="action-button" @click="manualDesign" v-if="showArtificial" :disabled="projectDisableFlag"
  58. :class="projectDisableFlag == true ? 'save-button-disabled' : ''">
  59. <van-icon class="icon" name="user-o" />
  60. <span class="text">转人工设计(原别墅之星小程序)</span>
  61. </button>
  62. </div>
  63. </div>
  64. </div>
  65. </template>
  66. <script lang="ts">
  67. import { ImagePreview, Dialog } from 'vant';
  68. import { Component, Vue } from "vue-property-decorator";
  69. import { GetEntity, GetReadState, UpdateReadState, insideGetEntity, insideGetReadState, insideUpdateReadState, GetProjectlist, GetDictList } from "@/api/indexAI";
  70. import { getWecomType, toLBHome ,getWxconfig } from '@/utils/index';
  71. import axios from "axios";
  72. declare let wx: any;
  73. @Component
  74. export default class extends Vue {
  75. private readState = true;
  76. // 数据属性
  77. // private imageUrl = require('@/assets/AIDesign/house-image.jpg'); // 替换为实际图片路径
  78. private imageUrl = ''; // 替换为实际图片路径
  79. private imageUrlSmall = '';
  80. private UserFilePathUrl = '';//用户原图
  81. private pollingTimer: number | null = null; // 轮询定时器引用
  82. private timer = null;
  83. private StateCode = null;
  84. private projectId = null;
  85. private regenerateDisable = true;
  86. private StateInfo = '';
  87. private type = '';
  88. private wallType = '';
  89. private F_OutsideType = null;
  90. // 处理内外墙api
  91. private GetEntityToApi = {
  92. outside: GetEntity,
  93. inside: insideGetEntity
  94. };
  95. private GetReadStateToApi = {
  96. outside: GetReadState,
  97. inside: insideGetReadState
  98. };
  99. private UpdateReadStateToApi = {
  100. outside: UpdateReadState,
  101. inside: insideUpdateReadState
  102. };
  103. private designPageApi = {
  104. outside: '/AIDesign/design',
  105. inside: '/AIDesign/insideDesign'
  106. };
  107. private designTimer = {
  108. outside: 50000,
  109. inside: 20000
  110. };
  111. private showArtificial = false;
  112. private agentFrom = window.localStorage.getItem('agentFromAI');
  113. private outsideDesignCount = 0;//项目下设计已用数量-接口获取
  114. private isNeedProjectFlag = true;//是否需要项目-接口获取
  115. private projectDisableFlag = true;//转人工不可点击
  116. private serviceCodeArray = [];
  117. created() {
  118. if (this.$route.query.WecomType) {
  119. sessionStorage.setItem("WecomType", this.$route.query.WecomType);
  120. } else {
  121. sessionStorage.removeItem("WecomType");
  122. }
  123. getWxconfig();
  124. // this.getServiceCode();
  125. }
  126. activated() {
  127. this.initialize();
  128. this.wallType = this.$route.query.wallType || 'outside';
  129. const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
  130. const customerCode = userInfo && userInfo.sysUserExt ? userInfo.sysUserExt.customerCode : '';
  131. const salesLevel = userInfo && userInfo.sysUserExt ? userInfo.sysUserExt.salesLevel : '';
  132. // 外墙-服务商随身邦
  133. if (this.agentFrom === 'stoneLikePaint' && this.wallType === 'outside') {
  134. this.showArtificial = true;
  135. this.getIsNeedProjectFlag();//获取是否关联了项目
  136. }
  137. // 和部分经销商展示转人工
  138. // else if(customerCode && (salesLevel === 'customer_level' || salesLevel === 'reseller_level')){
  139. // this.showArtificial = true;
  140. // }
  141. // 从经销商随身邦进入的,显示转人工按钮,没有次数限制
  142. if (this.agentFrom === 'ssb' && this.wallType === 'outside') {
  143. this.showArtificial = true;
  144. this.projectDisableFlag = false;
  145. }
  146. this.type = window.localStorage.getItem('type');
  147. this.GetReadStateFn();
  148. this.GetEntityDataFirst()
  149. }
  150. // 离开页面时清除定时器
  151. deactivated() {
  152. // console.log('resule页面销毁1')
  153. window.localStorage.removeItem('type');
  154. clearInterval(this.pollingTimer);
  155. clearTimeout(this.timer);
  156. this.pollingTimer = null;
  157. this.timer = null;
  158. }
  159. initialize() {
  160. clearInterval(this.pollingTimer);
  161. clearTimeout(this.timer);
  162. this.readState = true;
  163. this.imageUrl = ''; // 替换为实际图片路径
  164. this.imageUrlSmall = '';
  165. this.UserFilePathUrl = '';//用户原图
  166. this.pollingTimer = null; // 轮询定时器引用
  167. this.timer = null;
  168. this.StateCode = null;
  169. this.projectId = null;
  170. this.regenerateDisable = true;
  171. this.StateInfo = '';
  172. this.type = '';
  173. this.wallType = '';
  174. this.outsideDesignCount = 0;
  175. this.isNeedProjectFlag = true;
  176. this.projectDisableFlag = true;
  177. this.showArtificial = false;
  178. }
  179. // 是否关联了项目
  180. getIsNeedProjectFlag() {
  181. let that = this;
  182. const formData = new FormData();
  183. // const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
  184. // let roleIdArray = [];
  185. // if (userInfo.roles.length > 0) {
  186. // userInfo.roles.forEach(item => {
  187. // roleIdArray.push(item.roleId);
  188. // })
  189. // }
  190. // formData.append('roleIds', roleIdArray.join(','));
  191. // formData.append('WXuserid', userInfo.loginName);
  192. formData.append('baseType', 0);//0外墙--这里只用查询外墙
  193. const agentFrom = window.localStorage.getItem('agentFromAI');
  194. const wecomType = getWecomType(agentFrom);
  195. formData.append('wecomType', 5);
  196. GetDictList(formData).then(response => {
  197. if (response.StatusCode == 200) {
  198. if (that.serviceCodeArray.length == 0) {
  199. that.isNeedProjectFlag = false; //是否需要项目
  200. } else {
  201. that.isNeedProjectFlag = response.Data.isNeedProject;
  202. }
  203. if (that.isNeedProjectFlag) {
  204. that.updateCheckedProjectLastNum();
  205. } else {
  206. that.projectDisableFlag = false;//没有项目就可点击
  207. }
  208. }
  209. })
  210. }
  211. //获取选中关联项目已用数量
  212. private updateCheckedProjectLastNum() {
  213. let that = this;
  214. const formData = new FormData();
  215. // formData.append('ServiceCode', that.serviceCodeArray.join(','));
  216. formData.append('projectid', this.$route.query.projectid);
  217. GetProjectlist(formData).then(response => {
  218. if (response.StatusCode == 200 && response.Data && response.Data[0]) {
  219. that.outsideDesignCount = response.Data[0].DesignCount || 0;
  220. // 单一关联项目下设计了1~3套,转人工设计均置灰不可点。从第4套起等转人工设计均可点
  221. if (that.outsideDesignCount > 3) {
  222. that.projectDisableFlag = false;//有项目判断>3可点击
  223. }
  224. }
  225. });
  226. }
  227. private getServiceCode() {
  228. let that = this;
  229. const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
  230. let serviceCodeArray = [];
  231. if (userInfo.loginTypeList.length > 0) {
  232. userInfo.loginTypeList.forEach(item => {
  233. if (item.shopType == 'stoneLikePaint') {
  234. item.shopList.forEach(childItem => {
  235. serviceCodeArray.push(childItem.shop_code);
  236. })
  237. }
  238. })
  239. }
  240. that.serviceCodeArray = serviceCodeArray;
  241. }
  242. GetReadStateFn() {
  243. const formData = new FormData();
  244. // const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
  245. // formData.append('WXuserid', userInfo.loginName);
  246. if (this.wallType === 'outside') {
  247. const outsideType_val = this.F_OutsideType || 0;
  248. formData.append('outsideType', outsideType_val);
  249. }
  250. this.GetReadStateToApi[this.wallType](formData).then(response => {
  251. if (response.StatusCode == 200) {
  252. this.readState = response.Data.readState;
  253. }
  254. });
  255. }
  256. UpdateReadStateFn() {
  257. const formData = new FormData();
  258. // const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
  259. // formData.append('WXuserid', userInfo.loginName);
  260. const outsideType_val = this.F_OutsideType || 0;
  261. formData.append('outsideType', outsideType_val);
  262. this.UpdateReadStateToApi[this.wallType](formData).then(response => { });
  263. }
  264. returnPage() {
  265. this.$router.push({ path: this.designPageApi[this.wallType] });
  266. // this.$router.back();
  267. }
  268. toHome() {
  269. toLBHome()
  270. }
  271. imgClick(url) {
  272. ImagePreview([url]);
  273. }
  274. private startPolling(): void {
  275. // 立即执行一次检查
  276. this.GetEntityData();
  277. // 设置定时器每秒执行一次检查
  278. this.pollingTimer = window.setInterval(() => {
  279. if (!this.imageUrl) {
  280. if (this.StateCode == 3 || this.StateCode == 4) {
  281. clearInterval(this.pollingTimer);
  282. this.pollingTimer = null;
  283. } else {
  284. this.GetEntityData();
  285. }
  286. } else {
  287. // 当 imageUrl 有值时,清除定时器
  288. if (this.pollingTimer) {
  289. clearInterval(this.pollingTimer);
  290. this.pollingTimer = null;
  291. }
  292. }
  293. }, 3000);
  294. }
  295. //进入页面首次调用获取状态
  296. GetEntityDataFirst() {
  297. let that = this;
  298. const F_ID = this.$route.query.F_id || "";
  299. const formData = new FormData();
  300. formData.append('F_id', F_ID);
  301. this.GetEntityToApi[this.wallType](formData).then(response => {
  302. // console.log(response);
  303. if (response.StatusCode == 200) {
  304. if (response.Data == null) {
  305. that.regenerateDisable = true;
  306. if (that.timer) {
  307. clearTimeout(that.timer);
  308. that.timer = null;
  309. }
  310. that.timer = setTimeout(() => {
  311. that.startPolling();
  312. }, that.designTimer[that.wallType]);
  313. } else {
  314. this.StateCode = response.Data.StateCode;
  315. this.StateInfo = response.Data.Description;
  316. this.projectId = response.Data.ProjectID;
  317. this.F_OutsideType = response.Data.F_OutsideType;
  318. if (response.Data.F_ResultFilePath || response.Data.F_ResultlargeFilePath) {
  319. const high_Definition_img = response.Data.F_ResultFilePath || response.Data.F_ResultlargeFilePath || response.Data.F_ResultSmallFilePath;
  320. that.imageUrl = response.Data.BaseUrl + high_Definition_img;
  321. that.imageUrlSmall = response.Data.BaseUrl + response.Data.F_ResultSmallFilePath;
  322. that.UserFilePathUrl = response.Data.BaseUrl + response.Data.F_UserFilePath;
  323. }
  324. if (response.Data.StateCode == 1) {
  325. let createTime = new Date(response.Data.CreateDate);
  326. let currentTime = new Date().getTime();
  327. if (currentTime - createTime > that.designTimer[that.wallType]) {
  328. that.startPolling();
  329. } else {
  330. if (that.timer) {
  331. clearTimeout(that.timer);
  332. that.timer = null;
  333. }
  334. that.timer = setTimeout(() => {
  335. that.startPolling();
  336. }, currentTime - createTime);
  337. }
  338. } else {
  339. if (response.Data.StateCode == 2 || response.Data.StateCode == 3 || response.Data.StateCode == 4) {
  340. that.regenerateDisable = false;
  341. } else {
  342. that.regenerateDisable = true;
  343. }
  344. }
  345. }
  346. } else {
  347. this.$toast.fail(response.Info);
  348. }
  349. })
  350. }
  351. GetEntityData() {
  352. let that = this;
  353. const F_ID = this.$route.query.F_id || "";
  354. const formData = new FormData();
  355. formData.append('F_id', F_ID);
  356. this.GetEntityToApi[this.wallType](formData).then(response => {
  357. // console.log(response);
  358. if (response.StatusCode == 200) {
  359. if (response.Data) {
  360. this.StateCode = response.Data.StateCode;
  361. this.StateInfo = response.Data.Description;
  362. this.F_OutsideType = response.Data.F_OutsideType;
  363. if (response.Data.F_ResultFilePath || response.Data.F_ResultlargeFilePath) {
  364. const high_Definition_img = response.Data.F_ResultFilePath || response.Data.F_ResultlargeFilePath || response.Data.F_ResultlargeFilePath;
  365. that.imageUrl = response.Data.BaseUrl + high_Definition_img;
  366. that.imageUrlSmall = response.Data.BaseUrl + response.Data.F_ResultSmallFilePath;
  367. that.UserFilePathUrl = response.Data.BaseUrl + response.Data.F_UserFilePath;
  368. }
  369. if (response.Data.StateCode == 2 || response.Data.StateCode == 3 || response.Data.StateCode == 4) {
  370. that.regenerateDisable = false;
  371. } else {
  372. that.regenerateDisable = true;
  373. }
  374. } else {
  375. that.regenerateDisable = true;
  376. }
  377. } else {
  378. this.$toast.fail(response.Info);
  379. }
  380. })
  381. }
  382. // 点击保存图片按钮
  383. saveImageToAlbum() {
  384. const that = this;
  385. wx.ready(function () {
  386. // 1. 检查用户是否授权保存图片到相册
  387. wx.getSetting({
  388. success: (res) => {
  389. // 2. 如果未授权,请求授权
  390. if (!res.authSetting['scope.writePhotosAlbum']) {
  391. wx.authorize({
  392. scope: 'scope.writePhotosAlbum',
  393. success: () => {
  394. // 授权成功,执行保存逻辑
  395. that.downloadAndSaveImage();
  396. },
  397. fail: () => {
  398. // 用户拒绝授权,引导手动开启
  399. wx.showModal({
  400. title: '授权提示',
  401. content: '需要你的授权才能保存图片到相册,请在设置中开启授权',
  402. confirmText: '去设置',
  403. success: (modalRes) => {
  404. if (modalRes.confirm) {
  405. // 打开设置页面
  406. wx.openSetting({
  407. success: (settingRes) => {
  408. if (settingRes.authSetting['scope.writePhotosAlbum']) {
  409. that.downloadAndSaveImage();
  410. } else {
  411. that.$toast.fail('未授权,无法保存图片');
  412. }
  413. }
  414. });
  415. }
  416. }
  417. });
  418. }
  419. });
  420. } else {
  421. // 已授权,直接执行保存逻辑
  422. that.downloadAndSaveImage();
  423. }
  424. },
  425. fail: (err) => {
  426. console.error('获取设置失败', err);
  427. that.$toast.fail('获取授权状态失败,请重试');
  428. }
  429. });
  430. });
  431. }
  432. // 下载图片并保存到相册
  433. downloadAndSaveImage() {
  434. const that = this;
  435. const imageUrl = this.imageUrl;
  436. wx.ready(function () {
  437. // 1. 下载OSS图片到本地临时路径
  438. wx.downloadFile({
  439. url: imageUrl,
  440. success: (downloadRes) => {
  441. // 检查下载是否成功(临时文件路径存在)
  442. if (downloadRes.statusCode === 200 && downloadRes.tempFilePath) {
  443. // 2. 保存图片到相册
  444. wx.saveImageToPhotosAlbum({
  445. filePath: downloadRes.tempFilePath,
  446. success: () => {
  447. // 提示用户
  448. that.$toast.success('图片已成功保存到相册');
  449. },
  450. fail: (saveErr) => {
  451. console.error('保存图片失败', saveErr);
  452. that.$toast.fail('保存失败,请重试');
  453. }
  454. });
  455. } else {
  456. console.error('图片下载失败', downloadRes);
  457. that.$toast.fail('图片下载失败,请检查URL');
  458. }
  459. },
  460. fail: (downloadErr) => {
  461. console.error('下载请求失败', downloadErr);
  462. that.$toast.fail('下载图片失败,请重试');
  463. }
  464. });
  465. });
  466. }
  467. // 方法
  468. private saveImage(): void {
  469. if (!this.imageUrl) {
  470. this.$toast.fail('图片尚未生成完成');
  471. return;
  472. }
  473. // // 创建一个临时的下载链接
  474. // const link = document.createElement('a');
  475. // link.href = this.imageUrl;
  476. // link.download = `ai-design-${new Date().getTime()}.png`;
  477. // link.style.display = 'none';
  478. // // 将链接添加到页面并触发点击事件
  479. // document.body.appendChild(link);
  480. // link.click();
  481. // // 清理DOM元素
  482. // document.body.removeChild(link);
  483. fetch(this.imageUrl).then(response => response.blob()).then(blob => {
  484. const blobUrl = URL.createObjectURL(blob);
  485. // 创建一个临时的下载链接
  486. const link = document.createElement('a');
  487. link.href = blobUrl;
  488. link.download = `ai-design-${new Date().getTime()}.png`;
  489. link.style.display = 'none';
  490. // 将链接添加到页面并触发点击事件
  491. document.body.appendChild(link);
  492. link.click();
  493. // 清理DOM元素
  494. document.body.removeChild(link);
  495. // 释放Blob URL
  496. URL.revokeObjectURL(blobUrl);
  497. // this.$toast.success('图片保存成功');
  498. }).catch(error => {
  499. console.error('图片下载失败:', error);
  500. this.$toast.fail('图片下载失败');
  501. });
  502. }
  503. private regenerate(): void {
  504. // console.log('重新生成');
  505. let that = this;
  506. // 实现重新生成逻辑
  507. const F_ID = this.$route.query.F_id || "";
  508. this.$router.push({
  509. path: this.designPageApi[this.wallType],
  510. query: {
  511. F_id: F_ID,
  512. projectId: that.projectId
  513. }
  514. });
  515. }
  516. private viewHistory(): void {
  517. this.UpdateReadStateFn();
  518. // console.log('查看历史生成');
  519. // 实现查看历史生成逻辑
  520. this.$router.push({ path: '/AIDesign/history', query: { wallType: this.wallType } });
  521. }
  522. private manualDesign(): void {
  523. let that = this;
  524. // debugger
  525. // console.log('转人工设计');
  526. // 实现转人工设计逻辑
  527. // that.$magnetlogadd.setLog('AI外墙设计-转人工设计', function () {
  528. that.toXiaoChengxu(`${process.env.VUE_APP_BASE_DISID6}`);
  529. // })
  530. }
  531. }
  532. </script>
  533. <style scoped lang="scss">
  534. .resout-container {
  535. background-color: #f8f9fa;
  536. min-height: 100vh;
  537. flex-direction: column;
  538. }
  539. .header {
  540. /*height: 50px;*/
  541. /*line-height: 50px;*/
  542. /*text-align: center;*/
  543. /*background: #fff;*/
  544. border-bottom: 1px solid #f8f8f8;
  545. .van-nav-bar__title {
  546. font-size: 20px;
  547. color: #333;
  548. }
  549. .van-icon {
  550. font-size: 20px;
  551. color: #333 !important;
  552. }
  553. }
  554. .container {
  555. padding: 0 20px;
  556. }
  557. .image-container {
  558. position: relative;
  559. width: 100%;
  560. border-radius: 10px;
  561. overflow: hidden;
  562. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  563. margin-top: 20px;
  564. .comparisonWrapper {
  565. display: flex;
  566. flex-direction: column;
  567. padding: 35px 6px;
  568. .imgbox-t {
  569. display: flex;
  570. justify-content: space-between;
  571. align-items: center;
  572. >div {
  573. .tit {
  574. width: fit-content;
  575. padding: 0 5px;
  576. height: 17px;
  577. line-height: 17px;
  578. border-radius: 32px;
  579. font-size: 10px;
  580. }
  581. }
  582. >div:first-child {
  583. width: 36%;
  584. .tit {
  585. background: rgba(204, 204, 204, 1);
  586. }
  587. }
  588. >div:last-child {
  589. width: 58%;
  590. .tit {
  591. color: #fff;
  592. background: rgba(244, 155, 125, 1);
  593. }
  594. }
  595. }
  596. .imgbox-b {
  597. display: flex;
  598. justify-content: space-between;
  599. align-items: center;
  600. >div:first-child {
  601. width: 36%;
  602. }
  603. >div:last-child {
  604. width: 58%;
  605. }
  606. .image-wrapper {
  607. position: relative;
  608. width: 100%;
  609. margin: 5px 0;
  610. display: flex;
  611. align-items: center;
  612. justify-content: flex-start;
  613. .house-image {
  614. border-radius: 10px;
  615. display: block;
  616. width: 100%;
  617. height: auto;
  618. }
  619. .house-image+div {
  620. position: absolute;
  621. top: 0;
  622. left: 0;
  623. width: 100%;
  624. height: 100%;
  625. background: transparent;
  626. z-index: 2;
  627. }
  628. }
  629. }
  630. }
  631. }
  632. .loading-state {
  633. position: relative;
  634. width: 100%;
  635. height: 300px;
  636. display: flex;
  637. flex-direction: column;
  638. align-items: center;
  639. justify-content: center;
  640. color: #666;
  641. text-align: center;
  642. // background-color: rgba(0, 0, 0, 0.7);
  643. background-image: url('../../assets/AIDesign/bg.png');
  644. background-size: 100% 100%;
  645. /* 添加半透明黑色背景 */
  646. // border-radius: 16px;
  647. /* 圆角边框 */
  648. }
  649. .loading-text {
  650. margin-top: 10px;
  651. font-size: 14px;
  652. color: white;
  653. white-space: pre-line
  654. /* 文字颜色改为白色以提高对比度 */
  655. }
  656. .address-info {
  657. position: absolute;
  658. bottom: 10px;
  659. left: 10px;
  660. right: 10px;
  661. background-color: rgba(0, 0, 0, 0.5);
  662. color: white;
  663. padding: 8px;
  664. border-radius: 8px;
  665. font-size: 14px;
  666. text-align: center;
  667. }
  668. .project-name {
  669. margin: 0;
  670. font-weight: bold;
  671. }
  672. .address {
  673. margin: 4px 0 0;
  674. font-size: 12px;
  675. }
  676. .save-button {
  677. width: 100%;
  678. padding: 16px;
  679. margin-top: 20px;
  680. background-color: #E96337;
  681. color: white;
  682. border: none;
  683. border-radius: 12px;
  684. font-size: 16px;
  685. font-weight: bold;
  686. cursor: pointer;
  687. transition: background-color 0.3s ease;
  688. }
  689. .save-button-disabled {
  690. background-color: #cccccc !important;
  691. cursor: not-allowed !important;
  692. color: white !important;
  693. }
  694. /* .save-button:hover {
  695. background-color: #e04a1d;
  696. } */
  697. .button-group {
  698. margin-top: 20px;
  699. }
  700. .action-button {
  701. width: 100%;
  702. padding: 14px;
  703. margin-bottom: 12px;
  704. background-color: white;
  705. border: 1px solid #e0e0e0;
  706. border-radius: 12px;
  707. font-size: 15px;
  708. color: #333;
  709. cursor: pointer;
  710. display: flex;
  711. align-items: center;
  712. justify-content: flex-start;
  713. transition: background-color 0.3s ease;
  714. }
  715. .action-button:hover {
  716. background-color: #f5f5f5;
  717. }
  718. .badge-dot {
  719. margin-left: 10px;
  720. width: 5px;
  721. height: 5px;
  722. background-color: #ff4d4f;
  723. border-radius: 50%;
  724. }
  725. .icon {
  726. margin-right: 10px;
  727. font-size: 18px;
  728. }
  729. .text {
  730. font-weight: 500;
  731. }
  732. .van-nav-bar__title {
  733. font-size: 20px;
  734. color: #333;
  735. }
  736. </style>