123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 |
- import {
- IconStrikethrough,
- IconTextBold,
- IconTextCenter,
- IconTextItalic,
- IconTextJustify,
- IconTextLeft,
- IconTextLetterSpacing,
- IconTextLineHeight,
- IconTextRight,
- IconTextSize,
- IconTextUnderline,
- } from "@/assets/icons";
- import { css } from "@linaria/core";
- import { Button, Checkbox, InputNumber, Tooltip } from "ant-design-vue";
- import { useEditor } from "@/modules/editor";
- import Select from "@queenjs-modules/queditor/components/FormUI/Items/Select";
- import { queenApi } from "queenjs";
- import {
- defineComponent,
- nextTick,
- onMounted,
- onUnmounted,
- reactive,
- toRaw,
- watch,
- } from "vue";
- import { any, bool, func, number, object, string } from "vue-types";
- import NewColorPicker from "../../formItems/NewColorPicker";
- import { isNumber } from "lodash";
- interface ColumnItem {
- label?: string;
- component?: ((...args: any[]) => any) | Record<string, any>;
- dataIndex?: string;
- props?: { [name: string]: any };
- itemProps?: { [name: string]: any };
- changeExtra?: (data: any) => any;
- }
- export const TextColor = defineComponent({
- props: {
- value: string().def("#666666"),
- },
- emits: ["change"],
- setup(props, { emit }) {
- const state = reactive({
- color: props.value,
- });
- watch(
- () => props.value,
- () => {
- state.color = props.value;
- }
- );
- const colorChange = (value: string) => {
- emit("change", value);
- state.color = value;
- };
- return () => (
- <NewColorPicker
- value={state.color}
- onChange={colorChange}
- showGradient={false}
- />
- );
- },
- });
- export const AlignComp = defineComponent({
- props: {
- value: string<"left" | "right" | "center" | "justify">().def("left"),
- },
- emits: ["change"],
- setup(props, { emit }) {
- const aligns = [
- {
- label: "左对齐",
- key: "left",
- icon: <IconTextLeft />,
- },
- {
- label: "居中对齐",
- key: "center",
- icon: <IconTextCenter />,
- },
- {
- label: "右对齐",
- key: "right",
- icon: <IconTextRight />,
- },
- {
- label: "两端对齐",
- key: "justify",
- icon: <IconTextJustify />,
- },
- ];
- return () => (
- <div class={AlignCompWapper}>
- {aligns.map((e: any) => {
- return (
- <Tooltip title={e.label} placement="top">
- <Button
- class={props.value == e.key ? currStyle : null}
- icon={e.icon}
- type="text"
- onClick={() => {
- emit("change", e.key);
- }}
- ></Button>
- </Tooltip>
- );
- })}
- </div>
- );
- },
- });
- export const LetterSpacingComp = defineComponent({
- props: {
- value: any<string | number>().def(0),
- },
- emits: ["change"],
- setup(props, { emit }) {
- return () => {
- let value =
- typeof props.value === "string" ? parseInt(props.value) : props.value;
- value = isNumber(value) ? value : 0;
- return (
- <InputNumber
- prefix={<IconTextLetterSpacing class="text-22px mr-6px" />}
- defaultValue={0}
- min={0}
- max={100}
- step={1}
- value={value}
- onChange={(value: any) => {
- if (!value) {
- emit("change", "0px");
- return;
- }
- emit("change", value + "px");
- }}
- />
- );
- };
- },
- });
- export const LineHeightComp = defineComponent({
- props: {
- value: any<string | number>().def(1.5),
- },
- emits: ["change"],
- setup(props, { emit }) {
- return () => {
- let value =
- typeof props.value === "string" ? parseFloat(props.value) : props.value;
- value = isNumber(value) ? value : 1.5;
- return (
- <InputNumber
- prefix={<IconTextLineHeight class="text-22px mr-6px" />}
- defaultValue={1.5}
- min={0.5}
- max={3}
- step={0.5}
- value={value || 1.5}
- onChange={(value: any) => {
- if (!value) {
- emit("change", 1.5);
- return;
- }
- emit("change", value);
- }}
- />
- );
- };
- },
- });
- export const FontStyleWapper = defineComponent({
- emits: ["change"],
- setup(props, { emit }) {
- const fontStyleColumns = [
- {
- label: "加粗",
- dataIndex: "bold",
- icon: <IconTextBold />,
- },
- {
- label: "斜体",
- dataIndex: "italic",
- icon: <IconTextItalic />,
- },
- {
- label: "下划线",
- dataIndex: "underline",
- icon: <IconTextUnderline />,
- },
- {
- label: "删除线",
- dataIndex: "strikethrough",
- icon: <IconStrikethrough />,
- },
- ];
- const changeVal = (e: any) => {
- emit("change", e);
- };
- return () => (
- <div class={[FontStyleCompWapper]}>
- {fontStyleColumns.map((e: any) => {
- return (
- <TextToolItem
- key={e.dataIndex}
- class={"!mr-0"}
- column={{
- label: e.label,
- dataIndex: e.dataIndex,
- component: (props) => {
- return <FontStyleComp icon={e.icon} {...props} />;
- },
- }}
- onChange={changeVal}
- />
- );
- })}
- </div>
- );
- },
- });
- export const FontStyleComp = defineComponent({
- props: {
- icon: any(),
- value: bool().def(false),
- },
- emits: ["change"],
- setup(props, { emit }) {
- return () => {
- return (
- <Button
- type="text"
- class={props.value ? currStyle : null}
- icon={props.icon}
- onClick={() => {
- emit("change", !props.value);
- }}
- ></Button>
- );
- };
- },
- });
- export const FontFamily = defineComponent({
- props: {
- value: string().def(""),
- },
- emits: ["change"],
- setup(props, { emit }) {
- const options = [
- { label: "默认字体", value: "" },
- { label: "宋体", value: "宋体,Songti,STSong,serif" },
- { label: "黑体", value: "黑体,Heiti,STHeiti,sans-serif" },
- { label: "仿宋", value: "仿宋,FangSong,STFangsong,serif" },
- { label: "楷体", value: "楷体,KaiTi,STKaiti,sans-serif" },
- ];
- return () => {
- let item = options.find((e) => {
- return e.value.indexOf(props.value) != -1;
- });
- const value = item ? item.value : "";
- return (
- <Select
- options={options}
- value={value || ""}
- onChange={(v) => {
- emit("change", v);
- }}
- ></Select>
- );
- };
- },
- });
- export const FontSize = defineComponent({
- props: {
- value: any().def("12px"),
- },
- emits: ["change"],
- setup(props, { emit }) {
- return () => {
- let value =
- typeof props.value === "string" ? parseInt(props.value) : props.value;
- value = isNumber(value) ? value : 12;
- return (
- <InputNumber
- prefix={<IconTextSize class="text-22px mr-6px" />}
- defaultValue={12}
- min={12}
- max={60}
- value={value}
- onChange={(value: any) => {
- if (!value) {
- emit("change", "12px");
- return;
- }
- emit("change", value + "px");
- }}
- />
- );
- };
- },
- });
- export const TextStroke = defineComponent({
- props: {
- value: any().def(undefined),
- },
- emits: ["change"],
- setup(props, { emit }) {
- const state = reactive({
- visible: props.value ? true : false,
- width: 1,
- color: "#666666",
- });
- watch(
- () => props.value,
- () => {
- formatVal(props.value);
- }
- );
- const formatVal = (value: any) => {
- if (!value) {
- state.visible = false;
- state.color = "#666666";
- state.width = 1;
- return;
- }
- state.visible = true;
- const colorReg = /#[a-zA-Z0-9]{6}/g;
- let color = value.match(colorReg);
- if (color) {
- color = color[0];
- } else {
- color = "#666666";
- }
- state.color = color;
- const widthReg = /\d+px/g;
- let width = value.match(widthReg);
- if (width) {
- width = width[0];
- } else {
- width = 1;
- }
- state.width = parseInt(width);
- };
- const colorChange = (v: string) => {
- state.color = v;
- buildValueSub();
- };
- const visibleChange = (e: any) => {
- const checked = e.target.checked;
- state.visible = checked;
- buildValueSub();
- };
- const widthChange = (e: any) => {
- state.width = e;
- buildValueSub();
- };
- const buildValueSub = () => {
- if (!state.visible) {
- return;
- }
- const value = `${state.width}px ${state.color}`;
- emit("change", value);
- };
- return () => {
- return (
- <div class={"flex-1"}>
- <div class={"flex justify-between items-center w-full"}>
- <div class={"flex-1 flex items-center"}>
- <Checkbox checked={state.visible} onChange={visibleChange} />
- {state.visible && (
- <div class={"flex-1 px-20px"}>
- <Tooltip title={"描边宽度"} placement="top">
- <InputNumber
- class={StrokeStyle}
- min={1}
- max={5}
- step={1}
- value={state.width}
- onChange={widthChange}
- />
- </Tooltip>
- </div>
- )}
- </div>
- <Tooltip title={"描边颜色"} placement="top">
- <TextColor value={state.color} onChange={colorChange} />
- </Tooltip>
- </div>
- </div>
- );
- };
- },
- });
- // export const LinkButton = defineComponent({
- // props: {
- // icon: any(),
- // value: any(),
- // },
- // emits: ["change"],
- // setup(props, { emit }) {
- // const showLinkInput = async () => {
- // const res = await queenApi.showInput({
- // title: "请输入链接地址",
- // defaultValue: "http://",
- // });
- // emit("change", res);
- // };
- // return () => (
- // <Button type="text" icon={props.icon} onClick={showLinkInput}></Button>
- // );
- // },
- // });
- const stylesKey: { [key: string]: any } = {
- fontSize: "font-size",
- fontFamily: "font-family",
- letterSpacing: "letter-spacing",
- lineHeight: "line-height",
- alignment: "text-align",
- fontColor: "color",
- textStroke: "-webkit-text-stroke",
- };
- const tagsKey: { [key: string]: any } = {
- bold: "<strong>",
- italic: "<i>",
- underline: "<u>",
- strikethrough: "<s>",
- };
- export const TextToolItem = defineComponent({
- props: {
- column: object<ColumnItem>(),
- index: number(),
- onChange: func(),
- },
- setup(props) {
- const state = reactive({
- value: undefined as any,
- });
- const { controls, store, actions, helper } = useEditor();
- let editor: any = null;
- watch(
- () => controls.textEditorCtrl.state.currEditor,
- () => {
- editor = toRaw(controls.textEditorCtrl.state.currEditor);
- initCommands();
- }
- );
- watch(
- () => store.currComp.value,
- () => {
- editor = toRaw(controls.textEditorCtrl.state.currEditor);
- if (!editor && store.currComp.compKey == "Text") {
- initCommands();
- nextTick(() => {
- const element: HTMLElement | null = document.querySelector(
- `#editor_${store.currComp.id}`
- );
- if (!element) {
- return;
- }
- const h = helper.pxToDesignSize(element.clientHeight);
- console.log(h);
- actions.updateCompData(store.currComp, "layout.size.1", h);
- helper.extendStreamCard(store.currStreamCardId);
- actions.selectObjs([]);
- setTimeout(() => {
- actions.selectObjs([store.currComp.id]);
- }, 0);
- });
- }
- }
- );
- function handleValueChange() {
- const { column } = props;
- if (!editor) {
- return;
- }
- const command = editor.commands.get(column?.dataIndex);
- if (command) {
- state.value = command.value;
- }
- }
- const initCommands = () => {
- const { column } = props;
- if (!editor) {
- if (!column?.dataIndex) {
- return;
- }
- const compValue = store.currComp.value;
- if (tagsKey[column.dataIndex]) {
- const hasTag = compValue.indexOf(tagsKey[column.dataIndex]);
- if (hasTag != -1) {
- state.value = true;
- } else {
- state.value = false;
- }
- return;
- }
- const regString = `(${stylesKey[column.dataIndex]}:)([\\s\\S]*?)(\\;)`;
- const styleReg = new RegExp(regString, "ig");
- const values = compValue.match(styleReg);
- if (!values) {
- state.value = undefined;
- return;
- }
- let value = values[0];
- value = value.replace(styleReg, "$2");
- state.value = value;
- return;
- }
- const command = editor.commands.get(column?.dataIndex);
- if (command) {
- state.value = command.value;
- command.on("change:value", handleValueChange);
- }
- };
- onMounted(() => {
- initCommands();
- });
- onUnmounted(() => {
- const { column } = props;
- if (!editor) {
- return;
- }
- const command = editor.commands.get(column?.dataIndex);
- if (command) {
- command.off("change:value", handleValueChange);
- }
- });
- const changeVal = (value: any, ...args: any[]) => {
- const { column } = props;
- let params = {
- dataIndex: column?.dataIndex,
- value: { value },
- ...args,
- };
- if (column?.changeExtra) params = column.changeExtra?.(params);
- props.onChange?.(params);
- return params;
- };
- const component = props.column?.component || null;
- return () => {
- const { column, index } = props;
- return (
- <div
- key={column?.dataIndex || "" + index}
- class={formItemStyles}
- {...column?.itemProps}
- onClick={(e) => e.stopPropagation()}
- >
- {column?.label ? (
- <Tooltip title={column.label} placement="top">
- <component
- value={state.value}
- {...column.props}
- onChange={changeVal}
- />
- </Tooltip>
- ) : (
- <component
- value={state.value}
- {...column?.props}
- onChange={changeVal}
- />
- )}
- </div>
- );
- };
- },
- });
- const currStyle = css`
- color: @inf-primary-color;
- &:hover,
- &:focus {
- color: @inf-primary-color;
- }
- `;
- const StrokeStyle = css`
- input {
- height: 28px;
- }
- `;
- const AlignCompWapper = css`
- display: flex;
- background-color: #303030;
- .ant-btn {
- flex: 1;
- width: 100%;
- line-height: 1;
- .inficon {
- font-size: 22px;
- }
- }
- `;
- const FontStyleCompWapper = css`
- flex: 1;
- display: flex;
- align-items: center;
- margin-right: 12px;
- border-radius: 2px;
- background-color: #303030;
- & > div {
- flex: 1;
- border-radius: 0;
- .ant-btn {
- width: 100%;
- line-height: 1;
- .inficon {
- font-size: 22px;
- }
- }
- }
- `;
- const formItemStyles = css`
- height: 100%;
- flex: 1;
- margin-right: 12px;
- .ant-input-number-affix-wrapper,
- .ant-select {
- background-color: #303030;
- }
- border-radius: 2px;
- &:last-child {
- margin-right: 0;
- }
- &.disabled {
- cursor: not-allowed;
- }
- `;
|