index.vue 25 KB

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