index.vue 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992
  1. <template>
  2. <headerBar
  3. >
  4. <template #title><div @click="handleSettingClick" v-log="{ describe: { action: '点击设置标题' } }">设置</div></template>
  5. </headerBar>
  6. <div class="container setting-wrap">
  7. <nav class="settings-nav">
  8. <div class="nav-item" :class="{'active': activeIndex === 0}" @click="toggleTab(0)" v-log="{ describe: { action: '点击切换设置Tab', tab: '基础配置' } }">
  9. <img src="@/assets/images/setting/icon1.png" class="nav-icon" v-if="activeIndex !== 0"/>
  10. <img src="@/assets/images/setting/icon1a.png" class="nav-icon" v-else/>
  11. <span>基础配置</span>
  12. </div>
  13. <div class="nav-item" v-if="configInfoStore.appModel === 1" :class="{'active': activeIndex === 3}" @click="toggleTab(3)" v-log="{ describe: { action: '点击切换设置Tab', tab: '相机配置' } }">
  14. <img src="@/assets/images/setting/icon2.png" class="nav-icon" v-if="activeIndex !== 3"/>
  15. <img src="@/assets/images/setting/icon2a.png" class="nav-icon" v-else/>
  16. <span>相机配置</span>
  17. </div>
  18. <div class="nav-item" :class="{'active': activeIndex === 2}" @click="toggleTab(2)" v-log="{ describe: { action: '点击切换设置Tab', tab: '其他设置' } }">
  19. <img src="@/assets/images/setting/icon3.png" class="nav-icon" v-if="activeIndex !== 2"/>
  20. <img src="@/assets/images/setting/icon3a.png" class="nav-icon" v-else/>
  21. <span>其他设置</span>
  22. </div>
  23. <div class="nav-item" :class="{'active': activeIndex === 4}" @click="toggleTab(4)" v-log="{ describe: { action: '点击切换设置Tab', tab: '左右脚程序设置' } }">
  24. <img src="@/assets/images/setting/icon4.png" class="nav-icon" v-if="activeIndex !== 4"/>
  25. <img src="@/assets/images/setting/icon4a.png" class="nav-icon" v-else/>
  26. <span>左右脚程序设置</span>
  27. </div>
  28. <div class="nav-item" v-if="userInfoStore.isHLM && configInfoStore.appModel === 1" :class="{'active': activeIndex === 5}" @click="toggleTab(5)" v-log="{ describe: { action: '点击切换设置Tab', tab: '高级设置' } }">
  29. <img src="@/assets/images/setting/icon1.png" class="nav-icon" v-if="activeIndex !== 5"/>
  30. <img src="@/assets/images/setting/icon1a.png" class="nav-icon" v-else/>
  31. <span>高级设置</span>
  32. </div>
  33. </nav>
  34. <div class="form-container">
  35. <!--基础配置-->
  36. <div class="selectBox" v-if="activeIndex === 0">
  37. <div class="form-item">
  38. <label>主图尺寸:</label>
  39. <div class="select-wrapper">
  40. <el-select multiple
  41. collapse-tags
  42. multiple-limit="3"
  43. v-model="formData.basic_configs.main_image_size" placeholder="请选择">
  44. <el-option v-for="item in mainImageSizeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
  45. </el-select>
  46. </div>
  47. </div>
  48. <!-- 新增自定义输入框 -->
  49. <div class="form-item" v-if="formData.basic_configs.main_image_size.includes('custom')">
  50. <label>自定义尺寸:</label>
  51. <div class="select-wrapper">
  52. <el-input
  53. type="text"
  54. v-model.number="customInput"
  55. maxlength="4"
  56. placeholder="请输入1-3000的尺寸值"
  57. class="w-full px-3 py-2 border rounded-md"
  58. @keypress="handleKeyPress"
  59. @input="handleInput"
  60. />
  61. </div>
  62. </div>
  63. <div class="form-item">
  64. <label>图片输出格式:</label>
  65. <div class="select-wrapper">
  66. <el-select v-model="formData.basic_configs.image_out_format" placeholder="请选择">
  67. <el-option v-for="item in imageFormatList" :key="item.value" :label="item.label" :value="item.value"></el-option>
  68. </el-select>
  69. </div>
  70. </div>
  71. <div class="form-item">
  72. <label>图片锐化:</label>
  73. <div class="select-wrapper">
  74. <el-input-number
  75. v-model="formData.basic_configs.image_sharpening"
  76. :min="1"
  77. :max="5"
  78. style="width: 200px"
  79. :step="0.1"
  80. controls-position="right"
  81. placeholder="请输入1-5之间的值"
  82. />
  83. </div>
  84. <div class="tips fs-12 c-999 mar-left-10">数值1-5,值为1则代表不处理</div>
  85. </div>
  86. <div class="form-item">
  87. <label>阴影处理模式:</label>
  88. <div class="select-wrapper">
  89. <el-select v-model.number="formData.basic_configs.image_mask_config.mode" placeholder="请选择">
  90. <el-option label="默认模式" :value="0"></el-option>
  91. <el-option label="自定义模式" :value="1"></el-option>
  92. <el-option label="AI模式" :value="2"></el-option>
  93. </el-select>
  94. </div>
  95. </div>
  96. <template v-if="formData.basic_configs.image_mask_config.mode !== 0">
  97. <div class="form-item">
  98. <label>透明度:</label>
  99. <div class="select-wrapper">
  100. <!-- <el-select v-model.number="formData.basic_configs.image_mask_config.opacity" placeholder="请选择">
  101. <el-option label="默认模式" :value="0"></el-option>
  102. <el-option label="自定义模式" :value="1"></el-option>
  103. <el-option label="AI模式" :value="2"></el-option>
  104. </el-select>-->
  105. <el-input-number
  106. v-model="formData.basic_configs.image_mask_config.opacity"
  107. :min="0.5"
  108. :max="1"
  109. style="width: 200px"
  110. :step="0.01"
  111. controls-position="right"
  112. placeholder="请输入0.5-1之间的值"
  113. />
  114. </div>
  115. </div>
  116. <div class="form-item">
  117. <label>亮度范围:</label>
  118. <div class="select-wrapper">
  119. <el-input-number
  120. v-model="formData.basic_configs.image_mask_config.grenerate_main_pic_brightness"
  121. :min="200"
  122. :max="255"
  123. style="width: 200px"
  124. :step="1"
  125. controls-position="right"
  126. placeholder="请输入200-255之间的值"
  127. />
  128. </div>
  129. </div>
  130. </template>
  131. <div class="form-item">
  132. <label>800图自定义边距:</label>
  133. <div class="select-wrapper">
  134. <el-input
  135. type="number"
  136. v-model.number="formData.basic_configs.padding_800image"
  137. :min="0"
  138. :max="500"
  139. placeholder="请输入0-500的整数"
  140. @keypress="handlePaddingKeyPress"
  141. @input="handlePaddingInput"
  142. />
  143. </div>
  144. </div>
  145. <div class="form-item">
  146. <label>800图是否翻转:</label>
  147. <div class="select-wrapper">
  148. <el-select v-model.number="formData.basic_configs.is_flip_800image" placeholder="请选择">
  149. <el-option v-for="item in isFlip800ImageList" :key="item.value" :label="item.label" :value="item.value"></el-option>
  150. </el-select>
  151. </div>
  152. </div>
  153. <div class="form-item">
  154. <label>800图颜色配置:</label>
  155. <div class="select-wrapper flex left">
  156. <ColorPickerWithRecent v-model="formData.basic_configs.color_800image" />
  157. </div>
  158. </div>
  159. <DebugPanel ref="debugPanel" />
  160. </div>
  161. <!--基础配置-->
  162. <!--相机配置-->
  163. <template v-if="activeIndex === 3">
  164. <CameraConfig ref="cameraConfigRef" :key="activeIndex" :camera_configs="formData.camera_configs" @update:camera_configs="updateCameraConfigs"/>
  165. </template>
  166. <!--相机配置-->
  167. <!--其他设置-->
  168. <template v-if="activeIndex === 2">
  169. <div class="selectBox" style="padding-top: 0px" >
  170. <div class="form-item">
  171. <label>产品类型:</label>
  172. <div class="select-wrapper">
  173. <el-select v-model="formData.other_configs.product_type" placeholder="请选择">
  174. <el-option v-for="item in productTypeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
  175. </el-select>
  176. </div>
  177. </div>
  178. <div class="form-item">
  179. <label>默认抠图模式:</label>
  180. <div class="select-wrapper">
  181. <el-select v-model="formData.other_configs.cutout_mode" placeholder="请选择">
  182. <el-option v-for="item in defaultCutoutModeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
  183. </el-select>
  184. </div>
  185. </div>
  186. <div class="form-item">
  187. <label>设备运动速度:</label>
  188. <div class="select-wrapper">
  189. <el-select v-model="formData.other_configs.device_speed" placeholder="请选择">
  190. <el-option v-for="item in deviceSpeedList" :key="item.value" :label="item.label" :value="item.value"></el-option>
  191. </el-select>
  192. </div>
  193. </div>
  194. <other-config/>
  195. <!-- <div class="form-item">
  196. <label>运行模式:</label>
  197. <div class="select-wrapper">
  198. <el-select v-model="formData.other_configs.running_mode" placeholder="请选择">
  199. <el-option v-for="item in runModeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
  200. </el-select>
  201. </div>
  202. </div>-->
  203. </div>
  204. </template>
  205. <!--其他设置-->
  206. <div class="selectBox" style="padding-top: 0px;padding-left: 0;" v-if="activeIndex === 4">
  207. <actionConfig/>
  208. </div>
  209. <div v-if="activeIndex === 5">
  210. <AdvancedConfig />
  211. </div>
  212. </div>
  213. <div class="text-center mt-8">
  214. <button class="bg-gradient-to-r from-primary" @click="onSava(activeIndex)" v-if="activeIndex !== 4 && activeIndex !== 5" v-log="{ describe: { action: '点击保存设置', tabIndex: activeIndex } }">
  215. 保存
  216. </button>
  217. </div>
  218. </div>
  219. </template>
  220. <script setup>
  221. /**
  222. * Vue组件逻辑部分,包含与设备配置相关的功能。
  223. * 主要功能包括:表单数据管理、设备配置列表获取、新增/编辑/删除步骤、保存配置等。
  224. */
  225. // 引入Vue相关功能和第三方库
  226. import { ref, reactive } from 'vue';
  227. import { useRoute, useRouter } from 'vue-router';
  228. import { onMounted, watch } from 'vue';
  229. import { getAllUserConfigs, setAllUserConfigs } from '@/apis/setting'
  230. import socket from "@/stores/modules/socket";
  231. import headerBar from '@/components/header-bar/index.vue';
  232. import ColorPickerWithRecent from '@/views/components/common/ColorPickerWithRecent.vue'
  233. import client from "@/stores/modules/client";
  234. import icpList from '@/utils/ipc';
  235. const clientStore = client();
  236. import { ElMessage, ElMessageBox } from 'element-plus';
  237. import { digiCamControlWEB } from '@/utils/appconfig'
  238. import { useCheckInfo } from '@/composables/userCheck';
  239. import { preview } from '@planckdev/element-plus/utils'
  240. import actionConfig from './components/action_config.vue'
  241. import otherConfig from './components/otherConfig'
  242. import CameraConfig from './components/CameraConfig';
  243. import AdvancedConfig from './components/AdvancedConfig.vue';
  244. import useUserInfo from '@/stores/modules/user';
  245. useCheckInfo();
  246. const userInfoStore = useUserInfo();
  247. //点击三次 打开资源目录
  248. import DebugPanel from './components/DebugPanel.vue';
  249. // 在setup函数中创建调试面板实例
  250. const debugPanel = ref(null);
  251. const cameraConfigRef = ref(null);
  252. // 添加设置点击计数器
  253. const settingClickCount = ref(0);
  254. // 修改headerBar的点击处理函数
  255. function handleSettingClick() {
  256. console.log('handleSettingClickhandleSettingClick')
  257. settingClickCount.value++;
  258. if (settingClickCount.value >= 3) {
  259. if (debugPanel.value) {
  260. debugPanel.value.showDebugPanel();
  261. }
  262. settingClickCount.value = 0;
  263. }
  264. setTimeout(() => {
  265. settingClickCount.value = 0;
  266. }, 3000); // 3秒内未再次点击则重置计数器
  267. }
  268. // 路由和状态管理初始化
  269. const route = useRoute();
  270. const router = useRouter();
  271. // 定义响应式变量
  272. const folderPath = ref(''); // 文件夹路径
  273. const activeIndex = ref(0); // 当前激活的索引
  274. const socketStore = socket(); // WebSocket状态管理实例
  275. import configInfo from '@/stores/modules/config';
  276. const configInfoStore = configInfo();
  277. /**
  278. * 表单数据对象,用于存储设备配置信息。
  279. */
  280. const formData = reactive({
  281. //基础配置
  282. basic_configs:{
  283. "main_image_size": [],//主图尺寸
  284. "image_out_format": "",//图片输出格式
  285. "image_sharpening": 1, //图片锐化(默认值改为1)
  286. "padding_800image": 100, //800图自定义边距
  287. "is_flip_800image": 1, //800图是否翻转
  288. "color_800image": '#ffffff', //800图颜色配置
  289. "image_mask_config": { //阴影处理配置
  290. "mode": 0, //阴影处理模式:0默认模式,1自定义模式,2 AI模式
  291. "opacity": 0.5, //透明度,默认0.5,范围0.5-1
  292. "grenerate_main_pic_brightness": 254 //亮度范围,默认254,范围200-255
  293. }
  294. },
  295. //拍照配置
  296. take_photo_configs:{
  297. "repart_take_photo_warning": false,//重复拍摄警告
  298. "single_photo_warning": "",//单次张数警告
  299. "total_photo_warning": "",//累计拍照警告
  300. "camera_delay": ""//拍照停留
  301. },
  302. other_configs:{
  303. "product_type": "",//产品类型
  304. "cutout_mode": "",//默认抠图模式
  305. "device_speed": "",//设备运动速度
  306. "running_mode": "" //运行模式
  307. },
  308. /* captureOneFolder: '', // Capture One文件夹路径
  309. mainImageSize: '', // 主图尺寸
  310. imageFormat: '', // 图片格式
  311. imageSharpening: '', // 图片锐化
  312. repeatWarning: '', // 重复警告
  313. singleWarning: '', // 单次警告
  314. totalWarning: '', // 总警告
  315. focusTime: '', // 对焦时间
  316. photoTime: '', // 拍照时间
  317. productType: '', // 产品类型
  318. defaultCutoutMode: '', // 默认抠图模式
  319. deviceSpeed: '', // 设备速度
  320. runMode: '', // 运行模式
  321. receiver: '', // 接收器类型
  322. left: '', // 左脚配置
  323. right: '', // 右脚配置
  324. up: '', // 上移配置
  325. down: '', // 下移配置*/
  326. });
  327. // 配置选项列表
  328. const mainImageSizeList = ref([
  329. { label: '320*320', value: 320 },
  330. { label: '512*512', value: 512 },
  331. { label: '768*768', value: 768 },
  332. { label: '800*800', value: 800 },
  333. { label: '1024*1024', value: 1024 },
  334. { label: '1200*1200', value: 1200 },
  335. { label: '1400*1400', value: 1400 },
  336. { label: '1600*1600', value: 1600 },
  337. { label: '自定义', value: 'custom' } // 新增自定义选项
  338. ]);
  339. const customInput = ref(null); // 新增自定义输入值
  340. const imageFormatList = ref([
  341. { label: 'jpg', value: 'jpg' },
  342. { label: 'png', value: 'png' },
  343. { label: 'jpeg', value: 'jpeg' },
  344. { label: 'webp', value: 'webp' },
  345. { label: 'avif', value: 'avif' },
  346. ]);
  347. // 图片锐化列表已移除,改为输入框
  348. const isFlip800ImageList = ref([
  349. { label: '是', value: 1 },
  350. { label: '否', value: 0 },
  351. ]);
  352. const repeatWarningList = ref([
  353. { label: '关闭', value: false },
  354. { label: '开启', value: true },
  355. ]);
  356. const singleWarningList = ref([
  357. { label: '11', value: '11' },
  358. { label: '12', value: '12' },
  359. { label: '13', value: '13' },
  360. ]);
  361. const totalWarningList = ref([
  362. { label: '1.0', value: '1.0' },
  363. { label: '1.5', value: '1.5' },
  364. { label: '2.0', value: '2.0' },
  365. ]);
  366. const productTypeList = ref([
  367. { label: '鞋类', value: '鞋类' },
  368. { label: '服装', value: '服装' },
  369. { label: '箱包', value: '箱包' },
  370. ]);
  371. const defaultCutoutModeList = ref([
  372. { label: '普通抠图', value: '普通抠图' },
  373. { label: '精细化抠图', value: '精细化抠图' },
  374. { label: '旗舰AI抠图', value: '旗舰AI抠图' },
  375. { label: '旗舰AI抠图-精细', value: '旗舰AI抠图-精细' },
  376. ]);
  377. const deviceSpeedList = ref([
  378. { label: '快', value: '一档' },
  379. { label: '中', value: '二档' },
  380. { label: '慢', value: '三档' },
  381. ]);
  382. /*
  383. const runModeList = ref([
  384. { label: '普通模式', value: '普通模式' },
  385. { label: '待用户确认模式', value: '待用户确认模式' }
  386. ]);
  387. const receiverList = ref([
  388. { label: '蓝牙', value: '1' },
  389. { label: '2.4G', value: '2' },
  390. { label: '5.8G', value: '3' },
  391. ]);
  392. const leftList = ref([
  393. { label: '左脚', value: '1' },
  394. { label: '右脚', value: '2' },
  395. { label: '左右脚', value: '3' },
  396. ]);
  397. const rightList = ref([
  398. { label: '左脚', value: '1' },
  399. { label: '右脚', value: '2' },
  400. { label: '左右脚', value: '3' },
  401. ]);
  402. const upList = ref([
  403. { label: '上移', value: '1' },
  404. { label: '下移', value: '2' },
  405. { label: '左右移', value: '3' },
  406. ]);
  407. const downList = ref([
  408. { label: '上移', value: '1' },
  409. { label: '下移', value: '2' },
  410. { label: '左右移', value: '3' },
  411. ]);
  412. */
  413. const indexKey ={
  414. 0:"basic_configs",
  415. 1:"take_photo_configs",
  416. 2:"other_configs",
  417. }
  418. /**
  419. * 监听路由参数变化,更新activeIndex和activeTab。
  420. */
  421. watch(() => route.query.type, async (newType,oldType) => {
  422. const typeValue = parseInt(newType) || 0;
  423. getConfig()
  424. });
  425. /**
  426. * 监听activeIndex变化,更新URL中的查询参数。
  427. */
  428. watch(() => activeIndex.value, (newIndex) => {
  429. router.push({
  430. query: {
  431. ...route.query,
  432. type: newIndex.toString()
  433. }
  434. });
  435. });
  436. const getConfig = async (typeValue)=>{
  437. const resultPHP = await getAllUserConfigs();
  438. if(resultPHP.code == 0 && resultPHP.data.configs ){
  439. Object.keys(resultPHP.data.configs).map(item=>{
  440. formData[item] = resultPHP.data.configs[item]
  441. })
  442. const presetSizes = mainImageSizeList.value.map(item => item.value);
  443. const receivedSizes = formData.basic_configs.main_image_size ? [...formData.basic_configs.main_image_size] : [];
  444. // 分离自定义值
  445. const customValues = receivedSizes.filter(v => !presetSizes.includes(v));
  446. if (customValues.length > 0 && activeIndex.value == 0) {
  447. customInput.value = customValues[0]; // 保留第一个自定义值
  448. // 更新选中状态
  449. console.log('111')
  450. formData.basic_configs.main_image_size = receivedSizes
  451. .filter(v => presetSizes.includes(v))
  452. .concat('custom');
  453. } else {
  454. console.log('222')
  455. formData.basic_configs.main_image_size = receivedSizes;
  456. }
  457. console.log(formData.basic_configs.main_image_size);
  458. // 确保新字段有默认值(如果服务器没有返回)
  459. if (formData.basic_configs.padding_800image === undefined || formData.basic_configs.padding_800image === null) {
  460. formData.basic_configs.padding_800image = 100;
  461. }
  462. if (formData.basic_configs.is_flip_800image === undefined || formData.basic_configs.is_flip_800image === null) {
  463. formData.basic_configs.is_flip_800image = 0;
  464. }
  465. if (formData.basic_configs.color_800image === undefined || formData.basic_configs.color_800image === null) {
  466. formData.basic_configs.color_800image = '#ffffff';
  467. }
  468. // 确保图片锐化有默认值
  469. if (formData.basic_configs.image_sharpening === undefined || formData.basic_configs.image_sharpening === null) {
  470. formData.basic_configs.image_sharpening = 1;
  471. }
  472. // 确保阴影处理配置有默认值
  473. if (!formData.basic_configs.image_mask_config) {
  474. formData.basic_configs.image_mask_config = {
  475. mode: 0,
  476. opacity: 0.5,
  477. grenerate_main_pic_brightness: 254
  478. };
  479. } else {
  480. // 确保每个字段都有默认值
  481. if (formData.basic_configs.image_mask_config.mode === undefined || formData.basic_configs.image_mask_config.mode === null) {
  482. formData.basic_configs.image_mask_config.mode = 0;
  483. }
  484. if (formData.basic_configs.image_mask_config.opacity === undefined || formData.basic_configs.image_mask_config.opacity === null) {
  485. formData.basic_configs.image_mask_config.opacity = 0.5;
  486. }
  487. if (formData.basic_configs.image_mask_config.grenerate_main_pic_brightness === undefined || formData.basic_configs.image_mask_config.grenerate_main_pic_brightness === null) {
  488. formData.basic_configs.image_mask_config.grenerate_main_pic_brightness = 254;
  489. }
  490. }
  491. // 处理图片锐化:如果值为0或小于1,自动改为1
  492. const sharpeningValue = parseFloat(formData.basic_configs.image_sharpening);
  493. if (isNaN(sharpeningValue) || sharpeningValue < 1) {
  494. formData.basic_configs.image_sharpening = 1;
  495. }
  496. }
  497. }
  498. /**
  499. * 组件挂载时初始化activeIndex。
  500. */
  501. onMounted(async () => {
  502. // 确保用户信息已加载,特别是 brand_company_code 字段
  503. if (!userInfoStore.userInfo?.brand_company_code) {
  504. await userInfoStore.getInfo();
  505. }
  506. getConfig()
  507. let type = 0 ;
  508. if (route.query.type) {
  509. const typeValue = parseInt(route.query.type);
  510. type = typeValue
  511. if (!isNaN(typeValue) && typeValue >= 0 && typeValue <= 3) {
  512. activeIndex.value = typeValue;
  513. }
  514. }
  515. });
  516. const handleKeyPress = (event) => {
  517. const char = event.key;
  518. // 只允许输入数字字符
  519. if (!/^\d+$/.test(char)) {
  520. event.preventDefault(); // 阻止非数字输入
  521. }
  522. };
  523. const handleInput = (value) => {
  524. if (value > 3000) {
  525. customInput.value = 3000;
  526. } else if (value < 1) {
  527. customInput.value = 1;
  528. }
  529. };
  530. // 处理800图边距输入
  531. const handlePaddingKeyPress = (event) => {
  532. const char = event.key;
  533. // 只允许输入数字字符
  534. if (!/^\d+$/.test(char)) {
  535. event.preventDefault(); // 阻止非数字输入
  536. }
  537. };
  538. const handlePaddingInput = () => {
  539. let value = formData.basic_configs.padding_800image;
  540. // 处理空值或无效值
  541. if (value === null || value === undefined || value === '' || isNaN(value)) {
  542. formData.basic_configs.padding_800image = 0;
  543. return;
  544. }
  545. // 转换为数字并确保是整数
  546. value = Number(value);
  547. value = Math.floor(value);
  548. // 限制范围
  549. if (value > 500) {
  550. formData.basic_configs.padding_800image = 500;
  551. } else if (value < 0) {
  552. formData.basic_configs.padding_800image = 0;
  553. } else {
  554. formData.basic_configs.padding_800image = value;
  555. }
  556. };
  557. // 添加更新camera_configs的方法
  558. const updateCameraConfigs = (configs) => {
  559. formData.camera_configs = configs;
  560. };
  561. const toggleTab = async (item) => {
  562. const oldType = activeIndex.value;
  563. // 切换前保存当前 Tab 配置(包含相机配置 3),高级设置(5)有独立保存逻辑,跳过自动保存
  564. if ([0,1,2,3].includes(oldType)) {
  565. const next = await saveSetting(oldType);
  566. if (next === false) return false;
  567. }
  568. activeIndex.value = item;
  569. return true;
  570. };
  571. const onSava = async (index)=>{
  572. // 高级设置(5)有独立保存逻辑
  573. if (index === 5) {
  574. ElMessage.info('请在高级设置中使用保存按钮保存');
  575. return;
  576. }
  577. const next = await saveSetting(index)
  578. if(next !== false){
  579. ElMessage.success('保存成功')
  580. }
  581. }
  582. /**
  583. * 保存当前表单配置。
  584. */
  585. const saveSetting = async (index) => {
  586. // 构建临时提交数据
  587. if(index === 3) {
  588. if (cameraConfigRef.value && typeof cameraConfigRef.value.save === 'function') {
  589. if(! cameraConfigRef.value.save()) return false;
  590. }
  591. }
  592. const submitData = JSON.parse(JSON.stringify(formData));
  593. if(index === 0) {
  594. if (formData.basic_configs.main_image_size.length === 0) {
  595. ElMessage.error('请选择主图尺寸!');
  596. return false;
  597. }
  598. // 处理自定义尺寸逻辑
  599. const selectedSizes = [...formData.basic_configs.main_image_size]; // 创建副本避免修改原始数据
  600. if (selectedSizes.includes('custom')) {
  601. if (!customInput.value || isNaN(customInput.value) ||
  602. customInput.value < 1 || customInput.value > 3000) {
  603. ElMessage.error('请输入1-3000之间的有效数值');
  604. return false;
  605. }
  606. // 创建新数组用于提交
  607. const submitSizes = selectedSizes
  608. .filter(v => v !== 'custom')
  609. .concat(parseInt(customInput.value));
  610. submitData.basic_configs.main_image_size = submitSizes
  611. }
  612. // 验证800图自定义边距
  613. const paddingValue = formData.basic_configs.padding_800image;
  614. if (paddingValue === undefined || paddingValue === null || isNaN(paddingValue) ||
  615. paddingValue < 0 || paddingValue > 500) {
  616. ElMessage.error('800图自定义边距必须在0-500之间');
  617. return false;
  618. }
  619. // 确保是整数
  620. submitData.basic_configs.padding_800image = Math.floor(paddingValue);
  621. // 验证图片锐化
  622. const sharpeningValue = parseFloat(formData.basic_configs.image_sharpening);
  623. if (isNaN(sharpeningValue) || sharpeningValue < 1 || sharpeningValue > 5) {
  624. ElMessage.error('图片锐化必须在1-5之间');
  625. return false;
  626. }
  627. submitData.basic_configs.image_sharpening = sharpeningValue;
  628. // 验证阴影处理配置
  629. // 阴影处理模式:0默认模式,1自定义模式,2 AI模式
  630. const maskConfig = formData.basic_configs.image_mask_config;
  631. const maskMode = maskConfig != null ? Number(maskConfig.mode) : NaN;
  632. if (!maskConfig || isNaN(maskMode) || ![0, 1, 2].includes(maskMode)) {
  633. ElMessage.error('请选择有效的阴影处理模式');
  634. return false;
  635. }
  636. submitData.basic_configs.image_mask_config.mode = maskMode;
  637. // 自定义模式 / AI 模式需要验证透明度和亮度(与界面 mode !== 0 一致)
  638. if (maskMode !== 0) {
  639. const opacity = parseFloat(maskConfig.opacity);
  640. const brightness = parseInt(maskConfig.grenerate_main_pic_brightness);
  641. if (isNaN(opacity) || opacity < 0.5 || opacity > 1) {
  642. ElMessage.error('透明度必须在0.5-1之间');
  643. return false;
  644. }
  645. if (isNaN(brightness) || brightness < 200 || brightness > 255) {
  646. ElMessage.error('亮度范围必须在200-255之间');
  647. return false;
  648. }
  649. submitData.basic_configs.image_mask_config.opacity = opacity;
  650. submitData.basic_configs.image_mask_config.grenerate_main_pic_brightness = brightness;
  651. }
  652. // 验证800图是否翻转
  653. const flipValue = formData.basic_configs.is_flip_800image;
  654. if (flipValue === undefined || flipValue === null || (flipValue !== 0 && flipValue !== 1)) {
  655. ElMessage.error('请选择800图是否翻转');
  656. return false;
  657. }
  658. submitData.basic_configs.is_flip_800image = flipValue;
  659. }
  660. const params = JSON.parse(JSON.stringify({
  661. configs:submitData
  662. }))
  663. const result = await setAllUserConfigs(params)
  664. if(result.code != 0) return false;
  665. return true;
  666. };
  667. </script>
  668. <style lang="scss">
  669. .el-image-viewer__wrapper{
  670. z-index: 9999 !important;
  671. }
  672. .setting-wrap {
  673. .selectBox{
  674. padding-top: 30px;
  675. padding-left: 100px;
  676. border-bottom: 1px solid rgba(0,0,0,0.1);
  677. ::v-deep(.el-tabs__header){
  678. padding-left: 0;
  679. }
  680. ::v-deep(.el-tabs--card>.el-tabs__header){
  681. border-bottom: 1px solid #CCCCCC;
  682. }
  683. ::v-deep(.el-tabs__item){
  684. height: 30px;
  685. line-height: 30px;
  686. }
  687. ::v-deep(.el-tabs__nav-wrap){
  688. margin-bottom: 0;
  689. }
  690. ::v-deep(.el-tabs__item.is-active){
  691. color: #333;
  692. font-weight: bold;
  693. background: #fff;
  694. }
  695. }
  696. .form-item {
  697. margin-bottom: 24px;
  698. display: flex;
  699. align-items: center;
  700. }
  701. .form-item label {
  702. display: block;
  703. width: 140px;
  704. flex-shrink: 0;
  705. text-align: right;
  706. font-size: 14px;
  707. color: #1A1A1A;
  708. padding-right: 12px;
  709. }
  710. .select-wrapper {
  711. position: relative;
  712. width: 200px;
  713. ::v-deep(.el-input__inner){
  714. border-radius: 6px;
  715. }
  716. }
  717. }
  718. </style>
  719. <style lang="scss" scoped>
  720. body {
  721. background: #EAECED;
  722. }
  723. .container {
  724. margin: 0 auto;
  725. min-height: calc(100vh - 30px);
  726. background: #EAECED;
  727. display: flex;
  728. flex-direction: column;
  729. justify-content: flex-start;
  730. }
  731. .settings-nav {
  732. display: flex;
  733. justify-content: center;
  734. gap: 40px;
  735. padding: 30px 0;
  736. background: #EDEFF0;
  737. border-bottom: 1px solid rgba(0,0,0,0.1);
  738. }
  739. .nav-item {
  740. display: flex;
  741. flex-direction: column;
  742. align-items: center;
  743. cursor: pointer;
  744. color: #666;
  745. transition: all 0.3s;
  746. width: 100px;
  747. justify-content: center;
  748. height: 70px;
  749. font-size: 14px;
  750. }
  751. .nav-item.active {
  752. background: #DFE2E3;
  753. border-radius: 10px;
  754. color: #2957FF;
  755. }
  756. .nav-item i {
  757. font-size: 24px;
  758. margin-bottom: 8px;
  759. width: 40px;
  760. height: 40px;
  761. display: flex;
  762. justify-content: center;
  763. align-items: center;
  764. background: #fff;
  765. border-radius: 8px;
  766. }
  767. .form-container {
  768. border-radius: 12px;
  769. padding: 30px;
  770. padding-top: 10px;
  771. padding-bottom: 40px;
  772. width: 800px;
  773. margin: 0 auto;
  774. min-height: 400px;
  775. }
  776. .input-group {
  777. display: flex;
  778. gap: 12px;
  779. }
  780. .input-group input {
  781. flex: 1;
  782. border: 1px solid #e5e7eb;
  783. border-radius: 4px;
  784. padding: 8px 12px;
  785. font-size: 14px;
  786. }
  787. .error-text {
  788. color: #dc2626;
  789. font-size: 12px;
  790. margin-top: 4px;
  791. }
  792. .from-primary{
  793. width: 150px;
  794. height: 40px;
  795. color: #FFFFFF;
  796. background: linear-gradient( 135deg, #2FB0FF 0%, #B863FB 100%);
  797. }
  798. .nav-icon{
  799. width: 32px;
  800. height: 32px;
  801. }
  802. .captureBox{
  803. border-bottom: 1px solid rgba(0,0,0,0.1);
  804. }
  805. .select-btn{
  806. display: flex;
  807. align-items: center;
  808. flex-shrink: 0;
  809. width: 120px;
  810. height: 30px;
  811. background: #DFE2E3;
  812. border-radius: 6px;
  813. justify-content: center;
  814. font-size: 14px;
  815. color: #2957FF;
  816. gap: 5px;
  817. img{
  818. width: 16px;
  819. height: 16px;
  820. }
  821. }
  822. .mt-8{
  823. display: flex;
  824. align-items: center;
  825. justify-content: center;
  826. padding: 30px 0;
  827. height: 100px;
  828. }
  829. .config-type{
  830. font-size: 14px;
  831. color: #333333;
  832. display: flex;
  833. align-items: center;
  834. justify-content: flex-start;
  835. padding: 10px 0;
  836. .el-checkbox{
  837. margin-left: 10px;
  838. }
  839. }
  840. .editForm{
  841. display: grid;
  842. grid-template-columns: repeat(2, 1fr);
  843. gap: 0;
  844. .flex-row{
  845. display: flex;
  846. align-items: center;
  847. ::v-deep(.el-radio){
  848. margin-right: 6px !important;
  849. }
  850. ::v-deep(.el-radio__label){
  851. padding-left: 4px;
  852. }
  853. }
  854. ::v-deep(.el-form-item) {
  855. margin-bottom: 0;
  856. .el-form-item__label {
  857. width: 120px !important;
  858. padding-right: 0 !important;
  859. background: #FBFCFF;
  860. border: 1px solid #EEEEEE;
  861. height: 41px;
  862. line-height: 41px;
  863. padding-left: 5px;
  864. text-align: left;
  865. }
  866. .el-form-item__content {
  867. width: 190px;
  868. position: relative;
  869. height: 41px;
  870. background: #FFFFFF;
  871. padding-left: 7px;
  872. border: 1px solid #EEEEEE;
  873. .el-input__wrapper {
  874. box-shadow: none;
  875. }
  876. .error-msg{
  877. display: none;
  878. position: absolute;
  879. top: 41px;
  880. top: 28px;
  881. left: 8px;
  882. z-index: 22;
  883. color: #dc2626;
  884. font-size: 12px;
  885. }
  886. &:hover{
  887. .error-msg{
  888. display: block;
  889. }
  890. }
  891. // 确保number类型输入框的上下箭头始终显示
  892. input[type="number"]::-webkit-inner-spin-button,
  893. input[type="number"]::-webkit-outer-spin-button {
  894. opacity: 1;
  895. height: 28px;
  896. position: absolute;
  897. top: 2px;
  898. right: 2px;
  899. cursor: pointer;
  900. }
  901. input[type="number"] {
  902. -moz-appearance: textfield; /* Firefox */
  903. }
  904. }
  905. }
  906. }
  907. </style>