mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-01-23 17:50:25 +08:00
fix: add an example of markdown embedded in the form #138
This commit is contained in:
parent
10cd4fcdff
commit
7db0c5c49f
@ -4,6 +4,7 @@
|
||||
|
||||
- 新增 `v-ripple`水波纹指令
|
||||
- 新增左侧菜单混合模式
|
||||
- 新增 markdown 嵌入表单内示例
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
:wrapClass="`scrollbar__wrap`"
|
||||
:viewClass="`scrollbar__view`"
|
||||
class="scroll-container"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot />
|
||||
</Scrollbar>
|
||||
|
@ -2,14 +2,25 @@
|
||||
<div class="markdown" ref="wrapRef" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted, unref, onUnmounted, nextTick, watchEffect } from 'vue';
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
onMounted,
|
||||
unref,
|
||||
onUnmounted,
|
||||
nextTick,
|
||||
// watch,
|
||||
computed,
|
||||
} from 'vue';
|
||||
import Vditor from 'vditor';
|
||||
import 'vditor/dist/index.css';
|
||||
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { useLocale } from '/@/hooks/web/useLocale';
|
||||
|
||||
type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
|
||||
export default defineComponent({
|
||||
emits: ['update:value'],
|
||||
emits: ['change'],
|
||||
props: {
|
||||
height: propTypes.number.def(360),
|
||||
value: propTypes.string.def(''),
|
||||
@ -19,17 +30,42 @@
|
||||
const vditorRef = ref<Nullable<Vditor>>(null);
|
||||
const initedRef = ref(false);
|
||||
|
||||
const lang = ref<Lang>();
|
||||
|
||||
const { getLang } = useLocale();
|
||||
|
||||
const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => {
|
||||
switch (unref(getLang)) {
|
||||
case 'en':
|
||||
lang.value = 'en_US';
|
||||
break;
|
||||
case 'ja':
|
||||
lang.value = 'ja_JP';
|
||||
break;
|
||||
case 'ko':
|
||||
lang.value = 'ko_KR';
|
||||
break;
|
||||
default:
|
||||
lang.value = 'zh_CN';
|
||||
}
|
||||
return lang.value;
|
||||
});
|
||||
function init() {
|
||||
const wrapEl = unref(wrapRef);
|
||||
if (!wrapEl) return;
|
||||
const bindValue = { ...attrs, ...props };
|
||||
vditorRef.value = new Vditor(wrapEl, {
|
||||
lang: unref(getCurrentLang),
|
||||
mode: 'sv',
|
||||
preview: {
|
||||
actions: [],
|
||||
},
|
||||
input: (v) => {
|
||||
emit('update:value', v);
|
||||
// emit('update:value', v);
|
||||
emit('change', v);
|
||||
},
|
||||
blur: () => {
|
||||
unref(vditorRef)?.setValue(props.value);
|
||||
},
|
||||
...bindValue,
|
||||
cache: {
|
||||
@ -39,14 +75,20 @@
|
||||
initedRef.value = true;
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
nextTick(() => {
|
||||
const vditor = unref(vditorRef);
|
||||
if (unref(initedRef) && props.value && vditor) {
|
||||
vditor.setValue(props.value);
|
||||
}
|
||||
});
|
||||
});
|
||||
// watch(
|
||||
// () => props.value,
|
||||
// () => {
|
||||
// nextTick(() => {
|
||||
// const vditor = unref(vditorRef);
|
||||
// if (unref(initedRef) && props.value && vditor) {
|
||||
// vditor.setValue(props.value);
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// {
|
||||
// immediate: true,
|
||||
// }
|
||||
// );
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
|
@ -5,7 +5,7 @@
|
||||
import { withInstall } from '../util';
|
||||
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
export const Scrollbar = createAsyncComponent(() => import('./src/Scrollbar'));
|
||||
export const Scrollbar = createAsyncComponent(() => import('./src/index.vue'));
|
||||
|
||||
withInstall(Scrollbar);
|
||||
|
||||
|
@ -1,106 +0,0 @@
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import { renderThumbStyle, BAR_MAP } from './util';
|
||||
import { defineComponent, computed, unref, inject, Ref, reactive, ref, onBeforeUnmount } from 'vue';
|
||||
import { on, off } from '/@/utils/domUtils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Bar',
|
||||
props: {
|
||||
vertical: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
size: String as PropType<string>,
|
||||
move: Number as PropType<number>,
|
||||
},
|
||||
setup(props) {
|
||||
const thumbRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
const elRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
const commonState = reactive<Indexable>({});
|
||||
const getBarRef = computed(() => {
|
||||
return BAR_MAP[props.vertical ? 'vertical' : 'horizontal'];
|
||||
});
|
||||
const parentElRef = inject('scroll-bar-wrap') as Ref<Nullable<HTMLDivElement>>;
|
||||
|
||||
function clickThumbHandler(e: any) {
|
||||
const { ctrlKey, button, currentTarget } = e;
|
||||
// prevent click event of right button
|
||||
if (ctrlKey || button === 2 || !currentTarget) {
|
||||
return;
|
||||
}
|
||||
startDrag(e);
|
||||
const bar = unref(getBarRef);
|
||||
commonState[bar.axis] =
|
||||
currentTarget[bar.offset] -
|
||||
(e[bar.client as keyof typeof e] - currentTarget.getBoundingClientRect()[bar.direction]);
|
||||
}
|
||||
|
||||
function clickTrackHandler(e: any) {
|
||||
const bar = unref(getBarRef);
|
||||
const offset = Math.abs(e.target.getBoundingClientRect()[bar.direction] - e[bar.client]);
|
||||
const thumbEl = unref(thumbRef) as any;
|
||||
const parentEl = unref(parentElRef) as any;
|
||||
const el = unref(elRef) as any;
|
||||
if (!thumbEl || !el || !parentEl) return;
|
||||
const thumbHalf = thumbEl[bar.offset] / 2;
|
||||
const thumbPositionPercentage = ((offset - thumbHalf) * 100) / el[bar.offset];
|
||||
parentEl[bar.scroll] = (thumbPositionPercentage * parentEl[bar.scrollSize]) / 100;
|
||||
}
|
||||
|
||||
function startDrag(e: Event) {
|
||||
e.stopImmediatePropagation();
|
||||
commonState.cursorDown = true;
|
||||
|
||||
on(document, 'mousemove', mouseMoveDocumentHandler);
|
||||
on(document, 'mouseup', mouseUpDocumentHandler);
|
||||
document.onselectstart = () => false;
|
||||
}
|
||||
|
||||
function mouseMoveDocumentHandler(e: any) {
|
||||
if (commonState.cursorDown === false) return;
|
||||
const bar = unref(getBarRef);
|
||||
const prevPage = commonState[bar.axis];
|
||||
const el = unref(elRef) as any;
|
||||
const parentEl = unref(parentElRef) as any;
|
||||
const thumbEl = unref(thumbRef) as any;
|
||||
if (!prevPage || !el || !thumbEl || !parentEl) return;
|
||||
const rect = el.getBoundingClientRect() as any;
|
||||
const offset = (rect[bar.direction] - e[bar.client]) * -1;
|
||||
const thumbClickPosition = thumbEl[bar.offset] - prevPage;
|
||||
const thumbPositionPercentage = ((offset - thumbClickPosition) * 100) / el[bar.offset];
|
||||
|
||||
parentEl[bar.scroll] = (thumbPositionPercentage * parentEl[bar.scrollSize]) / 100;
|
||||
}
|
||||
|
||||
function mouseUpDocumentHandler() {
|
||||
const bar = unref(getBarRef);
|
||||
commonState.cursorDown = false;
|
||||
commonState[bar.axis] = 0;
|
||||
off(document, 'mousemove', mouseMoveDocumentHandler);
|
||||
document.onselectstart = null;
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
off(document, 'mouseup', mouseUpDocumentHandler);
|
||||
});
|
||||
return () => {
|
||||
const bar = unref(getBarRef);
|
||||
const { size, move } = props;
|
||||
return (
|
||||
<div
|
||||
class={['scrollbar__bar', 'is-' + bar.key]}
|
||||
onMousedown={clickTrackHandler}
|
||||
ref={elRef}
|
||||
>
|
||||
<div
|
||||
ref={thumbRef}
|
||||
class="scrollbar__thumb"
|
||||
onMousedown={clickThumbHandler}
|
||||
style={renderThumbStyle({ size, move, bar })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
@ -1,146 +0,0 @@
|
||||
import { addResizeListener, removeResizeListener } from '/@/utils/event/resizeEvent';
|
||||
import scrollbarWidth from '/@/utils/scrollbarWidth';
|
||||
import { toObject } from './util';
|
||||
import Bar from './Bar';
|
||||
import { isString } from '/@/utils/is';
|
||||
import {
|
||||
defineComponent,
|
||||
PropType,
|
||||
unref,
|
||||
reactive,
|
||||
ref,
|
||||
provide,
|
||||
onMounted,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
} from 'vue';
|
||||
import { getSlot } from '/@/utils/helper/tsxHelper';
|
||||
import './index.less';
|
||||
import { useExpose } from '/@/hooks/core/useExpose';
|
||||
import { ScrollbarType } from './types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Scrollbar',
|
||||
props: {
|
||||
native: Boolean as PropType<boolean>,
|
||||
wrapStyle: {
|
||||
type: Object as PropType<any>,
|
||||
},
|
||||
wrapClass: { type: String as PropType<string>, required: false },
|
||||
viewClass: { type: String as PropType<string> },
|
||||
viewStyle: { type: Object as PropType<any> },
|
||||
noresize: Boolean as PropType<boolean>,
|
||||
tag: {
|
||||
type: String as PropType<string>,
|
||||
default: 'div',
|
||||
},
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const resizeRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
const wrapElRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
provide('scroll-bar-wrap', wrapElRef);
|
||||
const state = reactive({
|
||||
sizeWidth: '0',
|
||||
sizeHeight: '0',
|
||||
moveX: 0,
|
||||
moveY: 0,
|
||||
});
|
||||
|
||||
function handleScroll() {
|
||||
const warpEl = unref(wrapElRef);
|
||||
if (!warpEl) return;
|
||||
const { scrollTop, scrollLeft, clientHeight, clientWidth } = warpEl;
|
||||
|
||||
state.moveY = (scrollTop * 100) / clientHeight;
|
||||
state.moveX = (scrollLeft * 100) / clientWidth;
|
||||
}
|
||||
function update() {
|
||||
const warpEl = unref(wrapElRef);
|
||||
if (!warpEl) return;
|
||||
const { scrollHeight, scrollWidth, clientHeight, clientWidth } = warpEl;
|
||||
const heightPercentage = (clientHeight * 100) / scrollHeight;
|
||||
const widthPercentage = (clientWidth * 100) / scrollWidth;
|
||||
|
||||
state.sizeHeight = heightPercentage < 100 ? heightPercentage + '%' : '';
|
||||
state.sizeWidth = widthPercentage < 100 ? widthPercentage + '%' : '';
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
useExpose<ScrollbarType>({
|
||||
wrap: unref(wrapElRef),
|
||||
});
|
||||
const { native, noresize } = props;
|
||||
const resizeEl = unref(resizeRef);
|
||||
const warpEl = unref(wrapElRef);
|
||||
if (native || !resizeEl || !warpEl) return;
|
||||
nextTick(update);
|
||||
if (!noresize) {
|
||||
addResizeListener(resizeEl, update);
|
||||
addResizeListener(warpEl, update);
|
||||
}
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
const { native, noresize } = props;
|
||||
const resizeEl = unref(resizeRef);
|
||||
const warpEl = unref(wrapElRef);
|
||||
if (native || !resizeEl || !warpEl) return;
|
||||
if (!noresize) {
|
||||
removeResizeListener(resizeEl, update);
|
||||
removeResizeListener(warpEl, update);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
const { native, tag, viewClass, viewStyle, wrapClass, wrapStyle } = props;
|
||||
let style: any = wrapStyle;
|
||||
const gutter = scrollbarWidth();
|
||||
|
||||
if (gutter) {
|
||||
const gutterWith = `-${gutter}px`;
|
||||
const gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};`;
|
||||
|
||||
if (Array.isArray(wrapStyle)) {
|
||||
style = toObject(wrapStyle);
|
||||
style.marginRight = style.marginBottom = gutterWith;
|
||||
} else if (isString(wrapStyle)) {
|
||||
style += gutterStyle;
|
||||
} else {
|
||||
style = gutterStyle;
|
||||
}
|
||||
}
|
||||
|
||||
const Tag = tag as any;
|
||||
const view = (
|
||||
<Tag class={['scrollbar__view', viewClass]} style={viewStyle} ref={resizeRef}>
|
||||
{getSlot(slots)}
|
||||
</Tag>
|
||||
);
|
||||
const wrap = (
|
||||
<div
|
||||
ref={wrapElRef}
|
||||
style={style}
|
||||
onScroll={handleScroll}
|
||||
class={[wrapClass, 'scrollbar__wrap', gutter ? '' : 'scrollbar__wrap--hidden-default']}
|
||||
>
|
||||
{[view]}
|
||||
</div>
|
||||
);
|
||||
let nodes: any[] = [];
|
||||
const { moveX, sizeWidth, moveY, sizeHeight } = state;
|
||||
if (!native) {
|
||||
nodes = [
|
||||
wrap,
|
||||
/* eslint-disable */
|
||||
<Bar move={moveX} size={sizeWidth}></Bar>,
|
||||
<Bar vertical move={moveY} size={sizeHeight}></Bar>,
|
||||
];
|
||||
} else {
|
||||
nodes = [
|
||||
<div ref="wrap" class={[wrapClass, 'scrollbar__wrap']} style={style}>
|
||||
{[view]}
|
||||
</div>,
|
||||
];
|
||||
}
|
||||
return <div class="scrollbar">{nodes}</div>;
|
||||
};
|
||||
},
|
||||
});
|
109
src/components/Scrollbar/src/bar.ts
Normal file
109
src/components/Scrollbar/src/bar.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import {
|
||||
defineComponent,
|
||||
h,
|
||||
computed,
|
||||
ref,
|
||||
getCurrentInstance,
|
||||
onUnmounted,
|
||||
inject,
|
||||
Ref,
|
||||
} from 'vue';
|
||||
import { on, off } from '/@/utils/domUtils';
|
||||
|
||||
import { renderThumbStyle, BAR_MAP } from './util';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Bar',
|
||||
|
||||
props: {
|
||||
vertical: Boolean,
|
||||
size: String,
|
||||
move: Number,
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
const instance = getCurrentInstance();
|
||||
const thumb = ref<any>(null);
|
||||
const wrap = inject('scroll-bar-wrap', {} as Ref<Nullable<HTMLElement>>) as any;
|
||||
const bar = computed(() => {
|
||||
return BAR_MAP[props.vertical ? 'vertical' : 'horizontal'];
|
||||
});
|
||||
const barStore = ref<Indexable>({});
|
||||
const cursorDown = ref<any>(null);
|
||||
const clickThumbHandler = (e: any) => {
|
||||
// prevent click event of right button
|
||||
if (e.ctrlKey || e.button === 2) {
|
||||
return;
|
||||
}
|
||||
startDrag(e);
|
||||
barStore.value[bar.value.axis] =
|
||||
e.currentTarget[bar.value.offset] -
|
||||
(e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction]);
|
||||
};
|
||||
|
||||
const clickTrackHandler = (e: any) => {
|
||||
const offset = Math.abs(
|
||||
e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]
|
||||
);
|
||||
const thumbHalf = thumb.value[bar.value.offset] / 2;
|
||||
const thumbPositionPercentage =
|
||||
((offset - thumbHalf) * 100) / instance?.vnode.el?.[bar.value.offset];
|
||||
|
||||
wrap.value[bar.value.scroll] =
|
||||
(thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100;
|
||||
};
|
||||
const startDrag = (e: any) => {
|
||||
e.stopImmediatePropagation();
|
||||
cursorDown.value = true;
|
||||
on(document, 'mousemove', mouseMoveDocumentHandler);
|
||||
on(document, 'mouseup', mouseUpDocumentHandler);
|
||||
document.onselectstart = () => false;
|
||||
};
|
||||
|
||||
const mouseMoveDocumentHandler = (e: any) => {
|
||||
if (cursorDown.value === false) return;
|
||||
const prevPage = barStore.value[bar.value.axis];
|
||||
|
||||
if (!prevPage) return;
|
||||
|
||||
const offset =
|
||||
(instance?.vnode.el?.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) *
|
||||
-1;
|
||||
const thumbClickPosition = thumb.value[bar.value.offset] - prevPage;
|
||||
const thumbPositionPercentage =
|
||||
((offset - thumbClickPosition) * 100) / instance?.vnode.el?.[bar.value.offset];
|
||||
wrap.value[bar.value.scroll] =
|
||||
(thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100;
|
||||
};
|
||||
|
||||
function mouseUpDocumentHandler() {
|
||||
cursorDown.value = false;
|
||||
barStore.value[bar.value.axis] = 0;
|
||||
off(document, 'mousemove', mouseMoveDocumentHandler);
|
||||
document.onselectstart = null;
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
off(document, 'mouseup', mouseUpDocumentHandler);
|
||||
});
|
||||
|
||||
return () =>
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
class: ['scrollbar__bar', 'is-' + bar.value.key],
|
||||
onMousedown: clickTrackHandler,
|
||||
},
|
||||
h('div', {
|
||||
ref: thumb,
|
||||
class: 'scrollbar__thumb',
|
||||
onMousedown: clickThumbHandler,
|
||||
style: renderThumbStyle({
|
||||
size: props.size,
|
||||
move: props.move,
|
||||
bar: bar.value,
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
@ -1,69 +0,0 @@
|
||||
.scrollbar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&__wrap {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
|
||||
&--hidden-default {
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__thumb {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
cursor: pointer;
|
||||
background-color: rgba(144, 147, 153, 0.3);
|
||||
border-radius: inherit;
|
||||
transition: 0.3s background-color;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(144, 147, 153, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&__bar {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
bottom: 2px;
|
||||
z-index: 1;
|
||||
border-radius: 4px;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 80ms ease;
|
||||
transition: opacity 80ms ease;
|
||||
|
||||
&.is-vertical {
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-horizontal {
|
||||
left: 2px;
|
||||
height: 5px;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scrollbar:active > .scrollbar__bar,
|
||||
.scrollbar:focus > .scrollbar__bar,
|
||||
.scrollbar:hover > .scrollbar__bar {
|
||||
opacity: 1;
|
||||
transition: opacity 180ms ease;
|
||||
}
|
195
src/components/Scrollbar/src/index.vue
Normal file
195
src/components/Scrollbar/src/index.vue
Normal file
@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div class="scrollbar">
|
||||
<div
|
||||
ref="wrap"
|
||||
:class="[wrapClass, 'scrollbar__wrap', native ? '' : 'scrollbar__wrap--hidden-default']"
|
||||
:style="style"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<component :is="tag" ref="resize" :class="['scrollbar__view', viewClass]" :style="viewStyle">
|
||||
<slot></slot>
|
||||
</component>
|
||||
</div>
|
||||
<template v-if="!native">
|
||||
<bar :move="moveX" :size="sizeWidth" />
|
||||
<bar vertical :move="moveY" :size="sizeHeight" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { addResizeListener, removeResizeListener } from '/@/utils/event/resizeEvent';
|
||||
|
||||
import { toObject } from './util';
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
nextTick,
|
||||
provide,
|
||||
computed,
|
||||
} from 'vue';
|
||||
import Bar from './bar';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Scrollbar',
|
||||
components: { Bar },
|
||||
props: {
|
||||
native: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
wrapStyle: {
|
||||
type: [String, Array],
|
||||
default: '',
|
||||
},
|
||||
wrapClass: {
|
||||
type: [String, Array],
|
||||
default: '',
|
||||
},
|
||||
viewClass: {
|
||||
type: [String, Array],
|
||||
default: '',
|
||||
},
|
||||
viewStyle: {
|
||||
type: [String, Array],
|
||||
default: '',
|
||||
},
|
||||
noresize: Boolean, // 如果 container 尺寸不会发生变化,最好设置它可以优化性能
|
||||
tag: {
|
||||
type: String,
|
||||
default: 'div',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const sizeWidth = ref('0');
|
||||
const sizeHeight = ref('0');
|
||||
const moveX = ref(0);
|
||||
const moveY = ref(0);
|
||||
const wrap = ref<any>(null);
|
||||
const resize = ref<any>(null);
|
||||
|
||||
provide('scroll-bar-wrap', wrap);
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!props.native) {
|
||||
moveY.value = (wrap.value.scrollTop * 100) / wrap.value.clientHeight;
|
||||
moveX.value = (wrap.value.scrollLeft * 100) / wrap.value.clientWidth;
|
||||
}
|
||||
};
|
||||
|
||||
const update = () => {
|
||||
if (!wrap.value) return;
|
||||
|
||||
const heightPercentage = (wrap.value.clientHeight * 100) / wrap.value.scrollHeight;
|
||||
const widthPercentage = (wrap.value.clientWidth * 100) / wrap.value.scrollWidth;
|
||||
|
||||
sizeHeight.value = heightPercentage < 100 ? heightPercentage + '%' : '';
|
||||
sizeWidth.value = widthPercentage < 100 ? widthPercentage + '%' : '';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (props.native) return;
|
||||
nextTick(update);
|
||||
!props.noresize && addResizeListener(resize.value, update);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (props.native) return;
|
||||
!props.noresize && removeResizeListener(resize.value, update);
|
||||
});
|
||||
const style = computed(() => {
|
||||
let style: any = props.wrapStyle;
|
||||
if (Array.isArray(props.wrapStyle)) {
|
||||
style = toObject(props.wrapStyle);
|
||||
}
|
||||
return style;
|
||||
});
|
||||
return {
|
||||
moveX,
|
||||
moveY,
|
||||
sizeWidth,
|
||||
sizeHeight,
|
||||
style,
|
||||
wrap,
|
||||
resize,
|
||||
update,
|
||||
handleScroll,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.scrollbar {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
&__wrap {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
|
||||
&--hidden-default {
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__thumb {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
cursor: pointer;
|
||||
background-color: rgba(144, 147, 153, 0.3);
|
||||
border-radius: inherit;
|
||||
transition: 0.3s background-color;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(144, 147, 153, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&__bar {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
bottom: 2px;
|
||||
z-index: 1;
|
||||
border-radius: 4px;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 80ms ease;
|
||||
transition: opacity 80ms ease;
|
||||
|
||||
&.is-vertical {
|
||||
top: 2px;
|
||||
width: 6px;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-horizontal {
|
||||
left: 2px;
|
||||
height: 6px;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scrollbar:active > .scrollbar__bar,
|
||||
.scrollbar:focus > .scrollbar__bar,
|
||||
.scrollbar:hover > .scrollbar__bar {
|
||||
opacity: 1;
|
||||
transition: opacity 340ms ease-out;
|
||||
}
|
||||
</style>
|
@ -59,6 +59,8 @@
|
||||
if (el.scrollHeight !== 0) {
|
||||
// for safari: add class after set height, or it will jump to zero height suddenly, weired
|
||||
addClass(el, 'collapse-transition');
|
||||
// in vue3.0.4, transitionProperty is set 'none' to avoid 'v-leave-from' issue
|
||||
el.style.transitionProperty = 'height';
|
||||
el.style.height = 0;
|
||||
el.style.paddingTop = 0;
|
||||
el.style.paddingBottom = 0;
|
||||
|
@ -1 +1 @@
|
||||
export type LocaleType = 'zh_CN' | 'en' | 'ru' | 'ja';
|
||||
export type LocaleType = 'zh_CN' | 'en' | 'ru' | 'ja' | 'ko';
|
||||
|
@ -180,6 +180,16 @@ const menu: MenuModule = {
|
||||
{
|
||||
path: 'markdown',
|
||||
name: t('routes.demo.editor.markdown'),
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
name: t('routes.demo.editor.tinymceBasic'),
|
||||
},
|
||||
{
|
||||
path: 'editor',
|
||||
name: t('routes.demo.editor.tinymceForm'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'tinymce',
|
||||
|
@ -288,12 +288,32 @@ const comp: AppRouteModule = {
|
||||
children: [
|
||||
{
|
||||
path: 'markdown',
|
||||
component: getParentLayout('MarkdownDemo'),
|
||||
name: 'MarkdownDemo',
|
||||
component: () => import('/@/views/demo/editor/Markdown.vue'),
|
||||
meta: {
|
||||
title: t('routes.demo.editor.markdown'),
|
||||
},
|
||||
redirect: '/comp/editor/markdown/index',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
name: 'MarkDownBasicDemo',
|
||||
component: () => import('/@/views/demo/editor/markdown/index.vue'),
|
||||
meta: {
|
||||
title: t('routes.demo.editor.tinymceBasic'),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'editor',
|
||||
name: 'MarkDownFormDemo',
|
||||
component: () => import('/@/views/demo/editor/markdown/Editor.vue'),
|
||||
meta: {
|
||||
title: t('routes.demo.editor.tinymceForm'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: 'tinymce',
|
||||
component: getParentLayout('TinymceDemo'),
|
||||
|
58
src/views/demo/editor/markdown/Editor.vue
Normal file
58
src/views/demo/editor/markdown/Editor.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="m-4">
|
||||
<CollapseContainer title="MarkDown表单">
|
||||
<BasicForm
|
||||
:labelWidth="100"
|
||||
:schemas="schemas"
|
||||
:actionColOptions="{ span: 24 }"
|
||||
@submit="handleSubmit"
|
||||
>
|
||||
</BasicForm>
|
||||
</CollapseContainer>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, h } from 'vue';
|
||||
import { BasicForm, FormSchema } from '/@/components/Form/index';
|
||||
import { CollapseContainer } from '/@/components/Container/index';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { MarkDown } from '/@/components/Markdown';
|
||||
|
||||
const schemas: FormSchema[] = [
|
||||
{
|
||||
field: 'title',
|
||||
component: 'Input',
|
||||
label: 'title',
|
||||
defaultValue: '标题',
|
||||
rules: [{ required: true }],
|
||||
},
|
||||
{
|
||||
field: 'markdown',
|
||||
component: 'Input',
|
||||
label: 'markdown',
|
||||
defaultValue: 'defaultValue',
|
||||
rules: [{ required: true, trigger: 'blur' }],
|
||||
render: ({ model, field }) => {
|
||||
return h(MarkDown, {
|
||||
value: model[field],
|
||||
onChange: (value: string) => {
|
||||
model[field] = value;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
export default defineComponent({
|
||||
components: { BasicForm, CollapseContainer },
|
||||
setup() {
|
||||
const { createMessage } = useMessage();
|
||||
|
||||
return {
|
||||
schemas,
|
||||
handleSubmit: (values: any) => {
|
||||
createMessage.success('click search,values:' + JSON.stringify(values));
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="p-4">
|
||||
<a-button @click="toggleTheme" class="mb-2" type="primary">黑暗主题</a-button>
|
||||
<MarkDown v-model:value="value" ref="markDownRef" />
|
||||
<MarkDown :value="value" @change="handleChange" ref="markDownRef" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
@ -23,10 +23,16 @@
|
||||
const vditor = markDown.getVditor();
|
||||
vditor.setTheme('dark');
|
||||
}
|
||||
|
||||
function handleChange(v: string) {
|
||||
valueRef.value = v;
|
||||
}
|
||||
|
||||
return {
|
||||
value: valueRef,
|
||||
toggleTheme,
|
||||
markDownRef,
|
||||
handleChange,
|
||||
};
|
||||
},
|
||||
});
|
@ -15,16 +15,11 @@
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "esnext"],
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"/@/*": [
|
||||
"src/*"
|
||||
]
|
||||
"/@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
@ -32,15 +27,6 @@
|
||||
"name": "@vuedx/typescript-plugin-vue"
|
||||
}
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"**/*.js"
|
||||
]
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
"exclude": ["node_modules", "dist", "**/*.js"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user