mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-01-24 02:00: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`水波纹指令
|
- 新增 `v-ripple`水波纹指令
|
||||||
- 新增左侧菜单混合模式
|
- 新增左侧菜单混合模式
|
||||||
|
- 新增 markdown 嵌入表单内示例
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
:wrapClass="`scrollbar__wrap`"
|
:wrapClass="`scrollbar__wrap`"
|
||||||
:viewClass="`scrollbar__view`"
|
:viewClass="`scrollbar__view`"
|
||||||
class="scroll-container"
|
class="scroll-container"
|
||||||
|
v-bind="$attrs"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</Scrollbar>
|
</Scrollbar>
|
||||||
|
@ -2,14 +2,25 @@
|
|||||||
<div class="markdown" ref="wrapRef" />
|
<div class="markdown" ref="wrapRef" />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<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 from 'vditor';
|
||||||
import 'vditor/dist/index.css';
|
import 'vditor/dist/index.css';
|
||||||
|
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
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({
|
export default defineComponent({
|
||||||
emits: ['update:value'],
|
emits: ['change'],
|
||||||
props: {
|
props: {
|
||||||
height: propTypes.number.def(360),
|
height: propTypes.number.def(360),
|
||||||
value: propTypes.string.def(''),
|
value: propTypes.string.def(''),
|
||||||
@ -19,17 +30,42 @@
|
|||||||
const vditorRef = ref<Nullable<Vditor>>(null);
|
const vditorRef = ref<Nullable<Vditor>>(null);
|
||||||
const initedRef = ref(false);
|
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() {
|
function init() {
|
||||||
const wrapEl = unref(wrapRef);
|
const wrapEl = unref(wrapRef);
|
||||||
if (!wrapEl) return;
|
if (!wrapEl) return;
|
||||||
const bindValue = { ...attrs, ...props };
|
const bindValue = { ...attrs, ...props };
|
||||||
vditorRef.value = new Vditor(wrapEl, {
|
vditorRef.value = new Vditor(wrapEl, {
|
||||||
|
lang: unref(getCurrentLang),
|
||||||
mode: 'sv',
|
mode: 'sv',
|
||||||
preview: {
|
preview: {
|
||||||
actions: [],
|
actions: [],
|
||||||
},
|
},
|
||||||
input: (v) => {
|
input: (v) => {
|
||||||
emit('update:value', v);
|
// emit('update:value', v);
|
||||||
|
emit('change', v);
|
||||||
|
},
|
||||||
|
blur: () => {
|
||||||
|
unref(vditorRef)?.setValue(props.value);
|
||||||
},
|
},
|
||||||
...bindValue,
|
...bindValue,
|
||||||
cache: {
|
cache: {
|
||||||
@ -39,14 +75,20 @@
|
|||||||
initedRef.value = true;
|
initedRef.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
watchEffect(() => {
|
// watch(
|
||||||
nextTick(() => {
|
// () => props.value,
|
||||||
const vditor = unref(vditorRef);
|
// () => {
|
||||||
if (unref(initedRef) && props.value && vditor) {
|
// nextTick(() => {
|
||||||
vditor.setValue(props.value);
|
// const vditor = unref(vditorRef);
|
||||||
}
|
// if (unref(initedRef) && props.value && vditor) {
|
||||||
});
|
// vditor.setValue(props.value);
|
||||||
});
|
// }
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// immediate: true,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import { withInstall } from '../util';
|
import { withInstall } from '../util';
|
||||||
|
|
||||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||||
export const Scrollbar = createAsyncComponent(() => import('./src/Scrollbar'));
|
export const Scrollbar = createAsyncComponent(() => import('./src/index.vue'));
|
||||||
|
|
||||||
withInstall(Scrollbar);
|
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) {
|
if (el.scrollHeight !== 0) {
|
||||||
// for safari: add class after set height, or it will jump to zero height suddenly, weired
|
// for safari: add class after set height, or it will jump to zero height suddenly, weired
|
||||||
addClass(el, 'collapse-transition');
|
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.height = 0;
|
||||||
el.style.paddingTop = 0;
|
el.style.paddingTop = 0;
|
||||||
el.style.paddingBottom = 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',
|
path: 'markdown',
|
||||||
name: t('routes.demo.editor.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',
|
path: 'tinymce',
|
||||||
|
@ -288,12 +288,32 @@ const comp: AppRouteModule = {
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'markdown',
|
path: 'markdown',
|
||||||
|
component: getParentLayout('MarkdownDemo'),
|
||||||
name: 'MarkdownDemo',
|
name: 'MarkdownDemo',
|
||||||
component: () => import('/@/views/demo/editor/Markdown.vue'),
|
|
||||||
meta: {
|
meta: {
|
||||||
title: t('routes.demo.editor.markdown'),
|
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',
|
path: 'tinymce',
|
||||||
component: getParentLayout('TinymceDemo'),
|
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>
|
<template>
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<a-button @click="toggleTheme" class="mb-2" type="primary">黑暗主题</a-button>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@ -23,10 +23,16 @@
|
|||||||
const vditor = markDown.getVditor();
|
const vditor = markDown.getVditor();
|
||||||
vditor.setTheme('dark');
|
vditor.setTheme('dark');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleChange(v: string) {
|
||||||
|
valueRef.value = v;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: valueRef,
|
value: valueRef,
|
||||||
toggleTheme,
|
toggleTheme,
|
||||||
markDownRef,
|
markDownRef,
|
||||||
|
handleChange,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
@ -15,16 +15,11 @@
|
|||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"lib": [
|
"lib": ["dom", "esnext"],
|
||||||
"dom",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"/@/*": [
|
"/@/*": ["src/*"]
|
||||||
"src/*"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
@ -32,15 +27,6 @@
|
|||||||
"name": "@vuedx/typescript-plugin-vue"
|
"name": "@vuedx/typescript-plugin-vue"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"include": [
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||||
"src/**/*.ts",
|
"exclude": ["node_modules", "dist", "**/*.js"]
|
||||||
"src/**/*.d.ts",
|
|
||||||
"src/**/*.tsx",
|
|
||||||
"src/**/*.vue"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules",
|
|
||||||
"dist",
|
|
||||||
"**/*.js"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user