Skip to content

Commit

Permalink
feat(data): ✨ 允许拖拽
Browse files Browse the repository at this point in the history
通过配置拖拽参数,开启拖拽,可选单层或全部

#14
  • Loading branch information
jeremyjone committed Jun 12, 2023
1 parent 31527bf commit 9daa076
Show file tree
Hide file tree
Showing 24 changed files with 346 additions and 31 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,15 @@
"dependencies": {
"@types/lodash": "^4.14.192",
"@vueuse/core": "^10.0.0",
"@vueuse/integrations": "^10.1.2",
"dayjs": "^1.11.7",
"lodash": "^4.17.21",
"sortablejs": "^1.15.0",
"vue": "^3.2.47"
},
"devDependencies": {
"@changesets/cli": "^2.26.1",
"@types/sortablejs": "^1.15.1",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@vitejs/plugin-vue": "^4.1.0",
"@vitest/coverage-istanbul": "^0.31.1",
Expand Down
29 changes: 26 additions & 3 deletions src/assets/iconfont/demo_index.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" targ
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">

<li class="dib">
<span class="icon iconfont">&#xe66d;</span>
<div class="name">drag</div>
<div class="code-name">&amp;#xe66d;</div>
</li>

<li class="dib">
<span class="icon iconfont">&#xe646;</span>
<div class="name">右箭头</div>
Expand All @@ -78,9 +84,9 @@ <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</co
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1683895307749') format('woff2'),
url('iconfont.woff?t=1683895307749') format('woff'),
url('iconfont.ttf?t=1683895307749') format('truetype');
src: url('iconfont.woff2?t=1685949329577') format('woff2'),
url('iconfont.woff?t=1685949329577') format('woff'),
url('iconfont.ttf?t=1685949329577') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
Expand All @@ -106,6 +112,15 @@ <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面
<div class="content font-class">
<ul class="icon_lists dib-box">

<li class="dib">
<span class="icon iconfont icon-drag"></span>
<div class="name">
drag
</div>
<div class="code-name">.icon-drag
</div>
</li>

<li class="dib">
<span class="icon iconfont icon-arrow-right"></span>
<div class="name">
Expand Down Expand Up @@ -142,6 +157,14 @@ <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h
<div class="content symbol">
<ul class="icon_lists dib-box">

<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-drag"></use>
</svg>
<div class="name">drag</div>
<div class="code-name">#icon-drag</div>
</li>

<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-arrow-right"></use>
Expand Down
10 changes: 7 additions & 3 deletions src/assets/iconfont/iconfont.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4065195 */
src: url('iconfont.woff2?t=1683895307749') format('woff2'),
url('iconfont.woff?t=1683895307749') format('woff'),
url('iconfont.ttf?t=1683895307749') format('truetype');
src: url('iconfont.woff2?t=1685949329577') format('woff2'),
url('iconfont.woff?t=1685949329577') format('woff'),
url('iconfont.ttf?t=1685949329577') format('truetype');
}

.iconfont {
Expand All @@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale;
}

.icon-drag:before {
content: "\e66d";
}

.icon-arrow-right:before {
content: "\e646";
}
Expand Down
2 changes: 1 addition & 1 deletion src/assets/iconfont/iconfont.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/assets/iconfont/iconfont.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "15838466",
"name": "drag",
"font_class": "drag",
"unicode": "e66d",
"unicode_decimal": 58989
},
{
"icon_id": "430540",
"name": "右箭头",
Expand Down
Binary file modified src/assets/iconfont/iconfont.ttf
Binary file not shown.
Binary file modified src/assets/iconfont/iconfont.woff
Binary file not shown.
Binary file modified src/assets/iconfont/iconfont.woff2
Binary file not shown.
15 changes: 6 additions & 9 deletions src/components/column/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,14 @@ const realWidth = computed(() => {
const selectionRef = ref(null) as any;
const prefixWidth = ref(0);
onMounted(() => {
const setPrefixWidth = async () => {
await nextTick();
prefixWidth.value = selectionRef.value?.clientWidth ?? 0;
});
};
watch(
() => [$styleBox.showCheckbox, $styleBox.showExpand],
async () => {
await nextTick();
prefixWidth.value = selectionRef.value?.clientWidth ?? 0;
}
);
onMounted(setPrefixWidth);
watch(() => [$styleBox.showCheckbox, $styleBox.showExpand], setPrefixWidth);
</script>
<style lang="scss">
Expand Down
12 changes: 10 additions & 2 deletions src/components/column/selection.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
<template>
<Icon
v-if="$styleBox.draggable.draggable !== false"
name="drag"
class="drag-icon"
/>

<div class="level-block" :style="{ width: `${data!.level * indent}px` }" />

<Icon
Expand Down Expand Up @@ -43,6 +49,9 @@ const props = defineProps({
});
const { rowHeight, $styleBox } = useStyle();
const { flattenData } = useData();
// #region checkbox
const checked = ref(props.data.isChecked);
const { EmitRowChecked } = useEvent();
Expand Down Expand Up @@ -72,8 +81,7 @@ const onRightClickCheckbox = (state: boolean) => {
);
}
};
const { flattenData } = useData();
// #endregion
</script>
<style lang="scss">
Expand Down
4 changes: 4 additions & 0 deletions src/components/column/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
.prefix {
display: inline-block;

.drag-icon {
cursor: move;
}

.expand-icon {
margin-right: 4px;
cursor: pointer;
Expand Down
2 changes: 2 additions & 0 deletions src/components/common/Icon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ const icon = computed(() => `icon-${props.name}`);
color: #999;
display: inline-block;
font-size: 14px;
width: 1em;
height: 1em;
}
</style>
24 changes: 19 additions & 5 deletions src/components/container/Row.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,27 @@
// 'xg-row__select':
// props.renderStyle && $param.selectItem?.uuid === props.data?.uuid
// },
{
'xg-row__ghost':
props.renderStyle &&
$param.moveStartItem &&
$param.moveStartItem.uuid === props.data?.uuid
},
{
'xg-row__drag-chosen':
props.renderStyle &&
$param.moveHoverItem &&
$param.moveHoverItem.uuid === props.data?.uuid
},
{ 'xg-row__only': !props.renderStyle }
]"
:style="{
top: `${(props.data?.flatIndex ?? 0) * rowHeight}px`,
height: `${rowHeight}px`,
borderWidth: props.renderStyle ? '1px' : 0,
color: $styleBox.headerStyle?.textColor,
'--color': $styleBox.headerStyle?.textColor,
// backgroundColor: props.renderStyle ? ($styleBox.levelColor[props.data!.level] || $styleBox.headerStyle?.bgColor) : undefined,
backgroundColor: bgColor,
'--backgroundColor': bgColor,
'border-color': $styleBox.borderColor
}"
@mouseenter.capture="onEnter"
Expand Down Expand Up @@ -107,17 +119,19 @@ onUnifyClick(rowRef, {
overflow: hidden;
border-bottom: 1px solid #e5e5e5;
box-sizing: border-box;
color: var(--color);
background-color: var(--backgroundColor);
}
.xg-row__only {
pointer-events: none;
}
.xg-row.xg-row__hover {
background-color: #f0f0f0aa !important;
background-color: #f0f0f0aa;
}
.xg-row__select {
background-color: #e0e0e0aa !important;
.xg-row.xg-row__select {
background-color: #e0e0e0aa;
}
</style>
119 changes: 115 additions & 4 deletions src/components/container/TableBody.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="xg-table-body" :style="{ height: bodyHeight }">
<template v-for="d in inView" :key="d.uuid">
<div ref="tableBodyRef" class="xg-table-body" :style="{ height: bodyHeight }">
<template v-for="d in inView" :key="d.id">
<RowVue class="xg-table-row" :data="d">
<template v-for="(c, i) in $slotsBox.cols" :key="`${d.uuid}_${i}`">
<component :is="c" :data="d" />
Expand All @@ -18,25 +18,136 @@
</template>

<script lang="ts" setup>
import { reactive, ref, Ref, watchEffect, WatchStopHandle } from 'vue';
import useInView from '@/composables/useInView';
import useSlotsBox from '@/composables/useSlotsBox';
import useStyle from '@/composables/useStyle';
import { useMouseInElement } from '@vueuse/core';
import { useSortable } from '@vueuse/integrations/useSortable';
import RowVue from './Row.vue';
import useData from '@/composables/useData';
import useParam from '@/composables/useParam';
type UseMouseInElement = ReturnType<typeof useMouseInElement>;
const props = defineProps<{ gap: number }>();
const { bodyHeight } = useStyle();
const { bodyHeight, rowHeight, $styleBox } = useStyle();
const { inView } = useInView();
const { $slotsBox } = useSlotsBox();
// #region 拖拽 row
const { $data } = useData();
const { $param } = useParam();
const tableBodyRef = ref(null) as Ref<HTMLElement | null>;
let mouse = null as UseMouseInElement | null;
let mouseWatch: WatchStopHandle;
useSortable(tableBodyRef, [], {
handle: '.drag-icon',
draggable: '.xg-row',
dragClass: 'xg-row-dragging',
dragoverBubble: true,
onStart: function (e) {
if (!e.item.classList.contains('xg-row')) return;
const flatIndex = Math.ceil(e.item.offsetTop / rowHeight.value);
$param.moveStartItem = $data.flatData[flatIndex];
mouse = reactive(
useMouseInElement(tableBodyRef)
) as unknown as UseMouseInElement;
mouseWatch = watchEffect(() => {
const y = ref(mouse?.elementY);
if (typeof y.value === 'number') {
const flatIndex = Math.floor(y.value / rowHeight.value);
const data = $data.flatData[flatIndex];
if ($param.moveHoverItem?.uuid !== data?.uuid) {
if (
!(
$param.moveHoverItem &&
$styleBox.draggable.level === 'current' &&
$param.moveHoverItem.level !== data?.level
)
) {
$param.moveHoverItem = data;
}
}
}
});
},
onEnd: function (e) {
if (e.item.classList.contains('xg-row__ghost')) {
e.item.parentElement?.removeChild(e.item);
}
const moveLastData = $param.moveHoverItem;
const originData = $param.moveStartItem;
$param.moveStartItem = null;
$param.moveHoverItem = null;
mouse?.stop();
mouseWatch?.();
if (!moveLastData || !originData || moveLastData.id === originData.id)
return;
$data.swap(moveLastData, originData);
}
});
// #endregion
</script>

<style lang="scss">
.xg-table-body {
width: 100%;
position: relative;
.xg-table-row {
.xg-table-row.xg-row.xg-row-dragging {
background-color: var(--primary-color);
border: 1px dashed #ccc;
border-radius: 4px;
box-shadow: 0 0 10px #ccc;
cursor: grabbing;
}
}
.xg-row.xg-row__ghost {
background-color: var(--primary-color);
opacity: 0.5;
transition: all 0.3s ease;
}
.xg-row.xg-row__drag-chosen {
background-color: green;
transition: all 0.3s ease;
animation: bling 1s forwards;
}
@keyframes bling {
0% {
opacity: 0.3;
}
40% {
opacity: 0.3;
}
55% {
opacity: 0.8;
}
70% {
opacity: 0.3;
}
85% {
opacity: 0.8;
}
100% {
opacity: 0.3;
}
}
</style>
Loading

0 comments on commit 9daa076

Please sign in to comment.