You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1731 lines
53 KiB

10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
2 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
2 months ago
2 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
  1. <template>
  2. <div class="property-form">
  3. <!-- 文本属性 -->
  4. <div v-if="element.type === 'text'" class="form-section">
  5. <el-form label-position="top" size="small">
  6. <el-form-item label="数据类型">
  7. <el-select v-model="element.dataType" size="mini" style="width: 100%;" @change="onDataTypeChange">
  8. <el-option label="普通文本" value="text" />
  9. <el-option label="数字" value="number" />
  10. <el-option label="日期" value="date" />
  11. <el-option label="字符串" value="string" />
  12. </el-select>
  13. </el-form-item>
  14. <el-form-item label="文本内容">
  15. <div class="input-with-button">
  16. <el-input v-model="element.data" placeholder="请输入文本" />
  17. <el-button type="primary" size="mini" @click="$emit('data-source', element)">
  18. 数据源
  19. </el-button>
  20. </div>
  21. </el-form-item>
  22. <el-form-item label="不显示规则(如:XXX=N)">
  23. <div class="font-size-row">
  24. <el-input
  25. v-model="element.showElement"
  26. controls-position="right"
  27. size="mini"
  28. class="font-size-input"
  29. />
  30. <el-tooltip content="勾选后内容前面展示☑" placement="top">
  31. <el-checkbox
  32. v-model="element.isChecked"
  33. size="small"
  34. class="inline-checkbox"
  35. >
  36. 打勾
  37. </el-checkbox>
  38. </el-tooltip>
  39. </div>
  40. </el-form-item>
  41. <el-form-item >
  42. <div class="form-row">
  43. <el-form-item label="字体" class="form-item-half">
  44. <el-select
  45. v-model="element.fontFamily"
  46. size="mini"
  47. style="width: 100%;"
  48. placeholder="选择字体"
  49. filterable
  50. :loading="fontLoading"
  51. @focus="loadAvailableFonts"
  52. @change="onFontFamilyChange"
  53. >
  54. <el-option-group
  55. v-for="group in groupedFonts"
  56. :key="group.category"
  57. :label="group.label"
  58. >
  59. <el-option
  60. v-for="font in group.fonts"
  61. :key="font.value"
  62. :label="font.name"
  63. :value="font.value"
  64. :disabled="!font.supported"
  65. >
  66. <span style="float: left">{{ font.name }}</span>
  67. <span style="float: right; color: #8492a6; font-size: 11px">
  68. {{ font.description }}
  69. <i v-if="!font.supported" class="el-icon-warning" style="color: #f56c6c;" title="不支持ZPL打印"></i>
  70. </span>
  71. </el-option>
  72. </el-option-group>
  73. </el-select>
  74. </el-form-item>
  75. <el-form-item label="文本对齐" class="form-item-half">
  76. <el-select v-model="element.textAlign" size="mini" style="width: 100%;" placeholder="对齐方式">
  77. <el-option label="左对齐" value="left" />
  78. <el-option label="居中" value="center" />
  79. <el-option label="右对齐" value="right" />
  80. </el-select>
  81. </el-form-item>
  82. </div>
  83. </el-form-item>
  84. <el-form-item label="字体大小">
  85. <div class="font-size-row">
  86. <el-input
  87. v-model="element.fontSize"
  88. :min="8"
  89. :max="200"
  90. controls-position="right"
  91. size="mini"
  92. class="font-size-input"
  93. />
  94. <el-checkbox v-model="element.bold" size="small" class="inline-checkbox">加粗</el-checkbox>
  95. <el-checkbox v-model="element.newline" size="small" class="inline-checkbox">自动换行</el-checkbox>
  96. </div>
  97. </el-form-item>
  98. <!-- <el-form-item label="字体样式">
  99. <div class="font-style-row">
  100. <el-checkbox v-model="element.fontItalic" size="small" class="inline-checkbox">斜体</el-checkbox>
  101. <el-checkbox v-model="element.fontUnderline" size="small" class="inline-checkbox">下划线</el-checkbox>
  102. <div class="spacing-controls">
  103. <span class="spacing-label">字符间距:</span>
  104. <el-input
  105. v-model="element.letterSpacing"
  106. :min="0"
  107. :max="20"
  108. controls-position="right"
  109. size="mini"
  110. class="spacing-input"
  111. placeholder="0"
  112. />
  113. </div>
  114. </div>
  115. </el-form-item>-->
  116. <template v-if="element.newline">
  117. <div class="form-row">
  118. <el-form-item label="文本宽度(mm)" class="form-item-half">
  119. <el-input
  120. v-model="element.lineWidth"
  121. :min="50"
  122. :max="1000"
  123. controls-position="right"
  124. size="mini"
  125. />
  126. </el-form-item>
  127. <el-form-item label="文本行数" class="form-item-half">
  128. <el-input
  129. v-model="element.lineRows"
  130. :min="1"
  131. :max="10"
  132. controls-position="right"
  133. size="mini"
  134. />
  135. </el-form-item>
  136. </div>
  137. </template>
  138. <template v-if="element.dataType === 'number'">
  139. <el-form-item label="小数位数" >
  140. <div class="font-size-row">
  141. <el-input v-model="element.decimalPlaces" controls-position="right" size="mini"/>
  142. <el-checkbox v-model="element.showDecimalPlaces" size="small" class="inline-checkbox">整数显示小数位</el-checkbox>
  143. <el-checkbox v-model="element.thousandsSeparator" size="small" class="inline-checkbox">千位分隔符</el-checkbox>
  144. <el-checkbox v-model="element.roundHalfUp" size="small" class="inline-checkbox">四舍五入</el-checkbox>
  145. </div>
  146. </el-form-item>
  147. </template>
  148. <!-- 日期类型设置 -->
  149. <template v-if="element.dataType === 'date'">
  150. <div class="form-row">
  151. <el-form-item label="数据源" class="form-item-half">
  152. <el-select
  153. v-model="element.dateSourceType"
  154. size="mini"
  155. style="width: 100%;"
  156. @change="onDateSourceTypeChange"
  157. >
  158. <el-option label="字段值" value="field" />
  159. <el-option label="当前日期" value="current" />
  160. </el-select>
  161. </el-form-item>
  162. <el-form-item label="提取类型" class="form-item-half">
  163. <el-select v-model="element.dateExtractType" size="mini" style="width: 100%;">
  164. <el-option label="完整日期" value="full" />
  165. <el-option label="提取年" value="year" />
  166. <el-option label="提取月" value="month" />
  167. <el-option label="提取日" value="day" />
  168. <el-option label="提取周别" value="week" />
  169. <el-option label="提取周几" value="weekday" />
  170. </el-select>
  171. </el-form-item>
  172. <el-form-item label="加减天数" class="form-item-half">
  173. <el-input
  174. v-model="element.dateOffsetDays"
  175. controls-position="right" size="mini"
  176. style="width: 100%;"/>
  177. </el-form-item>
  178. </div>
  179. <template v-if="element.dateExtractType === 'week'">
  180. <el-form-item label="每周第一天">
  181. <el-select v-model="element.firstDayOfWeek" size="mini" style="width: 100%;">
  182. <el-option label="周日" value="0" />
  183. <el-option label="周一" value="1" />
  184. </el-select>
  185. </el-form-item>
  186. </template>
  187. <template v-if="element.dateExtractType === 'weekday'">
  188. <el-form-item label="第一天是周几">
  189. <el-select v-model="element.firstDayOfWeek" size="mini" style="width: 100%;">
  190. <el-option label="周日" value="0" />
  191. <el-option label="周一" value="1" />
  192. <el-option label="周二" value="2" />
  193. <el-option label="周三" value="3" />
  194. <el-option label="周四" value="4" />
  195. <el-option label="周五" value="5" />
  196. <el-option label="周六" value="6" />
  197. </el-select>
  198. </el-form-item>
  199. </template>
  200. <template v-if="element.dateExtractType === 'full'">
  201. <div class="form-row">
  202. <el-form-item label="日期格式" class="form-item-half">
  203. <el-select v-model="element.dateFormat" size="mini" style="width: 100%;">
  204. <el-option label="年月日" value="ymd" />
  205. <el-option label="日月年" value="dmy" />
  206. <el-option label="月日年" value="mdy" />
  207. </el-select>
  208. </el-form-item>
  209. <el-form-item label="分隔符" class="form-item-half">
  210. <el-select v-model="element.dateSeparator" size="mini" style="width: 100%;">
  211. <el-option label="-" value="-" />
  212. <el-option label="/" value="/" />
  213. <el-option label="." value="." />
  214. <el-option label="无" value="" />
  215. </el-select>
  216. </el-form-item>
  217. </div>
  218. <div class="form-row">
  219. <el-form-item label="年份位数" class="form-item-half">
  220. <el-select v-model="element.yearDigits" size="mini" style="width: 100%;">
  221. <el-option label="4位(2024)" value="4" />
  222. <el-option label="2位(24)" value="2" />
  223. </el-select>
  224. </el-form-item>
  225. <el-form-item label="月日位数" class="form-item-half">
  226. <el-select v-model="element.monthDayDigits" size="mini" style="width: 100%;">
  227. <el-option label="2位(01)" value="2" />
  228. <el-option label="1位(1)" value="1" />
  229. </el-select>
  230. </el-form-item>
  231. </div>
  232. <div class="form-row">
  233. <el-form-item label="日期天数规则" class="form-item-half">
  234. <el-select v-model="element.firstWeekDate" size="mini" style="width: 100%;">
  235. <el-option label="自然日(18)" value="normal" />
  236. <el-option label="旬别日(01/11/21)" value="xun" />
  237. </el-select>
  238. </el-form-item>
  239. </div>
  240. <div class="form-row">
  241. <el-form-item label="" class="form-item-half">
  242. <el-checkbox v-model="element.showSerialNumber" size="small">流水号后缀</el-checkbox>
  243. </el-form-item>
  244. </div>
  245. <div class="form-row" v-if="element.showSerialNumber">
  246. <template>
  247. <el-form-item label="流水号位数" class="form-item-half">
  248. <el-input
  249. v-model="element.digits"
  250. controls-position="right"
  251. size="mini"
  252. style="width: 100%;"
  253. placeholder="2"
  254. />
  255. </el-form-item>
  256. <el-form-item label="流水号步长" class="form-item-half">
  257. <el-input
  258. v-model="element.step"
  259. controls-position="right"
  260. size="mini"
  261. style="width: 100%;"
  262. placeholder="1"
  263. />
  264. </el-form-item>
  265. </template>
  266. </div>
  267. </template>
  268. </template>
  269. <!-- 字符串类型设置 -->
  270. <template v-if="element.dataType === 'string'">
  271. <el-form-item label="字符串处理类型">
  272. <el-select v-model="element.stringProcessType" size="mini" style="width: 100%;">
  273. <el-option label="无处理" value="none" />
  274. <el-option label="截取前面字符" value="substring_start" />
  275. <el-option label="截取后面字符" value="substring_end" />
  276. <el-option label="按字符分割" value="split" />
  277. <el-option label="替换字符" value="replace" />
  278. </el-select>
  279. </el-form-item>
  280. <template v-if="element.stringProcessType === 'substring_start'">
  281. <el-form-item label="截取前面位数">
  282. <el-input
  283. v-model="element.substringStartLength"
  284. :min="1"
  285. :max="1000"
  286. controls-position="right"
  287. size="mini"
  288. placeholder="截取前面多少位"
  289. />
  290. </el-form-item>
  291. </template>
  292. <template v-if="element.stringProcessType === 'substring_end'">
  293. <el-form-item label="截取后面位数">
  294. <el-input
  295. v-model="element.substringEndLength"
  296. :min="1"
  297. :max="1000"
  298. controls-position="right"
  299. size="mini"
  300. placeholder="截取后面多少位"
  301. />
  302. </el-form-item>
  303. </template>
  304. <template v-if="element.stringProcessType === 'split'">
  305. <div class="form-row">
  306. <el-form-item label="分割字符" class="form-item-half">
  307. <el-input
  308. v-model="element.splitCharacter"
  309. size="mini"
  310. placeholder="分割字符"
  311. />
  312. </el-form-item>
  313. <el-form-item label="取第几段" class="form-item-half">
  314. <el-input
  315. v-model="element.splitIndex"
  316. :min="0"
  317. :max="100"
  318. controls-position="right"
  319. size="mini"
  320. placeholder="0表示第1段"
  321. />
  322. </el-form-item>
  323. </div>
  324. </template>
  325. <template v-if="element.stringProcessType === 'replace'">
  326. <div class="form-row">
  327. <el-form-item label="查找字符" class="form-item-half">
  328. <el-input
  329. v-model="element.replaceFrom"
  330. size="mini"
  331. placeholder="要替换的字符"
  332. />
  333. </el-form-item>
  334. <el-form-item label="替换为" class="form-item-half">
  335. <el-input
  336. v-model="element.replaceTo"
  337. size="mini"
  338. placeholder="替换成的字符"
  339. />
  340. </el-form-item>
  341. </div>
  342. </template>
  343. </template>
  344. </el-form>
  345. </div>
  346. <!-- 一维码属性 -->
  347. <div v-else-if="element.type === 'onecode'" class="form-section">
  348. <el-form label-position="top" size="small">
  349. <el-form-item label="数据内容">
  350. <div class="input-with-button">
  351. <el-input
  352. v-model="element.data"
  353. placeholder="请输入一维码数据"
  354. type="textarea"
  355. :rows="2"
  356. maxlength="1000"
  357. show-word-limit
  358. />
  359. <el-button type="primary" size="mini" @click="$emit('data-source', element)">
  360. 数据源
  361. </el-button>
  362. <el-button type="success" size="mini" @click="$emit('element-combination', element)">
  363. 元素组合
  364. </el-button>
  365. </div>
  366. </el-form-item>
  367. <el-form-item label="不显示规则(如:XXX=N)" class="form-item-half">
  368. <el-input v-model="element.showElement" controls-position="right" size="mini"/>
  369. </el-form-item>
  370. <div class="form-row">
  371. <el-form-item label="条码类型" class="form-item-half">
  372. <el-select v-model="element.barcodeType" size="mini" style="width: 100%;">
  373. <el-option label="CODE128" value="CODE128" />
  374. <el-option label="CODE39" value="CODE39" />
  375. <el-option label="CODE93" value="CODE93" />
  376. <el-option label="EAN13" value="EAN13" />
  377. <el-option label="EAN8" value="EAN8" />
  378. <el-option label="UPCA" value="UPCA" />
  379. <el-option label="UPCE" value="UPCE" />
  380. </el-select>
  381. </el-form-item>
  382. <el-form-item label="明文显示" class="form-item-half">
  383. <el-radio-group v-model="element.showContent" size="mini">
  384. <el-radio :label="true"></el-radio>
  385. <el-radio :label="false"></el-radio>
  386. </el-radio-group>
  387. </el-form-item>
  388. </div>
  389. <div class="form-row">
  390. <el-form-item label="窄条宽(范围0.085-0.85mm)" class="form-item-half">
  391. <el-input
  392. v-model="element.width"
  393. controls-position="right"
  394. size="mini"
  395. />
  396. </el-form-item>
  397. <el-form-item label="高度(mm)" class="form-item-half">
  398. <el-input
  399. v-model="element.height"
  400. controls-position="right"
  401. size="mini"
  402. />
  403. </el-form-item>
  404. </div>
  405. </el-form>
  406. </div>
  407. <!-- 二维码属性 -->
  408. <div v-else-if="element.type === 'qrcode'" class="form-section">
  409. <el-form label-position="top" size="small">
  410. <el-form-item label="数据内容">
  411. <div class="input-with-button">
  412. <el-input
  413. v-model="element.data"
  414. placeholder="请输入二维码数据"
  415. type="textarea"
  416. :rows="2"
  417. maxlength="1000"
  418. show-word-limit
  419. />
  420. <el-button type="primary" size="mini" @click="$emit('data-source', element)">
  421. 数据源
  422. </el-button>
  423. <el-button type="success" size="mini" @click="$emit('element-combination', element)">
  424. 元素组合
  425. </el-button>
  426. </div>
  427. </el-form-item>
  428. <el-form-item label="不显示规则(如:XXX=N)" class="form-item-half">
  429. <el-input v-model="element.showElement" controls-position="right" size="mini"/>
  430. </el-form-item>
  431. <div class="form-row">
  432. <el-form-item label="尺寸(mm)" class="form-item-half">
  433. <el-input
  434. v-model="element.height"
  435. :min="3"
  436. :max="50"
  437. :step="0.5"
  438. controls-position="right"
  439. size="mini"
  440. @change="validateQRSize"
  441. />
  442. </el-form-item>
  443. <el-form-item label="明文显示" class="form-item-half">
  444. <el-radio-group v-model="element.showContent" size="mini">
  445. <el-radio :label="true"></el-radio>
  446. <el-radio :label="false"></el-radio>
  447. </el-radio-group>
  448. </el-form-item>
  449. </div>
  450. <div class="form-tip">
  451. <div v-if="element.data && element.data.length > 200" class="tip-warning">
  452. 内容较长({{ element.data.length }}字符)建议尺寸15mm系统将自动优化
  453. </div>
  454. <div v-else-if="element.data && element.data.length > 100" class="tip-info">
  455. 内容中等({{ element.data.length }}字符)建议尺寸12mm系统将自动优化
  456. </div>
  457. <div v-else class="tip-normal">
  458. 内容较短({{ element.data ? element.data.length : 0 }}字符)当前尺寸足够
  459. </div>
  460. </div>
  461. </el-form>
  462. </div>
  463. <!-- 图片属性 -->
  464. <div v-else-if="element.type === 'pic'" class="form-section">
  465. <el-form label-position="top" size="small">
  466. <el-form-item label="图片上传">
  467. <div class="image-upload">
  468. <input
  469. ref="fileInput"
  470. type="file"
  471. accept="image/*"
  472. @change="handleImageUpload"
  473. style="display: none;"
  474. />
  475. <el-button type="primary"
  476. icon="el-icon-upload"
  477. @click="handleSelectImage">
  478. 选择图片
  479. </el-button>
  480. </div>
  481. </el-form-item>
  482. <div v-if="element.previewUrl" class="image-preview">
  483. <img :src="element.previewUrl" alt="预览" />
  484. </div>
  485. <!-- 图片尺寸设置 -->
  486. <div class="form-row">
  487. <el-form-item label="宽度(像素)" class="form-item-half">
  488. <el-input
  489. v-model="element.width"
  490. :min="50"
  491. :max="800"
  492. controls-position="right"
  493. size="mini"
  494. @change="onImageSizeChange"
  495. />
  496. </el-form-item>
  497. <el-form-item label="高度(像素)" class="form-item-half">
  498. <el-input
  499. v-model="element.height"
  500. :min="50"
  501. :max="800"
  502. controls-position="right"
  503. size="mini"
  504. @change="onImageSizeChange"
  505. />
  506. </el-form-item>
  507. </div>
  508. <el-form-item label="不显示规则(如:XXX=N)" class="form-item-half">
  509. <el-input v-model="element.showElement" controls-position="right" size="mini"/>
  510. </el-form-item>
  511. </el-form>
  512. </div>
  513. <!-- 线条属性 -->
  514. <div v-else-if="['hLine', 'vLine'].includes(element.type)" class="form-section">
  515. <el-form label-position="top" size="small">
  516. <div class="form-row">
  517. <el-form-item label="宽度" class="form-item-half">
  518. <el-input
  519. v-model="element.width"
  520. :min="1"
  521. :max="1000"
  522. controls-position="right"
  523. size="mini"
  524. />
  525. </el-form-item>
  526. <el-form-item label="高度" class="form-item-half">
  527. <el-input
  528. v-model="element.height"
  529. :min="1"
  530. :max="1000"
  531. controls-position="right"
  532. size="mini"
  533. />
  534. </el-form-item>
  535. </div>
  536. </el-form>
  537. </div>
  538. <!-- 流水号属性 -->
  539. <div v-else-if="element.type === 'serialNumber'" class="form-section">
  540. <el-form label-position="top" size="small">
  541. <div class="form-row">
  542. <el-form-item label="名称" class="form-item-half">
  543. <el-input v-model="element.seqName" controls-position="right" size="mini"/>
  544. </el-form-item>
  545. <el-form-item v-if="parentSerialElements.length>0" label="父标签流水号" class="form-item-half">
  546. <el-select clearable v-model="element.parentSerialLabelNo" size="mini" style="width: 100%;" filterable >
  547. <el-option v-for="item in parentSerialElements" :key="item.label_no" :label="(item.seq_name?item.seq_name:'')+'('+item.data+')'"
  548. :value="item.label_no"></el-option>
  549. </el-select>
  550. </el-form-item>
  551. </div>
  552. <div class="form-row" v-if="!element.parentSerialLabelNo">
  553. <el-form-item label="位数" class="form-item-half">
  554. <el-input
  555. v-model="element.digits"
  556. :min="1"
  557. :max="10"
  558. controls-position="right"
  559. size="mini"
  560. placeholder="6"
  561. />
  562. </el-form-item>
  563. <el-form-item label="步长" class="form-item-half">
  564. <el-input
  565. v-model="element.step"
  566. :min="1"
  567. :max="100"
  568. controls-position="right"
  569. size="mini"
  570. placeholder="1"
  571. />
  572. </el-form-item>
  573. </div>
  574. <div class="form-row" v-if="!element.parentSerialLabelNo">
  575. <el-form-item label="字体大小" class="form-item-half">
  576. <el-input
  577. v-model="element.fontSize"
  578. :min="8"
  579. :max="200"
  580. controls-position="right"
  581. size="mini"
  582. placeholder="30"
  583. />
  584. </el-form-item>
  585. <el-form-item label="加粗" class="form-item-half">
  586. <el-checkbox v-model="element.bold" size="middle"></el-checkbox>
  587. </el-form-item>
  588. </div>
  589. <div class="form-row" v-if="!element.parentSerialLabelNo">
  590. <el-form-item label="流水号规则" class="form-item-half">
  591. <el-input v-model="element.data" placeholder="请输入流水号规则" />
  592. <el-button type="primary" size="mini" @click="$emit('data-source', element)">
  593. 数据源
  594. </el-button>
  595. </el-form-item>
  596. </div>
  597. <div class="form-row">
  598. <el-form-item label="流水号信息" class="form-item-half">
  599. <el-button type="primary" size="mini" @click="serialInfoModal(element)">
  600. 查看
  601. </el-button>
  602. </el-form-item>
  603. <el-form-item label="是否显示" class="form-item-half">
  604. <el-checkbox v-model="element.showSerialNumber" size="middle">显示流水号</el-checkbox>
  605. </el-form-item>
  606. </div>
  607. </el-form>
  608. </div>
  609. <!-- 标签内容流水号信息 -->
  610. <comShowLabelSerialInfo ref="comShowLabelSerialInfo" v-drag></comShowLabelSerialInfo>
  611. </div>
  612. </template>
  613. <script>
  614. import comShowLabelSerialInfo from "../com_show_label_serial_info";
  615. import { availableFont,getParentLabelInfo ,getParentSerialElements} from '@/api/labelSetting/label_setting.js'
  616. export default {
  617. name: 'PropertyForm',
  618. props: {
  619. element: {
  620. type: Object,
  621. required: true
  622. }
  623. },
  624. emits: ['data-source', 'image-upload', 'element-combination'],
  625. data() {
  626. return {
  627. availableFonts: [],
  628. fontLoading: false,
  629. fontsLoaded: false,
  630. parentSerialElements: [] // 父标签的流水号元素列表
  631. }
  632. },
  633. computed: {
  634. groupedFonts() {
  635. if (!this.availableFonts.length) {
  636. return []
  637. }
  638. const groups = {}
  639. this.availableFonts.forEach(font => {
  640. const category = font.category || 'other'
  641. if (!groups[category]) {
  642. groups[category] = {
  643. category: category,
  644. label: this.getCategoryLabel(category),
  645. fonts: []
  646. }
  647. }
  648. groups[category].fonts.push(font)
  649. })
  650. // 排序分组
  651. const sortedGroups = Object.values(groups).sort((a, b) => {
  652. const order = ['system', 'chinese', 'english', 'monospace', 'decorative', 'other']
  653. return order.indexOf(a.category) - order.indexOf(b.category)
  654. })
  655. return sortedGroups
  656. }
  657. },
  658. /*组件*/
  659. components: {
  660. comShowLabelSerialInfo,/*标签内容流水号信息的組件*/
  661. },
  662. mounted() {
  663. // 初始化数据类型及相关设置
  664. this.initializeTypeSettings()
  665. // 初始化字体设置
  666. this.initializeFontSettings()
  667. if (this.element.type === 'serialNumber') {
  668. this.getParentLabelInfo()
  669. }
  670. },
  671. watch: {
  672. // 监听元素变化,确保文件输入框状态正确
  673. 'element.id'() {
  674. // 当切换到不同元素时,重置文件输入框
  675. this.$nextTick(() => {
  676. if (this.$refs.fileInput) {
  677. this.$refs.fileInput.value = ''
  678. }
  679. })
  680. },
  681. 'element'() {
  682. // 当切换到不同元素时,获取父流水号
  683. this.$nextTick(() => {
  684. this.initializeTypeSettings()
  685. if (this.element.type === 'serialNumber') {
  686. this.getParentLabelInfo()
  687. }
  688. })
  689. }
  690. },
  691. methods: {
  692. setElementDefault(key, defaultValue, keepEmptyString = false) {
  693. const value = this.element[key]
  694. if (value === undefined || value === null || (!keepEmptyString && value === '')) {
  695. this.$set(this.element, key, defaultValue)
  696. }
  697. },
  698. initializeTypeSettings() {
  699. if (this.element.type !== 'text') {
  700. return
  701. }
  702. this.setElementDefault('dataType', 'text')
  703. if (this.element.dataType === 'date') {
  704. this.setElementDefault('dateSourceType', this.inferDateSourceType(this.element.data))
  705. this.setElementDefault('dateExtractType', 'full')
  706. this.setElementDefault('dateFormat', 'ymd')
  707. this.setElementDefault('dateSeparator', '-', true)
  708. this.setElementDefault('yearDigits', '4')
  709. this.setElementDefault('monthDayDigits', '2')
  710. this.setElementDefault('firstWeekDate', 'normal')
  711. this.setElementDefault('firstDayOfWeek', '0')
  712. this.setElementDefault('dateOffsetDays', 0)
  713. this.setElementDefault('showSerialNumber', false)
  714. this.setElementDefault('digits', 2)
  715. this.setElementDefault('step', 1)
  716. } else if (this.element.dataType === 'number') {
  717. this.setElementDefault('roundHalfUp', true)
  718. } else if (this.element.dataType === 'string') {
  719. this.setElementDefault('stringProcessType', 'none')
  720. this.setElementDefault('substringStartLength', '')
  721. this.setElementDefault('substringEndLength', '')
  722. this.setElementDefault('splitCharacter', '')
  723. this.setElementDefault('splitIndex', '0')
  724. this.setElementDefault('replaceFrom', '')
  725. this.setElementDefault('replaceTo', '')
  726. }
  727. },
  728. // 获取父标签信息
  729. async getParentLabelInfo() {
  730. if (!this.element.reportId) return null
  731. try {
  732. // 使用API服务获取父标签信息
  733. const response = await getParentLabelInfo({ labelNo: this.element.reportId })
  734. if (response && response.data.code === 200) {
  735. const parentInfo = response.data.data
  736. if (parentInfo && parentInfo.parentLabelNo) {
  737. const response2 = await getParentSerialElements({ labelNo: parentInfo.parentLabelNo })
  738. if (response2.data && response2.data.code === 200) {
  739. this.parentSerialElements = response2.data.data || []
  740. } else {
  741. // 如果API调用失败,使用默认数据
  742. this.parentSerialElements = []
  743. }
  744. }
  745. } else {
  746. // 如果API调用失败,使用默认数据
  747. this.parentSerialElements = []
  748. }
  749. } catch (error) {
  750. console.error('获取父标签信息失败:', error)
  751. this.parentSerialElements = []
  752. }
  753. },
  754. onDataTypeChange(newType) {
  755. // 重置相关设置
  756. if (newType === 'date') {
  757. this.$set(this.element, 'dateSourceType', this.inferDateSourceType(this.element.data))
  758. this.$set(this.element, 'dateExtractType', 'full')
  759. this.$set(this.element, 'dateFormat', 'ymd')
  760. this.$set(this.element, 'dateSeparator', '-')
  761. this.$set(this.element, 'yearDigits', '4')
  762. this.$set(this.element, 'monthDayDigits', '2')
  763. this.$set(this.element, 'firstWeekDate', 'normal')
  764. this.$set(this.element, 'firstDayOfWeek', '0')
  765. this.$set(this.element, 'dateOffsetDays', 0)
  766. this.$set(this.element, 'showSerialNumber', false)
  767. this.$set(this.element, 'digits', 2)
  768. this.$set(this.element, 'step', 1)
  769. } else if (newType === 'number') {
  770. this.$set(this.element, 'roundHalfUp', true)
  771. } else if (newType === 'string') {
  772. this.$set(this.element, 'stringProcessType', 'none')
  773. this.$set(this.element, 'substringStartLength', '')
  774. this.$set(this.element, 'substringEndLength', '')
  775. this.$set(this.element, 'splitCharacter', '')
  776. this.$set(this.element, 'splitIndex', '0')
  777. this.$set(this.element, 'replaceFrom', '')
  778. this.$set(this.element, 'replaceTo', '')
  779. }
  780. },
  781. inferDateSourceType(dataValue) {
  782. return dataValue === '#{CURRENT_DATE_YYYY-MM-DD}' ? 'current' : 'field'
  783. },
  784. onDateSourceTypeChange(sourceType) {
  785. if (sourceType === 'current') {
  786. if (this.element.data && this.element.data !== '#{CURRENT_DATE_YYYY-MM-DD}') {
  787. this.$set(this.element, 'dateFieldDataBackup', this.element.data)
  788. }
  789. this.$set(this.element, 'data', '#{CURRENT_DATE_YYYY-MM-DD}')
  790. } else if (this.element.data === '#{CURRENT_DATE_YYYY-MM-DD}') {
  791. const backupData = this.element.dateFieldDataBackup || ''
  792. this.$set(this.element, 'data', backupData)
  793. }
  794. },
  795. handleSelectImage() {
  796. // 确保文件输入框被重置,然后触发点击
  797. if (this.$refs.fileInput) {
  798. this.$refs.fileInput.value = ''
  799. this.$refs.fileInput.click()
  800. }
  801. },
  802. /*流水号信息的modal*/
  803. serialInfoModal(row){
  804. let rowData = {
  805. labelNo: row.reportId,
  806. itemNo: row.itemNo,
  807. };
  808. //打开组件 去做复制其他标签业务
  809. this.$nextTick(() => {
  810. this.$refs.comShowLabelSerialInfo.init(rowData);
  811. })
  812. },
  813. validateQRSize() {
  814. const dataLength = this.element.data ? this.element.data.length : 0
  815. // 毫米单位限制
  816. if (this.element.height > 50) {
  817. this.$message.warning('二维码最大尺寸为50mm')
  818. this.element.height = 50
  819. } else if (this.element.height < 3) {
  820. this.element.height = 3
  821. }
  822. // 根据数据长度给出尺寸建议
  823. if (dataLength > 200 && this.element.height < 15) {
  824. this.$message.warning('长内容建议使用15mm或以上尺寸,以确保扫描成功')
  825. } else if (dataLength > 100 && this.element.height < 12) {
  826. this.$message.warning('中等长度内容建议使用12mm或以上尺寸')
  827. }
  828. },
  829. /**
  830. * 图片尺寸变化处理
  831. */
  832. onImageSizeChange() {
  833. // 限制图片尺寸范围
  834. if (this.element.width > 800) {
  835. this.$message.warning('图片宽度最大为800像素')
  836. this.element.width = 800
  837. } else if (this.element.width < 50) {
  838. this.element.width = 50
  839. }
  840. if (this.element.height > 800) {
  841. this.$message.warning('图片高度最大为800像素')
  842. this.element.height = 800
  843. } else if (this.element.height < 50) {
  844. this.element.height = 50
  845. }
  846. // 如果已经有图片数据,重新生成ZPL数据
  847. if (this.element.previewUrl) {
  848. this.regenerateImageZPL()
  849. }
  850. },
  851. /**
  852. * 重新生成图片的ZPL数据
  853. */
  854. regenerateImageZPL() {
  855. if (!this.element.previewUrl) return
  856. const img = new Image()
  857. img.onload = () => {
  858. try {
  859. const { zplData } = this.convertImageToZPL(img)
  860. this.element.zplData = zplData
  861. // 触发父组件更新
  862. this.$emit('image-upload', { zplData, previewUrl: this.element.previewUrl })
  863. } catch (error) {
  864. console.error('重新生成图片ZPL失败:', error)
  865. }
  866. }
  867. img.src = this.element.previewUrl
  868. },
  869. async handleImageUpload(event) {
  870. const file = event.target.files[0]
  871. if (!file) return
  872. try {
  873. const imageData = await this.processImage(file)
  874. this.$emit('image-upload', imageData)
  875. // 重置文件输入框的值,确保下次选择相同文件时也能触发change事件
  876. event.target.value = ''
  877. } catch (error) {
  878. this.$message.error('图片处理失败')
  879. // 即使出错也要重置文件输入框
  880. event.target.value = ''
  881. }
  882. },
  883. processImage(file) {
  884. return new Promise((resolve, reject) => {
  885. const reader = new FileReader()
  886. reader.onload = (e) => {
  887. const img = new Image()
  888. img.onload = () => {
  889. try {
  890. const { zplData, previewUrl } = this.convertImageToZPL(img)
  891. resolve({ zplData, previewUrl })
  892. } catch (error) {
  893. reject(error)
  894. }
  895. }
  896. img.onerror = reject
  897. img.src = e.target.result
  898. }
  899. reader.onerror = reject
  900. reader.readAsDataURL(file)
  901. })
  902. },
  903. convertImageToZPL(img) {
  904. // 使用元素的宽度和高度设置,如果没有设置则使用默认值
  905. const targetWidth = parseInt(this.element.width) || 200
  906. const targetHeight = parseInt(this.element.height) || Math.round(img.height * (targetWidth / img.width))
  907. const canvas = document.createElement('canvas')
  908. canvas.width = targetWidth
  909. canvas.height = targetHeight
  910. const ctx = canvas.getContext('2d')
  911. ctx.fillStyle = '#fff'
  912. ctx.fillRect(0, 0, canvas.width, canvas.height)
  913. ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
  914. // 转换为ZPL格式
  915. const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
  916. const { width, height, data } = imageData
  917. const bytesPerRow = Math.ceil(width / 8)
  918. const totalBytes = bytesPerRow * height
  919. let binaryData = ''
  920. for (let y = 0; y < height; y++) {
  921. for (let byteIdx = 0; byteIdx < bytesPerRow; byteIdx++) {
  922. let byteStr = ''
  923. for (let bit = 0; bit < 8; bit++) {
  924. const x = byteIdx * 8 + bit
  925. if (x >= width) {
  926. byteStr += '0'
  927. } else {
  928. const i = (y * width + x) * 4
  929. const r = data[i], g = data[i + 1], b = data[i + 2]
  930. const grayscale = (r + g + b) / 3
  931. byteStr += grayscale < 128 ? '1' : '0'
  932. }
  933. }
  934. const hex = parseInt(byteStr, 2).toString(16).padStart(2, '0').toUpperCase()
  935. binaryData += hex
  936. }
  937. }
  938. return {
  939. zplData: `${totalBytes},${totalBytes},${bytesPerRow},${binaryData}`,
  940. previewUrl: canvas.toDataURL()
  941. }
  942. },
  943. getSerialNumberPreview() {
  944. if (this.element.type !== 'serialNumber') return ''
  945. const prefix = this.element.prefix || ''
  946. const startValue = parseInt(this.element.startValue) || 1
  947. const digits = parseInt(this.element.digits) || 6
  948. const paddedNumber = startValue.toString().padStart(digits, '0')
  949. return `${prefix}${paddedNumber}`
  950. },
  951. // 初始化字体设置
  952. initializeFontSettings() {
  953. if (this.element.type === 'text') {
  954. // 设置默认字体属性
  955. if (!this.element.fontFamily) {
  956. this.$set(this.element, 'fontFamily', 'default')
  957. }
  958. if (!this.element.textAlign) {
  959. this.$set(this.element, 'textAlign', 'left')
  960. }
  961. if (this.element.letterSpacing === undefined) {
  962. this.$set(this.element, 'letterSpacing', 0)
  963. }
  964. if (this.element.fontItalic === undefined) {
  965. this.$set(this.element, 'fontItalic', false)
  966. }
  967. if (this.element.fontUnderline === undefined) {
  968. this.$set(this.element, 'fontUnderline', false)
  969. }
  970. }
  971. },
  972. // 获取字体预览样式
  973. getFontPreviewStyle() {
  974. if (this.element.type !== 'text') return {}
  975. const style = {}
  976. // 字体族
  977. if (this.element.fontFamily && this.element.fontFamily !== 'default') {
  978. style.fontFamily = this.element.fontFamily
  979. }
  980. // 字体大小
  981. if (this.element.fontSize) {
  982. style.fontSize = this.element.fontSize + 'px'
  983. }
  984. // 加粗
  985. if (this.element.bold) {
  986. style.fontWeight = 'bold'
  987. }
  988. // 斜体
  989. if (this.element.fontItalic) {
  990. style.fontStyle = 'italic'
  991. }
  992. // 下划线
  993. if (this.element.fontUnderline) {
  994. style.textDecoration = 'underline'
  995. }
  996. // 文本对齐
  997. if (this.element.textAlign) {
  998. style.textAlign = this.element.textAlign
  999. }
  1000. // 字符间距
  1001. if (this.element.letterSpacing) {
  1002. style.letterSpacing = this.element.letterSpacing + 'px'
  1003. }
  1004. return style
  1005. },
  1006. // 加载可用字体
  1007. async loadAvailableFonts() {
  1008. if (this.fontsLoaded || this.fontLoading) {
  1009. return
  1010. }
  1011. this.fontLoading = true
  1012. try {
  1013. const response = await availableFont({})
  1014. if (response.data.success) {
  1015. this.availableFonts = response.data.data || []
  1016. this.fontsLoaded = true
  1017. console.log('加载字体列表成功,共', this.availableFonts.length, '个字体')
  1018. } else {
  1019. console.error('加载字体列表失败:', response.data.message)
  1020. this.$message.warning('加载字体列表失败,使用默认字体')
  1021. this.availableFonts = this.getDefaultFonts()
  1022. }
  1023. } catch (error) {
  1024. console.error('加载字体列表异常:', error)
  1025. this.$message.error('加载字体列表异常')
  1026. this.availableFonts = this.getDefaultFonts()
  1027. } finally {
  1028. this.fontLoading = false
  1029. }
  1030. },
  1031. // 获取分类标签
  1032. getCategoryLabel(category) {
  1033. const labels = {
  1034. 'system': '系统字体',
  1035. 'chinese': '中文字体',
  1036. 'english': '英文字体',
  1037. 'monospace': '等宽字体',
  1038. 'decorative': '装饰字体',
  1039. 'other': '其他字体'
  1040. }
  1041. return labels[category] || '其他字体'
  1042. },
  1043. // 获取默认字体列表(备用方案)
  1044. getDefaultFonts() {
  1045. return [
  1046. { name: '默认字体', value: 'default', category: 'system', description: '系统默认', supported: true },
  1047. { name: '微软雅黑', value: 'Microsoft YaHei', category: 'chinese', description: '中文字体', supported: true },
  1048. { name: '宋体', value: 'SimSun', category: 'chinese', description: '中文字体', supported: true },
  1049. { name: '黑体', value: 'SimHei', category: 'chinese', description: '中文字体', supported: true },
  1050. { name: 'Arial', value: 'Arial', category: 'english', description: '英文字体', supported: true },
  1051. { name: 'Times New Roman', value: 'Times New Roman', category: 'english', description: '英文字体', supported: true },
  1052. { name: 'Courier New', value: 'Courier New', category: 'monospace', description: '等宽字体', supported: true }
  1053. ]
  1054. },
  1055. // 字体族变化处理
  1056. onFontFamilyChange(value) {
  1057. console.log('字体族变化:', value)
  1058. console.log('当前元素:', this.element)
  1059. // 确保字体值正确设置
  1060. this.$set(this.element, 'fontFamily', value)
  1061. // 触发父组件更新
  1062. this.$emit('font-changed', {
  1063. elementId: this.element.id,
  1064. fontFamily: value
  1065. })
  1066. console.log('字体设置完成,当前字体族:', this.element.fontFamily)
  1067. },
  1068. }
  1069. }
  1070. </script>
  1071. <style scoped>
  1072. .property-form {
  1073. overflow-y: auto;
  1074. }
  1075. .property-form::-webkit-scrollbar {
  1076. width: 4px;
  1077. }
  1078. .property-form::-webkit-scrollbar-track {
  1079. background: #f1f1f1;
  1080. border-radius: 2px;
  1081. }
  1082. .property-form::-webkit-scrollbar-thumb {
  1083. //background: #409eff;
  1084. border-radius: 2px;
  1085. }
  1086. .form-section {
  1087. padding: 10px;
  1088. background: #fafafa;
  1089. border-radius: 4px;
  1090. border: 1px solid #e8e8e8;
  1091. }
  1092. .input-with-button {
  1093. display: flex;
  1094. gap: 6px;
  1095. align-items: flex-start;
  1096. }
  1097. .input-with-button .el-input {
  1098. flex: 1;
  1099. }
  1100. .input-with-button .el-button {
  1101. font-size: 11px;
  1102. padding: 5px 8px;
  1103. height: 28px;
  1104. }
  1105. .form-tip {
  1106. font-size: 11px;
  1107. color: #909399;
  1108. margin-top: 3px;
  1109. padding: 3px 6px;
  1110. background: #f8f9fa;
  1111. border-radius: 3px;
  1112. }
  1113. .form-tip .tip-warning {
  1114. color: #f56c6c;
  1115. font-weight: 500;
  1116. border-left: 2px solid #f56c6c;
  1117. padding-left: 6px;
  1118. }
  1119. .form-tip .tip-info {
  1120. color: #e6a23c;
  1121. font-weight: 500;
  1122. border-left: 2px solid #e6a23c;
  1123. padding-left: 6px;
  1124. }
  1125. .form-tip .tip-normal {
  1126. color: #67c23a;
  1127. font-weight: 500;
  1128. border-left: 2px solid #67c23a;
  1129. padding-left: 6px;
  1130. }
  1131. .image-upload {
  1132. margin-bottom: 8px;
  1133. }
  1134. .image-upload .el-button {
  1135. width: 100%;
  1136. height: 30px;
  1137. border: 1px dashed #d9d9d9;
  1138. border-radius: 4px;
  1139. background: #fafafa;
  1140. font-size: 12px;
  1141. color: black;
  1142. }
  1143. .image-upload .el-button:hover {
  1144. border-color: #409eff;
  1145. background: #f0f9ff;
  1146. }
  1147. .image-preview {
  1148. text-align: center;
  1149. padding: 8px;
  1150. background: #f8f9fa;
  1151. border-radius: 4px;
  1152. border: 1px solid #e8e8e8;
  1153. }
  1154. .image-preview img {
  1155. max-width: 80px;
  1156. max-height: 80px;
  1157. border-radius: 4px;
  1158. border: 1px solid #ddd;
  1159. }
  1160. /* Element UI 表单项紧凑化 */
  1161. .el-form-item {
  1162. margin-bottom: 2px;
  1163. }
  1164. .el-form-item:last-child {
  1165. margin-bottom: 0;
  1166. }
  1167. .el-form-item__label {
  1168. font-size: 12px !important;
  1169. padding-bottom: 6px !important;
  1170. line-height: 1.2 !important;
  1171. color: #606266 !important;
  1172. font-weight: 500 !important;
  1173. }
  1174. .el-input__inner {
  1175. height: 32px !important;
  1176. font-size: 13px !important;
  1177. border-radius: 6px !important;
  1178. border: 1px solid #dcdfe6 !important;
  1179. padding: 0 12px !important;
  1180. transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
  1181. }
  1182. .el-input__inner:hover {
  1183. border-color: #c0c4cc !important;
  1184. }
  1185. .el-input__inner:focus {
  1186. border-color: #409eff !important;
  1187. outline: none !important;
  1188. box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1) !important;
  1189. }
  1190. .el-input {
  1191. width: 100% !important;
  1192. }
  1193. .el-input .el-input__inner {
  1194. text-align: left !important;
  1195. height: 32px !important;
  1196. padding: 0 35px 0 12px !important;
  1197. }
  1198. .el-checkbox {
  1199. font-size: 12px !important;
  1200. height: 20px !important;
  1201. line-height: 20px !important;
  1202. }
  1203. .el-checkbox__label {
  1204. font-size: 12px !important;
  1205. padding-left: 6px !important;
  1206. }
  1207. .el-checkbox__inner {
  1208. width: 12px !important;
  1209. height: 12px !important;
  1210. }
  1211. .el-checkbox__inner::after {
  1212. height: 5px !important;
  1213. left: 3px !important;
  1214. top: 1px !important;
  1215. width: 2px !important;
  1216. }
  1217. /* 行布局样式 */
  1218. .form-row {
  1219. display: flex;
  1220. gap: 2px;
  1221. align-items: flex-start;
  1222. margin-bottom: 2px;
  1223. }
  1224. .form-row:last-child {
  1225. margin-bottom: 0;
  1226. }
  1227. .form-item-half {
  1228. flex: 1;
  1229. margin-bottom: 0 !important;
  1230. }
  1231. .form-item-half .el-form-item__label {
  1232. margin-bottom: 2px !important;
  1233. }
  1234. /* 数字输入框统一样式 */
  1235. .form-item-half .el-input {
  1236. width: 100% !important;
  1237. }
  1238. .form-item-half .el-input .el-input__inner {
  1239. height: 32px !important;
  1240. font-size: 13px !important;
  1241. padding: 0 35px 0 12px !important;
  1242. border: 1px solid #dcdfe6 !important;
  1243. border-radius: 6px !important;
  1244. transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
  1245. }
  1246. .form-item-half .el-input .el-input__inner:hover {
  1247. border-color: #c0c4cc !important;
  1248. }
  1249. .form-item-half .el-input .el-input__inner:focus {
  1250. border-color: #409eff !important;
  1251. outline: none !important;
  1252. box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1) !important;
  1253. }
  1254. .form-item-half .el-input .el-input__increase,
  1255. .form-item-half .el-input .el-input__decrease {
  1256. position: absolute !important;
  1257. right: 1px !important;
  1258. width: 32px !important;
  1259. height: 15px !important;
  1260. line-height: 15px !important;
  1261. background: #f5f7fa !important;
  1262. color: #606266 !important;
  1263. cursor: pointer !important;
  1264. font-size: 12px !important;
  1265. border: none !important;
  1266. border-radius: 0 !important;
  1267. transition: all 0.2s !important;
  1268. }
  1269. .form-item-half .el-input .el-input__increase {
  1270. top: 1px !important;
  1271. border-radius: 0 5px 0 0 !important;
  1272. }
  1273. .form-item-half .el-input .el-input__decrease {
  1274. bottom: 1px !important;
  1275. border-radius: 0 0 5px 0 !important;
  1276. }
  1277. .form-item-half .el-input .el-input__increase:hover,
  1278. .form-item-half .el-input .el-input__decrease:hover {
  1279. background: #e6a23c !important;
  1280. color: #fff !important;
  1281. }
  1282. .form-item-half .el-input .el-input__increase:active,
  1283. .form-item-half .el-input .el-input__decrease:active {
  1284. background: #cf9236 !important;
  1285. }
  1286. /* 复选框在行中的样式 */
  1287. .form-item-half .el-checkbox {
  1288. display: block !important;
  1289. margin-bottom: 1px !important;
  1290. margin-right: 0 !important;
  1291. padding-top: 5px;
  1292. padding-left: 1px;
  1293. }
  1294. .form-item-half .el-checkbox:last-child {
  1295. margin-bottom: 0 !important;
  1296. }
  1297. /* 单选按钮组样式 */
  1298. .form-item-half .el-radio-group {
  1299. display: flex !important;
  1300. gap: 8px !important;
  1301. align-items: center !important;
  1302. padding-top: 4px;
  1303. }
  1304. .form-item-half .el-radio {
  1305. margin-right: 0 !important;
  1306. font-size: 12px !important;
  1307. height: 20px !important;
  1308. line-height: 20px !important;
  1309. }
  1310. .form-item-half .el-radio__label {
  1311. font-size: 12px !important;
  1312. padding-left: 4px !important;
  1313. }
  1314. /* 字体样式行布局 */
  1315. .font-style-row {
  1316. display: flex;
  1317. align-items: center;
  1318. gap: 12px;
  1319. flex-wrap: wrap;
  1320. }
  1321. .font-style-row .inline-checkbox {
  1322. margin-right: 0 !important;
  1323. }
  1324. .spacing-controls {
  1325. display: flex;
  1326. align-items: center;
  1327. gap: 6px;
  1328. }
  1329. .spacing-label {
  1330. font-size: 12px;
  1331. color: #606266;
  1332. white-space: nowrap;
  1333. }
  1334. .spacing-input {
  1335. width: 80px !important;
  1336. }
  1337. .spacing-input .el-input__inner {
  1338. height: 28px !important;
  1339. font-size: 12px !important;
  1340. padding: 0 30px 0 8px !important;
  1341. }
  1342. /* 字体预览样式 */
  1343. .font-preview {
  1344. background: #f8f9fa;
  1345. border: 1px solid #e4e7ed;
  1346. border-radius: 4px;
  1347. padding: 8px;
  1348. margin-top: 8px;
  1349. text-align: center;
  1350. min-height: 40px;
  1351. display: flex;
  1352. align-items: center;
  1353. justify-content: center;
  1354. }
  1355. .font-preview-text {
  1356. font-size: 16px;
  1357. color: #333;
  1358. transition: all 0.3s ease;
  1359. }
  1360. .font-debug-info {
  1361. margin-top: 4px;
  1362. text-align: center;
  1363. font-size: 11px;
  1364. line-height: 1.2;
  1365. opacity: 0.8;
  1366. }
  1367. .form-item-half .el-radio__inner {
  1368. width: 12px !important;
  1369. height: 12px !important;
  1370. }
  1371. /* 下拉选择框样式 */
  1372. .form-item-half .el-select {
  1373. width: 100% !important;
  1374. }
  1375. .form-item-half .el-select .el-input__inner {
  1376. height: 32px !important;
  1377. font-size: 13px !important;
  1378. border: 1px solid #dcdfe6 !important;
  1379. border-radius: 6px !important;
  1380. padding: 0 30px 0 12px !important;
  1381. transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
  1382. }
  1383. .form-item-half .el-select .el-input__inner:hover {
  1384. border-color: #c0c4cc !important;
  1385. }
  1386. .form-item-half .el-select .el-input__inner:focus {
  1387. border-color: #409eff !important;
  1388. outline: none !important;
  1389. box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1) !important;
  1390. }
  1391. /* 复选框组样式 */
  1392. .checkbox-group {
  1393. position: relative;
  1394. display: flex;
  1395. flex-direction: column;
  1396. justify-content: flex-start;
  1397. align-items: flex-start;
  1398. }
  1399. .checkbox-label {
  1400. font-size: 12px !important;
  1401. color: #606266;
  1402. margin-bottom: 4px !important;
  1403. font-weight: 500;
  1404. line-height: 1.2 !important;
  1405. height: 14px;
  1406. padding-bottom: 4px !important;
  1407. display: block;
  1408. }
  1409. .checkbox-group .el-checkbox {
  1410. margin-bottom: 2px !important;
  1411. align-self: flex-start;
  1412. height: 16px !important;
  1413. line-height: 16px !important;
  1414. display: flex !important;
  1415. align-items: center !important;
  1416. }
  1417. .checkbox-group .el-checkbox:last-child {
  1418. margin-bottom: 0 !important;
  1419. }
  1420. .checkbox-group .el-checkbox .el-checkbox__input {
  1421. line-height: 1 !important;
  1422. }
  1423. .checkbox-group .el-checkbox .el-checkbox__label {
  1424. line-height: 1 !important;
  1425. padding-left: 4px !important;
  1426. }
  1427. /* 字体大小行布局 */
  1428. .font-size-row {
  1429. display: flex;
  1430. align-items: center;
  1431. gap: 12px;
  1432. }
  1433. .font-size-input {
  1434. width: 120px !important;
  1435. flex-shrink: 0;
  1436. }
  1437. .font-size-input .el-input__inner {
  1438. height: 32px !important;
  1439. font-size: 13px !important;
  1440. border: 1px solid #dcdfe6 !important;
  1441. border-radius: 6px !important;
  1442. padding: 0 35px 0 12px !important;
  1443. background-color: #fff !important;
  1444. transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
  1445. }
  1446. .font-size-input .el-input__inner:hover {
  1447. border-color: #c0c4cc !important;
  1448. }
  1449. .font-size-input .el-input__inner:focus {
  1450. border-color: #409eff !important;
  1451. outline: none !important;
  1452. box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1) !important;
  1453. }
  1454. .font-size-input .el-input__increase,
  1455. .font-size-input .el-input__decrease {
  1456. position: absolute !important;
  1457. right: 1px !important;
  1458. width: 32px !important;
  1459. height: 15px !important;
  1460. line-height: 15px !important;
  1461. background: #f5f7fa !important;
  1462. color: #606266 !important;
  1463. cursor: pointer !important;
  1464. font-size: 12px !important;
  1465. border: none !important;
  1466. border-radius: 0 !important;
  1467. transition: all 0.2s !important;
  1468. }
  1469. .font-size-input .el-input__increase {
  1470. top: 1px !important;
  1471. border-radius: 0 5px 0 0 !important;
  1472. }
  1473. .font-size-input .el-input__decrease {
  1474. bottom: 1px !important;
  1475. border-radius: 0 0 5px 0 !important;
  1476. }
  1477. .font-size-input .el-input__increase:hover,
  1478. .font-size-input .el-input__decrease:hover {
  1479. background: #e6a23c !important;
  1480. color: #fff !important;
  1481. }
  1482. .font-size-input .el-input__increase:active,
  1483. .font-size-input .el-input__decrease:active {
  1484. background: #cf9236 !important;
  1485. }
  1486. .inline-checkbox {
  1487. margin: 0 !important;
  1488. height: 28px !important;
  1489. line-height: 28px !important;
  1490. display: flex !important;
  1491. align-items: center !important;
  1492. }
  1493. .inline-checkbox .el-checkbox__label {
  1494. font-size: 12px !important;
  1495. padding-left: 6px !important;
  1496. line-height: 1 !important;
  1497. }
  1498. .inline-checkbox .el-checkbox__input {
  1499. line-height: 1 !important;
  1500. }
  1501. /* 统一所有输入框样式 */
  1502. .form-section .el-input {
  1503. width: 150px !important;
  1504. }
  1505. .form-section .el-input .el-input__inner {
  1506. height: 32px !important;
  1507. font-size: 13px !important;
  1508. border: 1px solid #dcdfe6 !important;
  1509. border-radius: 6px !important;
  1510. padding: 0 35px 0 12px !important;
  1511. transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
  1512. }
  1513. .form-section .el-input .el-input__inner:hover {
  1514. border-color: #c0c4cc !important;
  1515. }
  1516. .form-section .el-input .el-input__inner:focus {
  1517. border-color: #409eff !important;
  1518. outline: none !important;
  1519. box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1) !important;
  1520. }
  1521. .form-section .el-input .el-input__increase,
  1522. .form-section .el-input .el-input__decrease {
  1523. position: absolute !important;
  1524. right: 1px !important;
  1525. width: 32px !important;
  1526. height: 15px !important;
  1527. line-height: 15px !important;
  1528. background: #f5f7fa !important;
  1529. color: #606266 !important;
  1530. cursor: pointer !important;
  1531. font-size: 12px !important;
  1532. border: none !important;
  1533. border-radius: 0 !important;
  1534. transition: all 0.2s !important;
  1535. }
  1536. .form-section .el-input .el-input__increase {
  1537. top: 1px !important;
  1538. border-radius: 0 5px 0 0 !important;
  1539. }
  1540. .form-section .el-input .el-input__decrease {
  1541. bottom: 1px !important;
  1542. border-radius: 0 0 5px 0 !important;
  1543. }
  1544. .form-section .el-input .el-input__increase:hover,
  1545. .form-section .el-input .el-input__decrease:hover {
  1546. background: #e6a23c !important;
  1547. color: #fff !important;
  1548. }
  1549. .form-section .el-input .el-input__increase:active,
  1550. .form-section .el-input .el-input__decrease:active {
  1551. background: #cf9236 !important;
  1552. }
  1553. </style>