PensProps.vue 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. <template>
  2. <div class="props">
  3. <t-tabs v-model="data.tab">
  4. <t-tab-panel :value="1" :label="$t('外观')">
  5. <t-space direction="vertical" class="py-16 w-full">
  6. <div class="px-16 flex between" style="margin-bottom:2px;">
  7. <label>{{$t('选中了')}}{{ selections.pens.length }}{{$t( '个图元' )}}</label>
  8. <div class="icons">
  9. <t-tooltip class="mr-4" v-if="!data.locked" placement="top" :content="$t('可编辑')">
  10. <svg class="l-icon" aria-hidden="true" @click="lock(1)">
  11. <use xlink:href="#l-unlock"></use>
  12. </svg>
  13. </t-tooltip>
  14. <t-tooltip class="mr-4" v-else-if="data.locked == 1" placement="top" :content="$t('禁止编辑')">
  15. <svg class="l-icon" aria-hidden="true" @click="lock(2)">
  16. <use xlink:href="#l-lock"></use>
  17. </svg>
  18. </t-tooltip>
  19. <t-tooltip class="mr-4" v-else-if="data.locked == 2" placement="top" :content="$t('禁止编辑和移动')">
  20. <svg class="l-icon" aria-hidden="true" @click="lock(10)">
  21. <use xlink:href="#l-wufayidong"></use>
  22. </svg>
  23. </t-tooltip>
  24. <t-tooltip class="mr-4" v-else-if="data.locked == 10" placement="top" :content="$t('禁止所有事件')">
  25. <svg class="l-icon" aria-hidden="true" @click="lock(0)">
  26. <use xlink:href="#l-jinyong"></use>
  27. </svg>
  28. </t-tooltip>
  29. <browse-icon v-if="data.visible !== false" @click="visible(false)" class="ml-8"></browse-icon>
  30. <browse-off-icon v-else @click="visible(true)" class="ml-8"></browse-off-icon>
  31. <!-- <t-icon
  32. v-if="data.visible !== false"
  33. name="browse"
  34. @click="visible(false)"
  35. class="ml-8"
  36. />
  37. <t-icon
  38. v-else
  39. name="browse-off"
  40. @click="visible(true)"
  41. class="ml-8"
  42. /> -->
  43. </div>
  44. </div>
  45. <div class="form-item px-16" style="margin-top: -12px">
  46. <label style="width: 50px">{{$t( '分组' )}}</label>
  47. <t-select-input
  48. v-model:inputValue="data.inputTag"
  49. :value="data.tags"
  50. allow-input
  51. :placeholder="$t('请输入或选择分组')"
  52. multiple
  53. @tag-change="onChangeInputTag"
  54. @focus="data.tagPopupVisible = true"
  55. @blur="data.tagPopupVisible = false"
  56. :tag-input-props="{ excessTagsDisplayType: 'break-line' }"
  57. >
  58. <template #panel>
  59. <ul style="padding: 8px 12px">
  60. <li
  61. v-for="item in data.groups"
  62. :key="item"
  63. @click="onSelectTag(item)"
  64. >
  65. {{ item }}
  66. </li>
  67. </ul>
  68. </template>
  69. </t-select-input>
  70. </div>
  71. <div class="form-item px-16" style="margin-top: -12px">
  72. <label style="width: 50px">{{$t('画布层')}}</label>
  73. <t-select v-model="data.canvasLayer" @change="changeValue('canvasLayer')">
  74. <t-option :key="4" :disabled="!allImg" :value="4" :label="$t('上层图片层')"> {{$t('上层图片层')}} </t-option>
  75. <t-option :key="3" :disabled="hasDom" :value="3" :label="$t('主画布层')"> {{$t('主画布层')}} </t-option>
  76. <t-option :key="2" :disabled="!allImg" :value="2" :label="$t('下层图片层')"> {{$t('下层图片层')}} </t-option>
  77. <t-option :key="1" :disabled="hasDom" :value="1" :label="$t('模板层')"> {{$t('模板层')}} </t-option>
  78. </t-select>
  79. </div>
  80. <t-collapse :defaultValue="['1', '2', '3', '4']" expandIconPlacement="right" :borderless="true">
  81. <t-collapse-panel value="1" :header="$t('对齐')">
  82. <t-space direction="vertical" size="small" class="w-full">
  83. <div style="color: var(--color); margin-bottom: 2px">
  84. {{$t('大屏对齐')}}
  85. </div>
  86. <div class="icons">
  87. <t-tooltip
  88. v-for="item in vAligns"
  89. :content="item.label"
  90. placement="top"
  91. >
  92. <svg
  93. class="l-icon btn"
  94. aria-hidden="true"
  95. @mouseenter="previewAlign(1,item.value)"
  96. @mouseleave="recoverAlign()"
  97. @click="alignV(item.value)"
  98. >
  99. <use :xlink:href="item.icon"></use>
  100. </svg>
  101. </t-tooltip>
  102. </div>
  103. </t-space>
  104. <t-divider
  105. style="margin: 16px -16px 12px -16px; width: calc(100% + 32px)"
  106. />
  107. <t-space direction="vertical" size="small" class="w-full">
  108. <div style="color: var(--color); margin-bottom: 2px">
  109. {{$t('区域对齐')}}
  110. </div>
  111. <div class="icons">
  112. <t-tooltip
  113. v-for="item in aligns"
  114. :content="item.label"
  115. placement="top"
  116. >
  117. <svg
  118. class="l-icon btn"
  119. aria-hidden="true"
  120. @mouseenter="previewAlign(2,item.value)"
  121. @mouseleave="recoverAlign(item.value)"
  122. @click="align(item.value)"
  123. >
  124. <use :xlink:href="item.icon"></use>
  125. </svg>
  126. </t-tooltip>
  127. </div>
  128. </t-space>
  129. <t-divider
  130. style="margin: 16px -16px 12px -16px; width: calc(100% + 32px)"
  131. />
  132. <t-space direction="vertical" size="small" class="w-full">
  133. <div style="color: var(--color); margin-bottom: 2px">
  134. {{$t('以最后选中图元对齐')}}
  135. </div>
  136. <div class="icons">
  137. <t-tooltip
  138. v-for="item in aligns2"
  139. :content="item.label"
  140. placement="top"
  141. >
  142. <svg
  143. class="l-icon btn"
  144. aria-hidden="true"
  145. @mouseenter="previewAlign(3,item.value)"
  146. @mouseleave="recoverAlign()"
  147. @click="align2(item.value)"
  148. >
  149. <use :xlink:href="item.icon"></use>
  150. </svg>
  151. </t-tooltip>
  152. </div>
  153. </t-space>
  154. </t-collapse-panel>
  155. <t-collapse-panel value="2" :header="$t('外观')">
  156. <t-space direction="vertical" size="small" class="w-full">
  157. <div class="form-item">
  158. <t-color-picker class="simple mt-8 mr-4" format="CSS" :enable-alpha="true" :recent-colors="null" :swatch-colors="defaultPureColor" :color-modes="['monochrome']" :show-primary-color-preview="false" :clearable="true" v-model="data.color" @change="changeValue('color')"></t-color-picker>
  159. <label style="width: 64px">{{$t('前景颜色')}}</label>
  160. <t-color-picker class="simple mt-8 mr-4" format="CSS" :enable-alpha="true" :recent-colors="null" :swatch-colors="defaultPureColor" :color-modes="['monochrome']" :show-primary-color-preview="false" v-model="data.hoverColor" @change="changeValue('hoverColor')"></t-color-picker>
  161. <label style="width: 64px">{{$t('悬停颜色')}}</label>
  162. <t-color-picker class="simple mt-8 mr-4" format="CSS" :enable-alpha="true" :recent-colors="null" :swatch-colors="defaultPureColor" :color-modes="['monochrome']" :show-primary-color-preview="false" v-model="data.activeColor" @change="changeValue('activeColor')"></t-color-picker>
  163. <label style="width: 64px">{{$t('选中颜色')}}</label>
  164. </div>
  165. <div class="form-item">
  166. <label style="width: 32px">{{$t('线条')}} </label>
  167. <t-select v-model="data.dash" @change="changeValue('dash')" style="width: 80px" :placeholder="$t('线条样式')">
  168. <template #valueDisplay="{ value }">
  169. <svg
  170. xmlns="http://www.w3.org/2000/svg"
  171. version="1.1"
  172. style="width: 100%; height: 20px"
  173. >
  174. <g fill="none" stroke="var(--color)" stroke-width="1">
  175. <path v-if="value === 0" d="M0 9 l85 0" />
  176. <path
  177. v-else-if="value === 1"
  178. stroke-dasharray="5 5"
  179. d="M0 9 l85 0"
  180. />
  181. <path
  182. v-else-if="value === 2"
  183. stroke-dasharray="10 10"
  184. d="M0 9 l85 0"
  185. />
  186. <path
  187. v-else-if="value === 3"
  188. stroke-dasharray="10 10 2 10"
  189. d="M0 9 l85 0"
  190. />
  191. </g>
  192. </svg>
  193. </template>
  194. <t-option :key="0" :value="0">
  195. <svg
  196. xmlns="http://www.w3.org/2000/svg"
  197. version="1.1"
  198. style="width: 80px; height: 14px"
  199. >
  200. <g fill="none" stroke="var(--color)" stroke-width="1">
  201. <path d="M0 9 l85 0" />
  202. </g>
  203. </svg>
  204. </t-option>
  205. <t-option :key="1" :value="1">
  206. <svg
  207. xmlns="http://www.w3.org/2000/svg"
  208. version="1.1"
  209. style="width: 80px; height: 14px"
  210. >
  211. <g fill="none" stroke="var(--color)" stroke-width="1">
  212. <path stroke-dasharray="5 5" d="M0 9 l85 0" />
  213. </g>
  214. </svg>
  215. </t-option>
  216. <t-option :key="2" :value="2">
  217. <svg
  218. xmlns="http://www.w3.org/2000/svg"
  219. version="1.1"
  220. style="width: 80px; height: 14px"
  221. >
  222. <g fill="none" stroke="var(--color)" stroke-width="1">
  223. <path stroke-dasharray="10 10" d="M0 9 l85 0" />
  224. </g>
  225. </svg>
  226. </t-option>
  227. <t-option :key="3" :value="3">
  228. <svg
  229. xmlns="http://www.w3.org/2000/svg"
  230. version="1.1"
  231. style="width: 80px; height: 14px"
  232. >
  233. <g fill="none" stroke="var(--color)" stroke-width="1">
  234. <path stroke-dasharray="10 10 2 10" d="M0 9 l85 0" />
  235. </g>
  236. </svg>
  237. </t-option>
  238. </t-select>
  239. <t-input-number
  240. theme="normal"
  241. :placeholder="$t('线条宽度')"
  242. v-model="data.lineWidth"
  243. :min="1"
  244. :decimalPlaces="0"
  245. @change="changeValue('lineWidth')"
  246. class="ml-4"
  247. style="width: 40px"
  248. />
  249. <t-tooltip :content="$t('线条渐变')" placement="top">
  250. <div class="flex middle ml-8">
  251. <t-checkbox
  252. v-model="data.strokeType"
  253. @change="changeValue('strokeType')"
  254. style="width: 22px"
  255. />
  256. <t-color-picker
  257. v-if="data.strokeType"
  258. class="simple mr-4"
  259. format="CSS"
  260. :color-modes="['linear-gradient']"
  261. :show-primary-color-preview="false"
  262. :clearable="true"
  263. :enableAlpha="true"
  264. :recent-colors="null"
  265. :swatch-colors="defaultGradientColor"
  266. v-model="data.lineGradientColors"
  267. @change="changeValue('lineGradientColors')"
  268. :placeholder="$t('无')"
  269. />
  270. </div>
  271. </t-tooltip>
  272. </div>
  273. <div class="flex" style="margin-left: 40px">
  274. <div class="flex column middle">
  275. <t-radio-group
  276. size="small"
  277. v-model="data.lineCap"
  278. default-value="butt"
  279. @change="changeValue('lineCap')"
  280. >
  281. <t-radio-button value="butt">
  282. <t-tooltip :content="$t('默认')" placement="top">
  283. <svg class="l-icon" aria-hidden="true">
  284. <use xlink:href="#l-duandian1"></use>
  285. </svg>
  286. </t-tooltip>
  287. </t-radio-button>
  288. <t-radio-button value="round">
  289. <t-tooltip :content="$t('圆形')" placement="top">
  290. <svg class="l-icon" aria-hidden="true">
  291. <use xlink:href="#l-duandian2"></use>
  292. </svg>
  293. </t-tooltip>
  294. </t-radio-button>
  295. <t-radio-button value="square">
  296. <t-tooltip placement="top" :content="$t('方形')">
  297. <svg class="l-icon" aria-hidden="true">
  298. <use xlink:href="#l-duandian3"></use>
  299. </svg>
  300. </t-tooltip>
  301. </t-radio-button>
  302. </t-radio-group>
  303. <div class="mt-4" style="font-size: 12px">{{$t('末端样式')}}</div>
  304. </div>
  305. <div class="flex column middle ml-16">
  306. <t-radio-group
  307. size="small"
  308. v-model="data.lineJoin"
  309. default-value="miter"
  310. @change="changeValue('lineJoin')"
  311. >
  312. <t-radio-button value="miter">
  313. <t-tooltip placement="top" :content="$t('默认')">
  314. <svg class="l-icon" aria-hidden="true">
  315. <use xlink:href="#l-jiedian1"></use>
  316. </svg>
  317. </t-tooltip>
  318. </t-radio-button>
  319. <t-radio-button value="round">
  320. <t-tooltip placement="top" :content="$t('圆形')">
  321. <svg class="l-icon" aria-hidden="true">
  322. <use xlink:href="#l-jiedian2"></use>
  323. </svg>
  324. </t-tooltip>
  325. </t-radio-button>
  326. <t-radio-button value="bevel">
  327. <t-tooltip placement="top" :content="$t('斜角')">
  328. <svg class="l-icon" aria-hidden="true">
  329. <use xlink:href="#l-jiedian3"></use>
  330. </svg>
  331. </t-tooltip>
  332. </t-radio-button>
  333. </t-radio-group>
  334. <div class="mt-4" style="font-size: 12px">{{$t('连接样式')}}</div>
  335. </div>
  336. </div>
  337. <div class="form-item">
  338. <label style="width: 32px">{{$t('背景')}}</label>
  339. <div class="ml-8">
  340. <t-radio-group size="small" v-model="data.bkType" :default-value="0" @change="changeValue('bkType')">
  341. <t-radio-button :value="0"> {{$t('纯色')}} </t-radio-button>
  342. <t-radio-button :value="1"> {{$t('线性渐变')}} </t-radio-button>
  343. <t-radio-button :value="2"> {{$t('径向渐变')}} </t-radio-button>
  344. </t-radio-group>
  345. <div v-if="data.bkType === 0" class="mt-8 -ml-8">
  346. <t-color-picker
  347. class="w-full"
  348. format="CSS"
  349. :enable-alpha="true"
  350. :recent-colors="null"
  351. :swatch-colors="defaultPureColor"
  352. :color-modes="['monochrome']"
  353. :show-primary-color-preview="false"
  354. v-model="data.background"
  355. @change="changeValue('background')"
  356. />
  357. </div>
  358. <div
  359. v-else-if="data.bkType === 1"
  360. class="mt-8 -ml-8"
  361. style="width: 200px"
  362. >
  363. <t-color-picker
  364. class="w-full"
  365. format="CSS"
  366. :enable-alpha="true"
  367. :recent-colors="null"
  368. :swatch-colors="defaultGradientColor"
  369. :color-modes="['linear-gradient']"
  370. :show-primary-color-preview="false"
  371. v-model="data.gradientColors"
  372. @change="changeValue('gradientColors')"
  373. />
  374. </div>
  375. <div v-else-if="data.bkType === 2" class="mt-8 flex middle">
  376. <t-color-picker
  377. class="simple"
  378. format="CSS"
  379. :enable-alpha="true"
  380. :recent-colors="null"
  381. :swatch-colors="defaultGradientColor"
  382. :color-modes="['linear-gradient']"
  383. :show-primary-color-preview="false"
  384. v-model="data.gradientColors"
  385. @change="changeValue('gradientColors')"
  386. />
  387. <t-input-number
  388. theme="column"
  389. :placeholder="$t('渐变半径')"
  390. v-model="data.gradientRadius"
  391. :min="0"
  392. :max="1"
  393. :step="0.1"
  394. @change="changeValue('gradientRadius')"
  395. class="ml-8"
  396. style="width: 100px"
  397. />
  398. </div>
  399. </div>
  400. </div>
  401. <div class="form-item">
  402. <label style="width: 32px">{{$t('阴影')}} </label>
  403. <div class="flex middle ml-8">
  404. <t-checkbox
  405. v-model="data.shadow"
  406. @change="changeValue('shadow')"
  407. style="width: 22px"
  408. />
  409. <t-color-picker
  410. v-if="data.shadow"
  411. class="simple"
  412. format="CSS"
  413. :enable-alpha="true"
  414. :recent-colors="null"
  415. :swatch-colors="defaultPureColor"
  416. :color-modes="['monochrome']"
  417. :show-primary-color-preview="false"
  418. v-model="data.shadowColor"
  419. @change="changeValue('shadowColor')"
  420. />
  421. </div>
  422. <label v-if="data.shadow" style="width: 50px; margin-left: 25px">{{$t('文字阴影')}}
  423. </label>
  424. <div v-if="data.shadow" class="flex middle ml-8">
  425. <t-checkbox
  426. v-model="data.textHasShadow"
  427. @change="changeValue('textHasShadow')"
  428. style="width: 22px"
  429. />
  430. </div>
  431. </div>
  432. <div class="form-item" v-if="data.shadow">
  433. <label style="width: 28px"></label>
  434. <div class="flex" style="margin-top: -8px">
  435. <t-input class="ml-4" label="X" placeholder="0" v-model.number="data.shadowOffsetX" style="width: 60px" @change="changeValue('x')" :title="$t('X偏移')"></t-input>
  436. <t-input class="ml-4" label="Y" placeholder="0" v-model.number="data.shadowOffsetY" style="width: 60px" @change="changeValue('shadowOffsetY')" :title="$t('Y偏移')"></t-input>
  437. <t-input class="ml-4" placeholder="0" v-model.number="data.shadowBlur" style="width: 64px" @change="changeValue('shadowBlur')" :label="$t('模糊')" :title="$t('模糊大小')"></t-input>
  438. </div>
  439. </div>
  440. </t-space>
  441. </t-collapse-panel>
  442. <t-collapse-panel value="3" :header="$t('文字')">
  443. <t-space direction="vertical" size="small" class="w-full">
  444. <div class="form-item">
  445. <div class="flex middle" style="margin-left: -10px">
  446. <t-select-input
  447. :value="data.fontFamily"
  448. :popup-visible="data.fontFamilyPopupVisible"
  449. :placeholder="$t('字体名')"
  450. allow-input
  451. style="width: 170px"
  452. @change="changeValue('fontFamily')"
  453. @enter="changeValue('fontFamily')"
  454. @blur="changeValue('fontFamily')"
  455. @popup-visible-change="onFontPopupVisible"
  456. :popup-props="{
  457. overlayInnerStyle: { width: 'auto' },
  458. }"
  459. >
  460. <template #panel>
  461. <ul style="padding: 12px">
  462. <li
  463. v-for="item in fonts"
  464. :key="item"
  465. @click="onFontFamily(item)"
  466. >
  467. {{ item }}
  468. </li>
  469. </ul>
  470. </template>
  471. <template #suffixIcon>
  472. <chevron-down-icon />
  473. <!-- <t-icon name="chevron-down" /> -->
  474. </template>
  475. </t-select-input>
  476. <t-input class="ml-8" v-model.number="data.fontSize" style="width: 80px" :format="decimalRound" @change="changeValue('fontSize')" :placeholder="$t('字体大小')"></t-input>
  477. </div>
  478. </div>
  479. <div class="flex middle">
  480. <t-radio-group
  481. size="small"
  482. v-model="data.textAlign"
  483. default-value="center"
  484. @change="changeValue('textAlign')"
  485. >
  486. <t-radio-button value="left">
  487. <t-tooltip placement="top" :content="$t('居左')">
  488. <format-vertical-align-left-icon></format-vertical-align-left-icon>
  489. <!-- <t-icon name="format-vertical-align-left" /> -->
  490. </t-tooltip>
  491. </t-radio-button>
  492. <t-radio-button value="center">
  493. <t-tooltip placement="top" :content="$t('居中')">
  494. <format-vertical-align-center-icon></format-vertical-align-center-icon>
  495. <!-- <t-icon name="format-vertical-align-center" /> -->
  496. </t-tooltip>
  497. </t-radio-button>
  498. <t-radio-button value="right">
  499. <t-tooltip placement="top" :content="$t('居右')">
  500. <format-vertical-align-right-icon></format-vertical-align-right-icon>
  501. <!-- <t-icon name="format-vertical-align-right" /> -->
  502. </t-tooltip>
  503. </t-radio-button>
  504. </t-radio-group>
  505. <t-radio-group
  506. class="ml-8"
  507. size="small"
  508. v-model="data.textBaseline"
  509. default-value="top"
  510. @change="changeValue('textBaseline')"
  511. >
  512. <t-radio-button value="top">
  513. <t-tooltip placement="top" :content="$t('顶部对齐')">
  514. <format-horizontal-align-top-icon></format-horizontal-align-top-icon>
  515. <!-- <t-icon name="format-horizontal-align-top" /> -->
  516. </t-tooltip>
  517. </t-radio-button>
  518. <t-radio-button value="middle">
  519. <t-tooltip placement="middle" :content="$t('垂直居中')">
  520. <format-horizontal-align-center-icon></format-horizontal-align-center-icon>
  521. <!-- <t-icon name="format-horizontal-align-center" /> -->
  522. </t-tooltip>
  523. </t-radio-button>
  524. <t-radio-button value="bottom">
  525. <t-tooltip placement="top" :content="$t('底部对齐')">
  526. <format-horizontal-align-bottom-icon></format-horizontal-align-bottom-icon>
  527. <!-- <t-icon name="format-horizontal-align-bottom" /> -->
  528. </t-tooltip>
  529. </t-radio-button>
  530. </t-radio-group>
  531. <t-button
  532. :class="{ active: data.fontWeight === 'bold' }"
  533. class="ml-8 icon"
  534. shape="rectangle"
  535. variant="text"
  536. @click="
  537. data.fontWeight === 'bold'
  538. ? (data.fontWeight = 'normal')
  539. : (data.fontWeight = 'bold');
  540. changeValue('fontWeight');
  541. "
  542. >
  543. B
  544. </t-button>
  545. <t-button
  546. :class="{ active: data.fontStyle === 'italic' }"
  547. class="ml-4 icon"
  548. shape="rectangle"
  549. variant="text"
  550. @click="
  551. data.fontStyle === 'italic'
  552. ? (data.fontStyle = 'normal')
  553. : (data.fontStyle = 'italic');
  554. changeValue('fontStyle');
  555. "
  556. style="font-style: italic; font-family: serif"
  557. >I</t-button
  558. >
  559. </div>
  560. <div class="form-item">
  561. <t-color-picker
  562. class="simple mt-8 mr-4"
  563. format="CSS"
  564. :enable-alpha="true"
  565. :recentColors="null"
  566. :swatch-colors="defaultPureColor"
  567. :color-modes="['monochrome']"
  568. :show-primary-color-preview="false"
  569. v-model="data.textColor"
  570. @change="changeValue('textColor')"
  571. />
  572. <label style="width: 44px">{{$t( '前景' )}}</label>
  573. <t-color-picker
  574. class="simple mt-8 mr-4"
  575. :enable-alpha="true"
  576. :recent-colors="null"
  577. format="CSS"
  578. :swatch-colors="defaultPureColor"
  579. :color-modes="['monochrome']"
  580. :show-primary-color-preview="false"
  581. v-model="data.textBackground"
  582. @change="changeValue('textBackground')"
  583. />
  584. <label style="width: 44px">{{$t( '背景' )}}</label>
  585. <t-color-picker class="simple mt-8 mr-4" :enable-alpha="true" :recent-colors="null" format="CSS" :swatch-colors="defaultPureColor" :color-modes="['monochrome']" :show-primary-color-preview="false" v-model="data.hoverTextColor" @change="changeValue('hoverTextColor')"></t-color-picker>
  586. <label style="width: 44px">{{$t('悬停')}}</label>
  587. <t-color-picker class="simple mt-8 mr-4" format="CSS" :enable-alpha="true" :recent-colors="null" :swatch-colors="defaultPureColor" :color-modes="['monochrome']" :show-primary-color-preview="false" v-model="data.activeTextColor" @change="changeValue('activeTextColor')"></t-color-picker>
  588. <label style="width: 44px">{{$t('选中')}}</label>
  589. </div>
  590. <div class="form-item">
  591. <t-checkbox v-model="data.whiteSpace" @change="changeValue('whiteSpace')" style="width: 64px">
  592. {{$t('换行')}}
  593. </t-checkbox>
  594. <t-checkbox v-model="data.ellipsis" @change="changeValue('ellipsis')" style="width: 68px">
  595. {{$t('省略号')}}
  596. </t-checkbox>
  597. <t-tooltip :content="$t('行高')">
  598. <t-input v-model.number="data.lineHeight" style="width: 40px" @change="changeValue('lineHeight')" :placeholder="$t('行高')"></t-input>
  599. </t-tooltip>
  600. <t-tooltip :content="$t('显示时保留小数位数')">
  601. <t-input class="ml-4" v-model.number="data.keepDecimal" style="width: 60px" @change="changeValue('keepDecimal')" :placeholder="$t('小数')"></t-input>
  602. </t-tooltip>
  603. </div>
  604. <div class="form-item" style="margin-top: -4px">
  605. <t-tooltip :content="$t('水平偏移')">
  606. <t-input placeholder="X" v-model.number="data.textLeft" style="width: 60px; margin-left: -8px" @change="changeValue('textLeft')"></t-input>
  607. </t-tooltip>
  608. <t-tooltip :content="$t('垂直偏移')">
  609. <t-input class="ml-4" placeholder="Y" v-model.number="data.textTop" style="width: 60px" @change="changeValue('textTop')"></t-input>
  610. </t-tooltip>
  611. <t-tooltip :content="$t('宽')">
  612. <t-input class="ml-4" v-model.number="data.textWidth" style="width: 60px" @change="changeValue('textWidth')" :placeholder="$t('宽')"></t-input>
  613. </t-tooltip>
  614. <t-tooltip :content="$t('高')">
  615. <t-input class="ml-4" v-model.number="data.textHeight" style="width: 60px" @change="changeValue('textHeight')" :placeholder="$t('高')"></t-input>
  616. </t-tooltip>
  617. </div>
  618. <div class="flex middle">
  619. <t-checkbox v-model="data.disableInput" @change="changeValue('disableInput')" style="width: 64px">
  620. {{$t('只读')}}
  621. </t-checkbox>
  622. <t-checkbox v-model="data.hiddenText" @change="changeValue('hiddenText')" style="width: 90px">
  623. {{$t('隐藏文字')}}
  624. </t-checkbox>
  625. </div>
  626. </t-space>
  627. </t-collapse-panel>
  628. </t-collapse>
  629. </t-space>
  630. <t-divider style="margin-top: -8px" />
  631. <div class="form-item p-16">
  632. <t-checkbox v-model="data.flipX" @change="changeValue('flipX')" style="width: 90px">
  633. {{$t('水平翻转')}}
  634. </t-checkbox>
  635. <t-checkbox v-model="data.flipY" @change="changeValue('flipY')" style="width: 90px">
  636. {{$t('垂直翻转')}}
  637. </t-checkbox>
  638. <label style="width: 50px">{{$t('锚点半径')}}</label>
  639. <input class="ml-4" v-model.number="data.anchorRadius" style="width: 20px" @change="changeValue('anchorRadius')" placeholder="4">
  640. </div>
  641. <t-divider />
  642. <div class="form-item p-16" style="margin-bottom: 20px">
  643. <t-checkbox v-model="data.disableRotate" @change="changeValue('disableRotate')" style="width: 90px">
  644. {{$t('禁止旋转')}}
  645. </t-checkbox>
  646. <t-checkbox v-model="data.disableSize" @change="changeValue('disableSize')" style="width: 90px">
  647. {{$t('禁止缩放')}}
  648. </t-checkbox>
  649. <t-checkbox v-model="data.disableAnchor" @change="changeValue('disableAnchor')" style="width: 90px">
  650. {{$t('禁用锚点')}}
  651. </t-checkbox>
  652. </div>
  653. </t-tab-panel>
  654. </t-tabs>
  655. </div>
  656. </template>
  657. <script lang="ts" setup>
  658. import { onBeforeMount, onUnmounted, reactive, ref, computed,getCurrentInstance } from 'vue';
  659. import { LockState, Pen, isDomShapes } from '@meta2d/core';
  660. import { updatePen } from './pen';
  661. import { useSelection } from '@/services/selections';
  662. import { fonts, setChildrenVisible } from '@/services/common';
  663. import { defaultGradientColor, defaultPureColor } from '@/services/defaults';
  664. import { BrowseIcon, BrowseOffIcon, ChevronDownIcon, FormatVerticalAlignLeftIcon, FormatHorizontalAlignCenterIcon, FormatVerticalAlignCenterIcon, FormatVerticalAlignRightIcon, FormatHorizontalAlignTopIcon, FormatHorizontalAlignBottomIcon } from 'tdesign-icons-vue-next';
  665. const { selections } = useSelection();
  666. const { proxy } = getCurrentInstance();
  667. const $t = proxy.$t
  668. const data = reactive<any>({
  669. tab: 1,
  670. locked: 0,
  671. lineWidth: 1,
  672. });
  673. const vAligns = [
  674. {
  675. value: 'left',
  676. label: $t('左对齐'),
  677. icon: '#l-align-left',
  678. },
  679. {
  680. value: 'center',
  681. label: $t('垂直居中对齐'),
  682. icon: '#l-align-center',
  683. },
  684. {
  685. value: 'right',
  686. label: $t('右对齐'),
  687. icon: '#l-align-right',
  688. },
  689. {
  690. value: 'top',
  691. label: $t('顶部对齐'),
  692. icon: '#l-align-top',
  693. },
  694. {
  695. value: 'middle',
  696. label: $t('水平居中对齐'),
  697. icon: '#l-align-middle',
  698. },
  699. {
  700. value: 'bottom',
  701. label: $t('底部对齐'),
  702. icon: '#l-align-bottom',
  703. }
  704. ];
  705. const aligns = [
  706. {
  707. value: 'left',
  708. label: $t('左对齐'),
  709. icon: '#l-align-left',
  710. },
  711. {
  712. value: 'center',
  713. label: $t('垂直居中对齐'),
  714. icon: '#l-align-center',
  715. },
  716. {
  717. value: 'right',
  718. label: $t('右对齐'),
  719. icon: '#l-align-right',
  720. },
  721. {
  722. value: 'top',
  723. label: $t('顶部对齐'),
  724. icon: '#l-align-top',
  725. },
  726. {
  727. value: 'middle',
  728. label: $t('水平居中对齐'),
  729. icon: '#l-align-middle',
  730. },
  731. {
  732. value: 'bottom',
  733. label: $t('底部对齐'),
  734. icon: '#l-align-bottom',
  735. },
  736. {
  737. value: 'h-distribute',
  738. label: $t('水平等距'),
  739. icon: '#l-horizontal-between',
  740. },
  741. {
  742. value: 'v-distribute',
  743. label: $t('垂直等距'),
  744. icon: '#l-vertical-between',
  745. },
  746. ];
  747. const aligns2 = [
  748. {
  749. value: 'left',
  750. label: $t('左对齐'),
  751. icon: '#l-align-left',
  752. },
  753. {
  754. value: 'center',
  755. label: $t('垂直居中对齐'),
  756. icon: '#l-align-center',
  757. },
  758. {
  759. value: 'right',
  760. label: $t('右对齐'),
  761. icon: '#l-align-right',
  762. },
  763. {
  764. value: 'top',
  765. label: $t('顶部对齐'),
  766. icon: '#l-align-top',
  767. },
  768. {
  769. value: 'middle',
  770. label: $t('水平居中对齐'),
  771. icon: '#l-align-middle',
  772. },
  773. {
  774. value: 'bottom',
  775. label: $t('底部对齐'),
  776. icon: '#l-align-bottom',
  777. },
  778. {
  779. value: 'same-size',
  780. label: $t('相同大小'),
  781. icon: '#l-same-size',
  782. },
  783. ];
  784. function previewAlign(type,alignValue) {
  785. switch (type) {
  786. case 1:
  787. meta2d.alignNodesV(alignValue, meta2d.store.active)
  788. break;
  789. case 2:
  790. align(alignValue)
  791. break;
  792. case 3:
  793. align2(alignValue)
  794. }
  795. meta2d.render()
  796. }
  797. function recoverAlign(align?) {
  798. if(align && (align === 'h-distribute' || align === 'v-distribute') && meta2d.store.active.length <= 2) return
  799. meta2d.undo()
  800. }
  801. onBeforeMount(() => {
  802. const d = meta2d.store.data as any;
  803. if (!d.groups) {
  804. d.groups = [];
  805. }
  806. data.groups = d.groups;
  807. if (!data.tags) {
  808. data.tags = [];
  809. }
  810. let tags = selections.pens[0].tags;
  811. if(tags.length){
  812. tags.forEach((tag: string) => {
  813. let common = selections.pens.every((item: Pen) => {
  814. return item.tags.includes(tag);
  815. });
  816. if (common) {
  817. data.tags.push(tag);
  818. }
  819. });
  820. }
  821. });
  822. const lock = (v: LockState) => {
  823. data.locked = v;
  824. for (const item of selections.pens) {
  825. meta2d.setValue({
  826. id: item.id,
  827. locked: v,
  828. });
  829. }
  830. };
  831. const visible = (v: boolean) => {
  832. data.visible = v;
  833. for (const item of selections.pens) {
  834. meta2d.setVisible(item as any, v);
  835. }
  836. };
  837. const align = (align: string) => {
  838. if (align === 'h-distribute') {
  839. meta2d.spaceBetween(meta2d.store.active);
  840. } else if (align === 'v-distribute') {
  841. meta2d.spaceBetweenColumn(meta2d.store.active);
  842. } else {
  843. meta2d.alignNodes(align, meta2d.store.active);
  844. }
  845. };
  846. const alignV = (align: string) => {
  847. meta2d.alignNodesV(align, meta2d.store.active);
  848. // getRect();
  849. meta2d.render();
  850. };
  851. const align2 = (align: string) => {
  852. if (align === 'same-size') {
  853. meta2d.beSameByLast(meta2d.store.active);
  854. } else {
  855. meta2d.alignNodesByLast(align, meta2d.store.active);
  856. }
  857. };
  858. const changeValue = (prop: string) => {
  859. for (const item of selections.pens) {
  860. data.id = item.id;
  861. updatePen(data, prop, false);
  862. }
  863. meta2d.render();
  864. };
  865. const onFontFamily = (fontFamily: string) => {
  866. data.fontFamily = fontFamily;
  867. data.fontFamilyPopupVisible = false;
  868. changeValue('fontFamily');
  869. };
  870. const onFontPopupVisible = (val: boolean) => {
  871. data.fontFamilyPopupVisible = val;
  872. };
  873. const decimalRound = (val: number) => {
  874. return Math.round(+val || 0);
  875. };
  876. const onSelectTag = (tag: string) => {
  877. data.tagPopupVisible = false;
  878. if (data.tags.includes(tag)) {
  879. return;
  880. }
  881. data.tags.push(tag);
  882. // changeValue('tags');
  883. selections.pens.forEach((pen: Pen) => {
  884. pen.tags.push(tag);
  885. });
  886. };
  887. const onChangeInputTag = (currentTags: any, context: any) => {
  888. const { trigger, index, item } = context;
  889. if (['tag-remove', 'backspace'].includes(trigger)) {
  890. data.tags.splice(index, 1);
  891. selections.pens.forEach((pen: Pen) => {
  892. pen.tags.splice(pen.tags.indexOf(item), 1);
  893. });
  894. }
  895. if (trigger === 'enter') {
  896. onSelectTag(item);
  897. const d = meta2d.store.data as any;
  898. if (!d.groups.includes(item)) {
  899. d.groups.push(item);
  900. data.groups = d.groups;
  901. }
  902. data.inputTag = '';
  903. }
  904. data.tagPopupVisible = false;
  905. };
  906. const hasDom = computed(() => {
  907. return selections.pens.some((item: Pen) => {
  908. return isDomShapes.includes(item.name) ||
  909. item.name.endsWith('Dom') ||
  910. meta2d.store.options.domShapes.includes(item.name);
  911. });
  912. });
  913. const allImg = computed(() => {
  914. return selections.pens.every((item: Pen) => {
  915. return item.name === 'image';
  916. });
  917. });
  918. </script>
  919. <style lang="postcss" scoped>
  920. .props {
  921. .icons {
  922. display: flex;
  923. svg:hover {
  924. cursor: pointer;
  925. color: var(--color-primary);
  926. }
  927. .btn {
  928. font-size: 16px;
  929. margin-right: 16px;
  930. color: var(--color);
  931. }
  932. }
  933. }
  934. </style>