CatPtain's picture
Upload 339 files
89ce340 verified
<template>
<div class="rich-text-base">
<SelectGroup class="row">
<Select
style="width: 60%;"
:value="richTextAttrs.fontname"
search
searchLabel="搜索字体"
@update:value="value => emitRichTextCommand('fontname', value as string)"
:options="FONTS"
>
<template #icon>
<IconFontSize />
</template>
</Select>
<Select
style="width: 40%;"
:value="richTextAttrs.fontsize"
search
searchLabel="搜索字号"
@update:value="value => emitRichTextCommand('fontsize', value as string)"
:options="fontSizeOptions.map(item => ({
label: item, value: item
}))"
>
<template #icon>
<IconAddText />
</template>
</Select>
</SelectGroup>
<ButtonGroup class="row" passive>
<Popover trigger="click" style="width: 30%;">
<template #content>
<ColorPicker
:modelValue="richTextAttrs.color"
@update:modelValue="value => emitRichTextCommand('color', value)"
/>
</template>
<TextColorButton first v-tooltip="'文字颜色'" :color="richTextAttrs.color">
<IconText />
</TextColorButton>
</Popover>
<Popover trigger="click" style="width: 30%;">
<template #content>
<ColorPicker
:modelValue="richTextAttrs.backcolor"
@update:modelValue="value => emitRichTextCommand('backcolor', value)"
/>
</template>
<TextColorButton v-tooltip="'文字高亮'" :color="richTextAttrs.backcolor">
<IconHighLight />
</TextColorButton>
</Popover>
<Button
class="font-size-btn"
style="width: 20%;"
v-tooltip="'增大字号'"
@click="emitRichTextCommand('fontsize-add')"
><IconFontSize />+</Button>
<Button
last
class="font-size-btn"
style="width: 20%;"
v-tooltip="'减小字号'"
@click="emitRichTextCommand('fontsize-reduce')"
><IconFontSize />-</Button>
</ButtonGroup>
<ButtonGroup class="row">
<CheckboxButton
style="flex: 1;"
:checked="richTextAttrs.bold"
v-tooltip="'加粗'"
@click="emitRichTextCommand('bold')"
><IconTextBold /></CheckboxButton>
<CheckboxButton
style="flex: 1;"
:checked="richTextAttrs.em"
v-tooltip="'斜体'"
@click="emitRichTextCommand('em')"
><IconTextItalic /></CheckboxButton>
<CheckboxButton
style="flex: 1;"
:checked="richTextAttrs.underline"
v-tooltip="'下划线'"
@click="emitRichTextCommand('underline')"
><IconTextUnderline /></CheckboxButton>
<CheckboxButton
style="flex: 1;"
:checked="richTextAttrs.strikethrough"
v-tooltip="'删除线'"
@click="emitRichTextCommand('strikethrough')"
><IconStrikethrough /></CheckboxButton>
</ButtonGroup>
<ButtonGroup class="row">
<CheckboxButton
style="flex: 1;"
:checked="richTextAttrs.superscript"
v-tooltip="'上标'"
@click="emitRichTextCommand('superscript')"
></CheckboxButton>
<CheckboxButton
style="flex: 1;"
:checked="richTextAttrs.subscript"
v-tooltip="'下标'"
@click="emitRichTextCommand('subscript')"
>A₂</CheckboxButton>
<CheckboxButton
style="flex: 1;"
:checked="richTextAttrs.code"
v-tooltip="'行内代码'"
@click="emitRichTextCommand('code')"
><IconCode /></CheckboxButton>
<CheckboxButton
style="flex: 1;"
:checked="richTextAttrs.blockquote"
v-tooltip="'引用'"
@click="emitRichTextCommand('blockquote')"
><IconQuote /></CheckboxButton>
</ButtonGroup>
<ButtonGroup class="row" passive>
<CheckboxButton
first
style="flex: 1;"
v-tooltip="'清除格式'"
@click="emitRichTextCommand('clear')"
><IconFormat /></CheckboxButton>
<CheckboxButton
style="flex: 1;"
:checked="!!textFormatPainter"
v-tooltip="'格式刷(双击连续使用)'"
@click="toggleTextFormatPainter()"
@dblclick="toggleTextFormatPainter(true)"
><IconFormatBrush /></CheckboxButton>
<Popover placement="bottom-end" trigger="click" v-model:value="linkPopoverVisible" style="width: 33.33%;">
<template #content>
<div class="link-popover">
<Input v-model:value="link" placeholder="请输入超链接" />
<div class="btns">
<Button size="small" :disabled="!richTextAttrs.link" @click="removeLink()" style="margin-right: 5px;">移除</Button>
<Button size="small" type="primary" @click="updateLink(link)">确认</Button>
</div>
</div>
</template>
<CheckboxButton
last
style="width: 100%;"
:checked="!!richTextAttrs.link"
v-tooltip="'超链接'"
@click="openLinkPopover()"
><IconLinkOne /></CheckboxButton>
</Popover>
</ButtonGroup>
<Divider />
<RadioGroup
class="row"
button-style="solid"
:value="richTextAttrs.align"
@update:value="value => emitRichTextCommand('align', value)"
>
<RadioButton value="left" v-tooltip="'左对齐'" style="flex: 1;"><IconAlignTextLeft /></RadioButton>
<RadioButton value="center" v-tooltip="'居中'" style="flex: 1;"><IconAlignTextCenter /></RadioButton>
<RadioButton value="right" v-tooltip="'右对齐'" style="flex: 1;"><IconAlignTextRight /></RadioButton>
<RadioButton value="justify" v-tooltip="'两端对齐'" style="flex: 1;"><IconAlignTextBoth /></RadioButton>
</RadioGroup>
<div class="row" passive>
<ButtonGroup style="flex: 1;">
<Button
first
:type="richTextAttrs.bulletList ? 'primary' : 'default'"
style="flex: 1;"
v-tooltip="'项目符号'"
@click="emitRichTextCommand('bulletList')"
><IconList /></Button>
<Popover trigger="click" v-model:value="bulletListPanelVisible">
<template #content>
<div class="list-wrap">
<ul class="list"
v-for="item in bulletListStyleTypeOption"
:key="item"
:style="{ listStyleType: item }"
@click="emitRichTextCommand('bulletList', item)"
>
<li class="list-item" v-for="key in 3" :key="key"><span></span></li>
</ul>
</div>
</template>
<Button last class="popover-btn"><IconDown /></Button>
</Popover>
</ButtonGroup>
<div style="width: 10px;"></div>
<ButtonGroup style="flex: 1;" passive>
<Button
first
:type="richTextAttrs.orderedList ? 'primary' : 'default'"
style="flex: 1;"
v-tooltip="'编号'"
@click="emitRichTextCommand('orderedList')"
><IconOrderedList /></Button>
<Popover trigger="click" v-model:value="orderedListPanelVisible">
<template #content>
<div class="list-wrap">
<ul class="list"
v-for="item in orderedListStyleTypeOption"
:key="item"
:style="{ listStyleType: item }"
@click="emitRichTextCommand('orderedList', item)"
>
<li class="list-item" v-for="key in 3" :key="key"><span></span></li>
</ul>
</div>
</template>
<Button last class="popover-btn"><IconDown /></Button>
</Popover>
</ButtonGroup>
</div>
<div class="row">
<ButtonGroup style="flex: 1;" passive>
<Button first style="flex: 1;" v-tooltip="'减小段落缩进'" @click="emitRichTextCommand('indent', '-1')"><IconIndentLeft /></Button>
<Popover trigger="click" v-model:value="indentLeftPanelVisible">
<template #content>
<PopoverMenuItem @click="emitRichTextCommand('textIndent', '-1')">减小首行缩进</PopoverMenuItem>
</template>
<Button last class="popover-btn"><IconDown /></Button>
</Popover>
</ButtonGroup>
<div style="width: 10px;"></div>
<ButtonGroup style="flex: 1;" passive>
<Button first style="flex: 1;" v-tooltip="'增大段落缩进'" @click="emitRichTextCommand('indent', '+1')"><IconIndentRight /></Button>
<Popover trigger="click" v-model:value="indentRightPanelVisible">
<template #content>
<PopoverMenuItem @click="emitRichTextCommand('textIndent', '+1')">增大首行缩进</PopoverMenuItem>
</template>
<Button last class="popover-btn"><IconDown /></Button>
</Popover>
</ButtonGroup>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore } from '@/store'
import emitter, { EmitterEvents } from '@/utils/emitter'
import { FONTS } from '@/configs/font'
import useTextFormatPainter from '@/hooks/useTextFormatPainter'
import message from '@/utils/message'
import TextColorButton from '@/components/TextColorButton.vue'
import CheckboxButton from '@/components/CheckboxButton.vue'
import ColorPicker from '@/components/ColorPicker/index.vue'
import Input from '@/components/Input.vue'
import Button from '@/components/Button.vue'
import ButtonGroup from '@/components/ButtonGroup.vue'
import Select from '@/components/Select.vue'
import SelectGroup from '@/components/SelectGroup.vue'
import Divider from '@/components/Divider.vue'
import Popover from '@/components/Popover.vue'
import RadioButton from '@/components/RadioButton.vue'
import RadioGroup from '@/components/RadioGroup.vue'
import PopoverMenuItem from '@/components/PopoverMenuItem.vue'
const { richTextAttrs, textFormatPainter } = storeToRefs(useMainStore())
const { toggleTextFormatPainter } = useTextFormatPainter()
const fontSizeOptions = [
'12px', '14px', '16px', '18px', '20px', '22px', '24px', '28px', '32px',
'36px', '40px', '44px', '48px', '54px', '60px', '66px', '72px', '76px',
'80px', '88px', '96px', '104px', '112px', '120px',
]
const emitRichTextCommand = (command: string, value?: string) => {
emitter.emit(EmitterEvents.RICH_TEXT_COMMAND, { action: { command, value } })
}
const bulletListPanelVisible = ref(false)
const orderedListPanelVisible = ref(false)
const indentLeftPanelVisible = ref(false)
const indentRightPanelVisible = ref(false)
const bulletListStyleTypeOption = ref(['disc', 'circle', 'square'])
const orderedListStyleTypeOption = ref(['decimal', 'lower-roman', 'upper-roman', 'lower-alpha', 'upper-alpha', 'lower-greek'])
const link = ref('')
const linkPopoverVisible = ref(false)
watch(richTextAttrs, () => linkPopoverVisible.value = false)
const openLinkPopover = () => {
link.value = richTextAttrs.value.link
}
const updateLink = (link?: string) => {
const linkRegExp = /^(https?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/
if (!link || !linkRegExp.test(link)) return message.error('不是正确的网页链接地址')
emitRichTextCommand('link', link)
linkPopoverVisible.value = false
}
const removeLink = () => {
emitRichTextCommand('link')
linkPopoverVisible.value = false
}
</script>
<style lang="scss" scoped>
.rich-text-base {
user-select: none;
}
.row {
width: 100%;
display: flex;
align-items: center;
margin-bottom: 10px;
}
.font-size-btn {
padding: 0;
}
.link-popover {
width: 240px;
.btns {
margin-top: 10px;
text-align: right;
}
}
.list-wrap {
width: 176px;
color: #666;
padding: 8px;
margin: -12px;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.list {
background-color: $lightGray;
padding: 4px 4px 4px 20px;
cursor: pointer;
&:not(:nth-child(3n)) {
margin-right: 8px;
}
&:nth-child(4),
&:nth-child(5),
&:nth-child(6) {
margin-top: 8px;
}
&:hover {
color: $themeColor;
span {
background-color: $themeColor;
}
}
}
.list-item {
width: 24px;
height: 12px;
position: relative;
font-size: 12px;
top: -3px;
span {
width: 100%;
height: 2px;
display: inline-block;
position: absolute;
top: 8px;
background-color: #666;
}
}
.popover-btn {
padding: 0 3px;
}
</style>