refactor(scrollbar): 使用ts重构scrollbar组件

This commit is contained in:
vben 2020-08-25 13:26:21 +08:00
parent 1ff8693ced
commit d1ff6727a1
24 changed files with 544 additions and 529 deletions

View File

@ -275,7 +275,7 @@ yarn log # 生成CHANGELOG
- [x] 数据导入导出
- [x] 树组件
- [x] 可编辑表格
- [ ] 系统性能优化
- [x] 系统性能优化
- [ ] 黑暗主题
- [ ] 界面 ui 升级优化
- [ ] 兼容最新`vuex`,`vue-router`,

View File

@ -16,112 +16,114 @@ const resolve = require('../../build/getCwdPath');
function configOptimization(config) {
// 只为方便注释 true 这样写没意义
// config.when(true || isProductionFn(), (config) => {
// createScriptExtPlugin(config);
config.when(isProductionFn(), (config) => {
// createScriptExtPlugin(config);
config.optimization.runtimeChunk({
name: (entry) => `_r-${entry.name}`,
});
// config.optimization.runtimeChunk('single');
config.optimization.splitChunks({
chunks: 'all',
maxAsyncRequests: 6, //分割后按需加载的代码块最多允许的并行请求数在webpack5里默认值变为6
maxInitialRequests: 5, //分割后入口代码块最多允许的并行请求数在webpack5里默认值变为4
// maxInitialRequests: Infinity,
automaticNameMaxLength: 15, // 分割chunk自动命名最大长度
automaticNameDelimiter: '.', // 分割chunk自动命名分隔符
minSize: 30000, // 大小超过30kb的模块才会被提取
maxSize: 0, // 只是提示可以被违反会尽量将chunk分的比maxSize小当设为0代表能分则分分不了不会强制
minChunks: 1, //某个模块至少被多少代码块引用才会被提取成新的chunk
name: true, //每个缓存组打包得到的代码块的名称
cacheGroups: {
default: false,
// light: {
// name: 'theme-light',
// test: (m, c) => {
// return (
// m.constructor.name === 'CssModule' &&
// new RegExp('-light.less|theme=light').test(m._identifier)
// );
// },
// chunks: 'all',
// enforce: true,
// priority: 40,
// },
// dark: {
// name: 'theme-dark',
// test: (m, c) => {
// return (
// m.constructor.name === 'CssModule' &&
// new RegExp('-dark.less|theme=dark').test(m._identifier)
// );
// },
// chunks: 'all',
// enforce: true,
// priority: 40,
// },
// styles: {
// name: 'styles',
// test: /\.(css|scss|sass|less|styl)$/,
// chunks: 'async',
// enforce: true,
// priority: 30,
// },
commons: {
name: 'commons.chunk',
test: resolve('src/components'),
minChunks: 2,
priority: 20,
reuseExistingChunk: true,
},
vendor: {
priority: 10,
reuseExistingChunk: true,
test: /[\\/]node_modules[\\/]/,
name(module) {
const match = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)(.*?)([\\/]|$)/);
let packageName = match[1];
// if (match[1] === '@xxx') {
// packageName += `-${match[3]}`;
// }
if (
packageName.indexOf('axios') !== -1 ||
packageName.indexOf('babel') !== -1 ||
packageName.indexOf('crypto-js') !== -1 ||
packageName.indexOf('moment') !== -1 ||
packageName.indexOf('lodash') !== -1 ||
packageName.indexOf('mutationobserver') !== -1 ||
packageName.indexOf('resize-observer-polyfill') !== -1 ||
packageName.indexOf('vue') !== -1 ||
packageName.indexOf('dom-align') !== -1 ||
packageName.indexOf('async-validator') !== -1 ||
packageName.indexOf('readable-stream') !== -1 ||
packageName.indexOf('browserify-sign') !== -1 ||
packageName.indexOf('regenerator-runtime') !== -1 ||
packageName.indexOf('core-js') !== -1
) {
packageName = 'entry-lib';
} else if (packageName.indexOf('ant-design') !== -1) {
packageName = 'design';
} else if (
packageName.indexOf('echarts') !== -1 ||
packageName.indexOf('zrender') !== -1 ||
packageName.indexOf('vue-baidu-map') !== -1
) {
packageName = 'chart';
} else if (packageName.indexOf('xlsx') !== -1) {
packageName = 'xlsx';
}
// else {
// packageName = 'vendor';
// }
// const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `${packageName.replace('@', '')}.chunk`;
config.optimization.runtimeChunk({
name: (entry) => `_r-${entry.name}`,
});
// config.optimization.runtimeChunk('single');
config.optimization.splitChunks({
chunks: 'all',
maxAsyncRequests: 6, //分割后按需加载的代码块最多允许的并行请求数在webpack5里默认值变为6
maxInitialRequests: 5, //分割后入口代码块最多允许的并行请求数在webpack5里默认值变为4
// maxInitialRequests: Infinity,
automaticNameMaxLength: 15, // 分割chunk自动命名最大长度
automaticNameDelimiter: '.', // 分割chunk自动命名分隔符
minSize: 30000, // 大小超过30kb的模块才会被提取
maxSize: 0, // 只是提示可以被违反会尽量将chunk分的比maxSize小当设为0代表能分则分分不了不会强制
minChunks: 1, //某个模块至少被多少代码块引用才会被提取成新的chunk
name: true, //每个缓存组打包得到的代码块的名称
cacheGroups: {
default: false,
// light: {
// name: 'theme-light',
// test: (m, c) => {
// return (
// m.constructor.name === 'CssModule' &&
// new RegExp('-light.less|theme=light').test(m._identifier)
// );
// },
// chunks: 'all',
// enforce: true,
// priority: 40,
// },
// dark: {
// name: 'theme-dark',
// test: (m, c) => {
// return (
// m.constructor.name === 'CssModule' &&
// new RegExp('-dark.less|theme=dark').test(m._identifier)
// );
// },
// chunks: 'all',
// enforce: true,
// priority: 40,
// },
// styles: {
// name: 'styles',
// test: /\.(css|scss|sass|less|styl)$/,
// chunks: 'async',
// enforce: true,
// priority: 30,
// },
commons: {
name: 'commons.chunk',
test: resolve('src/components'),
minChunks: 2,
priority: 20,
reuseExistingChunk: true,
},
vendor: {
priority: 10,
reuseExistingChunk: true,
test: /[\\/]node_modules[\\/]/,
name(module) {
const match = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)(.*?)([\\/]|$)/
);
let packageName = match[1];
// if (match[1] === '@xxx') {
// packageName += `-${match[3]}`;
// }
if (
packageName.indexOf('axios') !== -1 ||
packageName.indexOf('babel') !== -1 ||
packageName.indexOf('crypto-js') !== -1 ||
packageName.indexOf('moment') !== -1 ||
packageName.indexOf('lodash') !== -1 ||
packageName.indexOf('mutationobserver') !== -1 ||
packageName.indexOf('resize-observer-polyfill') !== -1 ||
packageName.indexOf('vue') !== -1 ||
packageName.indexOf('dom-align') !== -1 ||
packageName.indexOf('async-validator') !== -1 ||
packageName.indexOf('readable-stream') !== -1 ||
packageName.indexOf('browserify-sign') !== -1 ||
packageName.indexOf('regenerator-runtime') !== -1 ||
packageName.indexOf('core-js') !== -1
) {
packageName = 'entry-lib';
} else if (packageName.indexOf('ant-design') !== -1) {
packageName = 'design';
} else if (
packageName.indexOf('echarts') !== -1 ||
packageName.indexOf('zrender') !== -1 ||
packageName.indexOf('vue-baidu-map') !== -1
) {
packageName = 'chart';
} else if (packageName.indexOf('xlsx') !== -1) {
packageName = 'xlsx';
}
// else {
// packageName = 'vendor';
// }
// const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `${packageName.replace('@', '')}.chunk`;
},
},
},
},
});
});
// });
}
module.exports = {

View File

@ -11,7 +11,8 @@
<script lang="tsx">
// component
import { defineComponent, PropOptions, ref, unref } from 'compatible-vue';
import { defineComponent, PropOptions, ref, unref, nextTick } from 'compatible-vue';
import { Scrollbar } from '@/components/scrollbar';
// hook
import { useDesign } from '@/hooks/core/useDesign';
@ -20,6 +21,7 @@
import { TypeEnum } from './types';
export default defineComponent({
name: 'ScrollContainer',
components: { Scrollbar },
props: {
//
type: {
@ -33,12 +35,14 @@
const scrollbarRef = ref<any>(null);
function scrollTo(to: number, duration = 500) {
const { start } = useScrollTo({
el: unref(unref(scrollbarRef).wrap),
to,
duration,
nextTick(() => {
const { start } = useScrollTo({
el: unref(unref(scrollbarRef).wrap),
to,
duration,
});
start();
});
start();
}
function getScrollWrap() {
@ -46,12 +50,14 @@
}
function scrollBottom() {
const scrollHeight = unref(scrollbarRef).wrap.scrollHeight as number;
const { start } = useScrollTo({
el: unref(unref(scrollbarRef).wrap),
to: scrollHeight,
nextTick(() => {
const scrollHeight = unref(scrollbarRef).wrap.scrollHeight as number;
const { start } = useScrollTo({
el: unref(unref(scrollbarRef).wrap),
to: scrollHeight,
});
start();
});
start();
}
return {
prefixCls,

View File

@ -1,71 +0,0 @@
.el-scrollbar {
position: relative;
overflow: hidden;
}
.el-scrollbar:active > .el-scrollbar__bar,
.el-scrollbar:focus > .el-scrollbar__bar,
.el-scrollbar:hover > .el-scrollbar__bar {
opacity: 1;
-webkit-transition: opacity 340ms ease-out;
transition: opacity 340ms ease-out;
}
.el-scrollbar__wrap {
height: 100%;
overflow: scroll;
}
.el-scrollbar__wrap--hidden-default {
scrollbar-width: none;
}
.el-scrollbar__wrap--hidden-default::-webkit-scrollbar {
width: 0;
height: 0;
}
.el-scrollbar__thumb {
position: relative;
display: block;
width: 0;
height: 0;
cursor: pointer;
background-color: rgba(144, 147, 153, 0.3);
border-radius: inherit;
-webkit-transition: 0.3s background-color;
transition: 0.3s background-color;
}
.el-scrollbar__thumb:hover {
background-color: rgba(144, 147, 153, 0.5);
}
.el-scrollbar__bar {
position: absolute;
right: 2px;
bottom: 2px;
z-index: 1;
border-radius: 4px;
opacity: 0;
-webkit-transition: opacity 120ms ease-out;
transition: opacity 120ms ease-out;
}
.el-scrollbar__bar.is-vertical {
top: 2px;
width: 6px;
}
.el-scrollbar__bar.is-vertical > div {
width: 100%;
}
.el-scrollbar__bar.is-horizontal {
left: 2px;
height: 6px;
}
.el-scrollbar__bar.is-horizontal > div {
height: 100%;
}

View File

@ -1,12 +0,0 @@
/**
* copy from element-ui
*/
import Scrollbar from './src/main';
import './index.css';
/* istanbul ignore next */
Scrollbar.install = function (Vue) {
Vue.component('Scrollbar', Scrollbar);
};
export default Scrollbar;

View File

@ -0,0 +1,5 @@
/**
* copy from element-ui
*/
export { default as Scrollbar } from './src/main';

View File

@ -1,100 +0,0 @@
import { renderThumbStyle, BAR_MAP, on, off } from './util';
import { defineComponent } from 'compatible-vue';
/* istanbul ignore next */
export default defineComponent({
name: 'Bar',
props: {
vertical: Boolean,
size: String,
move: Number,
},
watch: {
size(size) {
// @ts-ignore
this.$el.style.display = size && size !== '0' ? '' : 'none';
},
},
computed: {
bar() {
return BAR_MAP[this.vertical ? 'vertical' : 'horizontal'];
},
wrap() {
return this.$parent.wrap;
},
},
render(h) {
const { size, move, bar } = this;
return (
<div class={['el-scrollbar__bar', 'is-' + bar.key]} onMousedown={this.clickTrackHandler}>
<div
ref="thumb"
class="el-scrollbar__thumb"
onMousedown={this.clickThumbHandler}
style={renderThumbStyle({ size, move, bar })}
></div>
</div>
);
},
methods: {
clickThumbHandler(e) {
// prevent click event of right button
if (e.ctrlKey || e.button === 2) {
return;
}
this.startDrag(e);
this[this.bar.axis] =
e.currentTarget[this.bar.offset] -
(e[this.bar.client] - e.currentTarget.getBoundingClientRect()[this.bar.direction]);
},
clickTrackHandler(e) {
const offset = Math.abs(
e.target.getBoundingClientRect()[this.bar.direction] - e[this.bar.client]
);
const thumbHalf = this.$refs.thumb[this.bar.offset] / 2;
const thumbPositionPercentage = ((offset - thumbHalf) * 100) / this.$el[this.bar.offset];
this.wrap[this.bar.scroll] = (thumbPositionPercentage * this.wrap[this.bar.scrollSize]) / 100;
},
startDrag(e) {
e.stopImmediatePropagation();
this.cursorDown = true;
on(document, 'mousemove', this.mouseMoveDocumentHandler);
on(document, 'mouseup', this.mouseUpDocumentHandler);
document.onselectstart = () => false;
},
mouseMoveDocumentHandler(e) {
if (this.cursorDown === false) return;
const prevPage = this[this.bar.axis];
if (!prevPage) return;
const offset =
(this.$el.getBoundingClientRect()[this.bar.direction] - e[this.bar.client]) * -1;
const thumbClickPosition = this.$refs.thumb[this.bar.offset] - prevPage;
const thumbPositionPercentage =
((offset - thumbClickPosition) * 100) / this.$el[this.bar.offset];
this.wrap[this.bar.scroll] = (thumbPositionPercentage * this.wrap[this.bar.scrollSize]) / 100;
},
mouseUpDocumentHandler() {
this.cursorDown = false;
this[this.bar.axis] = 0;
off(document, 'mousemove', this.mouseMoveDocumentHandler);
document.onselectstart = null;
},
},
destroyed() {
off(document, 'mouseup', this.mouseUpDocumentHandler);
},
});

View File

@ -0,0 +1,112 @@
import { renderThumbStyle, BAR_MAP } from './util';
import {
defineComponent,
PropOptions,
computed,
unref,
inject,
Ref,
reactive,
ref,
onBeforeUnmount,
} from 'compatible-vue';
import { on, off } from '@/utils/domUtils';
export default defineComponent({
name: 'Bar',
props: {
vertical: Boolean as PropOptions<boolean>,
size: String as PropOptions<string>,
move: Number as PropOptions<number>,
},
setup(props) {
const thumbRef = ref<Nullable<HTMLDivElement>>(null);
const elRef = ref<Nullable<HTMLDivElement>>(null);
const commonState = reactive<KeyString>({});
const getBarRef = computed(() => {
return BAR_MAP[props.vertical ? 'vertical' : 'horizontal'];
});
const parentElRef = inject('scroll-bar-wrap') as Ref<Nullable<HTMLDivElement>>;
function clickThumbHandler(e: MouseEvent & ChangeEvent) {
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] -
((currentTarget as unknown) as Element).getBoundingClientRect()[bar.direction]);
}
function clickTrackHandler(e: ChangeEvent) {
const bar = unref(getBarRef);
const offset = Math.abs(e.target.getBoundingClientRect()[bar.direction] - e[bar.client]);
const thumbEl = unref(thumbRef);
const parentEl = unref(parentElRef);
const el = unref(elRef);
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: ChangeEvent) {
if (commonState.cursorDown === false) return;
const bar = unref(getBarRef);
const prevPage = commonState[bar.axis];
const el = unref(elRef);
const parentEl = unref(parentElRef);
const thumbEl = unref(thumbRef);
if (!prevPage || !el || !thumbEl || !parentEl) return;
const offset = (el.getBoundingClientRect()[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>
);
};
},
});

View File

@ -0,0 +1,69 @@
.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 120ms ease-out;
transition: opacity 120ms ease-out;
&.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 280ms ease-out;
}

View File

@ -1,138 +0,0 @@
// reference https://github.com/noeldelgado/gemini-scrollbar/blob/master/index.js
import { addResizeListener, removeResizeListener } from './resize-event';
import scrollbarWidth from './scrollbar-width';
import { toObject } from './util';
import Bar from './bar';
/* istanbul ignore next */
export default {
name: 'Scrollbar',
components: { Bar },
props: {
native: Boolean,
wrapStyle: {},
wrapClass: {},
viewClass: {},
viewStyle: {},
noresize: Boolean, // 如果 container 尺寸不会发生变化,最好设置它可以优化性能
tag: {
type: String,
default: 'div',
},
},
data() {
return {
sizeWidth: '0',
sizeHeight: '0',
moveX: 0,
moveY: 0,
};
},
computed: {
wrap() {
return this.$refs.wrap;
},
},
render(h) {
const gutter = scrollbarWidth();
let style = this.wrapStyle;
if (gutter) {
const gutterWith = `-${gutter}px`;
const gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};`;
if (Array.isArray(this.wrapStyle)) {
style = toObject(this.wrapStyle);
style.marginRight = style.marginBottom = gutterWith;
} else if (typeof this.wrapStyle === 'string') {
style += gutterStyle;
} else {
style = gutterStyle;
}
}
const view = h(
this.tag,
{
class: ['el-scrollbar__view', this.viewClass],
style: this.viewStyle,
ref: 'resize',
},
this.$slots.default
);
const wrap = (
<div
ref="wrap"
style={style}
onScroll={this.handleScroll}
class={[
this.wrapClass,
'el-scrollbar__wrap',
gutter ? '' : 'el-scrollbar__wrap--hidden-default',
]}
>
{[view]}
</div>
);
let nodes;
if (!this.native) {
nodes = [
wrap,
/* eslint-disable */
<Bar move={this.moveX} size={this.sizeWidth}></Bar>,
<Bar vertical move={this.moveY} size={this.sizeHeight}></Bar>,
];
} else {
nodes = [
<div ref="wrap" class={[this.wrapClass, 'el-scrollbar__wrap']} style={style}>
{[view]}
</div>,
];
}
return h('div', { class: 'el-scrollbar' }, nodes);
},
methods: {
handleScroll() {
const wrap = this.wrap;
this.moveY = (wrap.scrollTop * 100) / wrap.clientHeight;
this.moveX = (wrap.scrollLeft * 100) / wrap.clientWidth;
},
update() {
let heightPercentage, widthPercentage;
const wrap = this.wrap;
if (!wrap) return;
heightPercentage = (wrap.clientHeight * 100) / wrap.scrollHeight;
widthPercentage = (wrap.clientWidth * 100) / wrap.scrollWidth;
this.sizeHeight = heightPercentage < 100 ? heightPercentage + '%' : '';
this.sizeWidth = widthPercentage < 100 ? widthPercentage + '%' : '';
},
},
mounted() {
if (this.native) return;
this.$nextTick(this.update);
if (!this.noresize) {
addResizeListener(this.$refs.resize, this.update);
addResizeListener(this.$refs.wrap, this.update);
}
},
beforeDestroy() {
if (this.native) return;
if (!this.noresize) {
removeResizeListener(this.$refs.resize, this.update);
removeResizeListener(this.$refs.wrap, this.update);
}
},
};

View File

@ -0,0 +1,144 @@
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/index';
import './index.less';
import {
defineComponent,
PropOptions,
unref,
reactive,
ref,
provide,
onMounted,
nextTick,
onBeforeUnmount,
getCurrentInstance,
} from 'compatible-vue';
import { getSlot } from '@/utils/helper/tsxHelper';
export default defineComponent({
name: 'Scrollbar',
props: {
native: Boolean as PropOptions<boolean>,
wrapStyle: {} as PropOptions<any>,
wrapClass: {} as PropOptions<any>,
viewClass: {} as PropOptions<any>,
viewStyle: {} as PropOptions<any>,
noresize: Boolean as PropOptions<boolean>, // 如果 container 尺寸不会发生变化,最好设置它可以优化性能
tag: {
type: String,
default: 'div',
} as PropOptions<string>,
},
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(() => {
const currentInstance = getCurrentInstance() as any;
if (currentInstance) {
currentInstance.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 = 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>;
};
},
});

View File

@ -1,35 +0,0 @@
import ResizeObserver from 'resize-observer-polyfill';
const isServer = typeof window === 'undefined';
/* istanbul ignore next */
const resizeHandler = function (entries) {
for (const entry of entries) {
const listeners = entry.target.__resizeListeners__ || [];
if (listeners.length) {
listeners.forEach((fn) => {
fn();
});
}
}
};
/* istanbul ignore next */
export const addResizeListener = function (element, fn) {
if (isServer) return;
if (!element.__resizeListeners__) {
element.__resizeListeners__ = [];
element.__ro__ = new ResizeObserver(resizeHandler);
element.__ro__.observe(element);
}
element.__resizeListeners__.push(fn);
};
/* istanbul ignore next */
export const removeResizeListener = function (element, fn) {
if (!element || !element.__resizeListeners__) return;
element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
if (!element.__resizeListeners__.length) {
element.__ro__.disconnect();
}
};

View File

@ -0,0 +1,14 @@
export interface BarMapItem {
offset: string;
scroll: string;
scrollSize: string;
size: string;
key: string;
axis: string;
client: string;
direction: string;
}
export interface BarMap {
vertical: BarMapItem;
horizontal: BarMapItem;
}

View File

@ -1,4 +1,5 @@
export const BAR_MAP = {
import { BarMap } from './types';
export const BAR_MAP: BarMap = {
vertical: {
offset: 'offsetHeight',
scroll: 'scrollTop',
@ -22,7 +23,7 @@ export const BAR_MAP = {
};
export function renderThumbStyle({ move, size, bar }) {
const style = {};
const style = {} as any;
const translate = `translate${bar.axis}(${move}%)`;
style[bar.size] = size;
@ -32,32 +33,13 @@ export function renderThumbStyle({ move, size, bar }) {
return style;
}
/* istanbul ignore next */
export const on = (function () {
return function (element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
};
})();
/* istanbul ignore next */
export const off = (function () {
return function (element, event, handler) {
if (element && event) {
element.removeEventListener(event, handler, false);
}
};
})();
function extend(to, _from) {
for (const key in _from) {
to[key] = _from[key];
}
return to;
function extend<T, K>(to: T, _from: K): T & K {
return Object.assign(to, _from);
}
export function toObject(arr) {
var res = {};
export function toObject<T>(arr: Array<T>): Record<string, T> {
const res = {};
for (let i = 0; i < arr.length; i++) {
if (arr[i]) {
extend(res, arr[i]);

View File

@ -278,7 +278,7 @@
compact: true,
};
return (
<div class={prefixCls}>
<div class={[prefixCls, useSearchForm && 'table-form-container']}>
{useSearchForm && (
<BasicForm
submitButtonOptions={{ loading: unref(loadingRef) }}
@ -446,4 +446,31 @@
}
}
}
.table-form-container {
& > div {
padding: 16px;
}
.ant-form {
padding: 12px 12px 4px 12px;
margin-bottom: 12px;
background: #fff;
border-radius: 2px;
}
.ant-table-wrapper {
padding: 12px;
background: #fff;
border-radius: 2px;
.ant-table-title {
padding: 0 0 10px 0 !important;
}
.ant-table.ant-table-bordered .ant-table-title {
border: none !important;
}
}
}
</style>

View File

@ -1,22 +0,0 @@
.table-form-container {
.ant-form {
padding: 12px 12px 4px 12px;
margin-bottom: 12px;
background: #fff;
border-radius: 2px;
}
.ant-table-wrapper {
padding: 12px;
background: #fff;
border-radius: 2px;
.ant-table-title {
padding: 0 0 10px 0 !important;
}
.ant-table.ant-table-bordered .ant-table-title {
border: none !important;
}
}
}

View File

@ -1,5 +1,4 @@
@import './scrollbar.less';
@import './formTable.less';
// 标题样式
.common-title {

View File

@ -2,7 +2,7 @@ import VueCompositionAPI, { VueConstructor } from 'compatible-vue';
import setupSvgIcon from '@/assets/icons/setupSvgIcon';
import Scrollbar from '@/components/scrollbar';
// import Scrollbar from '@/components/scrollbar';
import '@/setup/ant-design-vue/index';
import '@/setup/ant-design-vue/spin';
@ -16,7 +16,7 @@ export default {
Vue.use(VueCompositionAPI);
// Vue.use(RunTimeVue);
Vue.use(Scrollbar);
// Vue.use(Scrollbar);
// 使用svg sprit
setupSvgIcon();
// error handler

View File

@ -129,6 +129,12 @@ class Tab extends VuexModule implements TabState {
this.keepAliveTabsState = names as string[];
}
@Mutation
commitResetState(): void {
this.tabsState = [];
this.keepAliveTabsState = [];
}
@Action({ rawError: true })
closeLeftTabAction(route: RouteEx | TabItem): void {
const index = this.tabsState.findIndex((item) => item.path === route.path);

View File

@ -14,6 +14,7 @@ import { routerInstance } from '@/router/index';
import { useMessage } from '@/hooks/core/useMessage';
import { permissionStore } from './permission';
import { tabStore } from './tab';
import { loginApi, getUserInfoById } from '@/api/sys/user';
import {
@ -111,6 +112,7 @@ class User extends VuexModule implements UserState {
clearSession();
permissionStore.commitHasRouteState(false);
permissionStore.commitReset();
tabStore.commitResetState();
this.resetState();
goLogin && getRouteInstance && getRouteInstance().replace(pageEnum.BASE_LOGIN);
}

View File

@ -38,3 +38,7 @@ declare type CustomizedHTMLElement<T> = HTMLElement & T;
declare type Indexable<T> = {
[key: string]: T;
};
declare type KeyString<T = any> = {
[key: string]: T;
};

View File

@ -127,3 +127,25 @@ export function hackCss(attr: string, value: string) {
[attr]: value,
};
}
/* istanbul ignore next */
export const on = function (
element: HTMLElement | Document | Window,
event: string,
handler: EventListenerOrEventListenerObject
): void {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
};
/* istanbul ignore next */
export const off = function (
element: HTMLElement | Document | Window,
event: string,
handler: EventListenerOrEventListenerObject
): void {
if (element && event && handler) {
element.removeEventListener(event, handler, false);
}
};

View File

@ -1,9 +1,9 @@
import { Vue } from 'compatible-vue';
import { isWindow } from './is/index';
let scrollBarWidth;
let scrollBarWidth: number;
export default function () {
if (Vue.prototype.$isServer) return 0;
export default function (): number {
if (!isWindow) return 0;
if (scrollBarWidth !== undefined) return scrollBarWidth;
const outer = document.createElement('div');
@ -22,9 +22,8 @@ export default function () {
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
if (outer && outer.parentNode) {
outer.parentNode.removeChild(outer);
}
const parentNode = outer.parentNode;
parentNode && parentNode.removeChild(outer);
scrollBarWidth = widthNoScroll - widthWithScroll;
return scrollBarWidth;

View File

@ -41,7 +41,7 @@
setup() {
const [register] = useTable();
return () => (
<div class="table-form-container">
<div>
<div>
<BasicTable
useSearchForm