ToolbarComp.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. import {
  2. IconLineHeight,
  3. IconTextCenter,
  4. IconTextJustify,
  5. IconTextLeft,
  6. IconTextRight,
  7. } from "@/assets/icons";
  8. import { css } from "@linaria/core";
  9. import { Button, Dropdown, InputNumber, Menu, Tooltip } from "ant-design-vue";
  10. import Select from "@queenjs-modules/queditor/components/FormUI/Items/Select";
  11. import Pickr from "@simonwep/pickr";
  12. import "@simonwep/pickr/dist/themes/nano.min.css";
  13. import { queenApi } from "queenjs";
  14. import { defineComponent, onMounted, ref, watch, onUnmounted } from "vue";
  15. import { any, bool, string } from "vue-types";
  16. export const TextColor = defineComponent({
  17. props: {
  18. value: string().def("#666666"),
  19. },
  20. emits: ["change"],
  21. setup(props, { emit }) {
  22. let picker: any = null;
  23. let emitFlagRef = ref(true);
  24. function initPicker() {
  25. picker = Pickr.create({
  26. el: ".color_picker",
  27. theme: "nano",
  28. default: props.value,
  29. i18n: {
  30. "btn:save": "确定",
  31. },
  32. components: {
  33. preview: true,
  34. opacity: false,
  35. hue: true,
  36. interaction: {
  37. hex: false,
  38. rgba: false,
  39. input: true,
  40. save: true,
  41. },
  42. },
  43. });
  44. picker.on("save", (color: any) => {
  45. picker.hide();
  46. const hexa = color.toHEXA().toString();
  47. if (!emitFlagRef.value) {
  48. emitFlagRef.value = true;
  49. return;
  50. }
  51. emit("change", hexa);
  52. });
  53. }
  54. watch(
  55. () => props.value,
  56. () => {
  57. console.log(props.value);
  58. emitFlagRef.value = false;
  59. picker.setColor(props.value);
  60. }
  61. );
  62. onMounted(() => {
  63. initPicker();
  64. });
  65. onUnmounted(() => {
  66. console.log("destroy");
  67. if (picker) {
  68. picker.destroyAndRemove();
  69. picker = null;
  70. }
  71. });
  72. return () => (
  73. <div
  74. class={ColorPicker}
  75. onClick={() => {
  76. picker?.show();
  77. }}
  78. >
  79. <div class={"color_picker"} id="color_picker"></div>
  80. </div>
  81. );
  82. },
  83. });
  84. export const AlignComp = defineComponent({
  85. props: {
  86. value: string<"left" | "right" | "center" | "justify">().def("left"),
  87. },
  88. emits: ["change"],
  89. setup(props, { emit }) {
  90. const aligns = [
  91. {
  92. label: "左对齐",
  93. key: "left",
  94. icon: <IconTextLeft />,
  95. },
  96. {
  97. label: "居中对齐",
  98. key: "center",
  99. icon: <IconTextCenter />,
  100. },
  101. {
  102. label: "右对齐",
  103. key: "right",
  104. icon: <IconTextRight />,
  105. },
  106. {
  107. label: "两端对齐",
  108. key: "justify",
  109. icon: <IconTextJustify />,
  110. },
  111. ];
  112. const overlay = () => {
  113. return (
  114. <Menu>
  115. {aligns.map((e: any) => {
  116. return (
  117. <Menu.Item
  118. class={props.value == e.key ? currStyle : null}
  119. onClick={() => {
  120. emit("change", e.key);
  121. }}
  122. >
  123. <Tooltip placement="right" title={e.label}>
  124. {e.icon}
  125. </Tooltip>
  126. </Menu.Item>
  127. );
  128. })}
  129. </Menu>
  130. );
  131. };
  132. const currIcon = (key: string) => {
  133. const item = aligns.find((e) => {
  134. return e.key == key;
  135. });
  136. return item ? item.icon : null;
  137. };
  138. return () => (
  139. <Dropdown
  140. overlay={overlay()}
  141. overlayClassName={"editor_toolbar_drop"}
  142. trigger={"click"}
  143. placement="bottom"
  144. >
  145. <Button type="text" icon={currIcon(props.value)}></Button>
  146. </Dropdown>
  147. );
  148. },
  149. });
  150. export const LineHeightComp = defineComponent({
  151. props: {
  152. value: string().def("1.5"),
  153. },
  154. emits: ["change"],
  155. setup(props, { emit }) {
  156. const options = [1, 1.5, 2, 2.5, 3];
  157. const overlay = () => {
  158. return (
  159. <Menu>
  160. {options.map((e: any) => {
  161. return (
  162. <Menu.Item
  163. class={props.value == e ? currStyle : null}
  164. onClick={() => {
  165. emit("change", e);
  166. }}
  167. >
  168. {e}
  169. </Menu.Item>
  170. );
  171. })}
  172. </Menu>
  173. );
  174. };
  175. return () => (
  176. <Dropdown
  177. overlay={overlay()}
  178. overlayClassName={"editor_toolbar_drop"}
  179. trigger={"click"}
  180. placement="bottom"
  181. >
  182. <Button type="text" icon={<IconLineHeight />}></Button>
  183. </Dropdown>
  184. );
  185. },
  186. });
  187. export const FontStyleComp = defineComponent({
  188. props: {
  189. icon: any(),
  190. value: bool().def(false),
  191. },
  192. emits: ["change"],
  193. setup(props, { emit }) {
  194. const triggerStyle = () => {
  195. emit("change", !props.value);
  196. };
  197. return () => (
  198. <Button
  199. type="text"
  200. class={props.value ? currStyle : null}
  201. icon={props.icon}
  202. onClick={triggerStyle}
  203. ></Button>
  204. );
  205. },
  206. });
  207. export const FontFamily = defineComponent({
  208. props: {
  209. value: string().def(""),
  210. },
  211. emits: ["change"],
  212. setup(props, { emit }) {
  213. const options = [
  214. { label: "默认字体", value: "" },
  215. { label: "宋体", value: "宋体,Songti,STSong,serif" },
  216. { label: "黑体", value: "黑体,Heiti,STHeiti,sans-serif" },
  217. { label: "仿宋", value: "仿宋,FangSong,STFangsong,serif" },
  218. { label: "楷体", value: "楷体,KaiTi,STKaiti,sans-serif" },
  219. ];
  220. return () => {
  221. return (
  222. <Select
  223. options={options}
  224. value={props.value || ""}
  225. onChange={(v) => {
  226. emit("change", v);
  227. }}
  228. ></Select>
  229. );
  230. };
  231. },
  232. });
  233. export const FontSize = defineComponent({
  234. props: {
  235. value: string().def("12px"),
  236. },
  237. emits: ["change"],
  238. setup(props, { emit }) {
  239. return () => {
  240. return (
  241. <InputNumber
  242. defaultValue={12}
  243. min={12}
  244. max={60}
  245. value={parseInt(props.value) || 12}
  246. onChange={(value: any) => {
  247. emit("change", value + "px");
  248. }}
  249. />
  250. );
  251. };
  252. },
  253. });
  254. export const LinkButton = defineComponent({
  255. props: {
  256. icon: any(),
  257. value: any(),
  258. },
  259. emits: ["change"],
  260. setup(props, { emit }) {
  261. const showLinkInput = async () => {
  262. const res = await queenApi.showInput({
  263. title: "请输入链接地址",
  264. defaultValue: "http://",
  265. });
  266. emit("change", res);
  267. };
  268. return () => (
  269. <Button type="text" icon={props.icon} onClick={showLinkInput}></Button>
  270. );
  271. },
  272. });
  273. const currStyle = css`
  274. color: @inf-primary-color;
  275. &:hover,
  276. &:focus {
  277. color: @inf-primary-color;
  278. }
  279. `;
  280. const ColorPicker = css`
  281. position: relative;
  282. width: 32px;
  283. height: 32px;
  284. border-radius: 2px;
  285. cursor: pointer;
  286. .pickr {
  287. width: 100%;
  288. height: 100%;
  289. .pcr-button {
  290. width: 100%;
  291. height: 100%;
  292. border: 1px solid transparent;
  293. }
  294. border-radius: 2px;
  295. input {
  296. &:focus,
  297. &.pcr-active {
  298. border-color: @inf-primary-color;
  299. box-shadow: 0 0 0 2px rgba(232, 139, 0, 0.2);
  300. }
  301. }
  302. button {
  303. &:focus,
  304. &.pcr-active {
  305. border-color: @inf-primary-color;
  306. box-shadow: 0 0 0 2px rgba(232, 139, 0, 0.2);
  307. }
  308. }
  309. }
  310. .pcr-app {
  311. input {
  312. &:focus,
  313. &.pcr-active {
  314. border-color: @inf-primary-color;
  315. box-shadow: 0 0 0 2px rgba(232, 139, 0, 0.2);
  316. }
  317. }
  318. button {
  319. &:focus,
  320. &.pcr-active {
  321. border-color: @inf-primary-color;
  322. box-shadow: 0 0 0 2px rgba(232, 139, 0, 0.2);
  323. }
  324. }
  325. }
  326. `;