PenProps.vue 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360
  1. <template>
  2. <div class="props">
  3. <t-tabs v-model="data.tab">
  4. <t-tab-panel :value="1" label="外观">
  5. <t-space direction="vertical" class="py-16 w-full">
  6. <div class="form-item px-12">
  7. <label style="width: 50px">ID</label>
  8. <t-input
  9. class="w-full"
  10. placeholder="ID"
  11. :value="data.pen.id"
  12. @change="changeID($event)"
  13. />
  14. </div>
  15. <div class="form-item px-12" style="margin-top: -12px">
  16. <label style="width: 50px">名称</label>
  17. <t-input
  18. class="w-full"
  19. placeholder="名称"
  20. v-model.number="data.pen.name"
  21. @change="changeValue('name')"
  22. />
  23. </div>
  24. <div class="form-item px-12" style="margin-top: -12px">
  25. <label style="width: 50px">分组</label>
  26. <t-select-input
  27. v-model:inputValue="data.inputTag"
  28. :value="data.pen.tags"
  29. v-model:popupVisible="data.tagPopupVisible"
  30. allow-input
  31. placeholder="请输入或选择分组"
  32. multiple
  33. @tag-change="onChangeInputTag"
  34. @focus="data.tagPopupVisible = true"
  35. @blur="data.tagPopupVisible = false"
  36. :tag-input-props="{ excessTagsDisplayType: 'scroll' }"
  37. >
  38. <template #panel>
  39. <ul style="padding: 8px 12px">
  40. <li
  41. v-for="item in data.groups"
  42. :key="item"
  43. @click="onSelectTag(item)"
  44. >
  45. {{ item }}
  46. </li>
  47. </ul>
  48. </template>
  49. </t-select-input>
  50. </div>
  51. <t-divider style="margin: -8px 0" />
  52. <div class="form-item" style="margin-top: -12px">
  53. <t-input
  54. class="ml-4"
  55. label="X"
  56. placeholder="X"
  57. v-model.number="data.rect.x"
  58. style="width: 80px"
  59. :format="decimalPlaces"
  60. @change="changeRectValue('x')"
  61. />
  62. <t-icon name="link" class="hidden ml-4" />
  63. <t-input
  64. class="ml-4"
  65. label="Y"
  66. placeholder="Y"
  67. v-model.number="data.rect.y"
  68. style="width: 80px"
  69. :format="decimalPlaces"
  70. @change="changeRectValue('y')"
  71. />
  72. <t-input
  73. class="ml-16"
  74. v-model.number="data.pen.rotate"
  75. placeholder="旋转"
  76. style="width: 80px"
  77. :format="decimalRound"
  78. @change="changeValue('rotate')"
  79. >
  80. <template #prefix-icon>
  81. <svg class="l-icon" aria-hidden="true">
  82. <use xlink:href="#l-rotate"></use>
  83. </svg>
  84. </template>
  85. </t-input>
  86. </div>
  87. <div class="form-item hover-icons" style="margin-top: -12px">
  88. <t-input
  89. class="ml-4"
  90. label="W"
  91. v-model.number="data.rect.width"
  92. placeholder="宽"
  93. min="1"
  94. style="width: 80px"
  95. :format="decimalPlaces"
  96. @change="changeRectValue('width')"
  97. />
  98. <t-tooltip v-if="data.pen.ratio" content="固定比例" placement="top">
  99. <t-icon
  100. name="link"
  101. class="ml-4 hover"
  102. @click="data.pen.ratio = !data.pen.ratio"
  103. />
  104. </t-tooltip>
  105. <t-tooltip v-else content="不固定比例" placement="top">
  106. <t-icon
  107. name="link-unlink"
  108. class="ml-4 hover icon"
  109. @click="data.pen.ratio = !data.pen.ratio"
  110. />
  111. </t-tooltip>
  112. <t-input
  113. class="ml-4"
  114. label="H"
  115. placeholder="高"
  116. v-model.number="data.rect.height"
  117. min="1"
  118. style="width: 80px"
  119. :format="decimalPlaces"
  120. @change="changeRectValue('height')"
  121. />
  122. <t-input
  123. class="ml-16"
  124. v-model.number="data.pen.borderRadius"
  125. placeholder="圆角"
  126. style="width: 80px"
  127. @change="changeValue('borderRadius')"
  128. >
  129. <template #prefix-icon>
  130. <svg class="l-icon" aria-hidden="true">
  131. <use xlink:href="#l-border-radius"></use>
  132. </svg>
  133. </template>
  134. </t-input>
  135. </div>
  136. <t-divider style="margin: -8px 0" />
  137. <div class="form-item px-16" style="margin-top: -12px">
  138. <label>不透明度</label>
  139. <t-slider
  140. v-model="data.pen.globalAlpha"
  141. :min="0"
  142. :max="1"
  143. :step="0.01"
  144. @change="changeValue('globalAlpha')"
  145. />
  146. <span class="ml-16" style="width: 50px; line-height: 30px">
  147. {{ data.pen.globalAlpha }}
  148. </span>
  149. </div>
  150. <t-collapse
  151. :defaultValue="['1', '2', '3', '4', '5']"
  152. expandIconPlacement="right"
  153. :borderless="true"
  154. >
  155. <t-collapse-panel
  156. v-if="data.pen.props.look !== false"
  157. value="1"
  158. header="外观"
  159. >
  160. <t-space direction="vertical" size="small" class="w-full">
  161. <div class="form-item">
  162. <t-color-picker
  163. class="simple mt-8 mr-4"
  164. format="CSS"
  165. :enable-alpha="true"
  166. :color-modes="['monochrome']"
  167. :show-primary-color-preview="false"
  168. :clearable="true"
  169. v-model="data.pen.color"
  170. @change="changeValue('color')"
  171. />
  172. <label style="width: 64px">前景颜色</label>
  173. <t-color-picker
  174. class="simple mt-8 mr-4"
  175. format="CSS"
  176. :enable-alpha="true"
  177. :color-modes="['monochrome']"
  178. :show-primary-color-preview="false"
  179. v-model="data.pen.hoverColor"
  180. @change="changeValue('hoverColor')"
  181. />
  182. <label style="width: 64px">悬停颜色</label>
  183. <t-color-picker
  184. class="simple mt-8 mr-4"
  185. format="CSS"
  186. :enable-alpha="true"
  187. :color-modes="['monochrome']"
  188. :show-primary-color-preview="false"
  189. v-model="data.pen.activeColor"
  190. @change="changeValue('activeColor')"
  191. />
  192. <label style="width: 64px">选中颜色</label>
  193. </div>
  194. <div class="form-item">
  195. <label style="width: 32px">线条 </label>
  196. <t-select
  197. v-model="data.pen.dash"
  198. placeholder="线条样式"
  199. @change="changeValue('dash')"
  200. style="width: 80px"
  201. >
  202. <template #valueDisplay="{ value }">
  203. <svg
  204. xmlns="http://www.w3.org/2000/svg"
  205. version="1.1"
  206. style="width: 100%; height: 20px"
  207. >
  208. <g fill="none" stroke="var(--color)" stroke-width="1">
  209. <path v-if="value === 0" d="M0 9 l85 0" />
  210. <path
  211. v-else-if="value === 1"
  212. stroke-dasharray="5 5"
  213. d="M0 9 l85 0"
  214. />
  215. <path
  216. v-else-if="value === 2"
  217. stroke-dasharray="10 10"
  218. d="M0 9 l85 0"
  219. />
  220. <path
  221. v-else-if="value === 3"
  222. stroke-dasharray="10 10 2 10"
  223. d="M0 9 l85 0"
  224. />
  225. </g>
  226. </svg>
  227. </template>
  228. <t-option :key="0" :value="0">
  229. <svg
  230. xmlns="http://www.w3.org/2000/svg"
  231. version="1.1"
  232. style="width: 80px; height: 14px"
  233. >
  234. <g fill="none" stroke="var(--color)" stroke-width="1">
  235. <path d="M0 9 l85 0" />
  236. </g>
  237. </svg>
  238. </t-option>
  239. <t-option :key="1" :value="1">
  240. <svg
  241. xmlns="http://www.w3.org/2000/svg"
  242. version="1.1"
  243. style="width: 80px; height: 14px"
  244. >
  245. <g fill="none" stroke="var(--color)" stroke-width="1">
  246. <path stroke-dasharray="5 5" d="M0 9 l85 0" />
  247. </g>
  248. </svg>
  249. </t-option>
  250. <t-option :key="2" :value="2">
  251. <svg
  252. xmlns="http://www.w3.org/2000/svg"
  253. version="1.1"
  254. style="width: 80px; height: 14px"
  255. >
  256. <g fill="none" stroke="var(--color)" stroke-width="1">
  257. <path stroke-dasharray="10 10" d="M0 9 l85 0" />
  258. </g>
  259. </svg>
  260. </t-option>
  261. <t-option :key="3" :value="3">
  262. <svg
  263. xmlns="http://www.w3.org/2000/svg"
  264. version="1.1"
  265. style="width: 80px; height: 14px"
  266. >
  267. <g fill="none" stroke="var(--color)" stroke-width="1">
  268. <path stroke-dasharray="10 10 2 10" d="M0 9 l85 0" />
  269. </g>
  270. </svg>
  271. </t-option>
  272. </t-select>
  273. <t-input-number
  274. theme="normal"
  275. placeholder="线条宽度"
  276. v-model="data.pen.lineWidth"
  277. :min="0"
  278. :decimalPlaces="0"
  279. @change="changeValue('lineWidth')"
  280. class="ml-4"
  281. style="width: 40px"
  282. />
  283. <t-tooltip content="线条渐变" placement="top">
  284. <div class="flex middle ml-8">
  285. <t-checkbox
  286. v-model="data.pen.strokeType"
  287. @change="changeValue('strokeType')"
  288. style="width: 22px"
  289. />
  290. <t-color-picker
  291. v-if="data.pen.strokeType"
  292. class="simple mr-4"
  293. format="CSS"
  294. :color-modes="['linear-gradient']"
  295. :show-primary-color-preview="false"
  296. :clearable="true"
  297. :enableAlpha="true"
  298. v-model="data.pen.lineGradientColors"
  299. @change="changeValue('lineGradientColors')"
  300. placeholder="无"
  301. />
  302. </div>
  303. </t-tooltip>
  304. </div>
  305. <div class="flex" style="margin-left: 40px">
  306. <div class="flex column middle">
  307. <t-radio-group
  308. size="small"
  309. v-model="data.pen.lineCap"
  310. default-value="butt"
  311. @change="changeValue('lineCap')"
  312. >
  313. <t-radio-button value="butt">
  314. <t-tooltip content="默认" placement="top">
  315. <svg class="l-icon" aria-hidden="true">
  316. <use xlink:href="#l-duandian1"></use>
  317. </svg>
  318. </t-tooltip>
  319. </t-radio-button>
  320. <t-radio-button value="round">
  321. <t-tooltip content="圆形" placement="top">
  322. <svg class="l-icon" aria-hidden="true">
  323. <use xlink:href="#l-duandian2"></use>
  324. </svg>
  325. </t-tooltip>
  326. </t-radio-button>
  327. <t-radio-button value="square">
  328. <t-tooltip content="方形" placement="top">
  329. <svg class="l-icon" aria-hidden="true">
  330. <use xlink:href="#l-duandian3"></use>
  331. </svg>
  332. </t-tooltip>
  333. </t-radio-button>
  334. </t-radio-group>
  335. <div class="mt-4" style="font-size: 12px">末端样式</div>
  336. </div>
  337. <div class="flex column middle ml-16">
  338. <t-radio-group
  339. size="small"
  340. v-model="data.pen.lineJoin"
  341. default-value="miter"
  342. @change="changeValue('lineJoin')"
  343. >
  344. <t-radio-button value="miter">
  345. <t-tooltip content="默认" placement="top">
  346. <svg class="l-icon" aria-hidden="true">
  347. <use xlink:href="#l-jiedian1"></use>
  348. </svg>
  349. </t-tooltip>
  350. </t-radio-button>
  351. <t-radio-button value="round">
  352. <t-tooltip content="圆形" placement="top">
  353. <svg class="l-icon" aria-hidden="true">
  354. <use xlink:href="#l-jiedian2"></use>
  355. </svg>
  356. </t-tooltip>
  357. </t-radio-button>
  358. <t-radio-button value="bevel">
  359. <t-tooltip content="斜角" placement="top">
  360. <svg class="l-icon" aria-hidden="true">
  361. <use xlink:href="#l-jiedian3"></use>
  362. </svg>
  363. </t-tooltip>
  364. </t-radio-button>
  365. </t-radio-group>
  366. <div class="mt-4" style="font-size: 12px">连接样式</div>
  367. </div>
  368. </div>
  369. <div class="form-item">
  370. <label style="width: 32px">背景</label>
  371. <div class="ml-8">
  372. <t-radio-group
  373. size="small"
  374. v-model="data.pen.bkType"
  375. :default-value="0"
  376. @change="changeValue('bkType')"
  377. >
  378. <t-radio-button :value="0"> 纯色 </t-radio-button>
  379. <t-radio-button :value="1"> 线性渐变 </t-radio-button>
  380. <t-radio-button :value="2"> 径向渐变 </t-radio-button>
  381. </t-radio-group>
  382. <div v-if="data.pen.bkType === 0" class="mt-8 -ml-8">
  383. <t-color-picker
  384. class="w-full"
  385. format="CSS"
  386. :color-modes="['monochrome']"
  387. :enable-alpha="true"
  388. :show-primary-color-preview="false"
  389. enable-alpha
  390. v-model="data.pen.background"
  391. @change="changeValue('background')"
  392. />
  393. </div>
  394. <div
  395. v-else-if="data.pen.bkType === 1"
  396. class="mt-8 -ml-8"
  397. style="width: 200px"
  398. >
  399. <t-color-picker
  400. class="w-full"
  401. format="CSS"
  402. :enable-alpha="true"
  403. :color-modes="['linear-gradient']"
  404. :show-primary-color-preview="false"
  405. v-model="data.pen.gradientColors"
  406. @change="changeValue('gradientColors')"
  407. />
  408. </div>
  409. <div
  410. v-else-if="data.pen.bkType === 2"
  411. class="mt-8 flex middle"
  412. >
  413. <t-color-picker
  414. class="simple"
  415. format="CSS"
  416. :enable-alpha="true"
  417. :color-modes="['linear-gradient']"
  418. :show-primary-color-preview="false"
  419. v-model="data.pen.gradientColors"
  420. @change="changeValue('gradientColors')"
  421. />
  422. <t-input-number
  423. theme="column"
  424. placeholder="渐变半径"
  425. v-model="data.pen.gradientRadius"
  426. :min="0"
  427. :max="1"
  428. :step="0.1"
  429. @change="changeValue('gradientRadius')"
  430. class="ml-8"
  431. style="width: 100px"
  432. />
  433. </div>
  434. </div>
  435. </div>
  436. <div class="form-item">
  437. <label style="width: 32px">阴影 </label>
  438. <div class="flex middle ml-8">
  439. <t-checkbox
  440. v-model="data.pen.shadow"
  441. @change="changeValue('shadow')"
  442. style="width: 22px"
  443. />
  444. <t-color-picker
  445. v-if="data.pen.shadow"
  446. class="simple"
  447. format="CSS"
  448. :enable-alpha="true"
  449. :color-modes="['monochrome']"
  450. :show-primary-color-preview="false"
  451. v-model="data.pen.shadowColor"
  452. @change="changeValue('shadowColor')"
  453. />
  454. </div>
  455. </div>
  456. <div class="form-item" v-if="data.pen.shadow">
  457. <label style="width: 28px"></label>
  458. <div class="flex" style="margin-top: -8px">
  459. <t-input
  460. class="ml-4"
  461. label="X"
  462. placeholder="0"
  463. v-model.number="data.pen.shadowOffsetX"
  464. style="width: 60px"
  465. @change="changeValue('shadowOffsetX')"
  466. title="X偏移"
  467. />
  468. <t-input
  469. class="ml-4"
  470. label="Y"
  471. placeholder="0"
  472. v-model.number="data.pen.shadowOffsetY"
  473. style="width: 60px"
  474. @change="changeValue('shadowOffsetY')"
  475. title="Y偏移"
  476. />
  477. <t-input
  478. class="ml-4"
  479. label="模糊"
  480. placeholder="0"
  481. v-model.number="data.pen.shadowBlur"
  482. style="width: 64px"
  483. @change="changeValue('shadowBlur')"
  484. title="模糊大小"
  485. />
  486. </div>
  487. </div>
  488. </t-space>
  489. </t-collapse-panel>
  490. <t-collapse-panel
  491. v-if="data.pen.props.text"
  492. value="2"
  493. header="文字"
  494. >
  495. <t-space direction="vertical" size="small" class="w-full">
  496. <div class="form-item">
  497. <div class="flex middle" style="margin-left: -10px">
  498. <t-select-input
  499. :value="data.pen.fontFamily"
  500. :popup-visible="data.fontFamilyPopupVisible"
  501. placeholder="字体名"
  502. allow-input
  503. style="width: 170px"
  504. @change="changeValue('fontFamily')"
  505. @enter="changeValue('fontFamily')"
  506. @blur="changeValue('fontFamily')"
  507. @popup-visible-change="onFontPopupVisible"
  508. :popup-props="{
  509. overlayInnerStyle: { width: 'auto' },
  510. }"
  511. >
  512. <template #panel>
  513. <ul style="padding: 12px">
  514. <li
  515. v-for="item in fonts"
  516. :key="item"
  517. @click="onFontFamily(item)"
  518. >
  519. {{ item }}
  520. </li>
  521. </ul>
  522. </template>
  523. <template #suffixIcon>
  524. <t-icon name="chevron-down" />
  525. </template>
  526. </t-select-input>
  527. <t-input
  528. class="ml-8"
  529. placeholder="字体大小"
  530. v-model.number="data.pen.fontSize"
  531. style="width: 80px"
  532. :format="decimalRound"
  533. @change="changeValue('fontSize')"
  534. />
  535. </div>
  536. </div>
  537. <div class="flex middle">
  538. <t-radio-group
  539. size="small"
  540. v-model="data.pen.textAlign"
  541. default-value="center"
  542. @change="changeValue('textAlign')"
  543. >
  544. <t-radio-button value="left">
  545. <t-tooltip content="居左" placement="top">
  546. <t-icon name="format-vertical-align-left" />
  547. </t-tooltip>
  548. </t-radio-button>
  549. <t-radio-button value="center">
  550. <t-tooltip content="居中" placement="top">
  551. <t-icon name="format-vertical-align-center" />
  552. </t-tooltip>
  553. </t-radio-button>
  554. <t-radio-button value="right">
  555. <t-tooltip content="居右" placement="top">
  556. <t-icon name="format-vertical-align-right" />
  557. </t-tooltip>
  558. </t-radio-button>
  559. </t-radio-group>
  560. <t-radio-group
  561. class="ml-8"
  562. size="small"
  563. v-model="data.pen.textBaseline"
  564. default-value="top"
  565. @change="changeValue('textBaseline')"
  566. >
  567. <t-radio-button value="top">
  568. <t-tooltip content="顶部对齐" placement="top">
  569. <t-icon name="format-horizontal-align-top" />
  570. </t-tooltip>
  571. </t-radio-button>
  572. <t-radio-button value="middle">
  573. <t-tooltip content="垂直居中" placement="middle">
  574. <t-icon name="format-horizontal-align-center" />
  575. </t-tooltip>
  576. </t-radio-button>
  577. <t-radio-button value="bottom">
  578. <t-tooltip content="底部对齐" placement="top">
  579. <t-icon name="format-horizontal-align-bottom" />
  580. </t-tooltip>
  581. </t-radio-button>
  582. </t-radio-group>
  583. <t-button
  584. :class="{ active: data.pen.fontWeight === 'bold' }"
  585. class="ml-8 icon"
  586. shape="rectangle"
  587. variant="text"
  588. @click="
  589. data.pen.fontWeight === 'bold'
  590. ? (data.pen.fontWeight = 'normal')
  591. : (data.pen.fontWeight = 'bold');
  592. changeValue('fontWeight');
  593. "
  594. >
  595. B
  596. </t-button>
  597. <t-button
  598. :class="{ active: data.pen.fontStyle === 'italic' }"
  599. class="ml-4 icon"
  600. shape="rectangle"
  601. variant="text"
  602. @click="
  603. data.pen.fontStyle === 'italic'
  604. ? (data.pen.fontStyle = 'normal')
  605. : (data.pen.fontStyle = 'italic');
  606. changeValue('fontStyle');
  607. "
  608. style="font-style: italic; font-family: serif"
  609. >I</t-button
  610. >
  611. </div>
  612. <div class="form-item">
  613. <t-color-picker
  614. class="simple mt-8 mr-4"
  615. format="CSS"
  616. :enable-alpha="true"
  617. :color-modes="['monochrome']"
  618. :show-primary-color-preview="false"
  619. :clearable="true"
  620. v-model="data.pen.textColor"
  621. @change="changeValue('textColor')"
  622. />
  623. <label style="width: 44px">前景</label>
  624. <t-color-picker
  625. class="simple mt-8 mr-4"
  626. format="CSS"
  627. :enable-alpha="true"
  628. :color-modes="['monochrome']"
  629. :show-primary-color-preview="false"
  630. v-model="data.pen.textBackground"
  631. @change="changeValue('textBackground')"
  632. />
  633. <label style="width: 44px">背景</label>
  634. <t-color-picker
  635. class="simple mt-8 mr-4"
  636. format="CSS"
  637. :enable-alpha="true"
  638. :color-modes="['monochrome']"
  639. :show-primary-color-preview="false"
  640. v-model="data.pen.hoverTextColor"
  641. @change="changeValue('hoverTextColor')"
  642. />
  643. <label style="width: 44px">悬停</label>
  644. <t-color-picker
  645. class="simple mt-8 mr-4"
  646. format="CSS"
  647. :enable-alpha="true"
  648. :color-modes="['monochrome']"
  649. :show-primary-color-preview="false"
  650. v-model="data.pen.activeTextColor"
  651. @change="changeValue('activeTextColor')"
  652. />
  653. <label style="width: 44px">选中</label>
  654. </div>
  655. <div class="form-item">
  656. <t-checkbox
  657. v-model="data.pen.whiteSpace"
  658. @change="changeValue('whiteSpace')"
  659. style="width: 64px"
  660. >
  661. 换行
  662. </t-checkbox>
  663. <t-checkbox
  664. v-model="data.pen.ellipsis"
  665. @change="changeValue('ellipsis')"
  666. style="width: 68px"
  667. >
  668. 省略号
  669. </t-checkbox>
  670. <t-tooltip content="行高">
  671. <t-input
  672. placeholder="行高"
  673. v-model.number="data.pen.lineHeight"
  674. style="width: 40px"
  675. @change="changeValue('lineHeight')"
  676. />
  677. </t-tooltip>
  678. <t-tooltip content="显示时保留小数位数">
  679. <t-input
  680. class="ml-4"
  681. placeholder="小数"
  682. v-model.number="data.pen.keepDecimal"
  683. style="width: 60px"
  684. @change="changeValue('keepDecimal')"
  685. />
  686. </t-tooltip>
  687. </div>
  688. <div class="form-item" style="margin-top: -4px">
  689. <t-tooltip content="水平偏移">
  690. <t-input
  691. placeholder="X"
  692. v-model.number="data.pen.textLeft"
  693. style="width: 60px; margin-left: -8px"
  694. @change="changeValue('textLeft')"
  695. />
  696. </t-tooltip>
  697. <t-tooltip content="垂直偏移">
  698. <t-input
  699. class="ml-4"
  700. placeholder="Y"
  701. v-model.number="data.pen.textTop"
  702. style="width: 60px"
  703. @change="changeValue('textTop')"
  704. />
  705. </t-tooltip>
  706. <t-tooltip content="宽">
  707. <t-input
  708. class="ml-4"
  709. placeholder="宽"
  710. v-model.number="data.pen.textWidth"
  711. style="width: 60px"
  712. @change="changeValue('textWidth')"
  713. />
  714. </t-tooltip>
  715. <t-tooltip content="高">
  716. <t-input
  717. class="ml-4"
  718. placeholder="高"
  719. v-model.number="data.pen.textHeight"
  720. style="width: 60px"
  721. @change="changeValue('textHeight')"
  722. />
  723. </t-tooltip>
  724. </div>
  725. <div class="flex middle">
  726. <t-checkbox
  727. v-model="data.pen.disableInput"
  728. @change="changeValue('disableInput')"
  729. style="width: 64px"
  730. >
  731. 只读
  732. </t-checkbox>
  733. <t-checkbox
  734. v-model="data.pen.hiddenText"
  735. @change="changeValue('hiddenText')"
  736. style="width: 90px"
  737. >
  738. 隐藏文字
  739. </t-checkbox>
  740. <t-checkbox
  741. v-model="data.pen.textAutoAdjust"
  742. @change="changeValue('textAutoAdjust')"
  743. style="width: 90px"
  744. >
  745. 自动调整
  746. </t-checkbox>
  747. </div>
  748. </t-space>
  749. </t-collapse-panel>
  750. <t-collapse-panel
  751. v-if="data.pen.props.image"
  752. value="3"
  753. header="图片"
  754. >
  755. <t-space direction="vertical" size="small" class="w-full">
  756. <div>
  757. <t-upload
  758. ref="uploadRef"
  759. v-model="data.images"
  760. action="/api/image/upload"
  761. theme="image"
  762. accept="image/*"
  763. :headers="headers"
  764. :data="updataData"
  765. :before-upload="beforeUpload"
  766. draggable
  767. @success="fileSuccessed"
  768. @remove="fileRemoved"
  769. >
  770. <template #fileListDisplay>
  771. <div style="z-index: 20">
  772. <a class="mr-4" @click="upload"> 点击上传 </a>
  773. / 拖拽图片到此区域
  774. </div>
  775. </template>
  776. </t-upload>
  777. </div>
  778. <div class="form-item hover-icons" style="margin-left: -12px">
  779. <t-input
  780. class="ml-4"
  781. label="W"
  782. v-model.number="data.pen.iconWidth"
  783. placeholder="自适应"
  784. min="1"
  785. style="width: 80px"
  786. :format="decimalPlaces"
  787. @change="changeValue('iconWidth')"
  788. />
  789. <t-tooltip
  790. v-if="data.pen.imageRatio"
  791. content="固定比例"
  792. placement="top"
  793. >
  794. <t-icon
  795. name="link"
  796. class="ml-4 hover"
  797. @click="data.pen.imageRatio = !data.pen.imageRatio"
  798. />
  799. </t-tooltip>
  800. <t-tooltip v-else content="不固定比例" placement="top">
  801. <t-icon
  802. name="link-unlink"
  803. class="ml-4 hover icon"
  804. @click="data.pen.imageRatio = !data.pen.imageRatio"
  805. />
  806. </t-tooltip>
  807. <t-input
  808. class="ml-4"
  809. label="H"
  810. placeholder="自适应"
  811. v-model.number="data.pen.iconHeight"
  812. min="1"
  813. style="width: 80px"
  814. :format="decimalPlaces"
  815. @change="changeValue('iconHeight')"
  816. />
  817. <t-checkbox
  818. class="ml-8"
  819. v-model="data.pen.isBottom"
  820. @change="changeValue('isBottom')"
  821. >
  822. 置底
  823. </t-checkbox>
  824. </div>
  825. <div class="flex">
  826. <label style="width: 30px; color: var(--color)">Url:</label>
  827. {{ data.pen.image }}
  828. </div>
  829. </t-space>
  830. </t-collapse-panel>
  831. <t-collapse-panel
  832. v-if="data.pen.props.icon"
  833. value="4"
  834. header="图标"
  835. >
  836. <t-space direction="vertical" size="small" class="w-full">
  837. <div class="form-item">
  838. <label style="width: 32px">图标 </label>
  839. <i
  840. class="ml-8"
  841. :class="data.pen.iconFamily"
  842. style="line-height: 30px; height: 30px; color: var(--color)"
  843. >
  844. {{ data.pen.icon }}
  845. </i>
  846. <a class="ml-12 mt-4" @click="iconsDrawer.show = true">
  847. 选择
  848. </a>
  849. <t-drawer
  850. v-model:visible="iconsDrawer.show"
  851. header="选择图标"
  852. :footer="null"
  853. >
  854. <Iconfonts :urls="data.iconUrls" @change="onChangeIcon" />
  855. </t-drawer>
  856. </div>
  857. </t-space>
  858. </t-collapse-panel>
  859. <t-collapse-panel
  860. v-if="data.pen.props.custom"
  861. value="5"
  862. header="属性"
  863. >
  864. <t-space direction="vertical" size="small" class="w-full">
  865. <div v-for="item in data.pen.props.custom" class="form-item">
  866. <label>{{ item.label }}</label>
  867. <t-checkbox
  868. class="ml-8"
  869. v-if="item.type === 'bool'"
  870. v-model="data.pen[item.key]"
  871. @change="changeValue(item.key)"
  872. />
  873. <t-input-number
  874. class="w-full"
  875. v-else-if="item.type === 'number'"
  876. v-model.number="data.pen[item.key]"
  877. theme="column"
  878. :max="item.max"
  879. :min="item.min"
  880. @change="changeValue(item.key)"
  881. :placeholder="item.placeholder"
  882. />
  883. <t-color-picker
  884. class="w-full"
  885. v-else-if="item.type === 'color'"
  886. :enable-alpha="true"
  887. format="CSS"
  888. :color-modes="['monochrome']"
  889. :show-primary-color-preview="false"
  890. v-model="data.pen[item.key]"
  891. @change="changeValue(item.key)"
  892. :placeholder="item.placeholder"
  893. />
  894. <t-select
  895. class="w-full"
  896. v-else-if="item.type === 'select'"
  897. size="small"
  898. :options="item.options"
  899. v-model="data.pen[item.key]"
  900. @change="changeValue(item.key)"
  901. :placeholder="item.placeholder"
  902. />
  903. <t-button
  904. v-else-if="item.type === 'code'"
  905. shape="square"
  906. variant="outline"
  907. style="width: 24px"
  908. @click="showPropsEdit(item)"
  909. >
  910. <t-icon name="ellipsis" slot="icon"
  911. /></t-button>
  912. <t-input
  913. class="w-full"
  914. v-else
  915. v-model="data.pen[item.key]"
  916. @change="changeValue(item.key)"
  917. :placeholder="item.placeholder"
  918. />
  919. </div>
  920. </t-space>
  921. </t-collapse-panel>
  922. </t-collapse>
  923. <t-divider style="margin: -8px 0" />
  924. <div class="form-item px-16" style="margin-top: -12px">
  925. <t-checkbox
  926. v-model="data.pen.flipX"
  927. @change="changeValue('flipX')"
  928. style="width: 90px"
  929. >
  930. 水平翻转
  931. </t-checkbox>
  932. <t-checkbox
  933. v-model="data.pen.flipY"
  934. @change="changeValue('flipY')"
  935. style="width: 90px"
  936. >
  937. 垂直翻转
  938. </t-checkbox>
  939. <label style="width: 50px">锚点半径</label>
  940. <input
  941. class="ml-4"
  942. v-model.number="data.pen.anchorRadius"
  943. style="width: 20px"
  944. @change="changeValue('anchorRadius')"
  945. placeholder="4"
  946. />
  947. </div>
  948. <t-divider style="margin: -8px 0" />
  949. <div class="form-item px-16" style="margin-top: -12px">
  950. <t-checkbox
  951. v-model="data.pen.disableRotate"
  952. @change="changeValue('disableRotate')"
  953. style="width: 90px"
  954. >
  955. 禁止旋转
  956. </t-checkbox>
  957. <t-checkbox
  958. v-model="data.pen.disableSize"
  959. @change="changeValue('disableSize')"
  960. style="width: 90px"
  961. >
  962. 禁止缩放
  963. </t-checkbox>
  964. <t-checkbox
  965. v-model="data.pen.disableAnchor"
  966. @change="changeValue('disableAnchor')"
  967. style="width: 90px"
  968. >
  969. 禁用锚点
  970. </t-checkbox>
  971. </div>
  972. <t-divider style="margin: -8px 0" />
  973. <div class="form-item px-16" style="margin-top: -12px">
  974. <label style="width: 60px">鼠标提示</label>
  975. <t-button
  976. shape="square"
  977. variant="outline"
  978. style="width: 24px"
  979. @click="showTooltip"
  980. >
  981. <t-icon name="ellipsis" slot="icon"
  982. /></t-button>
  983. </div>
  984. <t-dialog
  985. v-if="tooltipDialog.show"
  986. :visible="true"
  987. header="鼠标提示"
  988. @confirm="onOkTooltip"
  989. @close="tooltipDialog.show = false"
  990. :width="700"
  991. >
  992. <t-radio-group v-model="tooltipDialog.type">
  993. <t-radio value="1">文字</t-radio>
  994. <t-radio value="2">函数</t-radio>
  995. </t-radio-group>
  996. <div class="py-8">
  997. <CodeEditor
  998. v-show="tooltipDialog.type == 1"
  999. v-model="tooltipDialog.title"
  1000. style="height: 300px"
  1001. />
  1002. <div v-show="tooltipDialog.type == 2">
  1003. <div>function tooltip(pen) {</div>
  1004. <CodeEditor
  1005. v-model="tooltipDialog.titleFnJs"
  1006. class="mt-4"
  1007. style="height: 248px"
  1008. />
  1009. <div class="mt-4">}</div>
  1010. </div>
  1011. </div>
  1012. <div class="gray" style="font-size: 12px">支持Markdown格式</div>
  1013. </t-dialog>
  1014. <t-dialog
  1015. v-if="propsDialog.show"
  1016. :visible="true"
  1017. :header="propsDialog.header"
  1018. @confirm="onOkPropsEdit"
  1019. @close="propsDialog.show = false"
  1020. :width="700"
  1021. >
  1022. <div class="py-8">
  1023. <CodeEditor
  1024. :json="true"
  1025. :language="'json'"
  1026. v-model="propsDialog.value"
  1027. style="height: 300px"
  1028. />
  1029. </div>
  1030. <div class="gray" style="font-size: 12px">
  1031. {{ propsDialog.placeholder }}
  1032. </div>
  1033. </t-dialog>
  1034. <t-space />
  1035. </t-space>
  1036. </t-tab-panel>
  1037. <t-tab-panel :value="2" label="动画">
  1038. <PenAnimates :pen="data.pen" />
  1039. </t-tab-panel>
  1040. <t-tab-panel :value="3" label="数据">
  1041. <PenDatas :pen="data.pen" />
  1042. </t-tab-panel>
  1043. <t-tab-panel :value="4" label="交互">
  1044. <PenEvents :pen="data.pen" />
  1045. </t-tab-panel>
  1046. <t-tab-panel :value="5" label="结构">
  1047. <ElementTree />
  1048. </t-tab-panel>
  1049. </t-tabs>
  1050. </div>
  1051. </template>
  1052. <script lang="ts" setup>
  1053. import { onBeforeMount, onUnmounted, reactive, ref, watch } from 'vue';
  1054. import CodeEditor from './common/CodeEditor.vue';
  1055. import Iconfonts from './common/Iconfonts.vue';
  1056. import PenAnimates from './PenAnimates.vue';
  1057. import PenDatas from './PenDatas.vue';
  1058. import PenEvents from './PenEvents.vue';
  1059. import ElementTree from './ElementTree.vue';
  1060. import { getCookie } from '@/services/cookie';
  1061. import { useSelection } from '@/services/selections';
  1062. import { autoSave, fonts, inTreePanel } from '@/services/common';
  1063. import { updatePen } from './pen';
  1064. import { MessagePlugin } from 'tdesign-vue-next';
  1065. import { useUser } from '@/services/user';
  1066. const { user } = useUser();
  1067. const headers = {
  1068. Authorization: 'Bearer ' + (localStorage.token || getCookie('token') || ''),
  1069. };
  1070. const updataData = { directory: '/项目' };
  1071. const uploadRef = ref();
  1072. const data = reactive<any>({
  1073. tab: 1,
  1074. pen: {},
  1075. rect: {},
  1076. });
  1077. const { selections } = useSelection();
  1078. const tooltipDialog = reactive<any>({
  1079. show: false,
  1080. });
  1081. const propsDialog = reactive<any>({
  1082. show: false,
  1083. });
  1084. const iconsDrawer = reactive<any>({
  1085. show: false,
  1086. });
  1087. onBeforeMount(() => {
  1088. if (inTreePanel.value) {
  1089. data.tab = 5;
  1090. }
  1091. const d = meta2d.store.data as any;
  1092. if (!d.groups) {
  1093. d.groups = [];
  1094. }
  1095. if (!d.iconUrls) {
  1096. d.iconUrls = [];
  1097. }
  1098. data.iconUrls = d.iconUrls;
  1099. data.groups = d.groups;
  1100. initPenData();
  1101. meta2d.on('translatePens', getRect);
  1102. meta2d.on('resizePens', getRect);
  1103. meta2d.on('rotatePens', getRect);
  1104. });
  1105. function initPenData() {
  1106. data.pen = selections.pen;
  1107. if (!data.pen.props) {
  1108. data.pen.props = {};
  1109. }
  1110. if (!data.pen.globalAlpha) {
  1111. data.pen.globalAlpha = 1;
  1112. }
  1113. if (!data.pen.dash) {
  1114. data.pen.dash = 0;
  1115. }
  1116. if (!data.pen.props.text) {
  1117. if (data.pen.text || data.pen.name === 'text') {
  1118. data.pen.props.text = true;
  1119. }
  1120. }
  1121. if (!data.pen.props.image) {
  1122. if (data.pen.image) {
  1123. data.pen.props.image = true;
  1124. }
  1125. }
  1126. if (!data.pen.props.icon) {
  1127. if (data.pen.icon) {
  1128. data.pen.props.icon = true;
  1129. }
  1130. }
  1131. if (data.pen.image) {
  1132. data.images = [
  1133. {
  1134. url: data.pen.image,
  1135. },
  1136. ];
  1137. }
  1138. if (!data.pen.tags) {
  1139. data.pen.tags = [];
  1140. }
  1141. if (data.pen.bkType == undefined) {
  1142. data.pen.bkType = 0;
  1143. }
  1144. if (!data.pen.animations) {
  1145. data.pen.animations = [];
  1146. }
  1147. data.pen.shadow = !!data.pen.shadowColor;
  1148. getRect();
  1149. }
  1150. const watcher = watch(() => selections.pen.id, initPenData);
  1151. const getRect = () => {
  1152. data.rect = meta2d.getPenRect(data.pen);
  1153. };
  1154. const decimalPlaces = (val: number) => {
  1155. if (!val) {
  1156. return 0;
  1157. }
  1158. return Math.round(+val * 100) / 100;
  1159. };
  1160. const decimalRound = (val: number) => {
  1161. return Math.round(+val || 0);
  1162. };
  1163. const changeValue = (prop: string) => {
  1164. updatePen(data.pen, prop);
  1165. };
  1166. const changeID = (value: any) => {
  1167. if (!value) {
  1168. initPenData();
  1169. MessagePlugin.error('id 不能为空');
  1170. return;
  1171. }
  1172. const oldID: string = data.pen.id;
  1173. try {
  1174. meta2d.changePenId(oldID, value);
  1175. initPenData();
  1176. } catch (error) {
  1177. console.warn(error.message);
  1178. MessagePlugin.error('id 修改失败,请检查 id 是否重复');
  1179. return;
  1180. }
  1181. };
  1182. const changeRectValue = (prop: string) => {
  1183. data.rect.id = data.pen.id;
  1184. data.rect.ratio = data.pen.ratio;
  1185. updatePen(data.rect, prop);
  1186. };
  1187. const onFontPopupVisible = (val: boolean) => {
  1188. data.fontFamilyPopupVisible = val;
  1189. };
  1190. const onFontFamily = (fontFamily: string) => {
  1191. data.pen.fontFamily = fontFamily;
  1192. data.fontFamilyPopupVisible = false;
  1193. changeValue('fontFamily');
  1194. };
  1195. const beforeUpload = (file: any) => {
  1196. // if (file.size > 5 * 1024 * 1024) {
  1197. // MessagePlugin.warning('上传的图片不能大于5M');
  1198. // return false;
  1199. // }
  1200. if (!(user && user.id)) {
  1201. MessagePlugin.warning('请先登录!');
  1202. return false;
  1203. }
  1204. return true;
  1205. };
  1206. const fileSuccessed = async (content: any) => {
  1207. // meta2d.store.patchFlagsBackground = true;
  1208. // meta2d.setBackgroundImage(content.response.url);
  1209. // meta2d.store.patchFlagsBackground = true;
  1210. data.pen.image = content.response.url;
  1211. updatePen(data.pen, 'image');
  1212. meta2d.render();
  1213. };
  1214. const fileRemoved = () => {
  1215. // meta2d.setBackgroundImage('');
  1216. // meta2d.store.patchFlagsBackground = true;
  1217. data.pen.image = '';
  1218. updatePen(data.pen, 'image');
  1219. meta2d.render();
  1220. // data.background = [];
  1221. };
  1222. const upload = () => {
  1223. uploadRef.value.triggerUpload();
  1224. };
  1225. const onSelectTag = (tag: string) => {
  1226. data.tagPopupVisible = false;
  1227. if (data.pen.tags.includes(tag)) {
  1228. return;
  1229. }
  1230. data.pen.tags.push(tag);
  1231. changeValue('tags');
  1232. };
  1233. const onChangeInputTag = (currentTags: any, context: any) => {
  1234. const { trigger, index, item } = context;
  1235. if (['tag-remove', 'backspace'].includes(trigger)) {
  1236. data.pen.tags.splice(index, 1);
  1237. }
  1238. if (trigger === 'enter') {
  1239. onSelectTag(item);
  1240. const d = meta2d.store.data as any;
  1241. if (!d.groups.includes(item)) {
  1242. d.groups.push(item);
  1243. data.groups = d.groups;
  1244. }
  1245. data.inputTag = '';
  1246. }
  1247. data.tagPopupVisible = false;
  1248. };
  1249. const showTooltip = () => {
  1250. tooltipDialog.title = data.pen.title || '';
  1251. tooltipDialog.titleFnJs =
  1252. data.pen.titleFnJs || '// 例如:return `${pen.name}<br/>${pen.text}`;';
  1253. tooltipDialog.type = data.pen.titleFnJs ? '2' : '1';
  1254. tooltipDialog.show = true;
  1255. };
  1256. const onOkTooltip = () => {
  1257. if (tooltipDialog.type === '1') {
  1258. data.pen.title = tooltipDialog.title;
  1259. data.pen.titleFnJs = '';
  1260. } else {
  1261. data.pen.title = '';
  1262. data.pen.titleFnJs = tooltipDialog.titleFnJs;
  1263. }
  1264. tooltipDialog.show = false;
  1265. };
  1266. const showPropsEdit = (item: any) => {
  1267. propsDialog.key = item.key;
  1268. propsDialog.header = `${item.label}(${item.key})`;
  1269. propsDialog.value = data.pen[item.key];
  1270. propsDialog.placeholder = item.placeholder;
  1271. propsDialog.show = true;
  1272. };
  1273. const onOkPropsEdit = () => {
  1274. if (!propsDialog.value) {
  1275. MessagePlugin.error('数据不满足json格式');
  1276. return;
  1277. }
  1278. data.pen[propsDialog.key] = propsDialog.value;
  1279. updatePen(data.pen, propsDialog.key);
  1280. propsDialog.show = false;
  1281. };
  1282. const onChangeIcon = (params: any) => {
  1283. Object.assign(data.pen, params);
  1284. meta2d.setValue({
  1285. id: data.pen.id,
  1286. icon: params.icon,
  1287. iconFamily: params.iconFamily,
  1288. });
  1289. autoSave(true);
  1290. };
  1291. onUnmounted(() => {
  1292. watcher();
  1293. meta2d.off('translatePens', getRect);
  1294. meta2d.off('resizePens', getRect);
  1295. meta2d.off('rotatePens', getRect);
  1296. });
  1297. </script>
  1298. <style lang="postcss" scoped>
  1299. .props {
  1300. }
  1301. </style>