insideDesign.vue 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450
  1. <template>
  2. <div class="design-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="#000" size="26" />
  7. </template>
  8. </van-nav-bar>
  9. </div>
  10. <div class="container">
  11. <!-- 历史生图 -->
  12. <div class="history-section">
  13. <div class="history-header" @click="viewHistory">
  14. <van-icon name="clock-o" color="#EC8868" />
  15. <span>历史生图</span>
  16. <span v-if="!readState" class="badge-dot"></span>
  17. </div>
  18. </div>
  19. <!-- 图片选择 -->
  20. <div class="image-selection">
  21. <div class="image-placeholder">
  22. <div v-if="selectedImage" class="selected-image-preview">
  23. <img :src="selectedImage" alt="预览图片" class="preview-image" @click="imgClick(selectedImage)" />
  24. </div>
  25. <div v-else class="placeholder">
  26. <img width="50" :src="require('@/assets/AIDesign/picture.png')" />
  27. <p class="placeholder-text">请上传一张待设计内墙的房屋照片</p>
  28. <p class="placeholder-text-tit">(避免模糊、光线不佳)</p>
  29. </div>
  30. <div class="image-buttons">
  31. <!-- 原生上传按钮 -->
  32. <div class="upload-container">
  33. <input type="file" accept="image/*" class="native-upload-input" @change="handleFileChange">
  34. <button class="image-btn">
  35. <span>选择图片</span>
  36. </button>
  37. </div>
  38. </div>
  39. </div>
  40. </div>
  41. <div class="insideInfo">
  42. <van-tabs v-model="activeName" @change="tabsChange" title-active-color="#383838" color="#383838"
  43. title-inactive-color="#b3b3b3" line-width="78" line-height="4">
  44. <van-tab title="魔术漆" name="魔术漆"></van-tab>
  45. <van-tab title="内墙平涂" name="内墙平涂"></van-tab>
  46. <van-tab title="风格" name="风格"></van-tab>
  47. </van-tabs>
  48. <!-- 风格 -->
  49. <div class="radio-group-container" v-show="activeName === '风格'">
  50. <van-radio-group v-model="DesignStyle_selValue" class="custom-radio-group3" @change="handleChange">
  51. <van-radio v-for="(item, index) in DesignStyle_options" :key="index" :name="item.value"
  52. class="custom-radio custom-radio3">
  53. <div class="radio-content radio-content-text
  54. ">
  55. {{ item.text }}
  56. <div v-if="DesignStyle_selValue === item.value" class="check-indicator">
  57. <van-icon name="success" color="#fff" size="10" />
  58. </div>
  59. </div>
  60. </van-radio>
  61. </van-radio-group>
  62. </div>
  63. <!--魔术漆 -->
  64. <div class="radio-group-container1" v-show="activeName === '魔术漆'">
  65. <div class="toggle-div">
  66. <van-icon :name="isExpanded ? 'arrow-up' : 'arrow-down'" class="toggle-icon" @click="toggleExpand" />
  67. </div>
  68. <!-- 纹理 -->
  69. <!-- 单选组:默认显示第一行,展开后显示全部 -->
  70. <van-radio-group v-model="textureCode_selValue" class="custom-radio-group" @change="handleTextureCodeChange">
  71. <div class="radio-wrapper" :class="isExpanded ? 'radio-column' : 'radio-row'" ref="textureRef">
  72. <van-radio class="custom-radio" v-for="(item, index) in textureCode_options" :key="index"
  73. :name="item.text" @click="selectRadio(item.text, 'textureRef', index)">
  74. <div class="radio-content" :class="{ 'radio-content--active': textureCode_selValue === item.text }">
  75. {{ item.text }}
  76. </div>
  77. </van-radio>
  78. </div>
  79. </van-radio-group>
  80. <!--纹理-颜色-放大版 -->
  81. <div class="wenli-plus" v-if="textureCodeWithColor_selItem">
  82. <van-image class="cardImg" fit="cover" :src="imgBaseUrl + textureCodeWithColor_selItem.imgPath" />
  83. <div class="wenli-plus-tit">{{ textureCodeWithColor_selItem.text }}</div>
  84. <div class="wenli-plus-subheading" v-if="textureCode_selItem && textureCode_selItem.text">
  85. 立邦魔术漆,{{ textureCode_selItem.text }}系列</div>
  86. </div>
  87. <!-- 纹理-颜色 -->
  88. <van-radio-group v-model="textureCodeWithColor_selValue" class="custom-radio-group4"
  89. @change="handleTextureCodeWithColorChange">
  90. <van-radio v-for="(item, index) in textureCodeWithColor_options" :key="index" :name="item.value"
  91. class="custom-radio custom-radio4">
  92. <div class="radio-content radio-content-color">
  93. <!-- <van-image class="cardImg" lazy-load :src="imgBaseUrl + item.imgPath" /> -->
  94. <img class="cardImg" :src="imgBaseUrl + item.imgPath" />
  95. <div v-if="textureCodeWithColor_selValue === item.value" class="check-indicator">
  96. <van-icon name="success" color="#fff" size="10" />
  97. </div>
  98. <div class="custom-radio-text">{{ item.text }}</div>
  99. </div>
  100. </van-radio>
  101. </van-radio-group>
  102. </div>
  103. <!--内墙平涂 -->
  104. <div class="radio-group-container1" v-show="activeName === '内墙平涂'">
  105. <!--内墙平涂-放大版 -->
  106. <div class="color-plus" v-if="color_selItem">
  107. <div class="cardImg" :style="{ backgroundColor: color_selItem.value.slice(0, 7) }"></div>
  108. <div class="color-plus-info">
  109. <div class="color-plus-tit">{{ color_selItem.text }}</div>
  110. <p>{{ color_selItem.ext1 }}</p>
  111. <p>{{ color_selItem.ext2 }}</p>
  112. <div class="color-plus-subheading">{{ color_selItem.ext3 }}</div>
  113. </div>
  114. </div>
  115. <van-radio-group v-model="color_selValue" class="custom-radio-group4" @change="handleColorChange">
  116. <van-radio v-for="(item, index) in color_options" :key="index" :name="item.value"
  117. class="custom-radio custom-radio4">
  118. <div class="radio-content radio-content-color">
  119. <div class="cardImg" :style="{ backgroundColor: item.value.slice(0, 7) }"></div>
  120. <div v-if="color_selValue === item.value" class="check-indicator">
  121. <van-icon name="success" color="#fff" size="10" />
  122. </div>
  123. <div class="custom-radio-text">{{ item.text }}</div>
  124. </div>
  125. </van-radio>
  126. </van-radio-group>
  127. </div>
  128. </div>
  129. <!-- AI生成按钮 -->
  130. <div class="generate-section">
  131. <van-button v-show="AIBtnDisabled" type="primary" block color="#E87838" disabled loading loading-type="spinner"
  132. loading-text="AI生成"></van-button>
  133. <van-button v-show="!AIBtnDisabled" type="primary" block @click="generateDesign"
  134. color="#E87838">AI生成</van-button>
  135. <div class="btn-note">*效果图仅供参考, 不作为交付标准</div>
  136. </div>
  137. </div>
  138. <!-- 上传 -->
  139. <van-action-sheet v-model="projectActionLoadingShow">
  140. <div class="project-action-box">
  141. <img src="../../assets/AIDesign/project_action_loading.png">
  142. <div class="tip" style="margin-top: 20px;">正在为您飞速上传...</div>
  143. <div class="tip-content">为了给您更好的生图效果,请避免上传模糊、光线不佳、非外墙的图片。<br />立邦AI效果图仅为意向图,不作为施工交付标准。</div>
  144. <div class="btn" @click="projectActionLoadingShow = false">我知道了</div>
  145. </div>
  146. </van-action-sheet>
  147. <van-dialog v-model="dialogShow" :show-confirm-button="false">
  148. <div class="dialog-box">
  149. <div class="dialog-content">{{ dialogContent }}</div>
  150. <div class="dialog-btn" @click="dialogShow = false">知道了</div>
  151. </div>
  152. </van-dialog>
  153. </div>
  154. </template>
  155. <script lang="ts">
  156. import { Component, Vue, Ref } from "vue-property-decorator";
  157. import { insideCreateDesign, insideGetEntity, insideGetReadState, insideUpdateReadState, GetDictList } from "@/api/indexAI";
  158. import { ImagePreview } from 'vant';
  159. import { Dialog } from 'vant';
  160. import { getWecomType, toLBHome } from '@/utils/index';
  161. declare let wx: any;
  162. import axios from "axios";
  163. @Component({
  164. })
  165. export default class extends Vue {
  166. // 添加鼠标拖拽相关引用
  167. $refs!: {
  168. stoneColorsRef: HTMLDivElement;
  169. processesRef: HTMLDivElement;
  170. splitsRef: HTMLDivElement;
  171. windowsRef: HTMLDivElement;
  172. railingsRef: HTMLDivElement;
  173. tilesRef: HTMLDivElement;
  174. };
  175. private imgBaseUrl = '';
  176. private activeName = "魔术漆";// tab选中的项
  177. private readState = true;//历史生土
  178. private projectActionLoadingShow = false;//图片上传中
  179. private serviceCodeArray = [];//服务商
  180. private dialogShow = false;//提交之后错误弹窗
  181. private dialogContent = '';//提交之后错误提示
  182. private AIBtnDisabled = false;//ai生成按钮状态
  183. private selectedImage: string | null = null; // 存储选择的图片
  184. private DesignStyle_selValue = '';// 风格选中的值
  185. private textureCode_selValue = '';// 深槽工艺选中的值
  186. private color_selValue = '';// 电子色卡选中的值
  187. private DesignStyle_options = [];//风格列表
  188. private textureCode_options = [];//深槽工艺列表
  189. private color_options = [];//电子色卡列表
  190. private textureCodeWithColor_selValue = '';//颜色纹理选中的值
  191. private textureCodeWithColor_options = [];//包含颜色和纹理的组合参数
  192. private textureCode_selItem = null;//选中的一级纹理
  193. private textureCodeWithColor_selItem = null;//选中的二级纹理
  194. private isExpanded = false;//展开/折叠状态
  195. private isEchoAssign: boolean = false;// 新增:标记是否为代码回显赋值(默认 false)
  196. private textureCode_selItem_index = 0;//纹理一级分类的选中索引
  197. private color_selItem = null;//选中的色卡
  198. // 当前选中的选项
  199. private oldf_id = null;
  200. private Userfile1 = null;
  201. // 添加鼠标拖拽相关数据
  202. private isMouseDown = false;
  203. private startX = 0;
  204. private scrollLeft = 0;
  205. private currentScrollElement: HTMLElement | null = null;
  206. @Ref('textureRef') private textureRef!: HTMLDivElement;
  207. created() {
  208. // this.getServiceCode();
  209. // 图片头
  210. if (window.location.href.indexOf('aidesign.') > -1) {
  211. this.imgBaseUrl = 'https://aidesign.nipponpaint.com.cn'
  212. } else {
  213. this.imgBaseUrl = 'https://aidesigntest.nipponpaint.com.cn'
  214. }
  215. }
  216. activated() {
  217. // 初始化数据
  218. this.initialize();
  219. this.getPicList();
  220. this.GetReadStateFn();
  221. }
  222. private initialize() {
  223. this.activeName = "魔术漆";
  224. this.readState = true;
  225. this.projectActionLoadingShow = false;
  226. this.dialogShow = false;//提交之后错误弹窗
  227. this.dialogContent = '';//提交之后错误提示
  228. this.AIBtnDisabled = false;//ai生成按钮状态
  229. this.selectedImage = '';
  230. this.DesignStyle_selValue = '';// 风格选中的值
  231. this.textureCode_selValue = '';// 深槽工艺选中的值
  232. this.color_selValue = '';// 电子色卡选中的值
  233. this.oldf_id = null;
  234. this.Userfile1 = null;
  235. this.textureCodeWithColor_selValue = '';//颜色纹理选中的值
  236. this.textureCodeWithColor_options = [];//包含颜色和纹理的组合参数
  237. this.textureCode_selItem = null;
  238. this.textureCodeWithColor_selItem = null;
  239. this.isExpanded = false;//展开/折叠状态
  240. this.isEchoAssign = false;
  241. this.textureCode_selItem_index = 0;
  242. this.color_selItem = null;
  243. }
  244. tabsChange(value: string) {
  245. this.DesignStyle_selValue = '';// 风格选中的值
  246. this.isExpanded = false;
  247. if (value === '魔术漆') {
  248. // 纹理默认第一个
  249. this.initDefaultTextureSelection()
  250. } else if (value === '内墙平涂') {
  251. // 色卡默认第一个
  252. this.color_selItem = this.color_options[0];
  253. this.color_selValue = this.color_options[0].value;
  254. }
  255. }
  256. // 初始化默认选择第一级纹理第一项
  257. initDefaultTextureSelection() {
  258. // 标记当前是初始化赋值,避免change事件清空二级
  259. this.isEchoAssign = true;
  260. if (this.textureCode_options.length > 0) {
  261. const firstTexture = this.textureCode_options[0];
  262. this.textureCode_selValue = firstTexture.text;
  263. this.textureCode_selItem = firstTexture;
  264. this.textureCode_selItem_index = 0;
  265. // 手动更新二级纹理选项
  266. this.textureCodeWithColor_options = firstTexture.subitems || [];
  267. if (this.textureCodeWithColor_options.length > 0) {
  268. const firstColor = this.textureCodeWithColor_options[0];
  269. this.textureCodeWithColor_selValue = firstColor.value;
  270. this.textureCodeWithColor_selItem = firstColor;
  271. }
  272. }
  273. this.textureCode_selItem_index = 0;
  274. // 初始化完成后重置标记
  275. this.$nextTick(() => {
  276. this.isEchoAssign = false;
  277. this.scrollIntoSel();
  278. });
  279. }
  280. // 选中变化时的回调
  281. handleChange(value: string | number) {
  282. // console.log("value=", value)
  283. // console.log("风格=",this.DesignStyle_selValue)
  284. // console.log("魔术漆=",this.textureCode_selValue)
  285. // console.log("内墙平涂=",this.color_selValue)
  286. }
  287. // 选择色卡
  288. handleColorChange(value: string | number) {
  289. // console.log("选择色卡")
  290. const selectedItem = this.color_options.find(
  291. item => item.value === value
  292. );
  293. this.color_selItem = selectedItem || null;
  294. }
  295. // 选择一级纹理
  296. handleTextureCodeChange(selectedValue: string | number) {
  297. // 防止回显触发
  298. if (!this.isEchoAssign) {
  299. // console.log("选一级纹理")
  300. this.textureCodeWithColor_selItem = null;
  301. // 找到选中的纹理项和索引
  302. let selectedItem = null;
  303. let selectedIndex = -1;
  304. this.textureCode_options.forEach((item, index) => {
  305. if (item.text === selectedValue) {
  306. selectedItem = item;
  307. selectedIndex = index;
  308. }
  309. });
  310. this.textureCode_selItem = selectedItem || null;
  311. this.textureCode_selItem_index = selectedIndex;
  312. // 更新颜色选项数组(如果选中项有subitems则赋值,否则置空)
  313. this.textureCodeWithColor_options = selectedItem && selectedItem.subitems ? selectedItem.subitems : [];
  314. this.textureCodeWithColor_selValue = '';
  315. }
  316. }
  317. // 选择二级纹理
  318. handleTextureCodeWithColorChange(value: string | number) {
  319. // 防止回显触发
  320. if (!this.isEchoAssign) {
  321. // console.log("选二级纹理")
  322. // 找到选中的纹理项
  323. const selectedItem = this.textureCodeWithColor_options.find(
  324. item => item.value === value
  325. );
  326. this.textureCodeWithColor_selItem = selectedItem || null;
  327. }
  328. }
  329. // 切换展开/折叠
  330. toggleExpand() {
  331. this.isExpanded = !this.isExpanded;
  332. this.scrollIntoSel();
  333. }
  334. // 滑动到用户所选位置
  335. scrollIntoSel() {
  336. if (!this.isExpanded && this.textureCode_selItem_index !== -1) {
  337. this.$nextTick(() => {
  338. const targetItem = this.textureRef.children[this.textureCode_selItem_index];
  339. if (targetItem) {
  340. try {
  341. // 滚动到选中项(居中显示,平滑滚动)
  342. (targetItem as HTMLElement).scrollIntoView({
  343. behavior: 'smooth',
  344. block: 'nearest',
  345. inline: 'center'
  346. });
  347. } catch (e) {
  348. const container = this.textureRef;
  349. const itemLeft = targetItem.offsetLeft;
  350. const itemWidth = targetItem.offsetWidth;
  351. const containerWidth = container.clientWidth;
  352. const targetScrollLeft = itemLeft - (containerWidth - itemWidth) / 2;
  353. container.scrollLeft = targetScrollLeft;
  354. }
  355. }
  356. });
  357. }
  358. }
  359. private selectRadio(code: string, refsName: string, selcIndex: number) {
  360. this.autoScrollToActive(refsName, selcIndex); // 选中后触发自动滚动
  361. }
  362. private autoScrollToActive(refsName: string, selcIndex: number) {
  363. const container = this.$refs[refsName];
  364. const activeItem = container.children[selcIndex];
  365. const itemWidth = activeItem.offsetWidth * 1.1;
  366. // 判断选中项是否完全在可视范围内
  367. const containerRect = container.getBoundingClientRect(); // 容器可视区域
  368. const itemRect = activeItem.getBoundingClientRect(); // 选中项位置
  369. // 完全可见的条件:左≥容器左,右≤容器右
  370. const isFullyVisibleL = itemRect.left >= containerRect.left;
  371. const isFullyVisibleR = itemRect.right <= containerRect.right - 26;
  372. if (!isFullyVisibleL) {
  373. container.scrollBy({
  374. left: -itemWidth, // 右边滑一格多的距离
  375. behavior: 'smooth' // 平滑滚动
  376. });
  377. } else if (!isFullyVisibleR) {
  378. container.scrollBy({
  379. left: itemWidth, // 左滑一格多的距离
  380. behavior: 'smooth' // 平滑滚动
  381. });
  382. }
  383. }
  384. private getServiceCode() {
  385. let that = this;
  386. const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
  387. let serviceCodeArray = [];
  388. if (userInfo.loginTypeList.length > 0) {
  389. userInfo.loginTypeList.forEach(item => {
  390. if (item.shopType == 'stoneLikePaint') {
  391. item.shopList.forEach(childItem => {
  392. serviceCodeArray.push(childItem.shop_code);
  393. })
  394. }
  395. })
  396. }
  397. that.serviceCodeArray = serviceCodeArray;
  398. }
  399. // 处理文件选择
  400. handleFileChange(e) {
  401. const file = e.target.files[0]; // 获取选中的文件
  402. if (!file) return; // 未选择文件直接返回
  403. // 1. 校验文件类型
  404. if (!file.type.match('image.*')) {
  405. this.$toast('请选择图片文件');
  406. this.clearInput(e.target); // 清除选择,避免重复触发同一文件
  407. return;
  408. }
  409. let FileSize = (file.size / 1024 / 1024).toFixed(2);
  410. console.log("用户选择的图片大小=", file.size + 'KB', ' =', FileSize + 'M')
  411. // 2. 校验文件大小)
  412. const maxSize = 20 * 1024 * 1024; // 10MB
  413. if (file.size > maxSize) {
  414. this.$toast('图片大小不能超过10MB');
  415. this.clearInput(e.target);
  416. return;
  417. }
  418. // 3. 处理选中的文件(对应 afterRead)
  419. this.handleAfterRead(file);
  420. // 清除 input 值,确保同一文件能被再次选择
  421. this.clearInput(e.target);
  422. }
  423. // 处理校验通过后的文件(对应 afterRead)
  424. handleAfterRead(file) {
  425. console.log("选中的图片信息:", file);
  426. // 生成图片预览(如果需要,和 van-uploader 的 content 类似)
  427. const reader = new FileReader();
  428. reader.onload = (event) => {
  429. this.selectedImage = event.target.result; // 预览图的 base64
  430. };
  431. reader.readAsDataURL(file); // 转换为 base64
  432. this.Userfile1 = file; // 保存原始文件对象(用于后续上传)
  433. }
  434. // 清除 input 值(解决同一文件无法重复选择的问题)
  435. clearInput(input) {
  436. input.value = '';
  437. }
  438. imgClick(url) {
  439. ImagePreview([url]);
  440. }
  441. GetReadStateFn() {
  442. const formData = new FormData();
  443. // const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
  444. // formData.append('WXuserid', userInfo.loginName);
  445. insideGetReadState(formData).then(response => {
  446. if (response.StatusCode == 200) {
  447. this.readState = response.Data.readState;
  448. }
  449. });
  450. }
  451. insideUpdateReadStateFn() {
  452. const formData = new FormData();
  453. // const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
  454. // formData.append('WXuserid', userInfo.loginName);
  455. insideUpdateReadState(formData).then(response => { });
  456. }
  457. GetEntityData(F_id) {
  458. const formData = new FormData();
  459. formData.append('F_id', F_id);
  460. insideGetEntity(formData).then(response => {
  461. // console.log(response);
  462. if (response.StatusCode == 200) {
  463. if (response.Data) {
  464. if (response.Data.F_UserFilePath) {
  465. this.selectedImage = response.Data.BaseUrl + response.Data.F_UserFilePath;
  466. }
  467. this.isEchoAssign = true;
  468. // 内墙结果-回显
  469. this.DesignStyle_selValue = response.Data.F_Style;// 风格选中的值
  470. // this.textureCode_selValue = response.Data.F_TextureCode;// 深槽工艺选中的值
  471. this.color_selValue = response.Data.F_Color;// 电子色卡选中的值
  472. const F_TextureCodeWithColor = response.Data.F_TextureCodeWithColor;
  473. // 处理纹理
  474. if (F_TextureCodeWithColor) {
  475. this.textureCode_selValue = F_TextureCodeWithColor.split("-")[1];
  476. // 找到选中的纹理项和索引
  477. let selectedItem = null;
  478. let selectedIndex = -1;
  479. this.textureCode_options.forEach((item, index) => {
  480. if (item.text === this.textureCode_selValue) {
  481. selectedItem = item;
  482. selectedIndex = index;
  483. }
  484. });
  485. this.textureCode_selItem_index = selectedIndex;
  486. // 找到选中的纹理颜色
  487. const textureCodeWithColorOp = selectedItem && selectedItem.subitems ? selectedItem.subitems : []
  488. const selectedItemColor = textureCodeWithColorOp.find(
  489. item => item.value === F_TextureCodeWithColor
  490. );
  491. this.textureCodeWithColor_selValue = F_TextureCodeWithColor;// 颜色纹理选中的值
  492. this.textureCode_selItem = selectedItem || null;
  493. this.textureCodeWithColor_selItem = selectedItemColor || null;
  494. this.textureCodeWithColor_options = textureCodeWithColorOp;
  495. this.$nextTick(() => {
  496. this.isEchoAssign = false;
  497. this.scrollIntoSel();
  498. });
  499. } else if (this.color_selValue) {
  500. // 找到选中的色卡
  501. const selectedItemColor = this.color_options.find(
  502. item => item.value === this.color_selValue
  503. );
  504. this.color_selItem = selectedItemColor;
  505. }
  506. this.oldf_id = response.Data.F_ID;
  507. if (this.DesignStyle_selValue && this.DesignStyle_selValue !== 'CUSTOM') {
  508. this.activeName = '风格';
  509. } else if (this.textureCode_selValue) {
  510. this.activeName = '魔术漆';
  511. } else if (this.color_selValue) {
  512. this.activeName = '内墙平涂';
  513. } else {
  514. this.activeName = '';
  515. }
  516. }
  517. }
  518. })
  519. }
  520. // 添加一个新的辅助方法用于将图片URL转换为File对象
  521. private async urlToFile(url: string, filename: string): Promise<File> {
  522. try {
  523. const response = await fetch(url);
  524. const blob = await response.blob();
  525. return new File([blob], filename, { type: blob.type });
  526. } catch (error) {
  527. console.error('图片转换失败:', error);
  528. this.$toast('图片加载失败');
  529. throw error;
  530. }
  531. }
  532. private viewHistory() {
  533. this.insideUpdateReadStateFn();
  534. // console.log('查看历史生成');
  535. // 实现查看历史生成逻辑
  536. this.$router.push({ path: '/AIDesign/history', query: { wallType: "inside" } });
  537. }
  538. // 添加鼠标滚轮事件处理
  539. private handleWheel(e: WheelEvent) {
  540. const container = e.currentTarget as HTMLElement;
  541. if (container) {
  542. container.scrollLeft += e.deltaY;
  543. }
  544. }
  545. // 添加鼠标按下事件处理
  546. private handleMouseDown(e: MouseEvent) {
  547. this.isMouseDown = true;
  548. this.currentScrollElement = e.currentTarget as HTMLElement;
  549. if (this.currentScrollElement) {
  550. this.currentScrollElement.style.cursor = 'grabbing';
  551. this.startX = e.pageX - this.currentScrollElement.offsetLeft;
  552. this.scrollLeft = this.currentScrollElement.scrollLeft;
  553. }
  554. }
  555. // 添加鼠标移动事件处理
  556. private handleMouseMove(e: MouseEvent) {
  557. if (!this.isMouseDown) return;
  558. e.preventDefault();
  559. if (this.currentScrollElement) {
  560. const x = e.pageX - this.currentScrollElement.offsetLeft;
  561. const walk = (x - this.startX) * 2;
  562. this.currentScrollElement.scrollLeft = this.scrollLeft - walk;
  563. }
  564. }
  565. // 添加鼠标释放事件处理
  566. private handleMouseUp() {
  567. this.isMouseDown = false;
  568. if (this.currentScrollElement) {
  569. this.currentScrollElement.style.cursor = 'grab';
  570. }
  571. this.currentScrollElement = null;
  572. }
  573. private generateDesign() {
  574. let that = this;
  575. if (!this.Userfile1 && !this.oldf_id) {
  576. this.$toast.fail('请选择图片');
  577. return;
  578. }
  579. if (this.activeName === '风格' && (!this.DesignStyle_selValue || this.DesignStyle_selValue === 'CUSTOM')) {
  580. this.$toast.fail(`请选择${this.activeName}`);
  581. return;
  582. }
  583. if (this.activeName === '魔术漆' && !this.textureCodeWithColor_selValue) {
  584. this.$toast.fail(`请选择${this.activeName}`);
  585. return;
  586. }
  587. const formData = new FormData();
  588. // const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
  589. // let serviceCodeArray = [];
  590. // if (userInfo.loginTypeList.length > 0) {
  591. // userInfo.loginTypeList.forEach(item => {
  592. // if (item.shopType == 'stoneLikePaint') {
  593. // item.shopList.forEach(childItem => {
  594. // serviceCodeArray.push(childItem.shop_code);
  595. // })
  596. // }
  597. // })
  598. // }
  599. // formData.append('WXuserid', userInfo.loginName);
  600. let DesignStyle_selValue_sub = this.DesignStyle_selValue;
  601. //风格有值,清除纹理和色卡
  602. if (this.activeName === '风格') {
  603. this.textureCodeWithColor_selValue = '';//纹理
  604. this.color_selValue = '';//色卡
  605. } else if (this.activeName === '魔术漆') {
  606. this.color_selValue = '';//色卡
  607. } else if (this.activeName === '内墙平涂') {
  608. this.textureCodeWithColor_selValue = '';//纹理
  609. }
  610. if (!this.DesignStyle_selValue) {
  611. DesignStyle_selValue_sub = 'CUSTOM';
  612. }
  613. formData.append('DesignStyle', DesignStyle_selValue_sub);
  614. formData.append('textureCodeWithColor', this.textureCodeWithColor_selValue);
  615. formData.append('color', this.color_selValue);
  616. if (this.Userfile1) {
  617. formData.append('Userfile1', this.Userfile1);
  618. } else {
  619. formData.append('oldf_id', this.oldf_id);
  620. }
  621. // 企微类型
  622. // const agentFrom = window.localStorage.getItem('agentFromAI');
  623. // const wecomType = getWecomType(agentFrom);
  624. formData.append('wecomType', 5);
  625. // // 服务商代码
  626. // if (serviceCodeArray.length > 0) {
  627. // formData.append('serivceCode', serviceCodeArray.join(','));
  628. // }
  629. // // 姓名
  630. // formData.append('userName', userInfo.userName);
  631. // const customerCode = userInfo.sysUserExt && userInfo.sysUserExt.customerCode ? userInfo.sysUserExt.customerCode : '';
  632. // // 经销商代码
  633. // formData.append('distributorCode', customerCode);
  634. // //原服务商用户、新增经销商用户
  635. // // 大区
  636. // formData.append('regionName', userInfo.officeName || '');
  637. // // 公司名称
  638. // formData.append('companyName', userInfo.companyName || '');
  639. // // 员工号
  640. // let employeeID = userInfo.sysUserExt && userInfo.sysUserExt.sapEmployeeId ? userInfo.sysUserExt.sapEmployeeId : '';
  641. // formData.append('employeeID', employeeID);
  642. // //销售部---原服务商用户、新增经销商用户
  643. // formData.append('salesDepartment', userInfo.subOfficeName || '');
  644. // // 销售部ID
  645. // formData.append('salesDepartmentCode', userInfo.subOfficeCode || '');
  646. // 遍历打印
  647. formData.forEach((value, key) => {
  648. console.log(`key: ${key}, value: ${value}`);
  649. });
  650. that.AIBtnDisabled = true;
  651. insideCreateDesign(formData).then(response => {
  652. // console.log(response);
  653. if (response.StatusCode == 200) {
  654. that.projectActionLoadingShow = false;
  655. that.AIBtnDisabled = false;
  656. window.localStorage.setItem("type", 'design');
  657. this.$router.push({
  658. path: '/AIDesign/result',
  659. query: {
  660. F_id: response.Data.F_ID,
  661. fromPage: 'design',
  662. wallType: 'inside'
  663. }
  664. });
  665. } else if (response.StatusCode == 410) {
  666. that.dialogContent = response.Info;
  667. that.projectActionLoadingShow = false;
  668. that.AIBtnDisabled = false;
  669. that.dialogShow = true;
  670. } else {
  671. that.projectActionLoadingShow = false;
  672. that.AIBtnDisabled = false;
  673. that.$toast(response.Info);
  674. }
  675. })
  676. .catch((err) => {
  677. that.projectActionLoadingShow = false;//弹窗
  678. that.AIBtnDisabled = false;//ai生成按钮
  679. })
  680. }
  681. //获取风格选项图片列表
  682. private getPicList() {
  683. let that = this;
  684. const formData = new FormData();
  685. // const userInfo: any = JSON.parse(window.localStorage.getItem("userInfoV1")!);
  686. // let roleIdArray = [];
  687. // if (userInfo.roles.length > 0) {
  688. // userInfo.roles.forEach(item => {
  689. // roleIdArray.push(item.roleId);
  690. // })
  691. // }
  692. // formData.append('roleIds', roleIdArray.join(','));
  693. // formData.append('WXuserid', userInfo.loginName);
  694. formData.append('baseType', 1);//必填 0外墙 1内墙
  695. const agentFrom = window.localStorage.getItem('agentFromAI');
  696. const wecomType = getWecomType(agentFrom);
  697. formData.append('wecomType', 5);
  698. // const isRefresh = userInfo.isRefreshProvider === '是' ? 1 : 0;//0=否 1=是
  699. // formData.append('isRefresh', isRefresh);
  700. GetDictList(formData).then(response => {
  701. if (response.StatusCode == 200) {
  702. if (response.Data && response.Data.dict) {
  703. this.DesignStyle_options = response.Data.dict.InnerStyle;
  704. this.textureCode_options = response.Data.dict.InnerNewTextureCode;
  705. this.color_options = response.Data.dict.color;
  706. let F_id = this.$route.query.F_id;
  707. if (F_id) {
  708. this.GetEntityData(F_id);
  709. } else {
  710. // 纹理默认第一个
  711. this.initDefaultTextureSelection()
  712. }
  713. } else {
  714. console.log(response.Info);
  715. }
  716. }
  717. });
  718. }
  719. returnPage() {
  720. this.$router.push({ path: "/AiDesign" });
  721. }
  722. toHome() {
  723. toLBHome()
  724. }
  725. }
  726. </script>
  727. <style scoped lang="scss">
  728. .design-container {
  729. /* margin: 0 auto;
  730. max-width: 750px; */
  731. background-color: #f8f9fa;
  732. min-height: 100vh;
  733. flex-direction: column;
  734. }
  735. .container {
  736. padding: 0 20px;
  737. }
  738. .top-nav {
  739. display: flex;
  740. justify-content: space-between;
  741. align-items: center;
  742. padding: 16px 20px;
  743. background-color: white;
  744. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  745. position: sticky;
  746. top: 0;
  747. z-index: 10;
  748. }
  749. .back-btn,
  750. .more-btn {
  751. width: 40px;
  752. height: 40px;
  753. display: flex;
  754. justify-content: center;
  755. align-items: center;
  756. cursor: pointer;
  757. }
  758. .title {
  759. font-size: 15px;
  760. font-weight: bold;
  761. color: #000;
  762. }
  763. .history-section {
  764. margin: 20px 0;
  765. }
  766. .history-header {
  767. display: flex;
  768. align-items: center;
  769. gap: 12px;
  770. padding: 12px 16px;
  771. background-color: #ffffff;
  772. border-radius: 18px;
  773. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  774. border: 1px solid #e9ecef;
  775. /* margin-bottom: 10px; */
  776. }
  777. .icon-clock {
  778. font-size: 12px;
  779. margin-right: 8px;
  780. }
  781. .history-header span {
  782. font-weight: 500;
  783. color: #EC8868;
  784. }
  785. .history-header .badge-dot {
  786. width: 5px;
  787. height: 5px;
  788. background-color: #ff4d4f;
  789. border-radius: 50%;
  790. }
  791. .history-content {
  792. padding: 20px;
  793. border-radius: 8px;
  794. background-color: white;
  795. text-align: center;
  796. // border: 1px dashed #ddd;
  797. }
  798. .no-history {
  799. color: #999;
  800. font-size: 12px;
  801. }
  802. .image-selection {
  803. text-align: center;
  804. .image-placeholder {
  805. position: relative;
  806. display: flex;
  807. flex-direction: column;
  808. justify-content: center;
  809. align-items: center;
  810. height: 200px;
  811. margin-bottom: 20px;
  812. background-color: #ffffff;
  813. border-radius: 12px;
  814. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  815. border: 1px dashed #ddd;
  816. padding: 10px;
  817. padding-bottom: 50px;
  818. }
  819. .image-buttons {
  820. position: absolute;
  821. right: 10px;
  822. bottom: 10px;
  823. display: flex;
  824. gap: 16px;
  825. width: 100%;
  826. justify-content: flex-end;
  827. }
  828. }
  829. // 内墙
  830. .insideInfo {
  831. padding: 1.2rem 0;
  832. background-color: #ffffff;
  833. border-radius: 8px;
  834. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  835. .radio-group-container {
  836. padding: 16px 10px;
  837. }
  838. /* 单个选项样式 */
  839. .custom-radio {
  840. flex-shrink: 0;
  841. align-items: flex-start;
  842. justify-content: center;
  843. }
  844. // 纯文字
  845. .custom-radio-group3 {
  846. display: flex;
  847. flex-wrap: wrap;
  848. // column-gap: 8px;
  849. // row-gap: 16px;
  850. }
  851. .custom-radio3 {
  852. width: calc(33% - 8px);
  853. margin: 0 4px 16px;
  854. }
  855. /* 最后一个元素取消右侧间距 */
  856. .custom-radio3:nth-child(3n) {
  857. margin-right: 0;
  858. }
  859. // 上图下文字
  860. .custom-radio-group4 {
  861. display: flex;
  862. flex-wrap: wrap;
  863. // column-gap: 2px;
  864. // row-gap: 16px;
  865. }
  866. .custom-radio4 {
  867. width: calc(25% - 2px);
  868. margin: 0 1px 16px;
  869. }
  870. .custom-radio4:nth-child(4n) {
  871. margin-right: 0;
  872. }
  873. .radio-content-text {
  874. padding: 12px 0;
  875. text-align: center;
  876. border: 1px solid #E5E5E5;
  877. border-radius: 4px;
  878. box-sizing: border-box;
  879. transition: all 0.2s;
  880. font-size: 12px;
  881. }
  882. /* 选中指示器(橙色三角形背景) */
  883. .check-indicator {
  884. position: absolute;
  885. width: 20px;
  886. height: 20px;
  887. background-color: #F0A43A;
  888. /* 三角形效果 */
  889. clip-path: polygon(0 0, 0 100%, 100% 0);
  890. .van-icon {
  891. position: relative;
  892. }
  893. }
  894. /* 选项内容容器 */
  895. .radio-content {
  896. position: relative;
  897. width: 100%;
  898. }
  899. .custom-radio-text {
  900. font-size: 12px;
  901. text-align: center;
  902. line-height: 14px;
  903. width: 100%;
  904. margin: 0 auto;
  905. position: absolute;
  906. z-index: 2;
  907. color: #000;
  908. bottom: 4px;
  909. left: 0;
  910. }
  911. .radio-content-color {
  912. font-size: 12px;
  913. width: 60px;
  914. height: 60px;
  915. position: relative;
  916. border: 1px solid #E5E5E5;
  917. margin: 0 auto;
  918. .cardImg {
  919. width: 100%;
  920. height: 100%;
  921. }
  922. .check-indicator {
  923. left: 0;
  924. top: 0;
  925. .van-icon {
  926. top: -5px;
  927. left: 0px;
  928. }
  929. }
  930. }
  931. .radio-content-text {
  932. border-radius: 4px;
  933. .check-indicator {
  934. border-radius: 2px 0 0 0;
  935. top: 0;
  936. left: 0;
  937. .van-icon {
  938. top: -5px;
  939. left: -3px;
  940. }
  941. }
  942. }
  943. /* 隐藏Vant默认的选中样式 */
  944. .custom-radio ::v-deep .van-radio__icon {
  945. display: none;
  946. }
  947. .custom-radio ::v-deep .van-radio__label {
  948. width: 100%;
  949. margin-left: 0;
  950. border-radius: inherit;
  951. flex: 1;
  952. }
  953. }
  954. ::v-deep .van-tab__text {
  955. font-size: 15px;
  956. font-weight: 500;
  957. line-height: 2.6rem;
  958. }
  959. ::v-deep .van-tab--active .van-tab__text {
  960. font-size: 17px;
  961. }
  962. /* 选中状态的边框样式 */
  963. ::v-deep .van-radio__icon--checked+.van-radio__label>.radio-content-text {
  964. border-color: #F0A43A;
  965. }
  966. ::v-deep .van-radio__icon--checked+.van-radio__label>.radio-content-color {
  967. border-color: #F0A43A;
  968. }
  969. ::v-deep .van-button--normal {
  970. font-size: 16px;
  971. border-radius: 10px;
  972. }
  973. .project-action-box {
  974. flex-direction: column;
  975. display: flex;
  976. box-sizing: border-box;
  977. padding: 15px 5px 45px 5px;
  978. width: 100%;
  979. img {
  980. width: 100%;
  981. height: auto;
  982. }
  983. .tip {
  984. padding: 0 20px;
  985. font-size: 16px;
  986. font-weight: 700;
  987. }
  988. .tip-content {
  989. padding: 0 20px;
  990. margin-top: 30px;
  991. font-size: 14px;
  992. }
  993. .btn {
  994. height: 44px;
  995. line-height: 44px;
  996. text-align: center;
  997. margin: 20px;
  998. font-size: 16px;
  999. color: #FFFFFF;
  1000. background-color: #2484F2;
  1001. border-radius: 10px;
  1002. }
  1003. .btn2 {
  1004. width: 100%;
  1005. font-size: 14px;
  1006. text-align: center;
  1007. }
  1008. }
  1009. .list-item-checked {
  1010. width: 100%;
  1011. flex-direction: column;
  1012. display: flex;
  1013. box-sizing: border-box;
  1014. border-radius: 10px;
  1015. // box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  1016. padding: 15px;
  1017. margin-bottom: 20px;
  1018. position: relative;
  1019. .img-item {
  1020. position: absolute;
  1021. bottom: 0;
  1022. right: 5px;
  1023. }
  1024. .img-checked {
  1025. width: 30px;
  1026. }
  1027. .title-item {
  1028. flex-direction: row;
  1029. display: flex;
  1030. justify-content: space-between;
  1031. align-items: center;
  1032. margin-bottom: 5px;
  1033. .title {
  1034. font-size: 15px;
  1035. font-weight: 700;
  1036. }
  1037. .status {
  1038. font-size: 14px;
  1039. color: #999999;
  1040. min-width: 45px;
  1041. margin-left: 10px;
  1042. }
  1043. }
  1044. .user-item {
  1045. flex-direction: row;
  1046. display: flex;
  1047. justify-content: flex-start;
  1048. align-items: center;
  1049. margin-top: 8px;
  1050. .txt {
  1051. font-size: 14px;
  1052. color: #999999;
  1053. margin-left: 10px;
  1054. }
  1055. }
  1056. }
  1057. .selected-image-preview {
  1058. width: 100%;
  1059. height: 100%;
  1060. display: flex;
  1061. justify-content: center;
  1062. align-items: center;
  1063. }
  1064. .preview-image {
  1065. max-width: 100%;
  1066. max-height: 100%;
  1067. object-fit: contain;
  1068. }
  1069. .placeholder-text {
  1070. color: #999;
  1071. font-size: 15px;
  1072. text-align: center;
  1073. margin-bottom: 20px;
  1074. margin-top: 10px;
  1075. }
  1076. .placeholder-text-tit {
  1077. color: #999;
  1078. font-size: 15px;
  1079. text-align: center;
  1080. margin-bottom: 20px;
  1081. margin-top: -10px;
  1082. }
  1083. .generate-section {
  1084. display: flex;
  1085. flex-direction: column;
  1086. align-items: center;
  1087. gap: 8px;
  1088. /* 按钮和文字说明之间的间距 */
  1089. margin-bottom: 20px;
  1090. margin-top: 50px;
  1091. }
  1092. .btn-note {
  1093. font-size: 12px;
  1094. color: #999;
  1095. text-align: center;
  1096. line-height: 1.2;
  1097. }
  1098. .header {
  1099. border-bottom: 1px solid #f8f8f8;
  1100. .van-nav-bar__title {
  1101. font-size: 20px;
  1102. color: #000;
  1103. }
  1104. .van-icon {
  1105. font-size: 20px;
  1106. color: #000 !important;
  1107. }
  1108. }
  1109. .dialog-box {
  1110. flex-direction: column;
  1111. display: flex;
  1112. box-sizing: border-box;
  1113. position: relative;
  1114. .dialog-content {
  1115. padding: 20px 20px 10px 20px;
  1116. }
  1117. .dialog-btn {
  1118. height: 44px;
  1119. line-height: 44px;
  1120. text-align: center;
  1121. margin: 20px;
  1122. font-size: 16px;
  1123. color: #FFFFFF;
  1124. background-color: #2484F2;
  1125. border-radius: 10px;
  1126. }
  1127. }
  1128. ::v-deep .van-button__loading+.van-button__text {
  1129. margin-left: 10px;
  1130. }
  1131. /* 样式调整:隐藏原生 input,按钮样式保持不变 */
  1132. .upload-container {
  1133. position: relative;
  1134. display: inline-block;
  1135. .native-upload-input {
  1136. position: absolute;
  1137. top: 0;
  1138. left: 0;
  1139. width: 100%;
  1140. height: 100%;
  1141. opacity: 0;
  1142. cursor: pointer;
  1143. z-index: 1;
  1144. }
  1145. .image-btn {
  1146. padding: 8px 16px;
  1147. border: none;
  1148. border-radius: 8px;
  1149. background-color: #2484F2;
  1150. color: white;
  1151. font-size: 12px;
  1152. cursor: pointer;
  1153. display: flex;
  1154. align-items: center;
  1155. gap: 8px;
  1156. transition: all 0.2s ease;
  1157. }
  1158. }
  1159. .wenli-plus,
  1160. .color-plus {
  1161. width: 100%;
  1162. height: 90px;
  1163. margin: 10px auto;
  1164. position: relative;
  1165. .cardImg {
  1166. width: 100%;
  1167. height: 100%;
  1168. img {
  1169. width: 100%;
  1170. height: auto;
  1171. }
  1172. }
  1173. .wenli-plus-tit {
  1174. font-size: 16px;
  1175. font-weight: 500;
  1176. line-height: 14px;
  1177. width: 100%;
  1178. position: absolute;
  1179. z-index: 2;
  1180. color: #000;
  1181. top: 25px;
  1182. left: 20px;
  1183. }
  1184. .wenli-plus-subheading {
  1185. font-size: 12px;
  1186. line-height: 14px;
  1187. width: 100%;
  1188. position: absolute;
  1189. z-index: 2;
  1190. color: #86909C;
  1191. top: 50px;
  1192. left: 20px;
  1193. }
  1194. }
  1195. .color-plus {
  1196. margin: 0px auto 10px;
  1197. .color-plus-info {
  1198. position: absolute;
  1199. width: calc(100% - 20px);
  1200. height: 100%;
  1201. top: 0;
  1202. left: 20px;
  1203. display: flex;
  1204. flex-direction: column;
  1205. justify-content: center;
  1206. .color-plus-tit {
  1207. font-size: 16px;
  1208. font-weight: 500;
  1209. line-height: 26px;
  1210. width: 100%;
  1211. color: #000;
  1212. }
  1213. p {
  1214. font-size: 11px;
  1215. line-height: 15px;
  1216. color: rgba(56, 56, 56, 1);
  1217. }
  1218. .color-plus-subheading {
  1219. font-size: 11px;
  1220. line-height: 15px;
  1221. width: 100%;
  1222. color: #86909C;
  1223. }
  1224. }
  1225. }
  1226. // 纹理
  1227. .radio-group-container1 {
  1228. padding: 16px 0;
  1229. position: relative;
  1230. .toggle-div {
  1231. position: absolute;
  1232. top: 19px;
  1233. right: 0px;
  1234. background-color: #F7F7F7;
  1235. width: 26px;
  1236. height: 26px;
  1237. border-radius: 50%;
  1238. display: flex;
  1239. align-items: center;
  1240. justify-content: center;
  1241. z-index: 5;
  1242. box-shadow: 0 4px 8px rgba(78, 78, 78, 0.3);
  1243. /* 展开/折叠图标 */
  1244. .toggle-icon {
  1245. cursor: pointer;
  1246. color: #999;
  1247. font-size: 18px;
  1248. transition: transform 0.2s ease;
  1249. }
  1250. }
  1251. .radio-row {
  1252. overflow-x: auto;
  1253. scroll-behavior: smooth;
  1254. cursor: grab;
  1255. flex-wrap: nowrap;
  1256. .custom-radio {
  1257. width: 72px;
  1258. }
  1259. }
  1260. .radio-column {
  1261. flex-wrap: wrap;
  1262. .custom-radio {
  1263. width: calc(25% - 10px);
  1264. }
  1265. }
  1266. .radio-wrapper {
  1267. padding: 0px 6px;
  1268. display: flex;
  1269. margin: 0 auto 8px;
  1270. user-select: none;
  1271. font-size: 12px;
  1272. .custom-radio {
  1273. flex-shrink: 0;
  1274. --van-radio-size: 0;
  1275. margin: 0 4px 15px;
  1276. border: 1px solid #383838;
  1277. transition: background-color 0.2s ease;
  1278. border-radius: 10px;
  1279. overflow: hidden;
  1280. padding: 0 !important;
  1281. box-sizing: border-box;
  1282. display: flex;
  1283. justify-content: flex-start;
  1284. }
  1285. .custom-radio ::v-deep .van-radio__label {
  1286. flex: 1 !important;
  1287. height: 100%;
  1288. display: flex;
  1289. align-items: center;
  1290. border-radius: inherit;
  1291. margin: 0 !important;
  1292. width: 100% !important;
  1293. }
  1294. /* 单选内容容器 */
  1295. .radio-content {
  1296. width: 100%;
  1297. height: 28px;
  1298. white-space: nowrap;
  1299. display: flex;
  1300. align-items: center;
  1301. justify-content: center;
  1302. background: #fff;
  1303. font-size: 11px;
  1304. position: relative;
  1305. text-align: center;
  1306. }
  1307. .radio-content--active {
  1308. background: #383838;
  1309. color: #fff;
  1310. }
  1311. }
  1312. ::-webkit-scrollbar {
  1313. display: none;
  1314. }
  1315. .custom-radio-group4 {
  1316. padding: 0 10px;
  1317. }
  1318. }
  1319. </style>