mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
style: 移除 interface 定义的分号
This commit is contained in:
parent
54fd34aab0
commit
8761d313e3
@ -229,7 +229,8 @@ A. 首先需要明确的一点,就是移动端无论怎么做,体验上都
|
|||||||
└── views // 业务组件目录,分为 `编辑器` 和 `播放器` 两个部分。
|
└── views // 业务组件目录,分为 `编辑器` 和 `播放器` 两个部分。
|
||||||
├── components // 公用的业务组件
|
├── components // 公用的业务组件
|
||||||
├── Editor // 编辑器模块
|
├── Editor // 编辑器模块
|
||||||
└── Screen // 播放器模块
|
├── Screen // 播放器模块
|
||||||
|
└── Screen // 移动端模块
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
266
src/components.d.ts
vendored
266
src/components.d.ts
vendored
@ -115,143 +115,143 @@ declare module 'vue' {
|
|||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
|
||||||
// antd 组件
|
// antd 组件
|
||||||
InputNumber: typeof import('ant-design-vue')['InputNumber'];
|
InputNumber: typeof import('ant-design-vue')['InputNumber']
|
||||||
Divider: typeof import('ant-design-vue')['Divider'];
|
Divider: typeof import('ant-design-vue')['Divider']
|
||||||
Button: typeof import('ant-design-vue')['Button'];
|
Button: typeof import('ant-design-vue')['Button']
|
||||||
ButtonGroup: typeof import('ant-design-vue')['Button']['Group'];
|
ButtonGroup: typeof import('ant-design-vue')['Button']['Group']
|
||||||
Tooltip: typeof import('ant-design-vue')['Tooltip'];
|
Tooltip: typeof import('ant-design-vue')['Tooltip']
|
||||||
Popover: typeof import('ant-design-vue')['Popover'];
|
Popover: typeof import('ant-design-vue')['Popover']
|
||||||
Slider: typeof import('ant-design-vue')['Slider'];
|
Slider: typeof import('ant-design-vue')['Slider']
|
||||||
Select: typeof import('ant-design-vue')['Select'];
|
Select: typeof import('ant-design-vue')['Select']
|
||||||
SelectOption: typeof import('ant-design-vue')['Select']['Option'];
|
SelectOption: typeof import('ant-design-vue')['Select']['Option']
|
||||||
SelectOptGroup: typeof import('ant-design-vue')['Select']['OptGroup'];
|
SelectOptGroup: typeof import('ant-design-vue')['Select']['OptGroup']
|
||||||
Switch: typeof import('ant-design-vue')['Switch'];
|
Switch: typeof import('ant-design-vue')['Switch']
|
||||||
Radio: typeof import('ant-design-vue')['Radio'];
|
Radio: typeof import('ant-design-vue')['Radio']
|
||||||
RadioGroup: typeof import('ant-design-vue')['Radio']['Group'];
|
RadioGroup: typeof import('ant-design-vue')['Radio']['Group']
|
||||||
RadioButton: typeof import('ant-design-vue')['Radio']['Button'];
|
RadioButton: typeof import('ant-design-vue')['Radio']['Button']
|
||||||
Input: typeof import('ant-design-vue')['Input'];
|
Input: typeof import('ant-design-vue')['Input']
|
||||||
InputGroup: typeof import('ant-design-vue')['Input']['Group'];
|
InputGroup: typeof import('ant-design-vue')['Input']['Group']
|
||||||
TextArea: typeof import('ant-design-vue')['Input']['TextArea'];
|
TextArea: typeof import('ant-design-vue')['Input']['TextArea']
|
||||||
Modal: typeof import('ant-design-vue')['Modal'];
|
Modal: typeof import('ant-design-vue')['Modal']
|
||||||
Dropdown: typeof import('ant-design-vue')['Dropdown'];
|
Dropdown: typeof import('ant-design-vue')['Dropdown']
|
||||||
Menu: typeof import('ant-design-vue')['Menu'];
|
Menu: typeof import('ant-design-vue')['Menu']
|
||||||
MenuItem: typeof import('ant-design-vue')['Menu']['Item'];
|
MenuItem: typeof import('ant-design-vue')['Menu']['Item']
|
||||||
Checkbox: typeof import('ant-design-vue')['Checkbox'];
|
Checkbox: typeof import('ant-design-vue')['Checkbox']
|
||||||
Drawer: typeof import('ant-design-vue')['Drawer'];
|
Drawer: typeof import('ant-design-vue')['Drawer']
|
||||||
Spin: typeof import('ant-design-vue')['Spin'];
|
Spin: typeof import('ant-design-vue')['Spin']
|
||||||
|
|
||||||
// 自定义组件
|
// 自定义组件
|
||||||
FileInput: typeof FileInput;
|
FileInput: typeof FileInput
|
||||||
CheckboxButton: typeof CheckboxButton;
|
CheckboxButton: typeof CheckboxButton
|
||||||
CheckboxButtonGroup: typeof CheckboxButtonGroup;
|
CheckboxButtonGroup: typeof CheckboxButtonGroup
|
||||||
ColorPicker: typeof ColorPicker;
|
ColorPicker: typeof ColorPicker
|
||||||
FullscreenSpin: typeof FullscreenSpin;
|
FullscreenSpin: typeof FullscreenSpin
|
||||||
|
|
||||||
// IconPark 图标组件
|
// IconPark 图标组件
|
||||||
IconPlayOne: typeof PlayOne;
|
IconPlayOne: typeof PlayOne
|
||||||
IconFullScreenPlay: typeof FullScreenPlay;
|
IconFullScreenPlay: typeof FullScreenPlay
|
||||||
IconLock: typeof Lock;
|
IconLock: typeof Lock
|
||||||
IconUnlock: typeof Unlock;
|
IconUnlock: typeof Unlock
|
||||||
IconPpt: typeof Ppt;
|
IconPpt: typeof Ppt
|
||||||
IconFormat: typeof Format;
|
IconFormat: typeof Format
|
||||||
IconPicture: typeof Picture;
|
IconPicture: typeof Picture
|
||||||
IconFullScreen: typeof FullScreen;
|
IconFullScreen: typeof FullScreen
|
||||||
IconList: typeof List;
|
IconList: typeof List
|
||||||
IconOrderedList: typeof OrderedList;
|
IconOrderedList: typeof OrderedList
|
||||||
IconHelpcenter: typeof Helpcenter;
|
IconHelpcenter: typeof Helpcenter
|
||||||
IconFlipVertically: typeof FlipVertically;
|
IconFlipVertically: typeof FlipVertically
|
||||||
IconFlipHorizontally: typeof FlipHorizontally;
|
IconFlipHorizontally: typeof FlipHorizontally
|
||||||
IconFontSize: typeof FontSize;
|
IconFontSize: typeof FontSize
|
||||||
IconCode: typeof Code;
|
IconCode: typeof Code
|
||||||
IconTextBold: typeof TextBold;
|
IconTextBold: typeof TextBold
|
||||||
IconTextItalic: typeof TextItalic;
|
IconTextItalic: typeof TextItalic
|
||||||
IconTextUnderline: typeof TextUnderline;
|
IconTextUnderline: typeof TextUnderline
|
||||||
IconStrikethrough: typeof Strikethrough;
|
IconStrikethrough: typeof Strikethrough
|
||||||
IconEdit: typeof Edit;
|
IconEdit: typeof Edit
|
||||||
IconQuote: typeof Quote;
|
IconQuote: typeof Quote
|
||||||
IconBackgroundColor: typeof BackgroundColor;
|
IconBackgroundColor: typeof BackgroundColor
|
||||||
IconGroup: typeof Group;
|
IconGroup: typeof Group
|
||||||
IconUngroup: typeof Ungroup;
|
IconUngroup: typeof Ungroup
|
||||||
IconBack: typeof Back;
|
IconBack: typeof Back
|
||||||
IconNext: typeof Next;
|
IconNext: typeof Next
|
||||||
IconFullwidth: typeof Fullwidth;
|
IconFullwidth: typeof Fullwidth
|
||||||
IconAlignTop: typeof AlignTop;
|
IconAlignTop: typeof AlignTop
|
||||||
IconAlignLeft: typeof AlignLeft;
|
IconAlignLeft: typeof AlignLeft
|
||||||
IconAlignRight: typeof AlignRight;
|
IconAlignRight: typeof AlignRight
|
||||||
IconAlignBottom: typeof AlignBottom;
|
IconAlignBottom: typeof AlignBottom
|
||||||
IconAlignVertically: typeof AlignVertically;
|
IconAlignVertically: typeof AlignVertically
|
||||||
IconAlignHorizontally: typeof AlignHorizontally;
|
IconAlignHorizontally: typeof AlignHorizontally
|
||||||
IconBringToFront: typeof BringToFront;
|
IconBringToFront: typeof BringToFront
|
||||||
IconSendToBack: typeof SendToBack;
|
IconSendToBack: typeof SendToBack
|
||||||
IconAlignTextLeft: typeof AlignTextLeft;
|
IconAlignTextLeft: typeof AlignTextLeft
|
||||||
IconAlignTextRight: typeof AlignTextRight;
|
IconAlignTextRight: typeof AlignTextRight
|
||||||
IconAlignTextCenter: typeof AlignTextCenter;
|
IconAlignTextCenter: typeof AlignTextCenter
|
||||||
IconRowHeight: typeof RowHeight;
|
IconRowHeight: typeof RowHeight
|
||||||
IconWrite: typeof Write;
|
IconWrite: typeof Write
|
||||||
IconInsertTable: typeof InsertTable;
|
IconInsertTable: typeof InsertTable
|
||||||
IconAddText: typeof AddText;
|
IconAddText: typeof AddText
|
||||||
IconFill: typeof Fill;
|
IconFill: typeof Fill
|
||||||
IconTailoring: typeof Tailoring;
|
IconTailoring: typeof Tailoring
|
||||||
IconEffects: typeof Effects;
|
IconEffects: typeof Effects
|
||||||
IconColorFilter: typeof ColorFilter;
|
IconColorFilter: typeof ColorFilter
|
||||||
IconDown: typeof Down;
|
IconDown: typeof Down
|
||||||
IconPlus: typeof Plus;
|
IconPlus: typeof Plus
|
||||||
IconMinus: typeof Minus;
|
IconMinus: typeof Minus
|
||||||
IconConnection: typeof Connection;
|
IconConnection: typeof Connection
|
||||||
IconBringToFrontOne: typeof BringToFrontOne;
|
IconBringToFrontOne: typeof BringToFrontOne
|
||||||
IconSentToBack: typeof SentToBack;
|
IconSentToBack: typeof SentToBack
|
||||||
IconGithub: typeof Github;
|
IconGithub: typeof Github
|
||||||
IconChartProportion: typeof ChartProportion;
|
IconChartProportion: typeof ChartProportion
|
||||||
IconChartHistogram: typeof ChartHistogram;
|
IconChartHistogram: typeof ChartHistogram
|
||||||
IconChartHistogramOne: typeof ChartHistogramOne;
|
IconChartHistogramOne: typeof ChartHistogramOne
|
||||||
IconChartLineArea: typeof ChartLineArea;
|
IconChartLineArea: typeof ChartLineArea
|
||||||
IconChartRing: typeof ChartRing;
|
IconChartRing: typeof ChartRing
|
||||||
IconChartScatter: typeof ChartScatter;
|
IconChartScatter: typeof ChartScatter
|
||||||
IconChartLine: typeof ChartLine;
|
IconChartLine: typeof ChartLine
|
||||||
IconChartPie: typeof ChartPie;
|
IconChartPie: typeof ChartPie
|
||||||
IconText: typeof Text;
|
IconText: typeof Text
|
||||||
IconRotate: typeof Rotate;
|
IconRotate: typeof Rotate
|
||||||
IconLeftTwo: typeof LeftTwo;
|
IconLeftTwo: typeof LeftTwo
|
||||||
IconRightTwo: typeof RightTwo;
|
IconRightTwo: typeof RightTwo
|
||||||
IconPlatte: typeof Platte;
|
IconPlatte: typeof Platte
|
||||||
IconUpOne: typeof UpOne;
|
IconUpOne: typeof UpOne
|
||||||
IconDownOne: typeof DownOne;
|
IconDownOne: typeof DownOne
|
||||||
IconClose: typeof Close;
|
IconClose: typeof Close
|
||||||
IconCloseSmall: typeof CloseSmall;
|
IconCloseSmall: typeof CloseSmall
|
||||||
IconUndo: typeof Undo;
|
IconUndo: typeof Undo
|
||||||
IconTransform: typeof Transform;
|
IconTransform: typeof Transform
|
||||||
IconClick: typeof Click;
|
IconClick: typeof Click
|
||||||
IconTheme: typeof Theme;
|
IconTheme: typeof Theme
|
||||||
IconArrowCircleLeft: typeof ArrowCircleLeft;
|
IconArrowCircleLeft: typeof ArrowCircleLeft
|
||||||
IconGraphicDesign: typeof GraphicDesign;
|
IconGraphicDesign: typeof GraphicDesign
|
||||||
IconLogout: typeof Logout;
|
IconLogout: typeof Logout
|
||||||
IconErase: typeof Erase;
|
IconErase: typeof Erase
|
||||||
IconClear: typeof Clear;
|
IconClear: typeof Clear
|
||||||
IconFolderClose: typeof FolderClose;
|
IconFolderClose: typeof FolderClose
|
||||||
IconAlignTextTopOne: typeof AlignTextTopOne;
|
IconAlignTextTopOne: typeof AlignTextTopOne
|
||||||
IconAlignTextBottomOne: typeof AlignTextBottomOne;
|
IconAlignTextBottomOne: typeof AlignTextBottomOne
|
||||||
IconAlignTextMiddleOne: typeof AlignTextMiddleOne;
|
IconAlignTextMiddleOne: typeof AlignTextMiddleOne
|
||||||
IconPause: typeof Pause;
|
IconPause: typeof Pause
|
||||||
IconVolumeMute: typeof VolumeMute;
|
IconVolumeMute: typeof VolumeMute
|
||||||
IconVolumeNotice: typeof VolumeNotice;
|
IconVolumeNotice: typeof VolumeNotice
|
||||||
IconVolumeSmall: typeof VolumeSmall;
|
IconVolumeSmall: typeof VolumeSmall
|
||||||
IconVideoTwo: typeof VideoTwo;
|
IconVideoTwo: typeof VideoTwo
|
||||||
IconFormula: typeof Formula;
|
IconFormula: typeof Formula
|
||||||
IconLinkOne: typeof LinkOne;
|
IconLinkOne: typeof LinkOne
|
||||||
IconFullScreenOne: typeof FullScreenOne;
|
IconFullScreenOne: typeof FullScreenOne
|
||||||
IconOffScreenOne: typeof OffScreenOne;
|
IconOffScreenOne: typeof OffScreenOne
|
||||||
IconPower: typeof Power;
|
IconPower: typeof Power
|
||||||
IconListView: typeof ListView;
|
IconListView: typeof ListView
|
||||||
IconMagic: typeof Magic;
|
IconMagic: typeof Magic
|
||||||
IconHighLight: typeof HighLight;
|
IconHighLight: typeof HighLight
|
||||||
IconShare: typeof Share;
|
IconShare: typeof Share
|
||||||
IconIndentLeft: typeof IndentLeft;
|
IconIndentLeft: typeof IndentLeft
|
||||||
IconIndentRight: typeof IndentRight;
|
IconIndentRight: typeof IndentRight
|
||||||
IconVerticalSpacingBetweenItems: typeof VerticalSpacingBetweenItems;
|
IconVerticalSpacingBetweenItems: typeof VerticalSpacingBetweenItems
|
||||||
IconCopy: typeof Copy;
|
IconCopy: typeof Copy
|
||||||
IconDelete: typeof Delete;
|
IconDelete: typeof Delete
|
||||||
IconSquare: typeof Square;
|
IconSquare: typeof Square
|
||||||
IconRound: typeof Round;
|
IconRound: typeof Round
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,9 +5,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
name: 'checkbox-button-group',
|
name: 'checkbox-button-group',
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
export interface ContextmenuItem {
|
export interface ContextmenuItem {
|
||||||
text?: string;
|
text?: string
|
||||||
subText?: string;
|
subText?: string
|
||||||
divider?: boolean;
|
divider?: boolean
|
||||||
disable?: boolean;
|
disable?: boolean
|
||||||
hide?: boolean;
|
hide?: boolean
|
||||||
children?: ContextmenuItem[];
|
children?: ContextmenuItem[]
|
||||||
handler?: (el: HTMLElement) => void;
|
handler?: (el: HTMLElement) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Axis {
|
export interface Axis {
|
||||||
x: number;
|
x: number
|
||||||
y: number;
|
y: number
|
||||||
}
|
}
|
@ -74,8 +74,8 @@ import FormulaContent from './FormulaContent.vue'
|
|||||||
import SymbolContent from './SymbolContent.vue'
|
import SymbolContent from './SymbolContent.vue'
|
||||||
|
|
||||||
interface Tab {
|
interface Tab {
|
||||||
label: string;
|
label: string
|
||||||
value: 'symbol' | 'formula';
|
value: 'symbol' | 'formula'
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabs: Tab[] = [
|
const tabs: Tab[] = [
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ChartType } from '@/types/slides'
|
import { ChartType } from '@/types/slides'
|
||||||
|
|
||||||
interface ChartTypes {
|
interface ChartTypes {
|
||||||
[propName: string]: ChartType;
|
[propName: string]: ChartType
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CHART_TYPES: ChartTypes = {
|
export const CHART_TYPES: ChartTypes = {
|
||||||
|
@ -2,17 +2,17 @@ import { LinePoint } from '@/types/slides'
|
|||||||
|
|
||||||
|
|
||||||
export interface LinePoolItem {
|
export interface LinePoolItem {
|
||||||
path: string;
|
path: string
|
||||||
style: 'solid' | 'dashed';
|
style: 'solid' | 'dashed'
|
||||||
points: [LinePoint, LinePoint];
|
points: [LinePoint, LinePoint]
|
||||||
isBroken?: boolean;
|
isBroken?: boolean
|
||||||
isCurve?: boolean;
|
isCurve?: boolean
|
||||||
isCubic?: boolean;
|
isCubic?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PresetLine {
|
interface PresetLine {
|
||||||
type: string;
|
type: string
|
||||||
children: LinePoolItem[];
|
children: LinePoolItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LINE_LIST: PresetLine[] = [
|
export const LINE_LIST: PresetLine[] = [
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { ShapePathFormulasKeys } from '@/types/slides'
|
import { ShapePathFormulasKeys } from '@/types/slides'
|
||||||
|
|
||||||
export interface ShapePoolItem {
|
export interface ShapePoolItem {
|
||||||
viewBox: [number, number];
|
viewBox: [number, number]
|
||||||
path: string;
|
path: string
|
||||||
special?: boolean;
|
special?: boolean
|
||||||
pathFormula?: ShapePathFormulasKeys;
|
pathFormula?: ShapePathFormulasKeys
|
||||||
outlined?: boolean;
|
outlined?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ShapeListItem {
|
interface ShapeListItem {
|
||||||
type: string;
|
type: string
|
||||||
children: ShapePoolItem[];
|
children: ShapePoolItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SHAPE_PATH_FORMULAS = {
|
export const SHAPE_PATH_FORMULAS = {
|
||||||
|
20
src/global.d.ts
vendored
20
src/global.d.ts
vendored
@ -1,16 +1,16 @@
|
|||||||
interface HTMLElement {
|
interface HTMLElement {
|
||||||
webkitRequestFullScreen(options?: FullscreenOptions): Promise<void>;
|
webkitRequestFullScreen(options?: FullscreenOptions): Promise<void>
|
||||||
mozRequestFullScreen(options?: FullscreenOptions): Promise<void>;
|
mozRequestFullScreen(options?: FullscreenOptions): Promise<void>
|
||||||
msRequestFullscreen(options?: FullscreenOptions): Promise<void>;
|
msRequestFullscreen(options?: FullscreenOptions): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Document {
|
interface Document {
|
||||||
webkitFullscreenElement: Element | null;
|
webkitFullscreenElement: Element | null
|
||||||
mozFullScreenElement: Element | null;
|
mozFullScreenElement: Element | null
|
||||||
msFullscreenElement: Element | null;
|
msFullscreenElement: Element | null
|
||||||
webkitCurrentFullScreenElement: Element | null;
|
webkitCurrentFullScreenElement: Element | null
|
||||||
|
|
||||||
mozCancelFullScreen(): Promise<void>;
|
mozCancelFullScreen(): Promise<void>
|
||||||
webkitExitFullscreen(): Promise<void>;
|
webkitExitFullscreen(): Promise<void>
|
||||||
msExitFullscreen(): Promise<void>;
|
msExitFullscreen(): Promise<void>
|
||||||
}
|
}
|
@ -10,17 +10,17 @@ import { CHART_TYPES } from '@/configs/chartTypes'
|
|||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
interface CommonElementPosition {
|
interface CommonElementPosition {
|
||||||
top: number;
|
top: number
|
||||||
left: number;
|
left: number
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LineElementPosition {
|
interface LineElementPosition {
|
||||||
top: number;
|
top: number
|
||||||
left: number;
|
left: number
|
||||||
start: [number, number];
|
start: [number, number]
|
||||||
end: [number, number];
|
end: [number, number]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
@ -16,9 +16,9 @@ import { message } from 'ant-design-vue'
|
|||||||
import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
|
import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
|
||||||
|
|
||||||
interface ExportImageConfig {
|
interface ExportImageConfig {
|
||||||
quality: number;
|
quality: number
|
||||||
width: number;
|
width: number
|
||||||
fontEmbedCSS?: string;
|
fontEmbedCSS?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
@ -4,8 +4,8 @@ import useCreateElement from '@/hooks/useCreateElement'
|
|||||||
import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
|
import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
|
||||||
|
|
||||||
interface PasteTextClipboardDataOptions {
|
interface PasteTextClipboardDataOptions {
|
||||||
onlySlide?: boolean;
|
onlySlide?: boolean
|
||||||
onlyElements?: boolean;
|
onlyElements?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
@ -6,32 +6,32 @@ import { getElementRange, getElementListRange, getRectRotatedOffset } from '@/ut
|
|||||||
import useHistorySnapshot from './useHistorySnapshot'
|
import useHistorySnapshot from './useHistorySnapshot'
|
||||||
|
|
||||||
interface ElementItem {
|
interface ElementItem {
|
||||||
min: number;
|
min: number
|
||||||
max: number;
|
max: number
|
||||||
el: PPTElement;
|
el: PPTElement
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GroupItem {
|
interface GroupItem {
|
||||||
groupId: string;
|
groupId: string
|
||||||
els: PPTElement[];
|
els: PPTElement[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GroupElementsItem {
|
interface GroupElementsItem {
|
||||||
min: number;
|
min: number
|
||||||
max: number;
|
max: number
|
||||||
els: PPTElement[];
|
els: PPTElement[]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item = ElementItem | GroupElementsItem
|
type Item = ElementItem | GroupElementsItem
|
||||||
|
|
||||||
interface ElementWithPos {
|
interface ElementWithPos {
|
||||||
pos: number;
|
pos: number
|
||||||
el: PPTElement;
|
el: PPTElement
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LastPos {
|
interface LastPos {
|
||||||
min: number;
|
min: number
|
||||||
max: number;
|
max: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
export interface KeyboardState {
|
export interface KeyboardState {
|
||||||
ctrlKeyState: boolean;
|
ctrlKeyState: boolean
|
||||||
shiftKeyState: boolean;
|
shiftKeyState: boolean
|
||||||
spaceKeyState: boolean;
|
spaceKeyState: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useKeyboardStore = defineStore('keyboard', {
|
export const useKeyboardStore = defineStore('keyboard', {
|
||||||
|
@ -10,27 +10,27 @@ import { isSupportFont } from '@/utils/font'
|
|||||||
import { useSlidesStore } from './slides'
|
import { useSlidesStore } from './slides'
|
||||||
|
|
||||||
export interface MainState {
|
export interface MainState {
|
||||||
activeElementIdList: string[];
|
activeElementIdList: string[]
|
||||||
handleElementId: string;
|
handleElementId: string
|
||||||
activeGroupElementId: string;
|
activeGroupElementId: string
|
||||||
canvasPercentage: number;
|
canvasPercentage: number
|
||||||
canvasScale: number;
|
canvasScale: number
|
||||||
canvasDragged: boolean;
|
canvasDragged: boolean
|
||||||
thumbnailsFocus: boolean;
|
thumbnailsFocus: boolean
|
||||||
editorAreaFocus: boolean;
|
editorAreaFocus: boolean
|
||||||
disableHotkeys: boolean;
|
disableHotkeys: boolean
|
||||||
showGridLines: boolean;
|
showGridLines: boolean
|
||||||
showRuler: boolean;
|
showRuler: boolean
|
||||||
creatingElement: CreatingElement | null;
|
creatingElement: CreatingElement | null
|
||||||
availableFonts: typeof SYS_FONTS;
|
availableFonts: typeof SYS_FONTS
|
||||||
toolbarState: ToolbarStates;
|
toolbarState: ToolbarStates
|
||||||
clipingImageElementId: string;
|
clipingImageElementId: string
|
||||||
isScaling: boolean;
|
isScaling: boolean
|
||||||
richTextAttrs: TextAttrs;
|
richTextAttrs: TextAttrs
|
||||||
selectedTableCells: string[];
|
selectedTableCells: string[]
|
||||||
selectedSlidesIndex: number[];
|
selectedSlidesIndex: number[]
|
||||||
dialogForExport: DialogForExportTypes;
|
dialogForExport: DialogForExportTypes
|
||||||
databaseId: string;
|
databaseId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const nanoid = customAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
|
const nanoid = customAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
export interface ScreenState {
|
export interface ScreenState {
|
||||||
screening: boolean;
|
screening: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useScreenStore = defineStore('screen', {
|
export const useScreenStore = defineStore('screen', {
|
||||||
|
@ -7,25 +7,25 @@ import { theme } from '@/mocks/theme'
|
|||||||
import { layouts } from '@/mocks/layout'
|
import { layouts } from '@/mocks/layout'
|
||||||
|
|
||||||
interface RemoveElementPropData {
|
interface RemoveElementPropData {
|
||||||
id: string;
|
id: string
|
||||||
propName: string | string[];
|
propName: string | string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateElementData {
|
interface UpdateElementData {
|
||||||
id: string | string[];
|
id: string | string[]
|
||||||
props: Partial<PPTElement>;
|
props: Partial<PPTElement>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormatedAnimation {
|
interface FormatedAnimation {
|
||||||
animations: PPTAnimation[];
|
animations: PPTAnimation[]
|
||||||
autoNext: boolean;
|
autoNext: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SlidesState {
|
export interface SlidesState {
|
||||||
theme: SlideTheme;
|
theme: SlideTheme
|
||||||
slides: Slide[];
|
slides: Slide[]
|
||||||
slideIndex: number;
|
slideIndex: number
|
||||||
viewportRatio: number;
|
viewportRatio: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSlidesStore = defineStore('slides', {
|
export const useSlidesStore = defineStore('slides', {
|
||||||
|
@ -6,8 +6,8 @@ import { useSlidesStore } from './slides'
|
|||||||
import { useMainStore } from './main'
|
import { useMainStore } from './main'
|
||||||
|
|
||||||
export interface ScreenState {
|
export interface ScreenState {
|
||||||
snapshotCursor: number;
|
snapshotCursor: number
|
||||||
snapshotLength: number;
|
snapshotLength: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSnapshotStore = defineStore('snapshot', {
|
export const useSnapshotStore = defineStore('snapshot', {
|
||||||
|
@ -46,47 +46,47 @@ export const enum OperateLineHandlers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AlignmentLineAxis {
|
export interface AlignmentLineAxis {
|
||||||
x: number;
|
x: number
|
||||||
y: number;
|
y: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AlignmentLineProps {
|
export interface AlignmentLineProps {
|
||||||
type: 'vertical' | 'horizontal';
|
type: 'vertical' | 'horizontal'
|
||||||
axis: AlignmentLineAxis;
|
axis: AlignmentLineAxis
|
||||||
length: number;
|
length: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MultiSelectRange {
|
export interface MultiSelectRange {
|
||||||
minX: number;
|
minX: number
|
||||||
maxX: number;
|
maxX: number
|
||||||
minY: number;
|
minY: number
|
||||||
maxY: number;
|
maxY: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ImageClipedEmitData {
|
export interface ImageClipedEmitData {
|
||||||
range: ImageClipDataRange;
|
range: ImageClipDataRange
|
||||||
position: {
|
position: {
|
||||||
left: number;
|
left: number
|
||||||
top: number;
|
top: number
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateElementSelectionData {
|
export interface CreateElementSelectionData {
|
||||||
start: [number, number];
|
start: [number, number]
|
||||||
end: [number, number];
|
end: [number, number]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreatingTextElement {
|
export interface CreatingTextElement {
|
||||||
type: 'text';
|
type: 'text'
|
||||||
}
|
}
|
||||||
export interface CreatingShapeElement {
|
export interface CreatingShapeElement {
|
||||||
type: 'shape';
|
type: 'shape'
|
||||||
data: ShapePoolItem;
|
data: ShapePoolItem
|
||||||
}
|
}
|
||||||
export interface CreatingLineElement {
|
export interface CreatingLineElement {
|
||||||
type: 'line';
|
type: 'line'
|
||||||
data: LinePoolItem;
|
data: LinePoolItem
|
||||||
}
|
}
|
||||||
export type CreatingElement = CreatingTextElement | CreatingShapeElement | CreatingLineElement
|
export type CreatingElement = CreatingTextElement | CreatingShapeElement | CreatingLineElement
|
@ -39,10 +39,10 @@ export const enum ElementTypes {
|
|||||||
* color: 阴影颜色
|
* color: 阴影颜色
|
||||||
*/
|
*/
|
||||||
export interface PPTElementShadow {
|
export interface PPTElementShadow {
|
||||||
h: number;
|
h: number
|
||||||
v: number;
|
v: number
|
||||||
blur: number;
|
blur: number
|
||||||
color: string;
|
color: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,9 +55,9 @@ export interface PPTElementShadow {
|
|||||||
* color?: 边框颜色
|
* color?: 边框颜色
|
||||||
*/
|
*/
|
||||||
export interface PPTElementOutline {
|
export interface PPTElementOutline {
|
||||||
style?: 'dashed' | 'solid';
|
style?: 'dashed' | 'solid'
|
||||||
width?: number;
|
width?: number
|
||||||
color?: string;
|
color?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,8 +68,8 @@ export interface PPTElementOutline {
|
|||||||
* target: 目标地址(网页链接、幻灯片页面ID)
|
* target: 目标地址(网页链接、幻灯片页面ID)
|
||||||
*/
|
*/
|
||||||
export interface PPTElementLink {
|
export interface PPTElementLink {
|
||||||
type: 'web' | 'slide';
|
type: 'web' | 'slide'
|
||||||
target: string;
|
target: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -95,15 +95,15 @@ export interface PPTElementLink {
|
|||||||
* link?: 超链接
|
* link?: 超链接
|
||||||
*/
|
*/
|
||||||
interface PPTBaseElement {
|
interface PPTBaseElement {
|
||||||
id: string;
|
id: string
|
||||||
left: number;
|
left: number
|
||||||
top: number;
|
top: number
|
||||||
lock?: boolean;
|
lock?: boolean
|
||||||
groupId?: string;
|
groupId?: string
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
rotate: number;
|
rotate: number
|
||||||
link?: PPTElementLink;
|
link?: PPTElementLink
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -135,18 +135,18 @@ interface PPTBaseElement {
|
|||||||
* paragraphSpace?: 段间距,默认 5px
|
* paragraphSpace?: 段间距,默认 5px
|
||||||
*/
|
*/
|
||||||
export interface PPTTextElement extends PPTBaseElement {
|
export interface PPTTextElement extends PPTBaseElement {
|
||||||
type: 'text';
|
type: 'text'
|
||||||
content: string;
|
content: string
|
||||||
defaultFontName: string;
|
defaultFontName: string
|
||||||
defaultColor: string;
|
defaultColor: string
|
||||||
outline?: PPTElementOutline;
|
outline?: PPTElementOutline
|
||||||
fill?: string;
|
fill?: string
|
||||||
lineHeight?: number;
|
lineHeight?: number
|
||||||
wordSpace?: number;
|
wordSpace?: number
|
||||||
opacity?: number;
|
opacity?: number
|
||||||
shadow?: PPTElementShadow;
|
shadow?: PPTElementShadow
|
||||||
textIndent?: number;
|
textIndent?: number
|
||||||
paragraphSpace?: number;
|
paragraphSpace?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -158,8 +158,8 @@ export interface PPTTextElement extends PPTBaseElement {
|
|||||||
* flipV?: 垂直翻转
|
* flipV?: 垂直翻转
|
||||||
*/
|
*/
|
||||||
export interface ImageOrShapeFlip {
|
export interface ImageOrShapeFlip {
|
||||||
flipH?: boolean;
|
flipH?: boolean
|
||||||
flipV?: boolean;
|
flipV?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,13 +182,13 @@ export interface ImageOrShapeFlip {
|
|||||||
* 'opacity'?: 不透明度,默认100(%)
|
* 'opacity'?: 不透明度,默认100(%)
|
||||||
*/
|
*/
|
||||||
export interface ImageElementFilters {
|
export interface ImageElementFilters {
|
||||||
'blur'?: string;
|
'blur'?: string
|
||||||
'brightness'?: string;
|
'brightness'?: string
|
||||||
'contrast'?: string;
|
'contrast'?: string
|
||||||
'grayscale'?: string;
|
'grayscale'?: string
|
||||||
'saturate'?: string;
|
'saturate'?: string
|
||||||
'hue-rotate'?: string;
|
'hue-rotate'?: string
|
||||||
'opacity'?: string;
|
'opacity'?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ImageClipDataRange = [[number, number], [number, number]]
|
export type ImageClipDataRange = [[number, number], [number, number]]
|
||||||
@ -201,8 +201,8 @@ export type ImageClipDataRange = [[number, number], [number, number]]
|
|||||||
* shape: 裁剪形状,见 configs/imageClip.ts CLIPPATHS
|
* shape: 裁剪形状,见 configs/imageClip.ts CLIPPATHS
|
||||||
*/
|
*/
|
||||||
export interface ImageElementClip {
|
export interface ImageElementClip {
|
||||||
range: ImageClipDataRange;
|
range: ImageClipDataRange
|
||||||
shape: string;
|
shape: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,15 +227,15 @@ export interface ImageElementClip {
|
|||||||
* shadow?: 阴影
|
* shadow?: 阴影
|
||||||
*/
|
*/
|
||||||
export interface PPTImageElement extends PPTBaseElement {
|
export interface PPTImageElement extends PPTBaseElement {
|
||||||
type: 'image';
|
type: 'image'
|
||||||
fixedRatio: boolean;
|
fixedRatio: boolean
|
||||||
src: string;
|
src: string
|
||||||
outline?: PPTElementOutline;
|
outline?: PPTElementOutline
|
||||||
filters?: ImageElementFilters;
|
filters?: ImageElementFilters
|
||||||
clip?: ImageElementClip;
|
clip?: ImageElementClip
|
||||||
flipH?: boolean;
|
flipH?: boolean
|
||||||
flipV?: boolean;
|
flipV?: boolean
|
||||||
shadow?: PPTElementShadow;
|
shadow?: PPTElementShadow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -249,9 +249,9 @@ export interface PPTImageElement extends PPTBaseElement {
|
|||||||
* rotate: 渐变角度(线性渐变)
|
* rotate: 渐变角度(线性渐变)
|
||||||
*/
|
*/
|
||||||
export interface ShapeGradient {
|
export interface ShapeGradient {
|
||||||
type: 'linear' | 'radial';
|
type: 'linear' | 'radial'
|
||||||
color: [string, string];
|
color: [string, string]
|
||||||
rotate: number;
|
rotate: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -266,10 +266,10 @@ export interface ShapeGradient {
|
|||||||
* align: 文本对齐方向(垂直方向)
|
* align: 文本对齐方向(垂直方向)
|
||||||
*/
|
*/
|
||||||
export interface ShapeText {
|
export interface ShapeText {
|
||||||
content: string;
|
content: string
|
||||||
defaultFontName: string;
|
defaultFontName: string
|
||||||
defaultColor: string;
|
defaultColor: string
|
||||||
align: 'top' | 'middle' | 'bottom';
|
align: 'top' | 'middle' | 'bottom'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -306,20 +306,20 @@ export interface ShapeText {
|
|||||||
* 但也有一些形状希望能更精确的控制一些关键点的位置,此时就需要提供路径计算公式,通过在缩放时更新 viewBox 并重新计算 path 来重新绘制形状
|
* 但也有一些形状希望能更精确的控制一些关键点的位置,此时就需要提供路径计算公式,通过在缩放时更新 viewBox 并重新计算 path 来重新绘制形状
|
||||||
*/
|
*/
|
||||||
export interface PPTShapeElement extends PPTBaseElement {
|
export interface PPTShapeElement extends PPTBaseElement {
|
||||||
type: 'shape';
|
type: 'shape'
|
||||||
viewBox: [number, number];
|
viewBox: [number, number]
|
||||||
path: string;
|
path: string
|
||||||
fixedRatio: boolean;
|
fixedRatio: boolean
|
||||||
fill: string;
|
fill: string
|
||||||
gradient?: ShapeGradient;
|
gradient?: ShapeGradient
|
||||||
outline?: PPTElementOutline;
|
outline?: PPTElementOutline
|
||||||
opacity?: number;
|
opacity?: number
|
||||||
flipH?: boolean;
|
flipH?: boolean
|
||||||
flipV?: boolean;
|
flipV?: boolean
|
||||||
shadow?: PPTElementShadow;
|
shadow?: PPTElementShadow
|
||||||
special?: boolean;
|
special?: boolean
|
||||||
text?: ShapeText;
|
text?: ShapeText
|
||||||
pathFormula?: ShapePathFormulasKeys;
|
pathFormula?: ShapePathFormulasKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -349,16 +349,16 @@ export type LinePoint = '' | 'arrow' | 'dot'
|
|||||||
* curve?: 三次曲线控制点位置([[x1, y1], [x2, y2]])
|
* curve?: 三次曲线控制点位置([[x1, y1], [x2, y2]])
|
||||||
*/
|
*/
|
||||||
export interface PPTLineElement extends Omit<PPTBaseElement, 'height' | 'rotate'> {
|
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';
|
style: 'solid' | 'dashed'
|
||||||
color: string;
|
color: string
|
||||||
points: [LinePoint, LinePoint];
|
points: [LinePoint, LinePoint]
|
||||||
shadow?: PPTElementShadow;
|
shadow?: PPTElementShadow
|
||||||
broken?: [number, number];
|
broken?: [number, number]
|
||||||
curve?: [number, number];
|
curve?: [number, number]
|
||||||
cubic?: [[number, number], [number, number]];
|
cubic?: [[number, number], [number, number]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -366,9 +366,9 @@ export type PresetChartType = 'bar' | 'horizontalBar' | 'line' | 'area' | 'scatt
|
|||||||
export type ChartType = 'bar' | 'line' | 'pie'
|
export type ChartType = 'bar' | 'line' | 'pie'
|
||||||
export type ChartOptions = ILineChartOptions & IBarChartOptions & IPieChartOptions
|
export type ChartOptions = ILineChartOptions & IBarChartOptions & IPieChartOptions
|
||||||
export interface ChartData {
|
export interface ChartData {
|
||||||
labels: string[];
|
labels: string[]
|
||||||
legends: string[];
|
legends: string[]
|
||||||
series: number[][];
|
series: number[][]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -393,15 +393,15 @@ export interface ChartData {
|
|||||||
* legend?: 图例/位置
|
* legend?: 图例/位置
|
||||||
*/
|
*/
|
||||||
export interface PPTChartElement extends PPTBaseElement {
|
export interface PPTChartElement extends PPTBaseElement {
|
||||||
type: 'chart';
|
type: 'chart'
|
||||||
fill?: string;
|
fill?: string
|
||||||
chartType: ChartType;
|
chartType: ChartType
|
||||||
data: ChartData;
|
data: ChartData
|
||||||
options?: ChartOptions;
|
options?: ChartOptions
|
||||||
outline?: PPTElementOutline;
|
outline?: PPTElementOutline
|
||||||
themeColor: string[];
|
themeColor: string[]
|
||||||
gridColor?: string;
|
gridColor?: string
|
||||||
legend?: '' | 'top' | 'bottom',
|
legend?: '' | 'top' | 'bottom'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -427,15 +427,15 @@ export interface PPTChartElement extends PPTBaseElement {
|
|||||||
* align?: 对齐方式
|
* align?: 对齐方式
|
||||||
*/
|
*/
|
||||||
export interface TableCellStyle {
|
export interface TableCellStyle {
|
||||||
bold?: boolean;
|
bold?: boolean
|
||||||
em?: boolean;
|
em?: boolean
|
||||||
underline?: boolean;
|
underline?: boolean
|
||||||
strikethrough?: boolean;
|
strikethrough?: boolean
|
||||||
color?: string;
|
color?: string
|
||||||
backcolor?: string;
|
backcolor?: string
|
||||||
fontsize?: string;
|
fontsize?: string
|
||||||
fontname?: string;
|
fontname?: string
|
||||||
align?: 'left' | 'center' | 'right';
|
align?: 'left' | 'center' | 'right'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -453,11 +453,11 @@ export interface TableCellStyle {
|
|||||||
* style?: 单元格样式
|
* style?: 单元格样式
|
||||||
*/
|
*/
|
||||||
export interface TableCell {
|
export interface TableCell {
|
||||||
id: string;
|
id: string
|
||||||
colspan: number;
|
colspan: number
|
||||||
rowspan: number;
|
rowspan: number
|
||||||
text: string;
|
text: string
|
||||||
style?: TableCellStyle;
|
style?: TableCellStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -474,11 +474,11 @@ export interface TableCell {
|
|||||||
* colFooter: 最后一列
|
* colFooter: 最后一列
|
||||||
*/
|
*/
|
||||||
export interface TableTheme {
|
export interface TableTheme {
|
||||||
color: string;
|
color: string
|
||||||
rowHeader: boolean;
|
rowHeader: boolean
|
||||||
rowFooter: boolean;
|
rowFooter: boolean
|
||||||
colHeader: boolean;
|
colHeader: boolean
|
||||||
colFooter: boolean;
|
colFooter: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -495,11 +495,11 @@ export interface TableTheme {
|
|||||||
* data: 表格数据
|
* data: 表格数据
|
||||||
*/
|
*/
|
||||||
export interface PPTTableElement extends PPTBaseElement {
|
export interface PPTTableElement extends PPTBaseElement {
|
||||||
type: 'table';
|
type: 'table'
|
||||||
outline: PPTElementOutline;
|
outline: PPTElementOutline
|
||||||
theme?: TableTheme;
|
theme?: TableTheme
|
||||||
colWidths: number[];
|
colWidths: number[]
|
||||||
data: TableCell[][];
|
data: TableCell[][]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -521,13 +521,13 @@ export interface PPTTableElement extends PPTBaseElement {
|
|||||||
* fixedRatio: 固定形状宽高比例
|
* fixedRatio: 固定形状宽高比例
|
||||||
*/
|
*/
|
||||||
export interface PPTLatexElement extends PPTBaseElement {
|
export interface PPTLatexElement extends PPTBaseElement {
|
||||||
type: 'latex';
|
type: 'latex'
|
||||||
latex: string;
|
latex: string
|
||||||
path: string;
|
path: string
|
||||||
color: string;
|
color: string
|
||||||
strokeWidth: number;
|
strokeWidth: number
|
||||||
viewBox: [number, number];
|
viewBox: [number, number]
|
||||||
fixedRatio: boolean;
|
fixedRatio: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -540,9 +540,9 @@ export interface PPTLatexElement extends PPTBaseElement {
|
|||||||
* poster: 预览封面
|
* poster: 预览封面
|
||||||
*/
|
*/
|
||||||
export interface PPTVideoElement extends PPTBaseElement {
|
export interface PPTVideoElement extends PPTBaseElement {
|
||||||
type: 'video';
|
type: 'video'
|
||||||
src: string;
|
src: string
|
||||||
poster?: string;
|
poster?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -561,12 +561,12 @@ export interface PPTVideoElement extends PPTBaseElement {
|
|||||||
* src: 音频地址
|
* src: 音频地址
|
||||||
*/
|
*/
|
||||||
export interface PPTAudioElement extends PPTBaseElement {
|
export interface PPTAudioElement extends PPTBaseElement {
|
||||||
type: 'audio';
|
type: 'audio'
|
||||||
fixedRatio: boolean;
|
fixedRatio: boolean
|
||||||
color: string,
|
color: string,
|
||||||
loop: boolean,
|
loop: boolean,
|
||||||
autoplay: boolean,
|
autoplay: boolean,
|
||||||
src: string;
|
src: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -589,12 +589,12 @@ export type PPTElement = PPTTextElement | PPTImageElement | PPTShapeElement | PP
|
|||||||
* trigger: 动画触发方式(click - 单击时、meantime - 与上一动画同时、auto - 上一动画之后)
|
* trigger: 动画触发方式(click - 单击时、meantime - 与上一动画同时、auto - 上一动画之后)
|
||||||
*/
|
*/
|
||||||
export interface PPTAnimation {
|
export interface PPTAnimation {
|
||||||
id: string;
|
id: string
|
||||||
elId: string;
|
elId: string
|
||||||
effect: string;
|
effect: string
|
||||||
type: 'in' | 'out' | 'attention';
|
type: 'in' | 'out' | 'attention'
|
||||||
duration: number;
|
duration: number
|
||||||
trigger: 'click' | 'meantime' | 'auto';
|
trigger: 'click' | 'meantime' | 'auto'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -615,13 +615,13 @@ export interface PPTAnimation {
|
|||||||
* gradientRotate?: 渐变角度(线性)
|
* gradientRotate?: 渐变角度(线性)
|
||||||
*/
|
*/
|
||||||
export interface SlideBackground {
|
export interface SlideBackground {
|
||||||
type: 'solid' | 'image' | 'gradient';
|
type: 'solid' | 'image' | 'gradient'
|
||||||
color?: string;
|
color?: string
|
||||||
image?: string;
|
image?: string
|
||||||
imageSize?: 'cover' | 'contain' | 'repeat';
|
imageSize?: 'cover' | 'contain' | 'repeat'
|
||||||
gradientType?: 'linear' | 'radial';
|
gradientType?: 'linear' | 'radial'
|
||||||
gradientColor?: [string, string];
|
gradientColor?: [string, string]
|
||||||
gradientRotate?: number;
|
gradientRotate?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -643,12 +643,12 @@ export type TurningMode = 'no' | 'fade' | 'slideX' | 'slideY'
|
|||||||
* turningMode?: 翻页方式
|
* turningMode?: 翻页方式
|
||||||
*/
|
*/
|
||||||
export interface Slide {
|
export interface Slide {
|
||||||
id: string;
|
id: string
|
||||||
elements: PPTElement[];
|
elements: PPTElement[]
|
||||||
remark?: string;
|
remark?: string
|
||||||
background?: SlideBackground;
|
background?: SlideBackground
|
||||||
animations?: PPTAnimation[];
|
animations?: PPTAnimation[]
|
||||||
turningMode?: TurningMode;
|
turningMode?: TurningMode
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -663,8 +663,8 @@ export interface Slide {
|
|||||||
* fontName: 字体
|
* fontName: 字体
|
||||||
*/
|
*/
|
||||||
export interface SlideTheme {
|
export interface SlideTheme {
|
||||||
backgroundColor: string;
|
backgroundColor: string
|
||||||
themeColor: string;
|
themeColor: string
|
||||||
fontColor: string;
|
fontColor: string
|
||||||
fontName: string;
|
fontName: string
|
||||||
}
|
}
|
@ -4,8 +4,8 @@ import { Slide } from '@/types/slides'
|
|||||||
import { LOCALSTORAGE_KEY_DISCARDED_DB } from '@/configs/storage'
|
import { LOCALSTORAGE_KEY_DISCARDED_DB } from '@/configs/storage'
|
||||||
|
|
||||||
export interface Snapshot {
|
export interface Snapshot {
|
||||||
index: number;
|
index: number
|
||||||
slides: Slide[];
|
slides: Slide[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const databaseNamePrefix = 'PPTist'
|
const databaseNamePrefix = 'PPTist'
|
||||||
|
@ -3,11 +3,11 @@ import { nanoid } from 'nanoid'
|
|||||||
import { PPTElement, PPTLineElement } from '@/types/slides'
|
import { PPTElement, PPTLineElement } from '@/types/slides'
|
||||||
|
|
||||||
interface RotatedElementData {
|
interface RotatedElementData {
|
||||||
left: number;
|
left: number
|
||||||
top: number;
|
top: number
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
rotate: number;
|
rotate: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,8 +127,8 @@ export const getElementListRange = (elementList: PPTElement[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AlignLine {
|
export interface AlignLine {
|
||||||
value: number;
|
value: number
|
||||||
range: [number, number];
|
range: [number, number]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,19 +7,19 @@ export const enum EmitterEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RichTextAction {
|
export interface RichTextAction {
|
||||||
command: string;
|
command: string
|
||||||
value?: string;
|
value?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RichTextCommand {
|
export interface RichTextCommand {
|
||||||
target?: string;
|
target?: string
|
||||||
action: RichTextAction | RichTextAction[];
|
action: RichTextAction | RichTextAction[]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Events = {
|
type Events = {
|
||||||
[EmitterEvents.RICH_TEXT_COMMAND]: RichTextCommand;
|
[EmitterEvents.RICH_TEXT_COMMAND]: RichTextCommand
|
||||||
[EmitterEvents.OPEN_CHART_DATA_EDITOR]: void;
|
[EmitterEvents.OPEN_CHART_DATA_EDITOR]: void
|
||||||
[EmitterEvents.OPEN_LATEX_EDITOR]: void;
|
[EmitterEvents.OPEN_LATEX_EDITOR]: void
|
||||||
}
|
}
|
||||||
|
|
||||||
const emitter: Emitter<Events> = mitt<Events>()
|
const emitter: Emitter<Events> = mitt<Events>()
|
||||||
|
@ -3,9 +3,9 @@ import { Token } from './types'
|
|||||||
import { childlessTags } from './tags'
|
import { childlessTags } from './tags'
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
str: string;
|
str: string
|
||||||
position: number;
|
position: number
|
||||||
tokens: Token[];
|
tokens: Token[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const jumpPosition = (state: State, end: number) => {
|
const jumpPosition = (state: State, end: number) => {
|
||||||
|
@ -2,14 +2,14 @@ import { Token, HTMLNode, TagToken, NormalElement, TagEndToken, AttributeToken,
|
|||||||
import { closingTags, closingTagAncestorBreakers, voidTags } from './tags'
|
import { closingTags, closingTagAncestorBreakers, voidTags } from './tags'
|
||||||
|
|
||||||
interface StackItem {
|
interface StackItem {
|
||||||
tagName: string | null;
|
tagName: string | null
|
||||||
children: HTMLNode[];
|
children: HTMLNode[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
stack: StackItem[];
|
stack: StackItem[]
|
||||||
cursor: number;
|
cursor: number
|
||||||
tokens: Token[];
|
tokens: Token[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parser = (tokens: Token[]) => {
|
export const parser = (tokens: Token[]) => {
|
||||||
|
@ -1,69 +1,69 @@
|
|||||||
export interface ElementAttribute {
|
export interface ElementAttribute {
|
||||||
key: string;
|
key: string
|
||||||
value: string | null;
|
value: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommentElement {
|
export interface CommentElement {
|
||||||
type: 'comment';
|
type: 'comment'
|
||||||
content: string;
|
content: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextElement {
|
export interface TextElement {
|
||||||
type: 'text';
|
type: 'text'
|
||||||
content: string;
|
content: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NormalElement {
|
export interface NormalElement {
|
||||||
type: 'element';
|
type: 'element'
|
||||||
tagName: string;
|
tagName: string
|
||||||
children: HTMLNode[];
|
children: HTMLNode[]
|
||||||
attributes: string[];
|
attributes: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HTMLNode = CommentElement | TextElement | NormalElement
|
export type HTMLNode = CommentElement | TextElement | NormalElement
|
||||||
|
|
||||||
export interface ElementAST {
|
export interface ElementAST {
|
||||||
type: 'element';
|
type: 'element'
|
||||||
tagName: string;
|
tagName: string
|
||||||
children: AST[];
|
children: AST[]
|
||||||
attributes: ElementAttribute[];
|
attributes: ElementAttribute[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommentOrTextAST {
|
export interface CommentOrTextAST {
|
||||||
type: 'comment' | 'text';
|
type: 'comment' | 'text'
|
||||||
content: string;
|
content: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AST = CommentOrTextAST | ElementAST
|
export type AST = CommentOrTextAST | ElementAST
|
||||||
|
|
||||||
export interface TagStartToken {
|
export interface TagStartToken {
|
||||||
type: 'tag-start';
|
type: 'tag-start'
|
||||||
close: boolean;
|
close: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TagEndToken {
|
export interface TagEndToken {
|
||||||
type: 'tag-end';
|
type: 'tag-end'
|
||||||
close: boolean;
|
close: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TagToken {
|
export interface TagToken {
|
||||||
type: 'tag';
|
type: 'tag'
|
||||||
content: string;
|
content: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextToken {
|
export interface TextToken {
|
||||||
type: 'text';
|
type: 'text'
|
||||||
content: string;
|
content: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommentToken {
|
export interface CommentToken {
|
||||||
type: 'comment';
|
type: 'comment'
|
||||||
content: string;
|
content: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AttributeToken {
|
export interface AttributeToken {
|
||||||
type: 'attribute';
|
type: 'attribute'
|
||||||
content: string;
|
content: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Token = TagStartToken | TagEndToken | TagToken | TextToken | CommentToken | AttributeToken
|
export type Token = TagStartToken | TagEndToken | TagToken | TextToken | CommentToken | AttributeToken
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
interface ImageSize {
|
interface ImageSize {
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
interface PageSize {
|
interface PageSize {
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
margin: number;
|
margin: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const createIframe = () => {
|
const createIframe = () => {
|
||||||
|
@ -14,9 +14,9 @@ export const setTextAlign = (tr: Transaction, schema: Schema, alignment: string)
|
|||||||
const paragraph = nodes.paragraph
|
const paragraph = nodes.paragraph
|
||||||
|
|
||||||
interface Task {
|
interface Task {
|
||||||
node: Node;
|
node: Node
|
||||||
pos: number;
|
pos: number
|
||||||
nodeType: NodeType;
|
nodeType: NodeType
|
||||||
}
|
}
|
||||||
|
|
||||||
const tasks: Task[] = []
|
const tasks: Task[] = []
|
||||||
|
@ -160,11 +160,11 @@ export const getAttrValueInSelection = (view: EditorView, attr: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface DefaultAttrs {
|
interface DefaultAttrs {
|
||||||
color?: string;
|
color?: string
|
||||||
backcolor?: string;
|
backcolor?: string
|
||||||
fontsize?: string;
|
fontsize?: string
|
||||||
fontname?: string;
|
fontname?: string
|
||||||
align?: string;
|
align?: string
|
||||||
}
|
}
|
||||||
const _defaultAttrs: DefaultAttrs = {
|
const _defaultAttrs: DefaultAttrs = {
|
||||||
color: '#000',
|
color: '#000',
|
||||||
|
@ -48,8 +48,8 @@ import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
|
|||||||
|
|
||||||
type TypeKey = 'web' | 'slide'
|
type TypeKey = 'web' | 'slide'
|
||||||
interface TabItem {
|
interface TabItem {
|
||||||
key: TypeKey;
|
key: TypeKey
|
||||||
label: string;
|
label: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -42,10 +42,10 @@ import { storeToRefs } from 'pinia'
|
|||||||
import { useMainStore } from '@/store'
|
import { useMainStore } from '@/store'
|
||||||
|
|
||||||
interface ViewportStyles {
|
interface ViewportStyles {
|
||||||
top: number;
|
top: number
|
||||||
left: number;
|
left: number
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -6,8 +6,8 @@ import { OperateLineHandlers } from '@/types/edit'
|
|||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
interface AdsorptionPoint {
|
interface AdsorptionPoint {
|
||||||
x: number;
|
x: number
|
||||||
y: number;
|
y: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (elementList: Ref<PPTElement[]>) => {
|
export default (elementList: Ref<PPTElement[]>) => {
|
||||||
|
@ -10,10 +10,10 @@ import { AlignLine, uniqAlignLines } from '@/utils/element'
|
|||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
interface RotateElementData {
|
interface RotateElementData {
|
||||||
left: number;
|
left: number
|
||||||
top: number;
|
top: number
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,8 +34,8 @@ import { message } from 'ant-design-vue'
|
|||||||
|
|
||||||
type TypeKey = 'video' | 'audio'
|
type TypeKey = 'video' | 'audio'
|
||||||
interface TabItem {
|
interface TabItem {
|
||||||
key: TypeKey;
|
key: TypeKey
|
||||||
label: string;
|
label: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -28,8 +28,8 @@ import ExportPPTX from './ExportPPTX.vue'
|
|||||||
import ExportSpecificFile from './ExportSpecificFile.vue'
|
import ExportSpecificFile from './ExportSpecificFile.vue'
|
||||||
|
|
||||||
interface TabItem {
|
interface TabItem {
|
||||||
key: DialogForExportTypes;
|
key: DialogForExportTypes
|
||||||
label: string;
|
label: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -151,8 +151,8 @@ for (const effect of ATTENTION_ANIMATIONS) {
|
|||||||
|
|
||||||
type AnimationType = 'in' | 'out' | 'attention'
|
type AnimationType = 'in' | 'out' | 'attention'
|
||||||
interface TabItem {
|
interface TabItem {
|
||||||
key: AnimationType;
|
key: AnimationType
|
||||||
label: string;
|
label: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const animationTypes: AnimationType[] = ['in', 'out', 'attention']
|
const animationTypes: AnimationType[] = ['in', 'out', 'attention']
|
||||||
|
@ -24,8 +24,8 @@ import { TurningMode } from '@/types/slides'
|
|||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
interface Animations {
|
interface Animations {
|
||||||
label: string;
|
label: string
|
||||||
value: TurningMode;
|
value: TurningMode
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -33,13 +33,13 @@ import { PPTImageElement } from '@/types/slides'
|
|||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
interface FilterOption {
|
interface FilterOption {
|
||||||
label: string;
|
label: string
|
||||||
key: string;
|
key: string
|
||||||
default: number;
|
default: number
|
||||||
value: number;
|
value: number
|
||||||
unit: string;
|
unit: string
|
||||||
max: number;
|
max: number
|
||||||
step: number;
|
step: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultFilters: FilterOption[] = [
|
const defaultFilters: FilterOption[] = [
|
||||||
|
@ -30,8 +30,8 @@ import MultiPositionPanel from './MultiPositionPanel.vue'
|
|||||||
import SymbolPanel from './SymbolPanel.vue'
|
import SymbolPanel from './SymbolPanel.vue'
|
||||||
|
|
||||||
interface ElementTabs {
|
interface ElementTabs {
|
||||||
label: string;
|
label: string
|
||||||
value: ToolbarStates;
|
value: ToolbarStates
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -133,8 +133,8 @@ import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
|
|||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
interface TabItem {
|
interface TabItem {
|
||||||
key: 'style' | 'common';
|
key: 'style' | 'common'
|
||||||
label: string;
|
label: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const colors = ['#000000', '#ffffff', '#eeece1', '#1e497b', '#4e81bb', '#e2534d', '#9aba60', '#8165a0', '#47acc5', '#f9974c', '#c21401', '#ff1e02', '#ffc12a', '#ffff3a', '#90cf5b', '#00af57']
|
const colors = ['#000000', '#ffffff', '#eeece1', '#1e497b', '#4e81bb', '#e2534d', '#9aba60', '#8165a0', '#47acc5', '#f9974c', '#c21401', '#ff1e02', '#ffc12a', '#ffff3a', '#90cf5b', '#00af57']
|
||||||
|
@ -48,18 +48,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, PropType, ref } from 'vue'
|
import { defineComponent, PropType, ref, StyleValue } from 'vue'
|
||||||
import WritingBoard from '@/components/WritingBoard.vue'
|
import WritingBoard from '@/components/WritingBoard.vue'
|
||||||
|
|
||||||
const writingBoardColors = ['#000000', '#ffffff', '#1e497b', '#4e81bb', '#e2534d', '#9aba60', '#8165a0', '#47acc5', '#f9974c', '#ffff3a']
|
const writingBoardColors = ['#000000', '#ffffff', '#1e497b', '#4e81bb', '#e2534d', '#9aba60', '#8165a0', '#47acc5', '#f9974c', '#ffff3a']
|
||||||
|
|
||||||
interface Position {
|
|
||||||
left?: number | string;
|
|
||||||
right?: number | string;
|
|
||||||
top?: number | string;
|
|
||||||
bottom?: number | string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type WritingBoardModel = 'pen' | 'mark' | 'eraser'
|
type WritingBoardModel = 'pen' | 'mark' | 'eraser'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -78,7 +71,7 @@ export default defineComponent({
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
position: {
|
position: {
|
||||||
type: Object as PropType<Position>,
|
type: Object as PropType<StyleValue>,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
right: '5px',
|
right: '5px',
|
||||||
bottom: '5px',
|
bottom: '5px',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user