FrontEditorPool.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. <template>
  2. <div>
  3. <el-scrollbar>
  4. <!-- 背景-->
  5. <el-card v-if="backGroundObject.permissionsConfig && (backGroundObject.permissionsConfig.length > 0)"
  6. :body-style="{background: '#f8f8f8'}"
  7. style="max-width: 100%; border-radius: 8px">
  8. <el-row style="margin-bottom: 10px">
  9. <el-col :span="22">
  10. <span class="eleTitle">编辑背景</span>
  11. </el-col>
  12. </el-row>
  13. <el-row class="permRow" :gutter="20">
  14. <el-col :span="10">
  15. <span class="eleLabel">显示背景</span>
  16. </el-col>
  17. <el-col :span="14">
  18. <el-switch
  19. v-model="backGroundObject.visible"
  20. :active-value="true"
  21. :inactive-value="false"
  22. @change="setBackGroudVisible"
  23. />
  24. </el-col>
  25. </el-row>
  26. <template v-for="(perm) in backGroundObject.permissionsConfig">
  27. <el-row class="permRow" v-if="perm == '0'">
  28. <el-row>
  29. <span class="eleLabel">选择颜色</span>
  30. </el-row>
  31. <el-row :gutter="10">
  32. <el-col :span="4" v-for="color in backGroundObject.userSelectableColors">
  33. <el-button class="colorButton" :color="color" @click="updateBackground({color: color, fill: color})"/>
  34. </el-col>
  35. </el-row>
  36. </el-row>
  37. <el-row class="permRow" v-if="perm == '1'">
  38. <el-row>
  39. <span class="eleLabel">背景尺寸</span>
  40. </el-row>
  41. <el-row :gutter="10">
  42. <el-col :span="11">
  43. <el-input
  44. v-model="canvasWidth"
  45. :value="pageSizeWidth"
  46. @change="changeTemplateWidth"
  47. oninput="value=value.replace(/[^\d.]/g,'')"
  48. >
  49. <template #prepend>宽</template>
  50. </el-input>
  51. </el-col>
  52. <el-col :span="2"/>
  53. <el-col :span="11">
  54. <el-input
  55. v-model="canvasHeight"
  56. :value="pageSizeHeight"
  57. @change="changeTemplateHeight"
  58. oninput="value=value.replace(/[^\d.]/g,'')"
  59. >
  60. <template #prepend>高</template>
  61. </el-input>
  62. </el-col>
  63. </el-row>
  64. </el-row>
  65. </template>
  66. </el-card>
  67. <!-- 文本-->
  68. <el-card v-if="textPermDisplay"
  69. :body-style="{background: '#f8f8f8'}"
  70. style="max-width: 100%; border-radius: 8px">
  71. <el-row style="margin-bottom: 10px">
  72. <el-col :span="24">
  73. <span class="eleTitle">编辑文本</span>
  74. </el-col>
  75. <el-col :span="24">
  76. <el-tabs v-model="activeTextTabIndex">
  77. <el-tab-pane v-for="(text,index) in textObjects" :label="text.itemName" :name="index">
  78. <template v-for="(perm) in text.permissionsConfig">
  79. <!-- 文本内容-->
  80. <el-row class="permRow" v-if="perm == '0'">
  81. <el-col :span="4">
  82. <span class="eleLabel">文本</span>
  83. </el-col>
  84. <el-col :span="20">
  85. <el-select
  86. v-model="text.text"
  87. filterable
  88. allow-create
  89. default-first-option
  90. @change="eleTextChange(text, $event)">
  91. <el-option v-for="str in text.userPresetTexts"
  92. :key="str.text"
  93. :label="str.text"
  94. :value="str.text"/>
  95. </el-select>
  96. </el-col>
  97. </el-row>
  98. <!-- 字号-->
  99. <el-row class="permRow" v-if="perm == '1'">
  100. <el-col :span="4">
  101. <span class="eleLabel">字号</span>
  102. </el-col>
  103. <!-- <el-col :span="20">-->
  104. <!-- <el-button-group class="full-group">-->
  105. <!-- <el-tooltip placement="top" content="增大字号" :hide-after="0">-->
  106. <!-- <el-button class="font-size" @click="handleElementFontsize(text.id,'+')">-->
  107. <!-- <IconFontSize/>-->
  108. <!-- +-->
  109. <!-- </el-button>-->
  110. <!-- </el-tooltip>-->
  111. <!-- <el-tooltip placement="top" content="减小字号" :hide-after="0">-->
  112. <!-- <el-button @click="handleElementFontsize(text.id,'-')">-->
  113. <!-- <IconFontSize/>-->
  114. <!-- - -->
  115. <!-- </el-button>-->
  116. <!-- </el-tooltip>-->
  117. <!-- </el-button-group>-->
  118. <!-- </el-col>-->
  119. <el-col :span="20">
  120. <el-select
  121. v-model="text.fontSize"
  122. filterable
  123. allow-create
  124. default-first-option
  125. placement="left"
  126. @change="handleElementFontSize(text.id,$event)"
  127. >
  128. <el-option v-for="item in FontSizeLibs" :key="item" :label="item" :value="item"></el-option>
  129. </el-select>
  130. </el-col>
  131. </el-row>
  132. <!-- 字体-->
  133. <el-row class="permRow" v-if="perm == '2'">
  134. <el-col :span="4">
  135. <span class="eleLabel">字体</span>
  136. </el-col>
  137. <el-col :span="20">
  138. <el-select v-model="text.fontFamily" placement="left"
  139. @change="handleElementFontFamily(text.id, $event)">
  140. <el-option-group v-for="group in fontOptionGroups" :key="group.label" :label="group.label">
  141. <template v-if="group.type == 0">
  142. <el-option v-for="item in group.options" :key="item" :value="item.value" :label="item.label"
  143. :style="{fontFamily: item.value}"></el-option>
  144. </template>
  145. <template v-else>
  146. <el-option v-for="item in group.options" :key="item.id" :value="item.fontFamily"
  147. :label="item.fontName">
  148. <el-image style="height: 18px; width: 90px" :src="item.fontThumbUrl"></el-image>
  149. </el-option>
  150. </template>
  151. </el-option-group>
  152. </el-select>
  153. </el-col>
  154. </el-row>
  155. <!--曲线文字需要调整曲率-->
  156. <el-row class="permRow" v-if="text.type.toLowerCase() === ElementNames.ARCTEXT && perm == '0'">
  157. <el-col :span="4">
  158. <span class="eleLabel">曲率</span>
  159. </el-col>
  160. <el-col :span="1"></el-col>
  161. <el-col :span="19" class="flex-align">
  162. <el-slider :min="1" :max="1000" :step="1" v-model="text.radius"
  163. @change="changeArcTextRadius(text.id, $event)" size="small"></el-slider>
  164. </el-col>
  165. </el-row>
  166. <!-- 颜色-->
  167. <el-row class="permRow" v-if="perm == '3'">
  168. <el-row>
  169. <span class="eleLabel">选择颜色</span>
  170. </el-row>
  171. <el-row :gutter="10">
  172. <el-col :span="4" v-for="color in text.userSelectableColors">
  173. <el-button class="colorButton" :color="color" @click="updateFontColor(text.id,color)"/>
  174. </el-col>
  175. </el-row>
  176. </el-row>
  177. <!-- 字间距-->
  178. <el-row class="permRow" v-if="perm == '4'">
  179. <el-col :span="4">
  180. <span class="eleLabel">缩进</span>
  181. </el-col>
  182. <el-col :span="20">
  183. <el-button-group class="full-group">
  184. <el-tooltip placement="top" content="减小缩进" :hide-after="0">
  185. <el-button @click="handleElementCharSpacing(text.id,'-')">
  186. <IconIndentLeft/>
  187. </el-button>
  188. </el-tooltip>
  189. <el-tooltip placement="top" content="增大缩进" :hide-after="0">
  190. <el-button @click="handleElementCharSpacing(text.id,'+')">
  191. <IconIndentRight/>
  192. </el-button>
  193. </el-tooltip>
  194. </el-button-group>
  195. </el-col>
  196. </el-row>
  197. <!-- 尺寸及位置-->
  198. <el-row class="permRow" v-if="perm == '5'">
  199. <el-col :span="4">
  200. <span class="eleLabel">尺寸</span>
  201. </el-col>
  202. <el-col :span="20">
  203. <el-tag effect="dark" type="info">请拖拽右侧元素更改元素尺寸</el-tag>
  204. </el-col>
  205. </el-row>
  206. <el-row class="permRow" v-if="perm == '6'">
  207. <el-col :span="4">
  208. <span class="eleLabel">位置</span>
  209. </el-col>
  210. <el-col :span="20">
  211. <el-tag effect="dark" type="info">请拖拽右侧元素更改元素位置</el-tag>
  212. </el-col>
  213. </el-row>
  214. </template>
  215. </el-tab-pane>
  216. </el-tabs>
  217. </el-col>
  218. </el-row>
  219. </el-card>
  220. <!-- 图片-->
  221. <el-card v-if="imgPermDisplay"
  222. :body-style="{background: '#f8f8f8'}"
  223. style="max-width: 100%; border-radius: 8px;">
  224. <el-row style="margin-bottom: 10px">
  225. <el-col :span="24">
  226. <span class="eleTitle">编辑图片</span>
  227. </el-col>
  228. <el-col :span="24">
  229. <el-tabs v-model="activeImgTabIndex">
  230. <el-tab-pane v-for="(img,index) in imgObjects" :label="img.itemName" :name="index">
  231. <template v-for="(perm) in img.permissionsConfig">
  232. <!-- 上传图片-->
  233. <el-row class="permRow" v-if="perm == '0'">
  234. <el-col :span="4">
  235. <span class="eleLabel">上传</span>
  236. </el-col>
  237. <el-col :span="20">
  238. <FileInput class="full-width-btn" @change="(files: FileList) => replaceImage(img.id,files)">
  239. <el-button class="full-btn">
  240. <IconTransform class="btn-icon"/>
  241. </el-button>
  242. </FileInput>
  243. </el-col>
  244. </el-row>
  245. <!-- 尺寸及位置-->
  246. <el-row class="permRow" v-if="perm == '1'">
  247. <el-col :span="4">
  248. <span class="eleLabel">尺寸</span>
  249. </el-col>
  250. <el-col :span="20">
  251. <el-tag effect="dark" type="info">请拖拽右侧元素更改元素尺寸</el-tag>
  252. </el-col>
  253. </el-row>
  254. <el-row class="permRow" v-if="perm == '2'">
  255. <el-col :span="4">
  256. <span class="eleLabel">位置</span>
  257. </el-col>
  258. <el-col :span="20">
  259. <el-tag effect="dark" type="info">请拖拽右侧元素更改元素位置</el-tag>
  260. </el-col>
  261. </el-row>
  262. </template>
  263. </el-tab-pane>
  264. </el-tabs>
  265. </el-col>
  266. </el-row>
  267. </el-card>
  268. </el-scrollbar>
  269. </div>
  270. </template>
  271. <script lang="ts" setup>
  272. import useCanvas from "@/views/Canvas/useCanvas";
  273. import {computed, ref} from "vue";
  274. import {MaxSize, MinSize, TransparentFill} from "@/configs/background";
  275. import {WorkSpaceElement} from "@/types/canvas";
  276. import {storeToRefs} from "pinia";
  277. import {useFabricStore, useMainStore, useTemplatesStore} from "@/store";
  278. import {propertiesToInclude, WorkSpaceDrawType} from "@/configs/canvas";
  279. import {Image, Pattern, Textbox, util} from "fabric";
  280. import useHandleBackground from "@/hooks/useHandleBackground";
  281. import {mm2px, px2mm} from "@/utils/image";
  282. import {ElMessage} from "element-plus";
  283. import useI18n from "@/hooks/useI18n";
  284. import {ArcText} from "@/extension/object/ArcText";
  285. import {ElementNames, FontGroupOption} from "@/types/elements";
  286. import {FontSizeLibs} from "@/configs/texts";
  287. const {t} = useI18n();
  288. const mainStore = useMainStore();
  289. const {sizeMode, unitMode} = storeToRefs(mainStore);
  290. const {canvasObject, systemFonts, onlineFonts} = storeToRefs(mainStore)
  291. const [canvas] = useCanvas();
  292. const templatesStore = useTemplatesStore();
  293. const {setBackgroudImage} = useHandleBackground();
  294. const {currentTemplate} = storeToRefs(templatesStore);
  295. const fabricStore = useFabricStore();
  296. const {clip, safe, zoom, opacity} = storeToRefs(fabricStore);
  297. const objects = canvas.getObjects().filter((object) => !["WorkSpaceMaskType", "WorkSpaceClipType", "WorkSpaceSafeType", "WorkSpaceClipType"].includes(object.id));
  298. // const objects = computed(() => {
  299. // return /*canvas == null ? [] :*/ canvas.getObjects().filter((object) => !["WorkSpaceMaskType", "WorkSpaceClipType", "WorkSpaceSafeType", "WorkSpaceClipType"].includes(object.id));
  300. // })
  301. // const backGroundObject = computed(() => {
  302. // const backGround = canvas.getObjects().filter((item) => item.id === WorkSpaceDrawType)[0];
  303. // return /*canvas == null ? {} :*/ backGround ? backGround : {};
  304. // })
  305. // const textObjects = computed(() => {
  306. // const textObjectList = canvas.getObjects().filter((item) => item.name === 'textbox')
  307. // return /*canvas == null ? [] :*/ textObjectList.length > 0 ? textObjectList : [];
  308. // })
  309. // const imgObjects = computed(() => {
  310. // const imgList = canvas.getObjects().filter((item) => item.name === 'image')
  311. // return /*canvas == null ? [] :*/ imgList.length > 0 ? imgList : [];
  312. // })
  313. const backGroundObject = canvas.getObjects().filter((item) => item.id === WorkSpaceDrawType)[0];
  314. const textObjects = currentTemplate.value.objects.filter((item) => item.name === 'textbox');
  315. const imgObjects = currentTemplate.value.objects.filter((item) => item.name === 'image');
  316. const textPermDisplay = computed(() => {
  317. let result = false;
  318. textObjects.forEach(x => {
  319. if (x.permissionsConfig && (x.permissionsConfig.length > 0)) {
  320. result = true;
  321. return;
  322. }
  323. })
  324. return result;
  325. })
  326. const imgPermDisplay = computed(() => {
  327. let result = false;
  328. imgObjects.forEach(x => {
  329. if (x.permissionsConfig && (x.permissionsConfig.length > 0)) {
  330. result = true;
  331. return;
  332. }
  333. })
  334. return result;
  335. })
  336. const activeTextTabIndex = ref(0)
  337. const activeImgTabIndex = ref(0)
  338. const pageSizeWidth = computed(() => {
  339. return Math.round(templateWidth.value * 100) / 100
  340. })
  341. const pageSizeHeight = computed(() => {
  342. return Math.round(templateHeight.value * 100) / 100
  343. })
  344. const templateWidth = computed(() => {
  345. const workWidth = currentTemplate.value.width / currentTemplate.value.zoom;
  346. return unitMode.value === 0 ? px2mm(workWidth) : workWidth;
  347. });
  348. const templateHeight = computed(() => {
  349. const workHeight = currentTemplate.value.height / currentTemplate.value.zoom;
  350. return unitMode.value === 0 ? px2mm(workHeight) : workHeight;
  351. });
  352. const canvasWidth = ref<number>(templateWidth.value);
  353. const canvasHeight = ref<number>(templateHeight.value);
  354. const background = computed(() => {
  355. if (!currentTemplate.value) {
  356. return {
  357. fillType: 0,
  358. fill: TransparentFill,
  359. backgroundColor: "#fff",
  360. } as WorkSpaceElement;
  361. }
  362. if (!currentTemplate.value.workSpace) {
  363. return {
  364. fillType: 0,
  365. fill: TransparentFill,
  366. backgroundColor: "#fff",
  367. } as WorkSpaceElement;
  368. }
  369. return currentTemplate.value.workSpace;
  370. });
  371. const fontOptionGroups = ref<FontGroupOption[]>([
  372. {
  373. type: 0,
  374. label: '系统字体',
  375. options: systemFonts.value
  376. },
  377. {
  378. type: 1,
  379. label: '在线字体',
  380. options: onlineFonts.value
  381. }
  382. ])
  383. // 固定宽高
  384. const isFixed = ref(false);
  385. // 设置背景
  386. const updateBackground = (props: Partial<WorkSpaceElement>) => {
  387. const [canvas] = useCanvas();
  388. const workSpaceDraw = canvas.getObjects().filter((item) => item.id === WorkSpaceDrawType)[0];
  389. if (!workSpaceDraw) return;
  390. workSpaceDraw.set({...props});
  391. if (props.fill instanceof Pattern) {
  392. props.fill = props.fill.toObject() as Pattern
  393. }
  394. templatesStore.updateWorkSpace({workSpace: {...background.value, ...props}});
  395. const workProps = workSpaceDraw.toObject(propertiesToInclude as any[]);
  396. templatesStore.updateElement({id: workSpaceDraw.id, props: {...workProps, ...props}});
  397. canvas.renderAll();
  398. };
  399. // 设置背景可见
  400. const setBackGroudVisible = (val) => {
  401. templatesStore.modifedElement(backGroundObject, {visible: val})
  402. }
  403. // 修改上传背景
  404. const changeBackgroundImage = async (imageURL: string) => {
  405. if (background.value.imageSize === "repeat") {
  406. const backgroundImage = await util.loadImage(imageURL);
  407. const workSpacePattern = new Pattern({
  408. source: backgroundImage,
  409. repeat: "repeat",
  410. });
  411. updateBackground({fill: workSpacePattern, imageURL});
  412. } else {
  413. setBackgroudImage(imageURL);
  414. updateBackground({fill: TransparentFill, imageURL});
  415. }
  416. };
  417. // 获取画布尺寸
  418. const getCanvasSize = () => {
  419. let width =
  420. unitMode.value === 0 ? mm2px(canvasWidth.value) : canvasWidth.value;
  421. let height =
  422. unitMode.value === 0 ? mm2px(canvasHeight.value) : canvasHeight.value;
  423. width = width * zoom.value;
  424. height = height * zoom.value;
  425. return {width, height};
  426. };
  427. // 修改画布宽度
  428. const changeTemplateWidth = () => {
  429. const [canvas] = useCanvas();
  430. const workSpaceDraw = canvas
  431. .getObjects()
  432. .filter((item) => item.id === WorkSpaceDrawType)[0];
  433. if (!workSpaceDraw) return;
  434. const ratio = currentTemplate.value.height / currentTemplate.value.width;
  435. let {width, height} = getCanvasSize();
  436. if (width / zoom.value < mm2px(MinSize)) {
  437. ElMessage({
  438. message: t("style.minimumSizeLimit") + MinSize,
  439. type: "warning",
  440. });
  441. width = mm2px(MinSize) * zoom.value;
  442. }
  443. if (width / zoom.value > mm2px(MaxSize)) {
  444. ElMessage({
  445. message: t("style.maximumSizeLimit") + MaxSize,
  446. type: "warning",
  447. });
  448. width = mm2px(MaxSize) * zoom.value;
  449. }
  450. height = isFixed.value ? width * ratio : height;
  451. workSpaceDraw.set({width: width / zoom.value, height: height / zoom.value});
  452. templatesStore.setSize(width, height, zoom.value);
  453. sizeMode.value = 2;
  454. canvas.renderAll();
  455. // resetCanvas()
  456. // addHistorySnapshot();
  457. };
  458. // 修改画布高度
  459. const changeTemplateHeight = () => {
  460. const [canvas] = useCanvas();
  461. const workSpaceDraw = canvas
  462. .getObjects()
  463. .filter((item) => item.id === WorkSpaceDrawType)[0];
  464. if (!workSpaceDraw) return;
  465. const ratio = currentTemplate.value.height / currentTemplate.value.width;
  466. let {width, height} = getCanvasSize();
  467. if (height / zoom.value < mm2px(MinSize)) {
  468. ElMessage({
  469. message: t("style.minimumSizeLimit") + MinSize,
  470. type: "warning",
  471. });
  472. height = mm2px(MinSize) * zoom.value;
  473. }
  474. if (height / zoom.value > mm2px(MaxSize)) {
  475. ElMessage({
  476. message: t("style.maximumSizeLimit") + MaxSize,
  477. type: "warning",
  478. });
  479. height = mm2px(MaxSize) * zoom.value;
  480. }
  481. width = isFixed.value ? height / ratio : width;
  482. workSpaceDraw.set({width: width / zoom.value, height: height / zoom.value});
  483. templatesStore.setSize(width, height, zoom.value);
  484. sizeMode.value = 2;
  485. canvas.renderAll();
  486. // resetCanvas()
  487. // addHistorySnapshot();
  488. };
  489. //查找目标元素
  490. const findObject = (objId: string) => {
  491. return objects.find((item) => item.id === objId);
  492. }
  493. // 文字核心调整函数0-默认
  494. function adjustFontDefault(textbox, maxHeight, maxWidth) {
  495. const textWidth = textbox.calcTextWidth();
  496. const scaleWidth = textWidth > maxWidth ? textWidth : maxWidth
  497. if (textbox.isEditing) {
  498. textbox.setSelectionStyles({width: scaleWidth})
  499. } else {
  500. templatesStore.modifedElement(textbox, {width: scaleWidth})
  501. }
  502. }
  503. // 文字核心调整函数1-尺寸
  504. function adjustFontSize(textbox, maxHeight, maxWidth) {
  505. let currentSize = textbox.fontSize;
  506. const maxFontSize = 80
  507. const minFontSize = 6
  508. console.log("宽" + maxWidth + "高" + maxHeight)
  509. // 若空间有余,尝试恢复最大字号
  510. while (/*textbox.width <= maxWidth*/ textbox.height < maxHeight && currentSize < maxFontSize) {
  511. currentSize += 1;
  512. const fontSize = currentSize
  513. if (textbox.isEditing) {
  514. textbox.setSelectionStyles({fontSize})
  515. } else {
  516. templatesStore.modifedElement(textbox, {fontSize})
  517. }
  518. }
  519. // 检测是否超出边界
  520. while (/*(textbox.width > maxWidth ||*/ textbox.height > maxHeight && currentSize > minFontSize) {
  521. currentSize -= 1;
  522. const fontSize = currentSize
  523. if (textbox.isEditing) {
  524. textbox.setSelectionStyles({fontSize})
  525. } else {
  526. templatesStore.modifedElement(textbox, {fontSize})
  527. }
  528. }
  529. }
  530. // 文字核心调整函数2-缩放
  531. function adjustFontScale(textbox, maxHeight, maxWidth, originScaleX) {
  532. let scaleX = originScaleX;
  533. // console.log('width ' + maxWidth);
  534. const textWidth = textbox.calcTextWidth();
  535. const scaleWidth = textWidth > maxWidth ? textWidth : maxWidth
  536. // console.log('textWidth ' + textWidth);
  537. scaleX *= maxWidth / scaleWidth;
  538. // console.log('scaleX ' + scaleX);
  539. if (textbox.isEditing) {
  540. textbox.setSelectionStyles({scaleX})
  541. textbox.setSelectionStyles({width: maxWidth})
  542. // textbox.setSelectionStyles({height: maxHeight})
  543. } else {
  544. templatesStore.modifedElement(textbox, {scaleX})
  545. templatesStore.modifedElement(textbox, {width: maxWidth})
  546. // templatesStore.modifedElement(textbox, {height: maxHeight})
  547. }
  548. }
  549. //修改文字信息
  550. const eleTextChange = (obj, text) => {
  551. const handleElement = findObject(obj.id) as Textbox | ArcText;
  552. const maxHeight = obj.objOriginHeight
  553. const maxWidth = obj.objOriginWidth
  554. const originScaleX = obj.objOriginScaleX
  555. if (handleElement.isEditing) {
  556. handleElement.setSelectionStyles({text})
  557. } else {
  558. templatesStore.modifedElement(handleElement, {text})
  559. }
  560. // canvas.renderAll()
  561. // 读取预设的缩放行为
  562. let scaleType = 0
  563. if (obj.textScaleType >= 0)
  564. scaleType = obj.textScaleType
  565. if (scaleType == 0)
  566. adjustFontDefault(handleElement, maxHeight, maxWidth)
  567. else if (scaleType == 1)
  568. adjustFontSize(handleElement, maxHeight, maxWidth)
  569. else if (scaleType == 2)
  570. adjustFontScale(handleElement, maxHeight, maxWidth, originScaleX)
  571. }
  572. // 修改字体大小
  573. const handleElementFontsize = (objId: string, mode: string) => {
  574. const handleElement = findObject(objId) as Textbox | ArcText;
  575. if (handleElement.fontSize <= 6) return
  576. const fontSize = mode === '+' ? Number(handleElement.fontSize) + 1 : Number(handleElement.fontSize) - 1
  577. if (handleElement.isEditing) {
  578. handleElement.setSelectionStyles({fontSize})
  579. } else {
  580. templatesStore.modifedElement(handleElement, {fontSize})
  581. }
  582. canvas.renderAll()
  583. }
  584. // 修改字体大小-直接输入
  585. const handleElementFontSize = (objId: string, fontSize: string) => {
  586. const handleElement = findObject(objId) as Textbox | ArcText;
  587. fontSize = fontSize.replace(/[^\d]/g, '')
  588. if (!fontSize) return
  589. if (handleElement.isEditing) {
  590. handleElement.setSelectionStyles({fontSize})
  591. } else {
  592. templatesStore.modifedElement(handleElement, {fontSize})
  593. }
  594. canvas.renderAll()
  595. }
  596. // 修改字体族
  597. const handleElementFontFamily = (objId: string, fontFamily: string) => {
  598. const handleElement = findObject(objId) as Textbox | ArcText;
  599. if (handleElement.isEditing) {
  600. handleElement.setSelectionStyles({fontFamily})
  601. } else {
  602. templatesStore.modifedElement(handleElement, {fontFamily})
  603. }
  604. canvas.renderAll()
  605. }
  606. // 修改字体颜色
  607. const updateFontColor = (objId: string, fill: string) => {
  608. const handleElement = findObject(objId) as Textbox | ArcText;
  609. if (handleElement.isEditing) {
  610. handleElement.setSelectionStyles({fill})
  611. } else {
  612. templatesStore.modifedElement(handleElement, {fill, color: fill})
  613. }
  614. }
  615. //修改字体曲率
  616. const changeArcTextRadius = (objId: string, val: number) => {
  617. const handleElement = findObject(objId) as Textbox | ArcText;
  618. (handleElement as ArcText).setRadius(val);
  619. templatesStore.modifedElement(handleElement, {radius: val});
  620. }
  621. // 修改缩进
  622. const handleElementCharSpacing = (objId: string, mode: '+' | '-') => {
  623. const handleElement = findObject(objId) as Textbox | ArcText;
  624. const handleCharSpacing = handleElement.charSpacing
  625. const charSpacing = mode === '+' ? handleCharSpacing + 10 : handleCharSpacing - 10
  626. templatesStore.modifedElement(handleElement, {charSpacing})
  627. }
  628. // 替换图片(保持当前的样式)
  629. const replaceImage = (objId: string, files: FileList) => {
  630. const handleElement = findObject(objId) as Image;
  631. const props = {src: files.fileLink};
  632. handleElement.setSrc(files.fileLink); //红就红吧type硬赋值了
  633. templatesStore.updateElement({id: handleElement.id, props});
  634. };
  635. onMounted(() => {
  636. // console.log('aaaaaaaaaaaaassssssssssssssss' + JSON.stringify(textObjects))
  637. //记录所有text的原始宽高,用于自动缩放字体
  638. textObjects.forEach(x => {
  639. const handleElement = findObject(x.id) as Textbox | ArcText;
  640. x.objOriginHeight = handleElement.height
  641. x.objOriginWidth = handleElement.width
  642. x.objOriginScaleX = handleElement.scaleX
  643. })
  644. })
  645. </script>
  646. <style lang="scss" scoped>
  647. :deep(.el-tabs__item) {
  648. padding: 0;
  649. }
  650. .layout-search {
  651. margin: 0 auto;
  652. width: 80%;
  653. padding: 20px 10px 10px;
  654. }
  655. .layout-upload {
  656. justify-content: center;
  657. }
  658. .layout-tabs {
  659. width: 90%;
  660. margin: 0 auto;
  661. }
  662. .layout-templates {
  663. display: flex;
  664. flex-wrap: wrap;
  665. padding: 2px;
  666. .thumbnail {
  667. display: flex;
  668. width: 124px;
  669. margin: 2px;
  670. }
  671. .thumbnail img {
  672. outline: 1px solid $borderColor;
  673. margin: 0 5px;
  674. cursor: pointer;
  675. &:hover {
  676. outline-color: $themeColor;
  677. }
  678. }
  679. }
  680. .col-img {
  681. height: 100px;
  682. img {
  683. max-height: 100%;
  684. }
  685. }
  686. .eleTitle {
  687. font-size: 1.0rem;
  688. font-weight: bold;
  689. }
  690. .eleLabel {
  691. font-size: 0.8rem;
  692. font-weight: bold;
  693. margin-bottom: 10px;
  694. text-align: center;
  695. }
  696. .colorButton {
  697. margin-bottom: 5px;
  698. }
  699. .permRow {
  700. margin-top: 10px;
  701. }
  702. </style>