Skip to content

Commit

Permalink
feat: 局部滚动列表
Browse files Browse the repository at this point in the history
  • Loading branch information
shiouhoo committed Apr 27, 2024
1 parent ffc329e commit b6cc1b6
Show file tree
Hide file tree
Showing 19 changed files with 376 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
es2021: true,
node: true,
},
ignorePatterns: ['!.vitepress', 'cache', 'useComponents.js'],
ignorePatterns: ['!.vitepress', 'cache', 'dist', 'useComponents.js'],
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
Expand Down
14 changes: 10 additions & 4 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,21 @@ export default defineConfig({
{
text: '输入组件',
items: [
{ text: 'cascader-radio', link: '/package/cascader-radio' },
{ text: 'expandcontainer', link: '/package/expandcontainer' },
{ text: 'searchscopeinput', link: '/package/searchscopeinput' },
{ text: 'CascaderRadio 单选级联选择器', link: '/package/cascader-radio' },
{ text: 'ExpandContainer 展开收起容器', link: '/package/expandcontainer' },
{ text: 'SearchScopeInput 下拉输入搜索框', link: '/package/searchscopeinput' },
]
},
{
text: '展示组件',
items: [
{ text: 'ScopeList 局部滚动列表', link: '/package/scope-list' },
]
},
{
text: '弹窗',
items: [
{ text: 'bottompopup', link: '/package/bottompopup' },
{ text: 'BottomPopup 底部弹窗', link: '/package/bottompopup' },
]
}
],
Expand Down
133 changes: 133 additions & 0 deletions docs/package/scope-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
outline: [2,3]
---

# ScopeList

## 局部滚动列表

滚动加载,用于展示长列表,当列表即将滚动到底部时,会触发事件并加载更多列表项。
<br/>
同vant的List组件不同的是,ScopeList是一个`局部滚动`的组件。

## 源文件

<div class="un-prefer-unocss"></div>

[ScopeList.vue](https://github.com/shiouhoo/hooui/blob/main/src/package/scope-list/Index.vue)

<div class="prefer-unocss"></div>

[ScopeList.vue](https://github.com/shiouhoo/hooui/blob/main/src/package/scope-list/Unocss.vue)

## 示例

### 基本使用

和vant的list组件一样,通过 loading 和 finished 两个变量控制加载状态,当组件滚动到底部时,会触发 load 事件并将 loading 设置成 true。此时可以发起异步操作并更新数据,数据更新完毕后,将 loading 设置成 false 即可。若数据已全部加载完毕,则直接将 finished 设置成 true 即可。

:::demo

```vue
<template>
<ScopeList
v-model:loading="loading"
:finished="finished"
@load="load"
class="wrapper"
>
<div v-for="(item,index) in list" :key="index">
<div class="item">{{ item }}</div>
</div>
</ScopeList>
</template>
<script setup lang="ts">
const loading = ref(false);
const finished = ref(false);
const list = ref<number[]>([]);
function load() {
// 异步更新数据
// setTimeout 仅做示例,真实场景中一般为 ajax 请求
setTimeout(() => {
for (let i = 0;i < 10;i++) {
list.value.push(list.value.length + 1);
}
// 加载状态结束
loading.value = false;
// 数据全部加载完成
if (list.value.length >= 20) {
finished.value = true;
}
}, 1000);
}
</script>
<style scoped lang="less">
.wrapper{
width: 400px;
height: 300px;
background-color: #f5f5f5;
.item{
display: flex;
justify-content: center;
align-items: center;
margin: 1rem 0;
}
}
</style>
```
:::


## props 参数

<script setup lang="ts">

const data = [
{
name: 'loading(v-model)',
desc: '是否处于加载状态,加载过程中不触发 load 事件',
type: 'boolean',
defaultValue: 'false',
},
{
name: 'finished',
desc: '是否已加载完成,加载完成后不再触发 load 事件',
type: 'boolean',
defaultValue: 'false',
},
];

const data2 = [
{
name: 'load',
desc: '滚动到底部时触发',
params: '',
},
];

const data3 = [
{
name: 'default',
desc: '内容插槽',
params: '-',
},
];
</script>

<ParamsTable :data="data"></ParamsTable>

## 事件

<EmitTable :data="data2"></EmitTable>

## 插槽

<SlotTable :data="data3"></SlotTable>
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hooui",
"version": "1.1.2",
"version": "1.2.0",
"private": true,
"type": "module",
"scripts": {
Expand Down
13 changes: 9 additions & 4 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import DemoBottomPopup from './demo/DemoBottomPopup.vue';
import DemoTabScroll from './demo/DemoTabScroll.vue';
import DemoFullScreen from './demo/DemoFullScreen.vue';
import DemoCascader from './demo/DemoCascader.vue';
import DemoScopeList from './demo/DemoScopeList.vue';
const selectedKeys = ref<string[]>(['Copy']);
const openKeys = ref<string[]>([]);
Expand Down Expand Up @@ -75,18 +76,22 @@ const tabs: Record<string, any>[] = [
name: 'BottomPopup',
component: DemoBottomPopup
},
{
name: 'Cascader',
component: DemoCascader
},
{
name: 'ExpandContainer',
component: DemoExpandContainer
},
{
name: 'ScopeList',
component: DemoScopeList
},
{
name: 'SearchScopeInput',
component: DemoSearchScopeInput
},
{
name: 'Cascader',
component: DemoCascader
}
]
}
];
Expand Down
66 changes: 66 additions & 0 deletions src/demo/DemoScopeList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script setup lang="ts">
import BaseTab from './BaseTab.vue';
import ScopeList from '../package/scope-list/Index.vue';
import ScopeListUnocss from '../package/scope-list/Unocss.vue';
const loading = ref(false);
const finished = ref(false);
const list = ref<number[]>([]);
function load() {
// 异步更新数据
// setTimeout 仅做示例,真实场景中一般为 ajax 请求
setTimeout(() => {
for (let i = 0;i < 10;i++) {
list.value.push(list.value.length + 1);
}
// 加载状态结束
loading.value = false;
// 数据全部加载完成
if (list.value.length >= 20) {
finished.value = true;
}
}, 1000);
}
</script>

<template>
<div class="wrapper">

<BaseTab>
<template #common>
<ScopeList
v-model:loading="loading"
:finished="finished"
@load="load"
class="w-100% max-w-50vw h-50vh bg-#f5f5f5"
>
<div v-for="(item,index) in list" :key="index">
<div class="flex flex-center mt-1rem">{{ item }}</div>
</div>
</ScopeList>
</template>
<template #unocss>
<ScopeListUnocss
v-model:loading="loading"
:finished="finished"
@load="load"
class="w-100% max-w-50vw h-50vh bg-#f5f5f5"
>
<div v-for="(item,index) in list" :key="index">
<div class="flex flex-center mt-1rem">{{ item }}</div>
</div>
</ScopeListUnocss>
</template>
</BaseTab>
</div>
</template>

<style scoped lang="less">
.wrapper{
width: 400px;
}
</style>
2 changes: 1 addition & 1 deletion src/directive/components/LoadingComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ defineExpose({
<div class="h-loader">
<div class="h-icon">
<svg class="circular" viewBox="0 0 50 50">
<circle class="path" cx="25" cy="25" r="20" fill="none"></circle>
<circle class="path" cx="25" cy="25" r="20" fill="none"/>
</svg>
</div>
<div class="h-tip-text">{{ text }}</div>
Expand Down
2 changes: 1 addition & 1 deletion src/directive/components/LoadingComponentUnocss.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ defineExpose({
<div class="h-loader flex flex-col items-center absolute top-50% left-50% translate--50%">
<div class="h-icon">
<svg class="circular inline w-42px h-42px" viewBox="0 0 50 50">
<circle class="path" cx="25" cy="25" r="20" fill="none"></circle>
<circle class="path" cx="25" cy="25" r="20" fill="none"/>
</svg>
</div>
<div class="h-tip-text color-#3793ff pt-10px">{{ text }}</div>
Expand Down
2 changes: 2 additions & 0 deletions src/directive/copy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-disable no-console */
import type { Directive, DirectiveBinding } from 'vue';
const icon1 = '<svg class="copy-icon" width="20px" style="display: inline-block;cursor: pointer;vertical-align: middle;flex-shrink: 0;margin-left:5px" title="复制" xmlns="http:https://www.w3.org/2000/svg" viewBox="0 0 1024 1024" data-v-ea893728=""><path fill="currentColor" d="M768 832a128 128 0 0 1-128 128H192A128 128 0 0 1 64 832V384a128 128 0 0 1 128-128v64a64 64 0 0 0-64 64v448a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64z"></path><path fill="currentColor" d="M384 128a64 64 0 0 0-64 64v448a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64V192a64 64 0 0 0-64-64zm0-64h448a128 128 0 0 1 128 128v448a128 128 0 0 1-128 128H384a128 128 0 0 1-128-128V192A128 128 0 0 1 384 64"></path></svg>';

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const icon2 = '<svg class="copy-icon" width="20px" style="display: inline-block;cursor: pointer;vertical-align: middle;flex-shrink: 0;margin-left:5px" title="复制" xmlns="http:https://www.w3.org/2000/svg" viewBox="0 0 1024 1024" data-v-ea893728=""><path fill="currentColor" d="M128 320v576h576V320zm-32-64h640a32 32 0 0 1 32 32v640a32 32 0 0 1-32 32H96a32 32 0 0 1-32-32V288a32 32 0 0 1 32-32M960 96v704a32 32 0 0 1-32 32h-96v-64h64V128H384v64h-64V96a32 32 0 0 1 32-32h576a32 32 0 0 1 32 32M256 672h320v64H256zm0-192h320v64H256z"></path></svg>';

// 插入节点
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useFullScreen.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-console */
import { ref, watch, isRef, nextTick, type Ref } from 'vue';

function getDom(target: Ref<HTMLElement>| string | HTMLElement): HTMLElement {
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useTabScroll.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-console */
import { isRef, onMounted, onUnmounted, ref, watch, nextTick, type Ref } from 'vue';

type TabValue = HTMLElement | Ref<HTMLElement> | string;
Expand Down
4 changes: 2 additions & 2 deletions src/package/expandcontainer/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
fill="currentColor"
aria-hidden="true"
viewBox="64 64 896 896"
><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg>
><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"/></svg>
</slot>
</template>
<template v-else>
Expand All @@ -31,7 +31,7 @@
fill="currentColor"
aria-hidden="true"
viewBox="64 64 896 896"
><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg>
><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"/></svg>
</slot>
</template>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/package/expandcontainer/Unocss.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
fill="currentColor"
aria-hidden="true"
viewBox="64 64 896 896"
><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg>
><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"/></svg>
</slot>
</template>
<template v-else>
Expand All @@ -33,7 +33,7 @@
fill="currentColor"
aria-hidden="true"
viewBox="64 64 896 896"
><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg>
><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"/></svg>
</slot>
</template>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/package/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import ExpandContainer from './expandcontainer/Index.vue';
import SearchScopeInput from './searchscopeinput/Index.vue';
import BottomPopup from './popup/BottomPopup.vue';
import CascaderRadio from './cascader/Index.vue';
import ScopeList from './scope-list/Index.vue';

export function installComponents(app: any) {
app.component('ExpandContainer', ExpandContainer);
app.component('SearchScopeInput', SearchScopeInput);
app.component('BottomPopup', BottomPopup);
app.component('CascaderRadio', CascaderRadio);
app.component('ScopeList', ScopeList);
}
2 changes: 1 addition & 1 deletion src/package/popup/BottomPopup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<div class="header" :style="`flex-direction: ${closepos === 'left' ? 'row': 'row-reverse'};`">
<slot name="header">
<span class="close-btn" @click="onClose()">
<svg xmlns="http:https://www.w3.org/2000/svg" viewBox="0 0 1024 1024" data-v-ea893728=""><path fill="currentColor" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"></path></svg>
<svg xmlns="http:https://www.w3.org/2000/svg" viewBox="0 0 1024 1024" data-v-ea893728=""><path fill="currentColor" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"/></svg>
</span>
<span class="title">{{ props.popupProps?.title || '' }}</span>
<span v-show="conformText" class="confirm-btn" @click="onConform">{{ conformText }}</span>
Expand Down
2 changes: 1 addition & 1 deletion src/package/popup/BottomPopupUnocss.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<div class="header flex justify-between items-center text-1.25rem px-0.5rem py-0.75rem" :style="`flex-direction: ${closepos === 'left' ? 'row': 'row-reverse'};`">
<slot name="header">
<span class="close-btn w-1.5rem h-1.5rem" @click="onClose()">
<svg class="w-100% h-100%" xmlns="http:https://www.w3.org/2000/svg" viewBox="0 0 1024 1024" data-v-ea893728=""><path fill="currentColor" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"></path></svg>
<svg class="w-100% h-100%" xmlns="http:https://www.w3.org/2000/svg" viewBox="0 0 1024 1024" data-v-ea893728=""><path fill="currentColor" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"/></svg>
</span>
<span class="title text-#666">{{ props.popupProps?.title || '' }}</span>
<span v-show="conformText" class="confirm-btn" @click="onConform">{{ conformText }}</span>
Expand Down
Loading

0 comments on commit b6cc1b6

Please sign in to comment.