123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- import {
- IconStrikethrough,
- IconTextBold,
- IconTextCenter,
- IconTextItalic,
- IconTextJustify,
- IconTextLeft,
- IconTextLetterSpacing,
- IconTextLineHeight,
- IconTextRight,
- IconTextSize,
- IconTextUnderline,
- } from "@/assets/icons";
- import { css } from "@linaria/core";
- import { Button, InputNumber, Tooltip } from "ant-design-vue";
- import { useEditor } from "@/modules/editor";
- import Select from "@queenjs-modules/queditor/components/FormUI/Items/Select";
- import "@simonwep/pickr/dist/themes/nano.min.css";
- import _ from "lodash";
- import { queenApi } from "queenjs";
- import {
- defineComponent,
- onMounted,
- onUnmounted,
- reactive,
- ref,
- toRaw,
- watch,
- } from "vue";
- import { any, bool, func, number, object, string } from "vue-types";
- 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 }) {
- let pickerRef = ref();
- const state = reactive({
- color: props.value,
- });
- watch(
- () => props.value,
- () => {
- state.color = props.value;
- }
- );
- const colorChange = (e: any) => {
- const hexa = e.target.value;
- emit("change", hexa);
- state.color = hexa;
- };
- return () => (
- <div
- class={ColorPicker}
- onClick={() => {
- pickerRef?.value.click();
- }}
- >
- <div class="color_picker" style={{ backgroundColor: state.color }}>
- <input
- type="color"
- class="color_input"
- ref={pickerRef}
- value={state.color}
- onInput={_.debounce(colorChange, 300)}
- />
- </div>
- </div>
- );
- },
- });
- 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 () => {
- const value =
- typeof props.value === "string" ? parseInt(props.value) : props.value;
- 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 () => {
- const value =
- typeof props.value === "string" ? parseFloat(props.value) : props.value;
- 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 () => {
- return (
- <Select
- options={options}
- value={props.value || ""}
- onChange={(v) => {
- emit("change", v);
- }}
- ></Select>
- );
- };
- },
- });
- export const FontSize = defineComponent({
- props: {
- value: any().def("12px"),
- },
- emits: ["change"],
- setup(props, { emit }) {
- return () => {
- return (
- <InputNumber
- prefix={<IconTextSize class="text-22px mr-6px" />}
- defaultValue={12}
- min={12}
- max={60}
- value={parseInt(props.value) || 12}
- onChange={(value: any) => {
- if (!value) {
- emit("change", "12px");
- return;
- }
- emit("change", value + "px");
- }}
- />
- );
- };
- },
- });
- 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>
- );
- },
- });
- export const TextToolItem = defineComponent({
- props: {
- column: object<ColumnItem>(),
- index: number(),
- onChange: func(),
- },
- setup(props) {
- const state = reactive({
- value: undefined,
- });
- const { controls } = useEditor();
- let editor: any = null;
- watch(
- () => controls.textEditorCtrl.state.currEditor,
- () => {
- editor = toRaw(controls.textEditorCtrl.state.currEditor);
- initCommands();
- }
- );
- 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) {
- return;
- }
- const command = editor.commands.get(column?.dataIndex);
- if (command) {
- console.log("init", column?.dataIndex, command.value);
- 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 ColorPicker = css`
- position: relative;
- width: 32px;
- height: 32px;
- border-radius: 2px;
- cursor: pointer;
- .color_picker {
- width: 100%;
- height: 100%;
- border-radius: 2px;
- border: 1px solid transparent;
- &:focus,
- &:hover {
- border-color: @inf-primary-color;
- box-shadow: 0 0 0 2px rgba(232, 139, 0, 0.2);
- }
- }
- .color_input {
- position: absolute;
- left: 0;
- bottom: 0;
- width: 100%;
- height: 0;
- padding: 0;
- border: none;
- visibility: hidden;
- }
- `;
- const AlignCompWapper = css`
- display: flex;
- .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;
- & > 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;
- background-color: #303030;
- border-radius: 2px;
- &:last-child {
- margin-right: 0;
- }
- &.disabled {
- cursor: not-allowed;
- }
- `;
|