mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
perf: 线条、边框样式配置项优化
This commit is contained in:
parent
c767934b69
commit
bc6a51e5c7
120
src/components/SelectCustom.vue
Normal file
120
src/components/SelectCustom.vue
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<div class="select-wrap" v-if="disabled">
|
||||||
|
<div class="select disabled" ref="selectRef">
|
||||||
|
<div class="selector"><slot name="label"></slot></div>
|
||||||
|
<div class="icon">
|
||||||
|
<slot name="icon">
|
||||||
|
<IconDown :size="14" />
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Popover
|
||||||
|
class="select-wrap"
|
||||||
|
trigger="click"
|
||||||
|
v-model:value="popoverVisible"
|
||||||
|
placement="bottom"
|
||||||
|
:contentStyle="{
|
||||||
|
padding: 0,
|
||||||
|
boxShadow: '0 6px 16px 0 rgba(0, 0, 0, 0.08)',
|
||||||
|
}"
|
||||||
|
v-else
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="options" :style="{ width: width + 2 + 'px' }" @click="popoverVisible = false">
|
||||||
|
<slot name="options"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="select" ref="selectRef">
|
||||||
|
<div class="selector"><slot name="label"></slot></div>
|
||||||
|
<div class="icon">
|
||||||
|
<slot name="icon">
|
||||||
|
<IconDown :size="14" />
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
import Popover from './Popover.vue'
|
||||||
|
|
||||||
|
withDefaults(defineProps<{
|
||||||
|
disabled?: boolean
|
||||||
|
}>(), {
|
||||||
|
disabled: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const popoverVisible = ref(false)
|
||||||
|
const selectRef = ref<HTMLElement>()
|
||||||
|
const width = ref(0)
|
||||||
|
|
||||||
|
const updateWidth = () => {
|
||||||
|
if (!selectRef.value) return
|
||||||
|
width.value = selectRef.value.clientWidth
|
||||||
|
}
|
||||||
|
const resizeObserver = new ResizeObserver(updateWidth)
|
||||||
|
onMounted(() => {
|
||||||
|
if (!selectRef.value) return
|
||||||
|
resizeObserver.observe(selectRef.value)
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (!selectRef.value) return
|
||||||
|
resizeObserver.unobserve(selectRef.value)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.select {
|
||||||
|
width: 100%;
|
||||||
|
height: 32px;
|
||||||
|
padding-right: 32px;
|
||||||
|
border-radius: $borderRadius;
|
||||||
|
transition: border-color .25s;
|
||||||
|
font-size: 13px;
|
||||||
|
user-select: none;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:not(.disabled):hover {
|
||||||
|
border-color: $themeColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-color: #dcdcdc;
|
||||||
|
color: #b7b7b7;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector {
|
||||||
|
min-width: 50px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
padding-left: 10px;
|
||||||
|
@include ellipsis-oneline();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.options {
|
||||||
|
max-height: 260px;
|
||||||
|
padding: 5px;
|
||||||
|
overflow: auto;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 13px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 30px;
|
||||||
|
color: #bfbfbf;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,9 +1,9 @@
|
|||||||
import type { LinePoint } from '@/types/slides'
|
import type { LinePoint, LineStyleType } from '@/types/slides'
|
||||||
|
|
||||||
|
|
||||||
export interface LinePoolItem {
|
export interface LinePoolItem {
|
||||||
path: string
|
path: string
|
||||||
style: 'solid' | 'dashed'
|
style: LineStyleType
|
||||||
points: [LinePoint, LinePoint]
|
points: [LinePoint, LinePoint]
|
||||||
isBroken?: boolean
|
isBroken?: boolean
|
||||||
isBroken2?: boolean
|
isBroken2?: boolean
|
||||||
|
@ -52,6 +52,8 @@ export interface Gradient {
|
|||||||
rotate: number
|
rotate: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LineStyleType = 'solid' | 'dashed' | 'dotted'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 元素阴影
|
* 元素阴影
|
||||||
*
|
*
|
||||||
@ -80,7 +82,7 @@ export interface PPTElementShadow {
|
|||||||
* color?: 边框颜色
|
* color?: 边框颜色
|
||||||
*/
|
*/
|
||||||
export interface PPTElementOutline {
|
export interface PPTElementOutline {
|
||||||
style?: 'dashed' | 'solid' | 'dotted'
|
style?: LineStyleType
|
||||||
width?: number
|
width?: number
|
||||||
color?: string
|
color?: string
|
||||||
}
|
}
|
||||||
@ -390,7 +392,7 @@ export interface PPTLineElement extends Omit<PPTBaseElement, 'height' | 'rotate'
|
|||||||
type: 'line'
|
type: 'line'
|
||||||
start: [number, number]
|
start: [number, number]
|
||||||
end: [number, number]
|
end: [number, number]
|
||||||
style: 'solid' | 'dashed' | 'dotted'
|
style: LineStyleType
|
||||||
color: string
|
color: string
|
||||||
points: [LinePoint, LinePoint]
|
points: [LinePoint, LinePoint]
|
||||||
shadow?: PPTElementShadow
|
shadow?: PPTElementShadow
|
||||||
|
@ -2,16 +2,16 @@
|
|||||||
<div class="line-style-panel">
|
<div class="line-style-panel">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="width: 40%;">线条样式:</div>
|
<div style="width: 40%;">线条样式:</div>
|
||||||
<Select
|
<SelectCustom style="width: 60%;">
|
||||||
style="width: 60%;"
|
<template #options>
|
||||||
:value="handleLineElement.style"
|
<div class="option" v-for="item in lineStyleOptions" :key="item" @click="updateLine({ style: item })">
|
||||||
@update:value="value => updateLine({ style: value as 'solid' | 'dashed' })"
|
<SVGLine :type="item" />
|
||||||
:options="[
|
</div>
|
||||||
{ label: '实线', value: 'solid' },
|
</template>
|
||||||
{ label: '虚线', value: 'dashed' },
|
<template #label>
|
||||||
{ label: '点线', value: 'dotted' },
|
<SVGLine :type="handleLineElement.style" />
|
||||||
]"
|
</template>
|
||||||
/>
|
</SelectCustom>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="width: 40%;">线条颜色:</div>
|
<div style="width: 40%;">线条颜色:</div>
|
||||||
@ -36,29 +36,29 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="width: 40%;">起点样式:</div>
|
<div style="width: 40%;">起点样式:</div>
|
||||||
<Select
|
<SelectCustom style="width: 60%;">
|
||||||
style="width: 60%;"
|
<template #options>
|
||||||
:value="handleLineElement.points[0]"
|
<div class="option" v-for="item in lineMarkerOptions" :key="item" @click="updateLine({ points: [item, handleLineElement.points[1]] })">
|
||||||
@update:value="value => updateLine({ points: [value as 'arrow' | 'dot', handleLineElement.points[1]] })"
|
<SVGLine :padding="5" :markers="[item, '']" />
|
||||||
:options="[
|
</div>
|
||||||
{ label: '无', value: '' },
|
</template>
|
||||||
{ label: '箭头', value: 'arrow' },
|
<template #label>
|
||||||
{ label: '圆点', value: 'dot' },
|
<SVGLine :padding="5" :markers="[handleLineElement.points[0], '']" />
|
||||||
]"
|
</template>
|
||||||
/>
|
</SelectCustom>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="width: 40%;">终点样式:</div>
|
<div style="width: 40%;">终点样式:</div>
|
||||||
<Select
|
<SelectCustom style="width: 60%;">
|
||||||
style="width: 60%;"
|
<template #options>
|
||||||
:value="handleLineElement.points[1]"
|
<div class="option" v-for="item in lineMarkerOptions" :key="item" @click="updateLine({ points: [handleLineElement.points[0], item] })">
|
||||||
@update:value="value => updateLine({ points: [handleLineElement.points[0], value as 'arrow' | 'dot'] })"
|
<SVGLine :padding="5" :markers="['', item]" />
|
||||||
:options="[
|
</div>
|
||||||
{ label: '无', value: '' },
|
</template>
|
||||||
{ label: '箭头', value: 'arrow' },
|
<template #label>
|
||||||
{ label: '圆点', value: 'dot' },
|
<SVGLine :padding="5" :markers="['', handleLineElement.points[1]]" />
|
||||||
]"
|
</template>
|
||||||
/>
|
</SelectCustom>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
@ -73,19 +73,20 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Ref } from 'vue'
|
import { type Ref, ref } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useMainStore, useSlidesStore } from '@/store'
|
import { useMainStore, useSlidesStore } from '@/store'
|
||||||
import type { PPTLineElement } from '@/types/slides'
|
import type { LinePoint, LineStyleType, PPTLineElement } from '@/types/slides'
|
||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
import ElementShadow from '../common/ElementShadow.vue'
|
import ElementShadow from '../common/ElementShadow.vue'
|
||||||
|
import SVGLine from '../common/SVGLine.vue'
|
||||||
import Button from '@/components/Button.vue'
|
import Button from '@/components/Button.vue'
|
||||||
import ColorButton from '@/components/ColorButton.vue'
|
import ColorButton from '@/components/ColorButton.vue'
|
||||||
import ColorPicker from '@/components/ColorPicker/index.vue'
|
import ColorPicker from '@/components/ColorPicker/index.vue'
|
||||||
import Divider from '@/components/Divider.vue'
|
import Divider from '@/components/Divider.vue'
|
||||||
import NumberInput from '@/components/NumberInput.vue'
|
import NumberInput from '@/components/NumberInput.vue'
|
||||||
import Select from '@/components/Select.vue'
|
import SelectCustom from '@/components/SelectCustom.vue'
|
||||||
import Popover from '@/components/Popover.vue'
|
import Popover from '@/components/Popover.vue'
|
||||||
|
|
||||||
const slidesStore = useSlidesStore()
|
const slidesStore = useSlidesStore()
|
||||||
@ -95,6 +96,9 @@ const handleLineElement = handleElement as Ref<PPTLineElement>
|
|||||||
|
|
||||||
const { addHistorySnapshot } = useHistorySnapshot()
|
const { addHistorySnapshot } = useHistorySnapshot()
|
||||||
|
|
||||||
|
const lineStyleOptions = ref<LineStyleType[]>(['solid', 'dashed', 'dotted'])
|
||||||
|
const lineMarkerOptions = ref<LinePoint[]>(['', 'arrow', 'dot'])
|
||||||
|
|
||||||
const updateLine = (props: Partial<PPTLineElement>) => {
|
const updateLine = (props: Partial<PPTLineElement>) => {
|
||||||
if (!handleElement.value) return
|
if (!handleElement.value) return
|
||||||
slidesStore.updateElement({ id: handleElement.value.id, props })
|
slidesStore.updateElement({ id: handleElement.value.id, props })
|
||||||
@ -135,4 +139,19 @@ const updateLine = (props: Partial<PPTLineElement>) => {
|
|||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.option {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: $borderRadius;
|
||||||
|
|
||||||
|
&:not(.selected):hover {
|
||||||
|
background-color: rgba($color: $themeColor, $alpha: .05);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
color: $themeColor;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -17,16 +17,16 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="width: 40%;">边框样式:</div>
|
<div style="width: 40%;">边框样式:</div>
|
||||||
<Select
|
<SelectCustom style="width: 60%;">
|
||||||
style="width: 60%;"
|
<template #options>
|
||||||
:value="outline.style || ''"
|
<div class="option" v-for="item in lineStyleOptions" :key="item" @click="updateOutline({ style: item })">
|
||||||
@update:value="value => updateOutline({ style: value as 'solid' | 'dashed' | 'dotted' })"
|
<SVGLine :type="item" />
|
||||||
:options="[
|
</div>
|
||||||
{ label: '实线边框', value: 'solid' },
|
</template>
|
||||||
{ label: '虚线边框', value: 'dashed' },
|
<template #label>
|
||||||
{ label: '点线边框', value: 'dotted' },
|
<SVGLine :type="outline.style" />
|
||||||
]"
|
</template>
|
||||||
/>
|
</SelectCustom>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="width: 40%;">边框颜色:</div>
|
<div style="width: 40%;">边框颜色:</div>
|
||||||
@ -137,11 +137,12 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useMainStore, useSlidesStore } from '@/store'
|
import { useMainStore, useSlidesStore } from '@/store'
|
||||||
import type { PPTElement, PPTElementOutline, TableCell } from '@/types/slides'
|
import type { LineStyleType, PPTElement, PPTElementOutline, TableCell } from '@/types/slides'
|
||||||
import emitter, { EmitterEvents } from '@/utils/emitter'
|
import emitter, { EmitterEvents } from '@/utils/emitter'
|
||||||
import { WEB_FONTS } from '@/configs/font'
|
import { WEB_FONTS } from '@/configs/font'
|
||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
|
import SVGLine from '../common/SVGLine.vue'
|
||||||
import ColorButton from '@/components/ColorButton.vue'
|
import ColorButton from '@/components/ColorButton.vue'
|
||||||
import TextColorButton from '@/components/TextColorButton.vue'
|
import TextColorButton from '@/components/TextColorButton.vue'
|
||||||
import ColorPicker from '@/components/ColorPicker/index.vue'
|
import ColorPicker from '@/components/ColorPicker/index.vue'
|
||||||
@ -153,6 +154,7 @@ import RadioGroup from '@/components/RadioGroup.vue'
|
|||||||
import NumberInput from '@/components/NumberInput.vue'
|
import NumberInput from '@/components/NumberInput.vue'
|
||||||
import Select from '@/components/Select.vue'
|
import Select from '@/components/Select.vue'
|
||||||
import SelectGroup from '@/components/SelectGroup.vue'
|
import SelectGroup from '@/components/SelectGroup.vue'
|
||||||
|
import SelectCustom from '@/components/SelectCustom.vue'
|
||||||
import Popover from '@/components/Popover.vue'
|
import Popover from '@/components/Popover.vue'
|
||||||
|
|
||||||
const slidesStore = useSlidesStore()
|
const slidesStore = useSlidesStore()
|
||||||
@ -165,6 +167,7 @@ const updateElement = (id: string, props: Partial<PPTElement>) => {
|
|||||||
addHistorySnapshot()
|
addHistorySnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lineStyleOptions = ref<LineStyleType[]>(['solid', 'dashed', 'dotted'])
|
||||||
const fontSizeOptions = [
|
const fontSizeOptions = [
|
||||||
'12px', '14px', '16px', '18px', '20px', '22px', '24px', '28px', '32px',
|
'12px', '14px', '16px', '18px', '20px', '22px', '24px', '28px', '32px',
|
||||||
'36px', '40px', '44px', '48px', '54px', '60px', '66px', '72px', '76px',
|
'36px', '40px', '44px', '48px', '54px', '60px', '66px', '72px', '76px',
|
||||||
@ -257,4 +260,19 @@ const updateFontStyle = (command: string, value: string) => {
|
|||||||
.font-size-btn {
|
.font-size-btn {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
.option {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: $borderRadius;
|
||||||
|
|
||||||
|
&:not(.selected):hover {
|
||||||
|
background-color: rgba($color: $themeColor, $alpha: .05);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
color: $themeColor;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -177,16 +177,16 @@
|
|||||||
<template v-if="moreThemeConfigsVisible">
|
<template v-if="moreThemeConfigsVisible">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="width: 40%;">边框样式:</div>
|
<div style="width: 40%;">边框样式:</div>
|
||||||
<Select
|
<SelectCustom style="width: 60%;">
|
||||||
style="width: 60%;"
|
<template #options>
|
||||||
:value="theme.outline.style || ''"
|
<div class="option" v-for="item in lineStyleOptions" :key="item" @click="updateTheme({ outline: { ...theme.outline, style: item } })">
|
||||||
@update:value="value => updateTheme({ outline: { ...theme.outline, style: value as 'dashed' | 'solid' | 'dotted' } })"
|
<SVGLine :type="item" />
|
||||||
:options="[
|
</div>
|
||||||
{ label: '实线边框', value: 'solid' },
|
</template>
|
||||||
{ label: '虚线边框', value: 'dashed' },
|
<template #label>
|
||||||
{ label: '点线边框', value: 'dotted' },
|
<SVGLine :type="theme.outline.style" />
|
||||||
]"
|
</template>
|
||||||
/>
|
</SelectCustom>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="width: 40%;">边框颜色:</div>
|
<div style="width: 40%;">边框颜色:</div>
|
||||||
@ -312,6 +312,7 @@ import type {
|
|||||||
SlideTheme,
|
SlideTheme,
|
||||||
SlideBackgroundImage,
|
SlideBackgroundImage,
|
||||||
SlideBackgroundImageSize,
|
SlideBackgroundImageSize,
|
||||||
|
LineStyleType,
|
||||||
} from '@/types/slides'
|
} from '@/types/slides'
|
||||||
import { PRESET_THEMES } from '@/configs/theme'
|
import { PRESET_THEMES } from '@/configs/theme'
|
||||||
import { WEB_FONTS } from '@/configs/font'
|
import { WEB_FONTS } from '@/configs/font'
|
||||||
@ -320,6 +321,7 @@ import useSlideTheme from '@/hooks/useSlideTheme'
|
|||||||
import { getImageDataURL } from '@/utils/image'
|
import { getImageDataURL } from '@/utils/image'
|
||||||
|
|
||||||
import ThemeStylesExtract from './ThemeStylesExtract.vue'
|
import ThemeStylesExtract from './ThemeStylesExtract.vue'
|
||||||
|
import SVGLine from './common/SVGLine.vue'
|
||||||
import ColorButton from '@/components/ColorButton.vue'
|
import ColorButton from '@/components/ColorButton.vue'
|
||||||
import FileInput from '@/components/FileInput.vue'
|
import FileInput from '@/components/FileInput.vue'
|
||||||
import ColorPicker from '@/components/ColorPicker/index.vue'
|
import ColorPicker from '@/components/ColorPicker/index.vue'
|
||||||
@ -328,6 +330,7 @@ import Slider from '@/components/Slider.vue'
|
|||||||
import Button from '@/components/Button.vue'
|
import Button from '@/components/Button.vue'
|
||||||
import Select from '@/components/Select.vue'
|
import Select from '@/components/Select.vue'
|
||||||
import Popover from '@/components/Popover.vue'
|
import Popover from '@/components/Popover.vue'
|
||||||
|
import SelectCustom from '@/components/SelectCustom.vue'
|
||||||
import NumberInput from '@/components/NumberInput.vue'
|
import NumberInput from '@/components/NumberInput.vue'
|
||||||
import Modal from '@/components/Modal.vue'
|
import Modal from '@/components/Modal.vue'
|
||||||
import GradientBar from '@/components/GradientBar.vue'
|
import GradientBar from '@/components/GradientBar.vue'
|
||||||
@ -339,6 +342,7 @@ const { slides, currentSlide, viewportRatio, theme } = storeToRefs(slidesStore)
|
|||||||
const moreThemeConfigsVisible = ref(false)
|
const moreThemeConfigsVisible = ref(false)
|
||||||
const themeStylesExtractVisible = ref(false)
|
const themeStylesExtractVisible = ref(false)
|
||||||
const currentGradientIndex = ref(0)
|
const currentGradientIndex = ref(0)
|
||||||
|
const lineStyleOptions = ref<LineStyleType[]>(['solid', 'dashed', 'dotted'])
|
||||||
|
|
||||||
const background = computed(() => {
|
const background = computed(() => {
|
||||||
if (!currentSlide.value.background) {
|
if (!currentSlide.value.background) {
|
||||||
@ -554,4 +558,19 @@ const updateViewportRatio = (value: number) => {
|
|||||||
transition: opacity $transitionDelay;
|
transition: opacity $transitionDelay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.option {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: $borderRadius;
|
||||||
|
|
||||||
|
&:not(.selected):hover {
|
||||||
|
background-color: rgba($color: $themeColor, $alpha: .05);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
color: $themeColor;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -12,16 +12,16 @@
|
|||||||
<template v-if="hasOutline && outline">
|
<template v-if="hasOutline && outline">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="width: 40%;">边框样式:</div>
|
<div style="width: 40%;">边框样式:</div>
|
||||||
<Select
|
<SelectCustom style="width: 60%;">
|
||||||
style="width: 60%;"
|
<template #options>
|
||||||
:value="outline.style || ''"
|
<div class="option" v-for="item in lineStyleOptions" :key="item" @click="updateOutline({ style: item })">
|
||||||
@update:value="value => updateOutline({ style: value as 'dashed' | 'solid' | 'dotted' })"
|
<SVGLine :type="item" />
|
||||||
:options="[
|
</div>
|
||||||
{ label: '实线边框', value: 'solid' },
|
</template>
|
||||||
{ label: '虚线边框', value: 'dashed' },
|
<template #label>
|
||||||
{ label: '点线边框', value: 'dotted' },
|
<SVGLine :type="outline.style" />
|
||||||
]"
|
</template>
|
||||||
/>
|
</SelectCustom>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="width: 40%;">边框颜色:</div>
|
<div style="width: 40%;">边框颜色:</div>
|
||||||
@ -51,14 +51,15 @@
|
|||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useMainStore, useSlidesStore } from '@/store'
|
import { useMainStore, useSlidesStore } from '@/store'
|
||||||
import type { PPTElementOutline } from '@/types/slides'
|
import type { LineStyleType, PPTElementOutline } from '@/types/slides'
|
||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
|
import SVGLine from '../common/SVGLine.vue'
|
||||||
import ColorButton from '@/components/ColorButton.vue'
|
import ColorButton from '@/components/ColorButton.vue'
|
||||||
import ColorPicker from '@/components/ColorPicker/index.vue'
|
import ColorPicker from '@/components/ColorPicker/index.vue'
|
||||||
import Switch from '@/components/Switch.vue'
|
import Switch from '@/components/Switch.vue'
|
||||||
import NumberInput from '@/components/NumberInput.vue'
|
import NumberInput from '@/components/NumberInput.vue'
|
||||||
import Select from '@/components/Select.vue'
|
import SelectCustom from '@/components/SelectCustom.vue'
|
||||||
import Popover from '@/components/Popover.vue'
|
import Popover from '@/components/Popover.vue'
|
||||||
|
|
||||||
withDefaults(defineProps<{
|
withDefaults(defineProps<{
|
||||||
@ -73,6 +74,7 @@ const { handleElement } = storeToRefs(useMainStore())
|
|||||||
|
|
||||||
const outline = ref<PPTElementOutline>()
|
const outline = ref<PPTElementOutline>()
|
||||||
const hasOutline = ref(false)
|
const hasOutline = ref(false)
|
||||||
|
const lineStyleOptions = ref<LineStyleType[]>(['solid', 'dashed', 'dotted'])
|
||||||
|
|
||||||
watch(handleElement, () => {
|
watch(handleElement, () => {
|
||||||
if (!handleElement.value) return
|
if (!handleElement.value) return
|
||||||
@ -113,4 +115,19 @@ const toggleOutline = (checked: boolean) => {
|
|||||||
.switch-wrapper {
|
.switch-wrapper {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.option {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: $borderRadius;
|
||||||
|
|
||||||
|
&:not(.selected):hover {
|
||||||
|
background-color: rgba($color: $themeColor, $alpha: .05);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
color: $themeColor;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
69
src/views/Editor/Toolbar/common/SVGLine.vue
Normal file
69
src/views/Editor/Toolbar/common/SVGLine.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 100 10">
|
||||||
|
<defs>
|
||||||
|
<LinePointMarker
|
||||||
|
v-if="markers && markers[0]"
|
||||||
|
:id="id"
|
||||||
|
position="start"
|
||||||
|
:type="markers[0]"
|
||||||
|
:color="color"
|
||||||
|
:baseSize="width"
|
||||||
|
/>
|
||||||
|
<LinePointMarker
|
||||||
|
v-if="markers && markers[1]"
|
||||||
|
:id="id"
|
||||||
|
position="end"
|
||||||
|
:type="markers[1]"
|
||||||
|
:color="color"
|
||||||
|
:baseSize="width"
|
||||||
|
/>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<line
|
||||||
|
:x1="padding"
|
||||||
|
:y1="5"
|
||||||
|
:x2="100 - padding"
|
||||||
|
:y2="5"
|
||||||
|
:stroke="color"
|
||||||
|
:stroke-width="width"
|
||||||
|
:stroke-dasharray="lineDashArray"
|
||||||
|
:marker-start="markers && markers[0] ? `url(#${id}-${markers[0]}-start)` : ''"
|
||||||
|
:marker-end="markers && markers[1] ? `url(#${id}-${markers[1]}-end)` : ''"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import { nanoid } from 'nanoid'
|
||||||
|
import type { LinePoint, LineStyleType } from '@/types/slides'
|
||||||
|
import LinePointMarker from '@/views/components/element/LineElement/LinePointMarker.vue'
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
width?: number
|
||||||
|
color?: string
|
||||||
|
markers?: [LinePoint, LinePoint]
|
||||||
|
type?: LineStyleType
|
||||||
|
padding?: number
|
||||||
|
}>(), {
|
||||||
|
width: 2,
|
||||||
|
color: '#333',
|
||||||
|
padding: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const id = ref('')
|
||||||
|
onMounted(() => {
|
||||||
|
id.value = nanoid()
|
||||||
|
})
|
||||||
|
|
||||||
|
const lineDashArray = computed(() => {
|
||||||
|
const size = props.width
|
||||||
|
if (props.type === 'dashed') return size <= 8 ? `${size * 5} ${size * 2.5}` : `${size * 5} ${size * 1.5}`
|
||||||
|
if (props.type === 'dotted') return size <= 8 ? `${size * 1.8} ${size * 1.6}` : `${size * 1.5} ${size * 1.2}`
|
||||||
|
return '0 0'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -18,11 +18,14 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import type { LinePoint } from '@/types/slides'
|
||||||
|
|
||||||
|
type NonEmptyLinePoint = Exclude<LinePoint, ''>
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
id: string
|
id: string
|
||||||
position: 'start' | 'end'
|
position: 'start' | 'end'
|
||||||
type: 'dot' | 'arrow'
|
type: NonEmptyLinePoint
|
||||||
baseSize: number
|
baseSize: number
|
||||||
color?: string
|
color?: string
|
||||||
}>()
|
}>()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user