From bda8d202deb57a6b89ee1d408fa70c383f4e60c6 Mon Sep 17 00:00:00 2001 From: Jecyu Date: Sun, 12 Sep 2021 23:51:18 +0800 Subject: [PATCH 01/32] =?UTF-8?q?feat(splitter):=20=E6=96=B0=E5=A2=9E=20sp?= =?UTF-8?q?litter=20=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devui/splitter/index.ts | 16 ++ devui/splitter/src/splitter-bar.scss | 115 ++++++++++ devui/splitter/src/splitter-bar.tsx | 215 ++++++++++++++++++ devui/splitter/src/splitter-pane.scss | 23 ++ devui/splitter/src/splitter-pane.tsx | 207 +++++++++++++++++ devui/splitter/src/splitter-store.ts | 180 +++++++++++++++ devui/splitter/src/splitter-types.ts | 29 +++ devui/splitter/src/splitter.scss | 18 ++ devui/splitter/src/splitter.tsx | 65 ++++++ devui/splitter/src/util/class.ts | 44 ++++ devui/splitter/src/util/d-resize-directive.ts | 82 +++++++ devui/splitter/src/util/set-style.ts | 35 +++ sites/components/splitter/index.md | 124 ++++++++++ sites/components/splitter/splitter-demo.scss | 9 + yarn.lock | 5 - 15 files changed, 1162 insertions(+), 5 deletions(-) create mode 100644 devui/splitter/index.ts create mode 100644 devui/splitter/src/splitter-bar.scss create mode 100644 devui/splitter/src/splitter-bar.tsx create mode 100644 devui/splitter/src/splitter-pane.scss create mode 100644 devui/splitter/src/splitter-pane.tsx create mode 100644 devui/splitter/src/splitter-store.ts create mode 100644 devui/splitter/src/splitter-types.ts create mode 100644 devui/splitter/src/splitter.scss create mode 100644 devui/splitter/src/splitter.tsx create mode 100644 devui/splitter/src/util/class.ts create mode 100644 devui/splitter/src/util/d-resize-directive.ts create mode 100644 devui/splitter/src/util/set-style.ts create mode 100644 sites/components/splitter/index.md create mode 100644 sites/components/splitter/splitter-demo.scss diff --git a/devui/splitter/index.ts b/devui/splitter/index.ts new file mode 100644 index 00000000..f69638a6 --- /dev/null +++ b/devui/splitter/index.ts @@ -0,0 +1,16 @@ +import type { App } from 'vue' +import Splitter from './src/splitter' + +Splitter.install = function (app: App): void { + app.component(Splitter.name, Splitter) +} + +export { Splitter } + +export default { + title: 'Splitter 分割器', + category: '布局', + install(app: App) { + app.use(Splitter as any) + }, +} diff --git a/devui/splitter/src/splitter-bar.scss b/devui/splitter/src/splitter-bar.scss new file mode 100644 index 00000000..1294e9a7 --- /dev/null +++ b/devui/splitter/src/splitter-bar.scss @@ -0,0 +1,115 @@ +@import '../../style/theme/color'; +@import '../../style/theme/corner'; + +.d-splitter-bar { + background-color: $devui-dividing-line; + display: flex; + position: relative; + align-items: center; + justify-content: center; + flex-grow: 0; + flex-shrink: 0; + + .devui-collapse { + background-color: $devui-dividing-line; + position: absolute; + z-index: 15; + cursor: pointer; + + &::before, + &::after { + content: ''; + width: 10px; + height: 2px; + background: #ffffff; + display: block; + position: absolute; + } + + &:hover { + background-color: $devui-brand-hover; + } + } + + &-horizontal { + .devui-collapse { + width: 12px; + height: 30px; + + &.prev { + border-radius: + 0 $devui-border-radius-feedback + $devui-border-radius-feedback 0; + left: 100%; + + &::before, + &.collapsed::before { + top: 9px; + left: 1px; + } + + // 设置线条 + &::before { + transform: rotate(-70deg); + } + + &.collapsed::before { + transform: rotate(70deg); + } + + &::after { + transform: rotate(70deg); + } + + &.collapsed::after { + transform: rotate(-70deg); + } + + &::after { + top: 18px; + left: 1px; + } + } + } + + &.resizable { + // 修正 IE 浏览器,css 伪元素中鼠标手型无效 + cursor: col-resize; + + &::after { + // content 由下面的 :not(.none-resizable) 控制显示 + cursor: col-resize; + height: 100%; + width: 10px; + top: 0; + } + + &:not(.none-resizable) { + // 非折叠的情况下 + &:hover, + &:focus, + &:active { + background-color: $devui-brand-hover; + } + + &::after { + content: ''; + display: block; + position: absolute; + z-index: 10; + } + } + } + } + + &-vertical.resizable { + cursor: row-resize; + + &::after { + cursor: row-resize; + width: 100%; + height: 10px; + left: 0; + } + } +} diff --git a/devui/splitter/src/splitter-bar.tsx b/devui/splitter/src/splitter-bar.tsx new file mode 100644 index 00000000..9c4a1220 --- /dev/null +++ b/devui/splitter/src/splitter-bar.tsx @@ -0,0 +1,215 @@ +import { + defineComponent, + ref, + watch, + nextTick, + reactive, + computed, + withDirectives, + onMounted, +} from 'vue' +import type { PropType, ExtractPropTypes } from 'vue' +import { SplitterOrientation } from './splitter-types' +import { useSplitterStore } from './splitter-store' +import { setStyle } from './util/set-style' +import { addClass, removeClass } from './util/class' +import dresize, { ResizeDirectiveProp } from './util/d-resize-directive' +import './splitter-bar.scss' + +export const splitterBarProps = { + /** + * 当前 pane 的索引 + */ + index: { + type: Number, + }, + /** + * 必选,指定 SplitterBar 的方向 + */ + orientation: { + type: String as PropType, + required: true, + }, + /** + * 分隔条大小 + */ + splitBarSize: { + type: String, + required: true, + }, + /** + * 是否显示展开/收缩按钮 + */ + showCollapseButton: { + type: Boolean, + }, +} as const + +export type SplitterBarProps = ExtractPropTypes + +export default defineComponent({ + name: 'DSplitterBar', + props: splitterBarProps, + setup(props: SplitterBarProps) { + const { + splitterState, + getPane, + isStaticBar, + isResizable, + dragState, + setSize, + tooglePane, + } = useSplitterStore() + const state = reactive({ + wrapperClass: `d-splitter-bar d-splitter-bar-${props.orientation}`, + }) + const domRef = ref() + + watch( + () => props.splitBarSize, + (curSplitBarSize) => { + nextTick(() => { + const ele = domRef?.value + setStyle(ele, { flexBasis: curSplitBarSize }) + }) + }, + { immediate: true } + ) + + watch( + () => splitterState.panes, + () => { + if (!isStaticBar(props.index)) { + state.wrapperClass += ' resizable' + } else { + // TODO 禁用的样式处理 + // console.log(666); + } + }, + { deep: true } + ) + + // 指令输入值 + const coordinate = { pageX: 0, pageY: 0, originalX: 0, originalY: 0 } + let initState + // TODO 待优化,如何像 angular rxjs 操作一样优雅 + const resizeProp: ResizeDirectiveProp = { + enableResize: true, + onPressEvent: ({ originalEvent }) => { + originalEvent.stopPropagation() // 按下的时候,阻止事件冒泡 + if (!isResizable(props.index)) return + initState = dragState(props.index) + coordinate.originalX = originalEvent.pageX + coordinate.originalY = originalEvent.pageX + }, + onDragEvent: function ({ originalEvent }) { + originalEvent.stopPropagation() // 移动的时候,阻止事件冒泡 + if (!isResizable(props.index)) return + coordinate.pageX = originalEvent.pageX + coordinate.pageY = originalEvent.pageX + let distance + if (props.orientation === 'vertical') { + distance = coordinate.pageY - coordinate.originalY + } else { + distance = coordinate.pageX - coordinate.originalX + } + setSize(initState, distance) + }, + onReleaseEvent: function ({ originalEvent }) { + originalEvent.stopPropagation() // 释放的时候,阻止事件冒泡 + if (!isResizable(props.index)) return + coordinate.pageX = originalEvent.pageX + coordinate.pageY = originalEvent.pageX + let distance + if (props.orientation === 'vertical') { + distance = coordinate.pageY - coordinate.originalY + } else { + distance = coordinate.pageX - coordinate.originalX + } + setSize(initState, distance) + }, + } + + const queryPanes = (index, nearIndex) => { + const pane = getPane(index) + const nearPane = getPane(nearIndex) + return { + pane, + nearPane, + } + } + + // 根据当前状态生成收起按钮样式 + const generateCollapseClass = (pane, nearPane, showIcon) => { + // 是否允许收起 + const isCollapsible = pane.component.props.collapsible && showIcon + // 当前收起状态 + const isCollapsed = pane.component.props.collapsed + // 一个 pane 收起的时候,隐藏相邻 pane 的收起按钮 + const isNearPaneCollapsed = nearPane.collapsed + return { + 'devui-collapse': isCollapsible, + collapsed: isCollapsed, + hidden: isNearPaneCollapsed, + } + } + + // 计算前面板收起操作样式 + const prevClass = computed(() => { + const { pane, nearPane } = queryPanes(props.index, props.index + 1) + // 第一个面板或者其它面板折叠方向不是向后的, 显示操作按钮 + const showIcon = + pane.component.props.collapseDirection !== 'after' || props.index === 0 + return generateCollapseClass(pane, nearPane, showIcon) + }) + + // 切换是否允许拖拽,收起时不能拖拽 + const toggleResize = () => { + const { pane, nearPane } = queryPanes(props.index, props.index + 1) + const isCollapsed = + pane.component.props.collapsed || nearPane.component.props.collapsed + if (isCollapsed) { + addClass(domRef.value, 'none-resizable') + } else { + removeClass(domRef.value, 'none-resizable') + } + } + + const handleCollapsePrePane = (lockStatus?) => { + tooglePane(props.index, props.index + 1, lockStatus) + toggleResize() + } + + const handleCollapseNextPane = () => { + /**TODO */ + } + + const initialCollapseStatus = () => { + handleCollapsePrePane(true) + // collapseNextPane(true); + } + + onMounted(() => { + initialCollapseStatus() + }) + + return () => { + return withDirectives( +
+ {props.showCollapseButton ? ( + + ) : null} +
+ {props.showCollapseButton ? ( + + ) : null} +
, + [[dresize, resizeProp]] + ) + } + }, +}) diff --git a/devui/splitter/src/splitter-pane.scss b/devui/splitter/src/splitter-pane.scss new file mode 100644 index 00000000..f165f55d --- /dev/null +++ b/devui/splitter/src/splitter-pane.scss @@ -0,0 +1,23 @@ +.d-splitter-pane { + position: relative; + flex: 1 1 auto; + display: block; + min-width: 0; + max-width: 100%; + min-height: 0; + max-height: 100%; + + &-fixed { + flex-grow: 0; + flex-shrink: 0; + } + + &-hidden { + flex: 0 !important; + overflow: hidden !important; + } + + &-grow { + flex-grow: 1 !important; + } +} diff --git a/devui/splitter/src/splitter-pane.tsx b/devui/splitter/src/splitter-pane.tsx new file mode 100644 index 00000000..cd7659d2 --- /dev/null +++ b/devui/splitter/src/splitter-pane.tsx @@ -0,0 +1,207 @@ +import type { ExtractPropTypes, PropType } from 'vue'; + +import { + defineComponent, + ref, + watch, + nextTick, + inject, + onMounted, + onUpdated, +} from 'vue'; +import { addClass, hasClass, removeClass } from './util/class'; +import { setStyle } from './util/set-style'; +import { useSplitterStore } from './splitter-store'; +import { CollapseDirection } from './splitter-types'; +import './splitter-pane.scss'; + +export const splitterPaneProps = { + /** + * 可选,指定 pane 宽度,设置像素值或者百分比 + * pane初始化大小 + */ + size: { + type: String, + }, + /** + * 可选,指定 pane 最小宽度,设置像素值或者百分比 + */ + minSize: { + type: String, + }, + /** + * 可选,指定 pane 最大宽度,设置像素值或者百分比 + */ + maxSize: { + type: String, + }, + /** + * 可选,指定 pane 是否可调整大小,会影响相邻 pane + */ + resizable: { + type: Boolean, + default: true, + }, + /** + * 可选,指定 pane 是否可折叠收起 + */ + collapsible: { + type: Boolean, + default: false, + }, + /** + * 可选,指定 pane 初始化是否收起,配合 collapsible 使用 + */ + collapsed: { + type: Boolean, + default: false, + }, + /** + * 非边缘面板折叠方向,before 只生成向前折叠的按钮,after 生成向后折叠按钮,both 生成两个 + */ + collapseDirection: { + type: String as PropType, + default: 'both', + }, + /** + * 可选,是否在 pane 进行折叠后收缩 pane 宽度而非收起 + */ + shrink: { + type: Boolean, + default: false, + }, + /** + * 可选,折叠后收缩的 pane 宽度 (单位:px) + */ + shrinkWidth: { + type: Number, + default: 36, + }, + /** + * 内部排版使用,不对外提供,TODO 待优化 + */ + order: { + type: Number, + }, +} as const; + +export type SplitterPaneProps = ExtractPropTypes; + +export default defineComponent({ + name: 'DSplitterPane', + props: splitterPaneProps, + emits: ['sizeChange', 'collapsedChange'], + setup(props: SplitterPaneProps, { slots, expose, emit }) { + const { setPanes } = useSplitterStore(); + + const domRef = ref(); + watch( + () => props.order, + (order) => { + nextTick(() => { + const ele = domRef.value; + setStyle(ele, { order }); + }); + }, + { immediate: true } + ); + + // pane 初始化大小 + const setSizeStyle = (curSize) => { + const ele = domRef.value; + ele.style.flexBasis = curSize; + const paneFixedClass = 'd-splitter-pane-fixed'; + if (curSize) { + // 设置 flex-grow 和 flex-shrink + addClass(ele, paneFixedClass); + } else { + removeClass(ele, paneFixedClass); + } + }; + + watch( + () => props.size, + (newSize) => { + nextTick(() => { + setSizeStyle(newSize); + }); + }, + { immediate: true } + ); + + const panes = inject('panes'); + const orientation = inject('orientation'); + let initialSize = ''; // 记录初始化挂载传入的大小 + onMounted(() => { + initialSize = props.size; + setPanes({ panes }); + }); + + onUpdated(() => { + setPanes({ panes }); + }); + + // 获取当前 pane大小 + const getPaneSize = (): number => { + const el = domRef?.value; + if (orientation === 'vertical') { + return el.offsetHeight; + } else { + return el.offsetWidth; + } + }; + + const toggleCollapseClass = () => { + const paneHiddenClass = 'd-splitter-pane-hidden'; + nextTick(() => { + const el = domRef.value; + if (!props.collapsed) { + removeClass(el, paneHiddenClass); + } else { + addClass(el, paneHiddenClass); + } + + if (props.collapsed && props.shrink) { + removeClass(el, paneHiddenClass); + setStyle(el, { flexBasis: `${props.shrinkWidth}px` }); + } else { + setStyle(el, { flexBasis: initialSize }); + } + }); + }; + watch( + () => props.collapsed, + () => { + nextTick(() => { + toggleCollapseClass(); + }); + } + ); + + // 收起时用于改变相邻 pane 的 flex-grow 属性来改变非自适应 pane 的 size + const toggleNearPaneFlexGrow = (collapsed) => { + nextTick(() => { + const flexGrowClass = 'd-splitter-pane-grow'; + if (hasClass(domRef.value, flexGrowClass)) { + removeClass(domRef.value, flexGrowClass); + } else if (collapsed) { + addClass(domRef.value, flexGrowClass); + } + }); + }; + + // 暴露给外部使用 + expose({ + getPaneSize, + toggleNearPaneFlexGrow, + }); + + return () => { + return ( +
+ {slots.default?.()} +
+ ); + }; + }, +}); diff --git a/devui/splitter/src/splitter-store.ts b/devui/splitter/src/splitter-store.ts new file mode 100644 index 00000000..11256756 --- /dev/null +++ b/devui/splitter/src/splitter-store.ts @@ -0,0 +1,180 @@ +import SplitterPane from './splitter-pane' +import { reactive, readonly } from 'vue' + +export interface Pane { + getPaneSize: () => number +} + +type SplitterPane = typeof SplitterPane & Pane +export interface splitterState { + panes: Array // 所有 pane 对象的一些关键信息 + splitterContainerSize: number +} + +const state: splitterState = reactive({ + panes: [], + splitterContainerSize: 0, +}) + +export function useSplitterStore() { + // 配置 pane 信息,panes 列表,方便后续计算使用 + const setPanes = ({ panes }) => { + state.panes = panes.map((pane: SplitterPane) => { + pane.getPaneSize = pane?.component?.exposed.getPaneSize + return pane + }) + } + const setSplitter = ({ containerSize }) => { + state.splitterContainerSize = containerSize + } + + // 获取 pane,防止没有初始化的时候调用内部方法取值 + const getPane = (index: number): SplitterPane => { + if (!state.panes || index < 0 || index >= state.panes.length) { + throw new Error('no pane can return.') + } + return state.panes[index] + } + + // 按下的时候计算 pane 的 size 信息 + const dragState = (splitbarIndex) => { + const prev = getPane(splitbarIndex) + const next = getPane(splitbarIndex + 1) + const total = prev.getPaneSize() + next.getPaneSize() + return { + prev: { + index: splitbarIndex, + initialSize: prev.getPaneSize(), + // 设置有最小值,直接取值,如果没有设置就用两个 pane 总和减去相邻 pane 的最大值,都没设置(NaN)再取0 + minSize: + toPixels(prev.component.props.minSize) || + total - toPixels(next.component.props.maxSize) || + 0, + // 设置最大值,直接取值,如果没有设置就用两个 pane 总和减去相邻 pane 的最小值,都没设置(NaN)再取两个 pane 总和 + maxSize: + toPixels(prev.component.props.maxSize) || + total - toPixels(next.component.props.minSize) || + total, + }, + next: { + index: splitbarIndex + 1, + initialSize: next.getPaneSize(), + minSize: + toPixels(next.component.props.minSize) || + total - toPixels(prev.component.props.maxSize) || + 0, + maxSize: + toPixels(next.component.props.maxSize) || + total - toPixels(prev.component.props.minSize) || + total, + }, + } + } + + // 大小限制函数,(max)小于最小值时取最小值,(min)大于最大值时取最大值 + const clamp = (minSize, maxSize, initialSize) => { + return Math.min(maxSize, Math.max(minSize, initialSize)) + } + + // resize pane的大小 + const resize = (paneState, moveSize) => { + const pane = getPane(paneState.index) + const splitterSize = state.splitterContainerSize + const newSize = clamp( + paneState.minSize, + paneState.maxSize, + paneState.initialSize + moveSize + ) + let size = '' + if (isPercent(pane.component.props.size)) { + size = (100 * newSize) / splitterSize + '%' + } else { + size = newSize + 'px' + } + pane.component.props.size = size + pane.component.emit('sizeChange', size) + } + + // 判断 pane 是否可以调整大小,只要有一边设置了不可调整或者收起,相邻 pane 调整就失效 + const isResizable = (splitBarIndex) => { + const prevPane = getPane(splitBarIndex) + const nextPane = getPane(splitBarIndex + 1) + const paneCollapsed = + prevPane.component.props.collapsed || nextPane.component.props.collapsed + return ( + prevPane.component.props.resizable && + nextPane.component.props.resizable && + !paneCollapsed + ) + } + + // 判断分割条是否是固定的,只要有一边不能调整, 就是禁用状态固定 bar + const isStaticBar = (splitBarIndex: number) => { + const prevPane = getPane(splitBarIndex) + const nextPane = getPane(splitBarIndex + 1) + return !( + prevPane.component.props.resizable && nextPane.component.props.resizable + ) + } + + // 判断是不是百分比设置宽度 + const isPercent = (size) => { + return /%$/.test(size) + } + + // 计算时把百分比转换为像素 + const toPixels = (size) => { + // 值不满足转换时,result 为 NaN,方便计算最小、最大宽度判断 + let result = parseFloat(size) + if (isPercent(size)) { + result = (state.splitterContainerSize * result) / 100 + } + return result + } + + // 切换 pane 展开,收起 + const tooglePane = (paneIndex, nearPaneIndex, lockStatus?) => { + const pane = getPane(paneIndex) + const nearPane = getPane(nearPaneIndex) + if (pane.component.props.collapsible) { + pane.component.props.collapsed = lockStatus + ? pane.component.props.collapsed + : !pane.component.props.collapsed + nearPane.component.exposed.toggleNearPaneFlexGrow( + pane.component.props.collapsed + ) + pane.component.emit('collapsedChange', pane.component.props.collapsed) + } + } + + // 设置 pane 大小 + const setSize = (state, distance) => { + const prev = getPane(state.prev.index) + const next = getPane(state.next.index) + if (prev.size && next.size) { + // 相邻的两个 pane 都指定了 size,需要同时修改 size + resize(state.prev, distance) + resize(state.next, distance) + } else if (next.size) { + // 只有 next pane指定了 size,直接修改 next pane + resize(state.next, -distance) + } else { + // 最后都没有指定 size,直接修改 pre pane + resize(state.prev, distance) + } + } + + const readonlyState = readonly(state) + + return { + setPanes, + getPane, + isStaticBar, + setSize, + setSplitter, + dragState, + isResizable, + tooglePane, + splitterState: readonlyState, + } +} diff --git a/devui/splitter/src/splitter-types.ts b/devui/splitter/src/splitter-types.ts new file mode 100644 index 00000000..73467eb8 --- /dev/null +++ b/devui/splitter/src/splitter-types.ts @@ -0,0 +1,29 @@ +import type { PropType, ExtractPropTypes } from 'vue'; +export type SplitterOrientation = 'vertical' | 'horizontal'; +export type CollapseDirection = 'before' | 'after' | 'both'; + +export const splitterProps = { + /** + * 可选,指定 Splitter 分割方向,可选值'vertical'|'horizontal' + */ + orientation: { + type: String as PropType, + default: 'horizontal', + }, + /** + * 可选,分隔条大小,默认 2px + */ + splitBarSize: { + type: String, + default: '2px', + }, + /** + * 是否显示展开/收缩按钮 + */ + showCollapseButton: { + type: Boolean, + default: true, + }, +} as const; + +export type SplitterProps = ExtractPropTypes; diff --git a/devui/splitter/src/splitter.scss b/devui/splitter/src/splitter.scss new file mode 100644 index 00000000..c8fbbe90 --- /dev/null +++ b/devui/splitter/src/splitter.scss @@ -0,0 +1,18 @@ +@import '../../style/theme/color'; +@import '../../style/theme/corner'; + +.d-splitter { + display: flex; + width: 100%; + height: auto; + position: relative; + border-radius: $devui-border-radius; + + &.devui-splitter-horizontal { + flex-direction: row; + } + + &.devui-splitter-vertical { + flex-direction: column; + } +} diff --git a/devui/splitter/src/splitter.tsx b/devui/splitter/src/splitter.tsx new file mode 100644 index 00000000..4c906981 --- /dev/null +++ b/devui/splitter/src/splitter.tsx @@ -0,0 +1,65 @@ +import { defineComponent, reactive, ref, provide, onMounted } from 'vue'; +import { splitterProps, SplitterProps } from './splitter-types'; +import DSplitterBar from './splitter-bar'; +import { useSplitterStore } from './splitter-store'; +import './splitter.scss'; + +export default defineComponent({ + name: 'DSplitter', + components: { + DSplitterBar, + }, + props: splitterProps, + emits: [], + setup(props: SplitterProps, ctx) { + const { setPanes, setSplitter } = useSplitterStore(); + const state = reactive({ + panes: [], // 内嵌面板 + }); + + state.panes = ctx.slots.DSplitterPane(); + setPanes({ panes: state.panes }); + + const domRef = ref(); + + provide('orientation', props.orientation); + provide('panes', state.panes); + + onMounted(() => { + let containerSize = 0; + if (props.orientation === 'vertical') { + containerSize = domRef.value.clientHeight; + } else { + containerSize = domRef.value.clientWidth; + } + setSplitter({ containerSize }); + }); + + return () => { + const { splitBarSize, orientation, showCollapseButton } = props; + const wrapperClass = ['d-splitter', `devui-splitter-${orientation}`]; + + return ( +
+ {state.panes.map((pane, index) => { + pane.props.order = index * 2; + return pane; + })} + {state.panes + .filter((pane, index, arr) => index !== arr.length - 1) + .map((pane, index) => { + return ( + + ); + })} +
+ ); + }; + }, +}); diff --git a/devui/splitter/src/util/class.ts b/devui/splitter/src/util/class.ts new file mode 100644 index 00000000..046f6ce6 --- /dev/null +++ b/devui/splitter/src/util/class.ts @@ -0,0 +1,44 @@ +/** + * 判断 DOM 中的元素是否含有某个类 + * @param el 元素 + * @param className 类名 + * @returns + */ +export function hasClass(el: HTMLElement, className: string): boolean { + if (el.classList) { + return el.classList.contains(className); + } + const originClass = el.className; + return ` ${originClass} `.indexOf(` ${className} `) > -1; +} + +/** + * 向 DOM 中的元素添加一个类 + * @param el 元素 + * @param className 类名 + */ +export function addClass(el: HTMLElement, className: string): void { + if (el.classList) { + el.classList.add(className); + } else { + if (!hasClass(el, className)) { + el.className = `${el.className} ${className}`; + } + } +} + +/** + * 从 DOM 中的元素移除一个类 + * @param el 元素 + * @param className 类名 + */ +export function removeClass(el: HTMLElement, className: string): void { + if (el.classList) { + el.classList.remove(className); + } else { + if (hasClass(el, className)) { + const originClass = el.className; + el.className = ` ${originClass} `.replace(` ${className} `, ' '); + } + } +} diff --git a/devui/splitter/src/util/d-resize-directive.ts b/devui/splitter/src/util/d-resize-directive.ts new file mode 100644 index 00000000..80747166 --- /dev/null +++ b/devui/splitter/src/util/d-resize-directive.ts @@ -0,0 +1,82 @@ +import type { Directive, DirectiveBinding } from 'vue' + +export class ResizeDirectiveProp { + enableResize = true // 是否允许拖动 + onPressEvent = function (...args: any[]): void { + /** */ + } + onDragEvent = function (...args: any[]): void { + /** */ + } + onReleaseEvent = function (...args: any[]): void { + /** */ + } +} + +let resizeDirectiveProp: ResizeDirectiveProp +const resize: Directive = { + mounted( + el, + { value = new ResizeDirectiveProp() }: DirectiveBinding + ) { + resizeDirectiveProp = value + // 是否允许拖动 + if (value.enableResize) { + bindEvent(el) + } + }, + unmounted( + el, + { value = new ResizeDirectiveProp() }: DirectiveBinding + ) { + if (value.enableResize) { + unbind(el, 'mousedown', onMousedown) + } + }, +} + +function bindEvent(el) { + // 绑定 mousedown 事件 + bind(el, 'mousedown', onMousedown) + // TODO 绑定触屏事件 +} + +function bind(el, event, callback) { + el.addEventListener && el.addEventListener(event, callback) +} + +function unbind(el, event, callback) { + el.removeEventListener && el.removeEventListener(event, callback) +} + +function onMousedown(e) { + bind(document, 'mousemove', onMousemove) + bind(document, 'mouseup', onMouseup) + resizeDirectiveProp.onPressEvent(normalizeEvent(e)) +} + +function onMousemove(e) { + resizeDirectiveProp.onDragEvent(normalizeEvent(e)) +} + +function onMouseup(e) { + unbind(document, 'mousemove', onMousemove) + unbind(document, 'mouseup', onMouseup) + resizeDirectiveProp.onReleaseEvent(normalizeEvent(e)) +} + +// 返回常用位置信息 +function normalizeEvent(e) { + return { + pageX: e.pageX, + pageY: e.pageY, + clientX: e.clientX, + clientY: e.clientY, + offsetX: e.offsetX, + offsetY: e.offsetY, + type: e.type, + originalEvent: e, + } +} + +export default resize diff --git a/devui/splitter/src/util/set-style.ts b/devui/splitter/src/util/set-style.ts new file mode 100644 index 00000000..e4a530e9 --- /dev/null +++ b/devui/splitter/src/util/set-style.ts @@ -0,0 +1,35 @@ +import type { CSSProperties } from 'vue'; + +/** + * 设置元素的样式,返回上一次的样式 + * @param element + * @param style + * @returns + */ +export function setStyle( + element: HTMLElement, + style: CSSProperties, +): CSSProperties { + const oldStyle: CSSProperties = {}; + + const styleKeys = Object.keys(style); + + styleKeys.forEach((key) => { + oldStyle[key] = element.style[key]; + }); + + styleKeys.forEach((key) => { + element.style[key] = style[key]; + }); + + return oldStyle; +} + +// /** +// * 移除样式 +// * @param element +// * @param style +// */ +// export function removeStyle( element: HTMLElement, style: CSSProperties) { + +// } \ No newline at end of file diff --git a/sites/components/splitter/index.md b/sites/components/splitter/index.md new file mode 100644 index 00000000..398c4857 --- /dev/null +++ b/sites/components/splitter/index.md @@ -0,0 +1,124 @@ +# Splitter 分割器 + +页面分割器。 + +**何时使用** + +需要动态调整不同页面布局区域大小的时候选择使用。 + +## 基本用法 + +
+ + + +
+ + + + +```vue + + + +``` + +## 垂直布局用法【TODO】 + +## 组合布局用法【TODO】 + +## 指定折叠收起方向【TODO】 + +## 折叠收缩显示菜单【TODO】 + diff --git a/sites/components/splitter/splitter-demo.scss b/sites/components/splitter/splitter-demo.scss new file mode 100644 index 00000000..0b75440a --- /dev/null +++ b/sites/components/splitter/splitter-demo.scss @@ -0,0 +1,9 @@ +@import "../../../devui/style/theme/color"; + +.splitter-border { + border: 1px solid $devui-dividing-line; +} + +.pane-content { + padding: 0 12px; +} diff --git a/yarn.lock b/yarn.lock index 41112737..d5d6cc7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2627,11 +2627,6 @@ babel-walk@3.0.0-canary-5: dependencies: "@babel/types" "^7.9.6" -babylon@^6.18.0: - version "6.18.0" - resolved "http://nexus3.ey.com.cn/repository/npm-group/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" - integrity sha1-ry87iPpvXB5MY00aD46sT1WzleM= - bail@^1.0.0: version "1.0.5" resolved "https://registry.nlark.com/bail/download/bail-1.0.5.tgz?cache=0&sync_timestamp=1621401842495&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fbail%2Fdownload%2Fbail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" -- Gitee From 723297e704f89e0b1dc01cbcce1387a84bc2ec7e Mon Sep 17 00:00:00 2001 From: Jecyu Date: Mon, 13 Sep 2021 22:47:33 +0800 Subject: [PATCH 02/32] =?UTF-8?q?refactor(splitter):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=B1=BB=E7=BB=91=E5=AE=9A=E3=80=81=E5=AE=8C=E5=96=84=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E6=A0=87=E6=B3=A8=E3=80=81list=20=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E9=A1=B9=E6=B7=BB=E5=8A=A0=20key=E3=80=81=E5=9B=9E=E6=BB=9A=20?= =?UTF-8?q?lock=20=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ISSUES CLOSED: #103 --- devui/splitter/src/splitter-bar.tsx | 3 +- devui/splitter/src/splitter-store.ts | 46 ++++++++++++++++++------- devui/splitter/src/splitter.tsx | 51 ++++++++++++++-------------- yarn.lock | 5 +++ 4 files changed, 65 insertions(+), 40 deletions(-) diff --git a/devui/splitter/src/splitter-bar.tsx b/devui/splitter/src/splitter-bar.tsx index 9c4a1220..72e790fc 100644 --- a/devui/splitter/src/splitter-bar.tsx +++ b/devui/splitter/src/splitter-bar.tsx @@ -198,8 +198,7 @@ export default defineComponent({
{props.showCollapseButton ? ( ) : null} diff --git a/devui/splitter/src/splitter-store.ts b/devui/splitter/src/splitter-store.ts index 11256756..1e9b0e80 100644 --- a/devui/splitter/src/splitter-store.ts +++ b/devui/splitter/src/splitter-store.ts @@ -5,6 +5,18 @@ export interface Pane { getPaneSize: () => number } +export interface PaneState { + index: number + initialSize: number + minSize: number + maxSize: number +} + +export interface DragState { + prev: PaneState + next: PaneState +} + type SplitterPane = typeof SplitterPane & Pane export interface splitterState { panes: Array // 所有 pane 对象的一些关键信息 @@ -18,13 +30,13 @@ const state: splitterState = reactive({ export function useSplitterStore() { // 配置 pane 信息,panes 列表,方便后续计算使用 - const setPanes = ({ panes }) => { + const setPanes = ({ panes }): void => { state.panes = panes.map((pane: SplitterPane) => { pane.getPaneSize = pane?.component?.exposed.getPaneSize return pane }) } - const setSplitter = ({ containerSize }) => { + const setSplitter = ({ containerSize }: { containerSize: number; }): void => { state.splitterContainerSize = containerSize } @@ -37,7 +49,7 @@ export function useSplitterStore() { } // 按下的时候计算 pane 的 size 信息 - const dragState = (splitbarIndex) => { + const dragState = (splitbarIndex: number): DragState => { const prev = getPane(splitbarIndex) const next = getPane(splitbarIndex + 1) const total = prev.getPaneSize() + next.getPaneSize() @@ -72,12 +84,16 @@ export function useSplitterStore() { } // 大小限制函数,(max)小于最小值时取最小值,(min)大于最大值时取最大值 - const clamp = (minSize, maxSize, initialSize) => { + const clamp = ( + minSize: number, + maxSize: number, + initialSize: number + ): number => { return Math.min(maxSize, Math.max(minSize, initialSize)) } // resize pane的大小 - const resize = (paneState, moveSize) => { + const resize = (paneState: PaneState, moveSize: number): void => { const pane = getPane(paneState.index) const splitterSize = state.splitterContainerSize const newSize = clamp( @@ -96,7 +112,7 @@ export function useSplitterStore() { } // 判断 pane 是否可以调整大小,只要有一边设置了不可调整或者收起,相邻 pane 调整就失效 - const isResizable = (splitBarIndex) => { + const isResizable = (splitBarIndex: number): boolean => { const prevPane = getPane(splitBarIndex) const nextPane = getPane(splitBarIndex + 1) const paneCollapsed = @@ -109,7 +125,7 @@ export function useSplitterStore() { } // 判断分割条是否是固定的,只要有一边不能调整, 就是禁用状态固定 bar - const isStaticBar = (splitBarIndex: number) => { + const isStaticBar = (splitBarIndex: number): boolean => { const prevPane = getPane(splitBarIndex) const nextPane = getPane(splitBarIndex + 1) return !( @@ -118,12 +134,12 @@ export function useSplitterStore() { } // 判断是不是百分比设置宽度 - const isPercent = (size) => { + const isPercent = (size: string) => { return /%$/.test(size) } // 计算时把百分比转换为像素 - const toPixels = (size) => { + const toPixels = (size: string): number => { // 值不满足转换时,result 为 NaN,方便计算最小、最大宽度判断 let result = parseFloat(size) if (isPercent(size)) { @@ -133,7 +149,11 @@ export function useSplitterStore() { } // 切换 pane 展开,收起 - const tooglePane = (paneIndex, nearPaneIndex, lockStatus?) => { + const tooglePane = ( + paneIndex: number, + nearPaneIndex: number, + lockStatus?: boolean + ): void => { const pane = getPane(paneIndex) const nearPane = getPane(nearPaneIndex) if (pane.component.props.collapsible) { @@ -148,14 +168,14 @@ export function useSplitterStore() { } // 设置 pane 大小 - const setSize = (state, distance) => { + const setSize = (state: DragState, distance: number): void => { const prev = getPane(state.prev.index) const next = getPane(state.next.index) - if (prev.size && next.size) { + if (prev.component.props.size && next.component.props.size) { // 相邻的两个 pane 都指定了 size,需要同时修改 size resize(state.prev, distance) resize(state.next, distance) - } else if (next.size) { + } else if (next.component.props.size) { // 只有 next pane指定了 size,直接修改 next pane resize(state.next, -distance) } else { diff --git a/devui/splitter/src/splitter.tsx b/devui/splitter/src/splitter.tsx index 4c906981..5ec1b043 100644 --- a/devui/splitter/src/splitter.tsx +++ b/devui/splitter/src/splitter.tsx @@ -1,8 +1,8 @@ -import { defineComponent, reactive, ref, provide, onMounted } from 'vue'; -import { splitterProps, SplitterProps } from './splitter-types'; -import DSplitterBar from './splitter-bar'; -import { useSplitterStore } from './splitter-store'; -import './splitter.scss'; +import { defineComponent, reactive, ref, provide, onMounted } from 'vue' +import { splitterProps, SplitterProps } from './splitter-types' +import DSplitterBar from './splitter-bar' +import { useSplitterStore } from './splitter-store' +import './splitter.scss' export default defineComponent({ name: 'DSplitter', @@ -12,54 +12,55 @@ export default defineComponent({ props: splitterProps, emits: [], setup(props: SplitterProps, ctx) { - const { setPanes, setSplitter } = useSplitterStore(); + const { setPanes, setSplitter } = useSplitterStore() const state = reactive({ panes: [], // 内嵌面板 - }); + }) - state.panes = ctx.slots.DSplitterPane(); - setPanes({ panes: state.panes }); + state.panes = ctx.slots.DSplitterPane() + setPanes({ panes: state.panes }) - const domRef = ref(); + const domRef = ref() - provide('orientation', props.orientation); - provide('panes', state.panes); + provide('orientation', props.orientation) + provide('panes', state.panes) onMounted(() => { - let containerSize = 0; + let containerSize = 0 if (props.orientation === 'vertical') { - containerSize = domRef.value.clientHeight; + containerSize = domRef.value.clientHeight } else { - containerSize = domRef.value.clientWidth; + containerSize = domRef.value.clientWidth } - setSplitter({ containerSize }); - }); + setSplitter({ containerSize }) + }) return () => { - const { splitBarSize, orientation, showCollapseButton } = props; - const wrapperClass = ['d-splitter', `devui-splitter-${orientation}`]; + const { splitBarSize, orientation, showCollapseButton } = props + const wrapperClass = ['d-splitter', `devui-splitter-${orientation}`] return (
{state.panes.map((pane, index) => { - pane.props.order = index * 2; - return pane; + pane.props.order = index * 2 + return pane })} {state.panes .filter((pane, index, arr) => index !== arr.length - 1) .map((pane, index) => { return ( - ); + ) })}
- ); - }; + ) + } }, -}); +}) diff --git a/yarn.lock b/yarn.lock index d5d6cc7e..41112737 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2627,6 +2627,11 @@ babel-walk@3.0.0-canary-5: dependencies: "@babel/types" "^7.9.6" +babylon@^6.18.0: + version "6.18.0" + resolved "http://nexus3.ey.com.cn/repository/npm-group/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha1-ry87iPpvXB5MY00aD46sT1WzleM= + bail@^1.0.0: version "1.0.5" resolved "https://registry.nlark.com/bail/download/bail-1.0.5.tgz?cache=0&sync_timestamp=1621401842495&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fbail%2Fdownload%2Fbail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" -- Gitee From 480942e7e456b19b0df08554d27e9cd339993501 Mon Sep 17 00:00:00 2001 From: nowisfuture Date: Fri, 27 Aug 2021 17:22:18 +0800 Subject: [PATCH 03/32] =?UTF-8?q?refactor(quadrant-diagram-axis):=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E4=BB=B6=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devui/quadrant-diagram/config.ts | 4 ++++ devui/quadrant-diagram/index.ts | 2 +- devui/quadrant-diagram/src/components/axis/index.tsx | 8 +++++--- .../{quadrant-diagram.tsx => quadrant-diagram/index.tsx} | 8 ++++---- .../types.ts} | 6 +++--- 5 files changed, 17 insertions(+), 11 deletions(-) rename devui/quadrant-diagram/src/{quadrant-diagram.tsx => quadrant-diagram/index.tsx} (89%) rename devui/quadrant-diagram/src/{quadrant-diagram-types.ts => quadrant-diagram/types.ts} (70%) diff --git a/devui/quadrant-diagram/config.ts b/devui/quadrant-diagram/config.ts index f5902f08..c89a9443 100644 --- a/devui/quadrant-diagram/config.ts +++ b/devui/quadrant-diagram/config.ts @@ -29,6 +29,10 @@ export const DEFAULT_QUADRANT_CONFIGS = [ { title: '不重要不紧急' }, { title: '不重要紧急' } ]; +export const DEFAULT_VIEW_CONFIGS = { + height: 900, + width: 950, +} export const AXIS_TITLE_SPACE = 15; export const SMALL_LABEL_SIZE_CENTER_POINT = { x: 6, y: 6 diff --git a/devui/quadrant-diagram/index.ts b/devui/quadrant-diagram/index.ts index 86648b6f..eff8bdbb 100644 --- a/devui/quadrant-diagram/index.ts +++ b/devui/quadrant-diagram/index.ts @@ -1,5 +1,5 @@ import type { App } from 'vue' -import QuadrantDiagram from './src/quadrant-diagram' +import QuadrantDiagram from './src/quadrant-diagram/index' QuadrantDiagram.install = function (app: App) { app.component(QuadrantDiagram.name, QuadrantDiagram) diff --git a/devui/quadrant-diagram/src/components/axis/index.tsx b/devui/quadrant-diagram/src/components/axis/index.tsx index f7353d00..cc026af8 100644 --- a/devui/quadrant-diagram/src/components/axis/index.tsx +++ b/devui/quadrant-diagram/src/components/axis/index.tsx @@ -114,7 +114,7 @@ const Axis = defineComponent({ * 绘制轴线刻度 */ const drawXAxisTicks = () => { - let deltaY; + let deltaY: number; for (let i = 1; i < axisInnerAttr.xAxisTicksNum; i++) { context.value.beginPath(); // 判断显示长刻度还是短刻度 @@ -132,7 +132,7 @@ const Axis = defineComponent({ } const drawYAxisTicks = () => { - let deltaX; + let deltaX: number; for (let i = 1; i < axisInnerAttr.yAxisTicksNum; i++) { context.value.beginPath(); if (i % axisConfigsVal.yAxisRange.step === 0) { @@ -147,7 +147,9 @@ const Axis = defineComponent({ context.value.stroke(); } } - + /** + * 绘制轴线标签 + */ const drawAxisLabels = () => { context.value.save(); context.value.fillStyle = AXIS_LABEL_COLOR.value; diff --git a/devui/quadrant-diagram/src/quadrant-diagram.tsx b/devui/quadrant-diagram/src/quadrant-diagram/index.tsx similarity index 89% rename from devui/quadrant-diagram/src/quadrant-diagram.tsx rename to devui/quadrant-diagram/src/quadrant-diagram/index.tsx index d5506a13..e151bc06 100644 --- a/devui/quadrant-diagram/src/quadrant-diagram.tsx +++ b/devui/quadrant-diagram/src/quadrant-diagram/index.tsx @@ -1,7 +1,7 @@ import { defineComponent, toRefs, ref, onMounted, reactive } from 'vue' -import { quadrantDiagramProps, QuadrantDiagramProps } from './quadrant-diagram-types' -import DQuadrantDiagramAxis from './components/axis'; -import { DEFAULT_AXIS_CONFIGS } from '../config'; +import { quadrantDiagramProps, QuadrantDiagramProps } from './types' +import DQuadrantDiagramAxis from '../components/axis'; +import { DEFAULT_AXIS_CONFIGS } from '../../config'; export default defineComponent({ name: 'DQuadrantDiagram', @@ -54,7 +54,7 @@ export default defineComponent({ const { diagramId, calAxisConfig, viewVal } = this; return ( -
+
) diff --git a/devui/quadrant-diagram/src/quadrant-diagram-types.ts b/devui/quadrant-diagram/src/quadrant-diagram/types.ts similarity index 70% rename from devui/quadrant-diagram/src/quadrant-diagram-types.ts rename to devui/quadrant-diagram/src/quadrant-diagram/types.ts index 3513e64f..774e7258 100644 --- a/devui/quadrant-diagram/src/quadrant-diagram-types.ts +++ b/devui/quadrant-diagram/src/quadrant-diagram/types.ts @@ -1,6 +1,6 @@ import type { ExtractPropTypes, PropType } from 'vue' -import { DEFAULT_AXIS_CONFIGS } from '../config'; -import { IViewConfigs, IAxisConfigs } from '../type'; +import { DEFAULT_AXIS_CONFIGS, DEFAULT_VIEW_CONFIGS } from '../../config'; +import { IViewConfigs, IAxisConfigs } from '../../type'; export const quadrantDiagramProps = { diagramId: { @@ -13,7 +13,7 @@ export const quadrantDiagramProps = { }, view: { type: Object as PropType, - default: { height: 720, width: 720 }, + default: DEFAULT_VIEW_CONFIGS, }, } as const -- Gitee From 0e29a7ea84febd0e3e9b7f2d85c49a905dc308a6 Mon Sep 17 00:00:00 2001 From: nowisfuture Date: Wed, 1 Sep 2021 21:49:40 +0800 Subject: [PATCH 04/32] =?UTF-8?q?refactor(quadrant-diagram-axis):=20s?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20watch=20=E8=BF=9B=E8=A1=8C=E5=9D=90?= =?UTF-8?q?=E6=A0=87=E7=B3=BB=E7=9A=84=E9=87=8D=E7=BB=98,=20=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E4=B8=8D=E9=9C=80=E8=A6=81=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/axis/index.tsx | 16 +++++++++------- .../src/quadrant-diagram/index.tsx | 16 ++++++++++------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/devui/quadrant-diagram/src/components/axis/index.tsx b/devui/quadrant-diagram/src/components/axis/index.tsx index cc026af8..b8c9ba99 100644 --- a/devui/quadrant-diagram/src/components/axis/index.tsx +++ b/devui/quadrant-diagram/src/components/axis/index.tsx @@ -1,15 +1,15 @@ -import { defineComponent, toRefs, onMounted, ExtractPropTypes, reactive, ref } from 'vue' +import { defineComponent, toRefs, onMounted, ExtractPropTypes, reactive, ref, watch } from 'vue' import { IViewConfigs, IAxisConfigs } from '../../../type'; import { AXIS_TITLE_SPACE } from '../../../config'; -import { quadrantDiagramAxisProps } from './types' +import { quadrantDiagramAxisProps, QuadrantDiagramAxisProps } from './types' import { debounce } from 'lodash'; import './index.scss' -const Axis = defineComponent({ +export default defineComponent({ name: 'DQuadrantDiagramAxis', props: quadrantDiagramAxisProps, - setup(props: ExtractPropTypes) { + setup(props: QuadrantDiagramAxisProps) { const { diagramId, view, axisConfigs } = toRefs(props); const AXIS_COLOR = ref('#0000ff'); @@ -39,6 +39,10 @@ const Axis = defineComponent({ onMounted(() => { resetAxis(); }); + + watch(viewVal, () => { + resetAxis(); + }); const resetAxis = debounce(() => { initAxisData(); @@ -221,6 +225,4 @@ const Axis = defineComponent({
); } -}) - -export default Axis; \ No newline at end of file +}); \ No newline at end of file diff --git a/devui/quadrant-diagram/src/quadrant-diagram/index.tsx b/devui/quadrant-diagram/src/quadrant-diagram/index.tsx index e151bc06..a589f4f3 100644 --- a/devui/quadrant-diagram/src/quadrant-diagram/index.tsx +++ b/devui/quadrant-diagram/src/quadrant-diagram/index.tsx @@ -1,4 +1,4 @@ -import { defineComponent, toRefs, ref, onMounted, reactive } from 'vue' +import { defineComponent, toRefs, reactive, watch } from 'vue' import { quadrantDiagramProps, QuadrantDiagramProps } from './types' import DQuadrantDiagramAxis from '../components/axis'; import { DEFAULT_AXIS_CONFIGS } from '../../config'; @@ -7,7 +7,7 @@ export default defineComponent({ name: 'DQuadrantDiagram', props: quadrantDiagramProps, emits: [], - setup(props, ctx) { + setup(props: QuadrantDiagramProps) { const { diagramId, axisConfigs, view } = toRefs(props); const axisConfigsVal = axisConfigs.value; @@ -48,14 +48,18 @@ export default defineComponent({ initAxisData(); - return { diagramId, calAxisConfig, viewVal }; + watch(viewVal, () => { + initAxisData(); + }) + + return { diagramId, calAxisConfig, view }; }, render() { - const { diagramId, calAxisConfig, viewVal } = this; + const { diagramId, calAxisConfig, view } = this; return ( -
- +
+
) } -- Gitee From 9e1b076529992584baf5a75bc8b608b99eee9d9f Mon Sep 17 00:00:00 2001 From: nowisfuture Date: Fri, 10 Sep 2021 08:54:07 +0800 Subject: [PATCH 05/32] =?UTF-8?q?refactor(quadrant-diagram):=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E6=96=87=E4=BB=B6=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devui/quadrant-diagram/index.ts | 2 +- .../types.ts => quadrant-diagram-types.ts} | 4 +-- .../index.tsx => quadrant-diagram.tsx} | 8 +++--- sites/components/quadrant-diagram/index.md | 27 ++++++++++++++++++- 4 files changed, 33 insertions(+), 8 deletions(-) rename devui/quadrant-diagram/src/{quadrant-diagram/types.ts => quadrant-diagram-types.ts} (76%) rename devui/quadrant-diagram/src/{quadrant-diagram/index.tsx => quadrant-diagram.tsx} (90%) diff --git a/devui/quadrant-diagram/index.ts b/devui/quadrant-diagram/index.ts index eff8bdbb..86648b6f 100644 --- a/devui/quadrant-diagram/index.ts +++ b/devui/quadrant-diagram/index.ts @@ -1,5 +1,5 @@ import type { App } from 'vue' -import QuadrantDiagram from './src/quadrant-diagram/index' +import QuadrantDiagram from './src/quadrant-diagram' QuadrantDiagram.install = function (app: App) { app.component(QuadrantDiagram.name, QuadrantDiagram) diff --git a/devui/quadrant-diagram/src/quadrant-diagram/types.ts b/devui/quadrant-diagram/src/quadrant-diagram-types.ts similarity index 76% rename from devui/quadrant-diagram/src/quadrant-diagram/types.ts rename to devui/quadrant-diagram/src/quadrant-diagram-types.ts index 774e7258..abef19f7 100644 --- a/devui/quadrant-diagram/src/quadrant-diagram/types.ts +++ b/devui/quadrant-diagram/src/quadrant-diagram-types.ts @@ -1,6 +1,6 @@ import type { ExtractPropTypes, PropType } from 'vue' -import { DEFAULT_AXIS_CONFIGS, DEFAULT_VIEW_CONFIGS } from '../../config'; -import { IViewConfigs, IAxisConfigs } from '../../type'; +import { DEFAULT_AXIS_CONFIGS, DEFAULT_VIEW_CONFIGS } from '../config'; +import { IViewConfigs, IAxisConfigs } from '../type'; export const quadrantDiagramProps = { diagramId: { diff --git a/devui/quadrant-diagram/src/quadrant-diagram/index.tsx b/devui/quadrant-diagram/src/quadrant-diagram.tsx similarity index 90% rename from devui/quadrant-diagram/src/quadrant-diagram/index.tsx rename to devui/quadrant-diagram/src/quadrant-diagram.tsx index a589f4f3..d04e64c9 100644 --- a/devui/quadrant-diagram/src/quadrant-diagram/index.tsx +++ b/devui/quadrant-diagram/src/quadrant-diagram.tsx @@ -1,7 +1,7 @@ import { defineComponent, toRefs, reactive, watch } from 'vue' -import { quadrantDiagramProps, QuadrantDiagramProps } from './types' -import DQuadrantDiagramAxis from '../components/axis'; -import { DEFAULT_AXIS_CONFIGS } from '../../config'; +import { quadrantDiagramProps, QuadrantDiagramProps } from './quadrant-diagram-types' +import DQuadrantDiagramAxis from './components/axis'; +import { DEFAULT_AXIS_CONFIGS } from '../config'; export default defineComponent({ name: 'DQuadrantDiagram', @@ -52,7 +52,7 @@ export default defineComponent({ initAxisData(); }) - return { diagramId, calAxisConfig, view }; + return { diagramId, calAxisConfig, }; }, render() { const { diagramId, calAxisConfig, view } = this; diff --git a/sites/components/quadrant-diagram/index.md b/sites/components/quadrant-diagram/index.md index 8a3e8f62..34ae9854 100644 --- a/sites/components/quadrant-diagram/index.md +++ b/sites/components/quadrant-diagram/index.md @@ -13,7 +13,32 @@ :::demo ```vue - + + + + ``` ::: -- Gitee From c0e677ce659ba5dd074fb81a7e3ce4d1717ef842 Mon Sep 17 00:00:00 2001 From: nowisfuture Date: Fri, 10 Sep 2021 08:56:47 +0800 Subject: [PATCH 06/32] =?UTF-8?q?refactor(quadrant-diagram):=20=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E5=88=86=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/axis/index.tsx | 216 +++++++++--------- .../src/components/axis/types.ts | 2 +- .../src/quadrant-diagram-types.ts | 4 +- .../quadrant-diagram/src/quadrant-diagram.tsx | 40 ++-- 4 files changed, 131 insertions(+), 131 deletions(-) diff --git a/devui/quadrant-diagram/src/components/axis/index.tsx b/devui/quadrant-diagram/src/components/axis/index.tsx index b8c9ba99..0e0d7d39 100644 --- a/devui/quadrant-diagram/src/components/axis/index.tsx +++ b/devui/quadrant-diagram/src/components/axis/index.tsx @@ -1,8 +1,8 @@ import { defineComponent, toRefs, onMounted, ExtractPropTypes, reactive, ref, watch } from 'vue' -import { IViewConfigs, IAxisConfigs } from '../../../type'; -import { AXIS_TITLE_SPACE } from '../../../config'; +import { IViewConfigs, IAxisConfigs } from '../../../type' +import { AXIS_TITLE_SPACE } from '../../../config' import { quadrantDiagramAxisProps, QuadrantDiagramAxisProps } from './types' -import { debounce } from 'lodash'; +import { debounce } from 'lodash' import './index.scss' @@ -11,12 +11,12 @@ export default defineComponent({ props: quadrantDiagramAxisProps, setup(props: QuadrantDiagramAxisProps) { - const { diagramId, view, axisConfigs } = toRefs(props); - const AXIS_COLOR = ref('#0000ff'); - const AXIS_LABEL_COLOR = ref('#ff0000'); + const { diagramId, view, axisConfigs } = toRefs(props) + const AXIS_COLOR = ref('#0000ff') + const AXIS_LABEL_COLOR = ref('#ff0000') - const quadrantAxis = ref(); - const context = ref(); + const quadrantAxis = ref() + const context = ref() const axisInnerAttr = reactive({ axisOrigin: { x: 0, @@ -32,185 +32,185 @@ export default defineComponent({ yTickSpacing: 0, }) - const axisConfigsVal: IAxisConfigs = axisConfigs.value; - const viewVal: IViewConfigs = view.value; + const axisConfigsVal: IAxisConfigs = axisConfigs.value + const viewVal: IViewConfigs = view.value onMounted(() => { - resetAxis(); - }); + resetAxis() + }) watch(viewVal, () => { - resetAxis(); - }); + resetAxis() + }) const resetAxis = debounce(() => { - initAxisData(); - setAxisData(); - drawAxis(); - drawAxisLabels(); - }, 200); + initAxisData() + setAxisData() + drawAxis() + drawAxisLabels() + }, 200) /** * 获取 canvas 并赋值宽高 */ const initAxisData = () => { - quadrantAxis.value = document.querySelector('#devui-quadrant-axis-' + diagramId.value); - quadrantAxis.value.width = viewVal.width; - quadrantAxis.value.height = viewVal.height; + quadrantAxis.value = document.querySelector('#devui-quadrant-axis-' + diagramId.value) + quadrantAxis.value.width = viewVal.width + quadrantAxis.value.height = viewVal.height } const setAxisData = () => { - context.value = quadrantAxis.value.getContext('2d'); - axisInnerAttr.axisOrigin = axisConfigsVal.axisOrigin; - axisInnerAttr.axisTop = axisConfigsVal.axisTop; - axisInnerAttr.axisRight = axisConfigsVal.axisRight; - axisInnerAttr.axisWidth = axisConfigsVal.axisWidth; - axisInnerAttr.axisHeight = axisConfigsVal.axisHeight; - axisInnerAttr.yAxisTicksNum = axisConfigsVal.yAxisTicksNum; - axisInnerAttr.xAxisTicksNum = axisConfigsVal.xAxisTicksNum; - axisInnerAttr.xTickSpacing = axisConfigsVal.xTickSpacing; - axisInnerAttr.yTickSpacing = axisConfigsVal.yTickSpacing; + context.value = quadrantAxis.value.getContext('2d') + axisInnerAttr.axisOrigin = axisConfigsVal.axisOrigin + axisInnerAttr.axisTop = axisConfigsVal.axisTop + axisInnerAttr.axisRight = axisConfigsVal.axisRight + axisInnerAttr.axisWidth = axisConfigsVal.axisWidth + axisInnerAttr.axisHeight = axisConfigsVal.axisHeight + axisInnerAttr.yAxisTicksNum = axisConfigsVal.yAxisTicksNum + axisInnerAttr.xAxisTicksNum = axisConfigsVal.xAxisTicksNum + axisInnerAttr.xTickSpacing = axisConfigsVal.xTickSpacing + axisInnerAttr.yTickSpacing = axisConfigsVal.yTickSpacing } /** * 执行绘制 */ const drawAxis = () => { - context.value.save(); - context.value.fillStyle = AXIS_COLOR.value; - context.value.strokeStyle = AXIS_COLOR.value; - drawXAxis(); - drawYAxis(); - context.value.lineWidth = 0.5; - drawXAxisTicks(); - drawYAxisTicks(); - context.value.restore(); + context.value.save() + context.value.fillStyle = AXIS_COLOR.value + context.value.strokeStyle = AXIS_COLOR.value + drawXAxis() + drawYAxis() + context.value.lineWidth = 0.5 + drawXAxisTicks() + drawYAxisTicks() + context.value.restore() } /** * 绘制 XY 轴 */ const drawYAxis = () => { - context.value.beginPath(); - context.value.moveTo(axisInnerAttr.axisOrigin.x, axisInnerAttr.axisOrigin.y); - context.value.lineTo(axisInnerAttr.axisOrigin.x, axisInnerAttr.axisTop - axisConfigsVal.axisMargin); - context.value.stroke(); - context.value.moveTo(axisInnerAttr.axisOrigin.x, axisInnerAttr.axisTop - axisConfigsVal.axisMargin); - context.value.lineTo(axisInnerAttr.axisOrigin.x + 5, axisInnerAttr.axisTop - axisConfigsVal.axisMargin + 10); - context.value.lineTo(axisInnerAttr.axisOrigin.x - 5, axisInnerAttr.axisTop - axisConfigsVal.axisMargin + 10); - context.value.fill(); + context.value.beginPath() + context.value.moveTo(axisInnerAttr.axisOrigin.x, axisInnerAttr.axisOrigin.y) + context.value.lineTo(axisInnerAttr.axisOrigin.x, axisInnerAttr.axisTop - axisConfigsVal.axisMargin) + context.value.stroke() + context.value.moveTo(axisInnerAttr.axisOrigin.x, axisInnerAttr.axisTop - axisConfigsVal.axisMargin) + context.value.lineTo(axisInnerAttr.axisOrigin.x + 5, axisInnerAttr.axisTop - axisConfigsVal.axisMargin + 10) + context.value.lineTo(axisInnerAttr.axisOrigin.x - 5, axisInnerAttr.axisTop - axisConfigsVal.axisMargin + 10) + context.value.fill() } const drawXAxis = () => { - context.value.beginPath(); - context.value.moveTo(axisInnerAttr.axisOrigin.x, axisInnerAttr.axisOrigin.y); - context.value.lineTo(axisInnerAttr.axisRight + axisConfigsVal.axisMargin - 10, axisInnerAttr.axisOrigin.y); - context.value.stroke(); + context.value.beginPath() + context.value.moveTo(axisInnerAttr.axisOrigin.x, axisInnerAttr.axisOrigin.y) + context.value.lineTo(axisInnerAttr.axisRight + axisConfigsVal.axisMargin - 10, axisInnerAttr.axisOrigin.y) + context.value.stroke() // 绘制坐标轴三角形 - context.value.moveTo(axisInnerAttr.axisRight + axisConfigsVal.axisMargin, axisInnerAttr.axisOrigin.y); - context.value.lineTo(axisInnerAttr.axisRight + axisConfigsVal.axisMargin - 10, axisInnerAttr.axisOrigin.y + 5); - context.value.lineTo(axisInnerAttr.axisRight + axisConfigsVal.axisMargin - 10, axisInnerAttr.axisOrigin.y - 5); - context.value.fill(); + context.value.moveTo(axisInnerAttr.axisRight + axisConfigsVal.axisMargin, axisInnerAttr.axisOrigin.y) + context.value.lineTo(axisInnerAttr.axisRight + axisConfigsVal.axisMargin - 10, axisInnerAttr.axisOrigin.y + 5) + context.value.lineTo(axisInnerAttr.axisRight + axisConfigsVal.axisMargin - 10, axisInnerAttr.axisOrigin.y - 5) + context.value.fill() } /** * 绘制轴线刻度 */ const drawXAxisTicks = () => { - let deltaY: number; + let deltaY: number for (let i = 1; i < axisInnerAttr.xAxisTicksNum; i++) { - context.value.beginPath(); + context.value.beginPath() // 判断显示长刻度还是短刻度 if (i % axisConfigsVal.xAxisRange.step === 0) { - deltaY = axisConfigsVal.tickWidth; + deltaY = axisConfigsVal.tickWidth } else { - deltaY = axisConfigsVal.tickWidth / 2; + deltaY = axisConfigsVal.tickWidth / 2 } context.value.moveTo(axisInnerAttr.axisOrigin.x + i * axisInnerAttr.xTickSpacing, - axisInnerAttr.axisOrigin.y - deltaY); + axisInnerAttr.axisOrigin.y - deltaY) context.value.lineTo(axisInnerAttr.axisOrigin.x + i * axisInnerAttr.xTickSpacing, - axisInnerAttr.axisOrigin.y + deltaY); - context.value.stroke(); + axisInnerAttr.axisOrigin.y + deltaY) + context.value.stroke() } } const drawYAxisTicks = () => { - let deltaX: number; + let deltaX: number for (let i = 1; i < axisInnerAttr.yAxisTicksNum; i++) { - context.value.beginPath(); + context.value.beginPath() if (i % axisConfigsVal.yAxisRange.step === 0) { - deltaX = axisConfigsVal.tickWidth; + deltaX = axisConfigsVal.tickWidth } else { - deltaX = axisConfigsVal.tickWidth / 2; + deltaX = axisConfigsVal.tickWidth / 2 } context.value.moveTo(axisInnerAttr.axisOrigin.x - deltaX, - axisInnerAttr.axisOrigin.y - i * axisInnerAttr.yTickSpacing); + axisInnerAttr.axisOrigin.y - i * axisInnerAttr.yTickSpacing) context.value.lineTo(axisInnerAttr.axisOrigin.x + deltaX, - axisInnerAttr.axisOrigin.y - i * axisInnerAttr.yTickSpacing); - context.value.stroke(); + axisInnerAttr.axisOrigin.y - i * axisInnerAttr.yTickSpacing) + context.value.stroke() } } /** * 绘制轴线标签 */ const drawAxisLabels = () => { - context.value.save(); - context.value.fillStyle = AXIS_LABEL_COLOR.value; - drawXTicksLabels(); - drawYTicksLabels(); - context.value.restore(); - drawAxisTitle(); + context.value.save() + context.value.fillStyle = AXIS_LABEL_COLOR.value + drawXTicksLabels() + drawYTicksLabels() + context.value.restore() + drawAxisTitle() } const drawXTicksLabels = () => { - context.value.textAlign = 'center'; - context.value.textBaseline = 'top'; + context.value.textAlign = 'center' + context.value.textBaseline = 'top' for (let i = 0; i <= axisInnerAttr.xAxisTicksNum; i++) { if (i % axisConfigsVal.xAxisRange.step === 0) { context.value.fillText(i, axisInnerAttr.axisOrigin.x + i * axisInnerAttr.xTickSpacing, - axisInnerAttr.axisOrigin.y + axisConfigsVal.spaceBetweenLabelsAxis); + axisInnerAttr.axisOrigin.y + axisConfigsVal.spaceBetweenLabelsAxis) } } - }; + } const drawYTicksLabels = () => { - context.value.textAlign = 'center'; - context.value.textBaseline = 'middle'; + context.value.textAlign = 'center' + context.value.textBaseline = 'middle' for (let i = 0; i <= axisInnerAttr.yAxisTicksNum; i++) { if (i % axisConfigsVal.yAxisRange.step === 0) { context.value.fillText(i, axisInnerAttr.axisOrigin.x - axisConfigsVal.spaceBetweenLabelsAxis, - axisInnerAttr.axisOrigin.y - i * axisInnerAttr.yTickSpacing); + axisInnerAttr.axisOrigin.y - i * axisInnerAttr.yTickSpacing) } } - }; + } const drawAxisTitle = () => { - context.value.font = '12px Microsoft YaHei'; - context.value.textAlign = 'left'; - context.value.fillStyle = AXIS_LABEL_COLOR.value; - const xLabelWidth = context.value.measureText(axisConfigsVal.xAxisLabel).width; + context.value.font = '12px Microsoft YaHei' + context.value.textAlign = 'left' + context.value.fillStyle = AXIS_LABEL_COLOR.value + const xLabelWidth = context.value.measureText(axisConfigsVal.xAxisLabel).width rotateLabel(axisConfigsVal.xAxisLabel, axisInnerAttr.axisRight + axisConfigsVal.axisMargin / 2, - axisInnerAttr.axisOrigin.y - xLabelWidth - AXIS_TITLE_SPACE); + axisInnerAttr.axisOrigin.y - xLabelWidth - AXIS_TITLE_SPACE) context.value.fillText(axisConfigsVal.yAxisLabel, - axisInnerAttr.axisOrigin.x + AXIS_TITLE_SPACE, axisInnerAttr.axisTop - axisConfigsVal.axisMargin / 2); - }; + axisInnerAttr.axisOrigin.x + AXIS_TITLE_SPACE, axisInnerAttr.axisTop - axisConfigsVal.axisMargin / 2) + } const rotateLabel = (name: string, x: number, y: number) => { for (let i = 0; i < name.length; i++) { - const str = name.slice(i, i + 1).toString(); + const str = name.slice(i, i + 1).toString() if (str.match(/[A-Za-z0-9]/)) { - context.value.save(); - context.value.translate(x, y); - context.value.rotate(Math.PI / 180 * 90); - context.value.textBaseline = 'bottom'; - context.value.fillText(str, 0, 0); - context.value.restore(); - y += context.value.measureText(str).width; + context.value.save() + context.value.translate(x, y) + context.value.rotate(Math.PI / 180 * 90) + context.value.textBaseline = 'bottom' + context.value.fillText(str, 0, 0) + context.value.restore() + y += context.value.measureText(str).width } else if (str.match(/[\u4E00-\u9FA5]/)) { - context.value.save(); - context.value.textBaseline = 'top'; - context.value.fillText(str, x, y); - context.value.restore(); - y += context.value.measureText(str).width; + context.value.save() + context.value.textBaseline = 'top' + context.value.fillText(str, x, y) + context.value.restore() + y += context.value.measureText(str).width } } } @@ -218,11 +218,11 @@ export default defineComponent({ }, render() { - const { diagramId } = this; + const { diagramId } = this return (
- ); + ) } -}); \ No newline at end of file +}) \ No newline at end of file diff --git a/devui/quadrant-diagram/src/components/axis/types.ts b/devui/quadrant-diagram/src/components/axis/types.ts index e22f059e..c6c84fdd 100644 --- a/devui/quadrant-diagram/src/components/axis/types.ts +++ b/devui/quadrant-diagram/src/components/axis/types.ts @@ -1,5 +1,5 @@ import type { ExtractPropTypes, PropType } from 'vue' -import { IViewConfigs, IAxisConfigs } from '../../../type'; +import { IViewConfigs, IAxisConfigs } from '../../../type' export const quadrantDiagramAxisProps = { diagramId: { diff --git a/devui/quadrant-diagram/src/quadrant-diagram-types.ts b/devui/quadrant-diagram/src/quadrant-diagram-types.ts index abef19f7..26f9e8df 100644 --- a/devui/quadrant-diagram/src/quadrant-diagram-types.ts +++ b/devui/quadrant-diagram/src/quadrant-diagram-types.ts @@ -1,6 +1,6 @@ import type { ExtractPropTypes, PropType } from 'vue' -import { DEFAULT_AXIS_CONFIGS, DEFAULT_VIEW_CONFIGS } from '../config'; -import { IViewConfigs, IAxisConfigs } from '../type'; +import { DEFAULT_AXIS_CONFIGS, DEFAULT_VIEW_CONFIGS } from '../config' +import { IViewConfigs, IAxisConfigs } from '../type' export const quadrantDiagramProps = { diagramId: { diff --git a/devui/quadrant-diagram/src/quadrant-diagram.tsx b/devui/quadrant-diagram/src/quadrant-diagram.tsx index d04e64c9..27b01404 100644 --- a/devui/quadrant-diagram/src/quadrant-diagram.tsx +++ b/devui/quadrant-diagram/src/quadrant-diagram.tsx @@ -1,17 +1,17 @@ import { defineComponent, toRefs, reactive, watch } from 'vue' import { quadrantDiagramProps, QuadrantDiagramProps } from './quadrant-diagram-types' -import DQuadrantDiagramAxis from './components/axis'; -import { DEFAULT_AXIS_CONFIGS } from '../config'; +import DQuadrantDiagramAxis from './components/axis' +import { DEFAULT_AXIS_CONFIGS } from '../config' export default defineComponent({ name: 'DQuadrantDiagram', props: quadrantDiagramProps, emits: [], setup(props: QuadrantDiagramProps) { - const { diagramId, axisConfigs, view } = toRefs(props); + const { diagramId, axisConfigs, view } = toRefs(props) - const axisConfigsVal = axisConfigs.value; - const viewVal = view.value; + const axisConfigsVal = axisConfigs.value + const viewVal = view.value const calAxisConfig = reactive({ axisOrigin: { x: null, y: null }, @@ -26,36 +26,36 @@ export default defineComponent({ }) const initAxisData = () => { - const axisConfigKeys = Object.keys(DEFAULT_AXIS_CONFIGS); + const axisConfigKeys = Object.keys(DEFAULT_AXIS_CONFIGS) for (let i = 0; i < axisConfigKeys.length; i++) { if (calAxisConfig[axisConfigKeys[i]] === undefined) { - calAxisConfig[axisConfigKeys[i]] = DEFAULT_AXIS_CONFIGS[axisConfigKeys[i]]; + calAxisConfig[axisConfigKeys[i]] = DEFAULT_AXIS_CONFIGS[axisConfigKeys[i]] } } calAxisConfig.axisOrigin = { x: axisConfigsVal.originPosition.left, y: viewVal.height - axisConfigsVal.originPosition.bottom - }; - calAxisConfig.axisTop = axisConfigsVal.axisMargin; - calAxisConfig.axisRight = viewVal.width - axisConfigsVal.axisMargin; - calAxisConfig.axisWidth = calAxisConfig.axisRight - calAxisConfig.axisOrigin.x; - calAxisConfig.axisHeight = calAxisConfig.axisOrigin.y - calAxisConfig.axisTop; - calAxisConfig.yAxisTicksNum = axisConfigsVal.yAxisRange.max - axisConfigsVal.yAxisRange.min; - calAxisConfig.xAxisTicksNum = axisConfigsVal.xAxisRange.max - axisConfigsVal.xAxisRange.min; - calAxisConfig.xTickSpacing = calAxisConfig.axisWidth / calAxisConfig.xAxisTicksNum; - calAxisConfig.yTickSpacing = calAxisConfig.axisHeight / calAxisConfig.yAxisTicksNum; + } + calAxisConfig.axisTop = axisConfigsVal.axisMargin + calAxisConfig.axisRight = viewVal.width - axisConfigsVal.axisMargin + calAxisConfig.axisWidth = calAxisConfig.axisRight - calAxisConfig.axisOrigin.x + calAxisConfig.axisHeight = calAxisConfig.axisOrigin.y - calAxisConfig.axisTop + calAxisConfig.yAxisTicksNum = axisConfigsVal.yAxisRange.max - axisConfigsVal.yAxisRange.min + calAxisConfig.xAxisTicksNum = axisConfigsVal.xAxisRange.max - axisConfigsVal.xAxisRange.min + calAxisConfig.xTickSpacing = calAxisConfig.axisWidth / calAxisConfig.xAxisTicksNum + calAxisConfig.yTickSpacing = calAxisConfig.axisHeight / calAxisConfig.yAxisTicksNum } - initAxisData(); + initAxisData() watch(viewVal, () => { - initAxisData(); + initAxisData() }) - return { diagramId, calAxisConfig, }; + return { diagramId, calAxisConfig, } }, render() { - const { diagramId, calAxisConfig, view } = this; + const { diagramId, calAxisConfig, view } = this return (
-- Gitee From 4ed48c280681870be444a5ff026cd2d334787daa Mon Sep 17 00:00:00 2001 From: dong Date: Sun, 12 Sep 2021 20:10:50 +0800 Subject: [PATCH 07/32] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96loading=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E7=9A=84demo=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devui/loading/src/loading.scss | 1 - sites/components/loading/customStyle.scss | 20 -- sites/components/loading/index.md | 280 +++++----------------- 3 files changed, 56 insertions(+), 245 deletions(-) delete mode 100644 sites/components/loading/customStyle.scss diff --git a/devui/loading/src/loading.scss b/devui/loading/src/loading.scss index 2e9fbef5..025dc9e6 100644 --- a/devui/loading/src/loading.scss +++ b/devui/loading/src/loading.scss @@ -48,7 +48,6 @@ } .devui-loading-text { - vertical-align: super; margin-left: 10px; } diff --git a/sites/components/loading/customStyle.scss b/sites/components/loading/customStyle.scss deleted file mode 100644 index 5f89cd60..00000000 --- a/sites/components/loading/customStyle.scss +++ /dev/null @@ -1,20 +0,0 @@ -.devui-infinity-loading { - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); -} - -.devui-circle-loading-container-2 { - position: absolute; - left: 5px; - top: 50%; - transform: translateY(-50%); -} - -.devui-circle-loading-container-3 { - position: absolute; - right: -20px; - top: 50%; - transform: translateY(-50%); -} \ No newline at end of file diff --git a/sites/components/loading/index.md b/sites/components/loading/index.md index 69e75b83..89f15425 100644 --- a/sites/components/loading/index.md +++ b/sites/components/loading/index.md @@ -7,32 +7,11 @@ 当执行指令时间较长(需要数秒以上)时,向用户展示正在执行的状态。 - ### 基本用法 -展示加载表格数据的场景中的基本使用方法。 - -click me! - - - - - - - - - - - -
序号姓名队伍操作
{{index}}张家齐跳水跳水队
- -```html + +:::demo 展示加载表格数据的场景中的基本使用方法。 + +```vue - ``` +::: -### 多promise -支持多个promise。 -click me! +### 多promise -
loading will show here2
+:::demo 支持多个promise。 -```html +```vue - ``` +::: ### 自定义样式 -通过 templateRef 自定义loading样式。 - -Loading Style 1 - -Loading Style 2 - -Loading Style 3 - -
loading will show here1
- -```html +:::demo 通过 templateRef 自定义loading样式。 + +```vue - -``` -```scss -// ./customStyle.scss + + ``` +::: ### 服务方式调用 -使用服务的方式全屏加载loading组件或者在指定宿主上加载loading组件。 -click me show full screen loading! +:::demo 使用服务的方式全屏加载loading组件或者在指定宿主上加载loading组件。 -click me show loading in target! - -click me close loading in target! - -
loading will show here3
- -```html +```vue - ``` +::: ### 参数 @@ -353,118 +300,3 @@ export default defineComponent({ | view | {top?:string,left?:string} | {top: '50%', left: '50%'} | 可选,调整 loading 的显示位置,相对于宿主元素的顶部距离与左侧距离 | [基本用法](#基本用法) | | zIndex | Number | -- | 可选,loading加载提示的 z-index 值 | [基本用法](#基本用法) | - - \ No newline at end of file -- Gitee From df9fa9c5c79278f4f9fbb9d41e9a735657a72aa6 Mon Sep 17 00:00:00 2001 From: dong Date: Mon, 13 Sep 2021 22:32:03 +0800 Subject: [PATCH 08/32] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E5=AD=90=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devui/pagination/src/components/jump-page.tsx | 84 +- devui/pagination/src/components/page-nums.tsx | 2 +- devui/pagination/src/pagination.scss | 22 +- devui/pagination/src/pagination.tsx | 54 +- sites/components/loading/index.md | 113 ++- sites/components/pagination/index.md | 824 ++++++++---------- 6 files changed, 517 insertions(+), 582 deletions(-) diff --git a/devui/pagination/src/components/jump-page.tsx b/devui/pagination/src/components/jump-page.tsx index 096a56c2..a4e7e2f8 100644 --- a/devui/pagination/src/components/jump-page.tsx +++ b/devui/pagination/src/components/jump-page.tsx @@ -1,22 +1,69 @@ -import { defineComponent, PropType } from 'vue'; +import { defineComponent, PropType, ref, watch, toRefs, ExtractPropTypes } from 'vue'; + +const jumpPageProps = { + goToText: String, + size: { + type: String as PropType<'lg' | '' | 'sm'>, + default: '' + }, + pageIndex: Number, + showJumpButton: Boolean, + totalPages: Number, + cursor: Number, + onChangeCursorEmit: Function as PropType<(v: number) => void> +} + +type JumpPageProps = ExtractPropTypes export default defineComponent({ - props: { - goToText: String, - size: { - type: String as PropType<'lg' | '' | 'sm'>, - default: '' - }, - inputPageNum: Number, - jump: Function, - jumpPageChange: Function, - showJumpButton: Boolean - } as const, + props: jumpPageProps, + emits: ['changeCursorEmit'], + setup(props: JumpPageProps, { emit }) { + const { + pageIndex, + totalPages, + cursor + } = toRefs(props) + + // 输入跳转页码 + const inputNum = ref(pageIndex.value) + watch( + () => pageIndex.value, + (val: number) => { + inputNum.value = val + } + ) + let curPage = pageIndex.value + const jumpPageChange = (currentPage: number) => { + curPage = +currentPage + inputNum.value = currentPage + if (isNaN(currentPage)) { + setTimeout(() => { + inputNum.value = pageIndex.value + }, 300) + } + } + // 跳转指定页码 + const jump = (e: KeyboardEvent | 'btn') => { + if (curPage > totalPages.value) { + return + } + if ((e === 'btn' || e.key === 'Enter') && cursor.value !== curPage) { + emit('changeCursorEmit', curPage) + } + } + + return { + inputNum, + jumpPageChange, + jump + } + }, render() { const { goToText, size, - inputPageNum, + inputNum, jumpPageChange, jump, showJumpButton @@ -24,14 +71,15 @@ export default defineComponent({ return (
- {goToText} + {goToText} + /> { // TODO 加入国际化后,替换为当前语言为中文的时候加上 '页' @@ -42,7 +90,7 @@ export default defineComponent({
diff --git a/devui/pagination/src/components/page-nums.tsx b/devui/pagination/src/components/page-nums.tsx index 8ebb51d1..eaccee59 100644 --- a/devui/pagination/src/components/page-nums.tsx +++ b/devui/pagination/src/components/page-nums.tsx @@ -12,7 +12,7 @@ const pageNumBtnProps = { cursor: Number, maxItems: Number, totalPages: Number, - onChangeCursorEmit: Function, + onChangeCursorEmit: Function as PropType<(v: number) => void>, showTruePageIndex: Boolean } as const diff --git a/devui/pagination/src/pagination.scss b/devui/pagination/src/pagination.scss index fa28bfe6..c950daef 100644 --- a/devui/pagination/src/pagination.scss +++ b/devui/pagination/src/pagination.scss @@ -25,11 +25,11 @@ max-width: 100px; .devui-select-input { - height: 44px; + height: 46px; } .devui-select-item { - height: 44px; + height: 46px; } } } @@ -124,25 +124,15 @@ vertical-align: middle; align-items: center; - .devui-input { + .devui-pagination-input { display: inline-block; width: 42px; - height: 28px; vertical-align: middle; margin: 0 3px; + } - &.devui-input-sm { - height: 24px; - padding: 4px 4px; - font-size: $devui-font-size; - line-height: 1.5; - border-radius: $devui-border-radius; - } - - &.devui-input-lg { - width: 56px; - height: 46px; - } + .devui-pagination-input-lg { + width: 56px; } } diff --git a/devui/pagination/src/pagination.tsx b/devui/pagination/src/pagination.tsx index 6e7fd2e8..a27c319b 100644 --- a/devui/pagination/src/pagination.tsx +++ b/devui/pagination/src/pagination.tsx @@ -1,9 +1,7 @@ -import { defineComponent, computed, ref, nextTick } from 'vue' +import { defineComponent, computed, nextTick } from 'vue' import { ComponentProps, componentProps } from './use-pagination' import { liteSelectOptions } from './utils' -import clickoutsideDirective from '../../shared/devui-directive/clickoutside' - import ConfigMenu from './components/config-menu' import JumpPage from './components/jump-page' import PageNumBtn from './components/page-nums' @@ -12,9 +10,6 @@ import './pagination.scss' export default defineComponent({ name: 'DPagination', - directives: { - clickoutside: clickoutsideDirective - }, components: { ConfigMenu, JumpPage, @@ -41,16 +36,6 @@ export default defineComponent({ emit('update:pageIndex', val) } }) - const changePageNo = ref(props.pageIndex) - // 输入框显示的页码 - const inputPageNum = computed({ - get() { - return props.pageIndex - }, - set(val: number) { - changePageNo.value = val - } - }) // 每页显示最大条目数量 const currentPageSize = computed({ get() { @@ -65,27 +50,12 @@ export default defineComponent({ const changeCursorEmit = (val: number) => { cursor.value = val - changePageNo.value = val emit('pageIndexChange', val) } - // 输入跳转页码 - const jumpPageChange = (currentPage: string) => { - const curPage = +currentPage - if (isNaN(curPage) || curPage < 1 || curPage > totalPages.value) { - inputPageNum.value = props.pageIndex - return - } - inputPageNum.value = curPage - } - // 跳转指定页码 - const jump = (e: KeyboardEvent | 'btn') => { - if ((e === 'btn' || e.key === 'Enter') && cursor.value !== changePageNo.value) { - cursor.value = changePageNo.value - } - } + // 每页条数改变 - const pageSizeChange = (value: number) => { - currentPageSize.value = value + const pageSizeChange = (val: Record) => { + currentPageSize.value = val.value as number // 页数改变后,如果当前页码超出最大页码时修正 if (props.autoFixPageIndex) { nextTick(() => { @@ -94,7 +64,7 @@ export default defineComponent({ } }) } - emit('pageSizeChange', value) + emit('pageSizeChange', val.value as number) } // 极简模式下的跳转页码 const litePageIndexChange = (page: {name: string; value: number;}) => { @@ -104,10 +74,7 @@ export default defineComponent({ return { cursor, totalPages, - jump, changeCursorEmit, - inputPageNum, - jumpPageChange, currentPageSize, pageSizeChange, litePageOptions, @@ -118,6 +85,7 @@ export default defineComponent({ const { total, + pageIndex, pageSizeOptions, pageSizeDirection, preLink, @@ -139,9 +107,6 @@ export default defineComponent({ cursor, totalPages, - jump, - inputPageNum, - jumpPageChange, currentPageSize, pageSizeChange, changeCursorEmit, @@ -206,11 +171,12 @@ export default defineComponent({ {...{ goToText, size, - inputPageNum, - jumpPageChange, - jump, + pageIndex, + totalPages, + cursor, showJumpButton }} + onChangeCursorEmit={changeCursorEmit} /> } { diff --git a/sites/components/loading/index.md b/sites/components/loading/index.md index 89f15425..8e467f72 100644 --- a/sites/components/loading/index.md +++ b/sites/components/loading/index.md @@ -8,8 +8,8 @@ ### 基本用法 - -:::demo 展示加载表格数据的场景中的基本使用方法。 +展示加载表格数据的场景中的基本使用方法。 +:::demo ```vue