insideDesign.vue 43 KB

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