文件
OmniCard/mpuniapp/pages/uni-ui/drag-cell/drag-cell.uvue

597 行
14 KiB
Plaintext

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1; padding: 15px;">
<!-- #endif -->
<!-- #ifndef APP -->
<view style="padding: 15px;">
<!-- #endif -->
<text class="page-title">drag-cell 拖拽排序组件</text>
<view class="section">
<text class="section-title">示例 1:标签拖拽排序</text>
<text class="section-desc">长按标签后可拖拽排序,点击标签可拿到 item 和 index</text>
<uni-drag-cell v-model="data.tagList" @change="onTagChange">
<template #default="{ item, index }">
<view class="tag-slot">
<view class="tag-item" @click="clickItem(item as string, index as number)">
<text class="tag-text">{{ item as string }}</text>
</view>
</view>
</template>
</uni-drag-cell>
<text class="result-text">当前顺序:{{ data.tagList.join(', ') }}</text>
</view>
<view class="section">
<text class="section-title">示例 2:宫格拖拽排序(3 列)</text>
<text class="section-desc">长按宫格项后可拖拽排序</text>
<uni-drag-cell v-model="data.gridList3" :column="3" @change="onGridChange">
<template #default="{ item }">
<view class="grid-slot">
<view class="grid-item">
<text class="grid-text">{{ (item as UTSJSONObject)['name'] }}</text>
</view>
</view>
</template>
</uni-drag-cell>
<text class="result-text">当前顺序:{{ getGridNames(data.gridList3) }}</text>
</view>
<view class="section">
<text class="section-title">示例 3:列表拖拽排序(1 列)</text>
<text class="section-desc">长按列表项后可拖拽排序</text>
<uni-drag-cell v-model="data.gridList4" :column="1" @change="onGridChange">
<template #default="{ item }">
<view class="grid-slot-small">
<view class="grid-item-small">
<text class="grid-text-small">{{ (item as UTSJSONObject)['name'] }}</text>
</view>
</view>
</template>
</uni-drag-cell>
<text class="result-text">当前顺序:{{ getGridNames(data.gridList4) }}</text>
</view>
<view class="section">
<text class="section-title">示例 4:可新增并删除的标签</text>
<text class="section-desc">长按标签可排序,拖到底部可删除,点击 + 可输入并新增标签</text>
<uni-drag-cell v-model="data.deleteList" :show-close="true" :show-append="true" @change="onDeleteChange">
<template #default="{ item }">
<view class="tag-slot">
<view class="tag-item-delete">
<text class="tag-text">{{ item as string }}</text>
</view>
</view>
</template>
<template #append>
<view class="tag-slot">
<view class="tag-item-add" @click="promptAddDeleteTag">
<text class="tag-plus-text">+</text>
</view>
</view>
</template>
</uni-drag-cell>
<text class="result-text">当前顺序:{{ data.deleteList.join(', ') }}</text>
<text class="result-text">剩余:{{ data.deleteList.length }} 项</text>
</view>
<view class="section">
<text class="section-title">示例 5:带删除功能的图标宫格</text>
<text class="section-desc">长按后可拖动图标宫格项,并支持拖到底部删除</text>
<uni-drag-cell v-model="data.iconGridList" :column="3" :show-close="true" @change="onIconGridChange">
<template #default="{ item }">
<view class="icon-grid-slot">
<view class="icon-grid-item">
<view class="icon-box">
<text class="icon-emoji">{{ (item as UTSJSONObject)['icon'] }}</text>
</view>
<text class="icon-label">{{ (item as UTSJSONObject)['label'] }}</text>
</view>
</view>
</template>
</uni-drag-cell>
<text class="result-text">当前顺序:{{ getIconNames(data.iconGridList) }}</text>
</view>
<view class="section">
<text class="section-title">示例 6:九宫格图片选择</text>
<text class="section-desc">点击 + 选择图片,支持拖拽排序,也支持拖到底部删除</text>
<uni-drag-cell v-model="data.imageList" :column="3" :show-close="true"
:show-append="data.imageList.length < maxImageCount" @change="onImageChange">
<template #default="{ item, index }">
<view class="image-grid-slot">
<view class="image-grid-item" @click="previewImages(index as number)">
<image class="image-grid-img" :src="item" mode="aspectFill"></image>
</view>
</view>
</template>
<template #append>
<view class="image-grid-slot">
<view class="image-grid-plus" @click="chooseImages">
<text class="image-grid-plus-icon">+</text>
</view>
</view>
</template>
</uni-drag-cell>
<text class="result-text">已选择:{{ data.imageList.length }}/{{ maxImageCount }}</text>
</view>
<view class="section">
<text class="section-title">示例 7:手柄拖拽排序</text>
<text class="section-desc">开启手柄模式后,不需要长按,只能拖动右侧手柄进行排序</text>
<uni-drag-cell v-model="data.handleList" :column="1" :handle-mode="true" @change="onHandleListChange">
<template #default="{ item, index }">
<view class="handle-demo-slot">
<view class="handle-demo-item">
<text class="handle-demo-index">{{ (index as number) + 1 }}</text>
<text class="handle-demo-text">{{ item as string }}</text>
</view>
</view>
</template>
<template #handle="{ item }">
<view class="handle-demo-grip">
<text class="handle-demo-grip-text">≡</text>
</view>
</template>
</uni-drag-cell>
<text class="result-text">当前顺序:{{ data.handleList.join(', ') }}</text>
</view>
<view style="height: 50px;"></view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
<!-- #ifndef APP -->
</view>
<!-- #endif -->
</template>
<script setup lang="uts">
const maxImageCount = 9
type Data = {
tagList: string[],
gridList3: UTSJSONObject[],
gridList4: UTSJSONObject[],
deleteList: string[],
iconGridList: UTSJSONObject[],
imageList: string[],
handleList: string[]
}
const data = reactive<Data>({
tagList: ['标签1', '标签2', '标签3', '标签4'],
gridList3: [
{ name: '宫格1' },
{ name: '宫格2' },
{ name: '宫格3' },
{ name: '宫格4' },
{ name: '宫格5' },
{ name: '宫格6' },
{ name: '宫格7' },
{ name: '宫格8' },
{ name: '宫格9' }
],
gridList4: [
{ name: '1' },
{ name: '2' },
{ name: '3' },
{ name: '4' },
{ name: '5' }
],
deleteList: ['标签A', '标签B', '标签C', '标签D', '标签E'],
iconGridList: [
{ icon: 'H', label: '首页' },
{ icon: 'M', label: '消息' },
{ icon: 'P', label: '我的' },
{ icon: 'S', label: '设置' },
{ icon: 'A', label: '相册' },
{ icon: 'Y', label: '音乐' }
],
imageList: [],
handleList: ['微信支付', '支付宝', 'Apple Pay']
})
const clickItem = (item : string, index : number) => {
uni.showToast({
title: '点击了第 ' + (index + 1) + ' 项:' + item,
icon: 'none'
})
}
const onTagChange = (list : string[]) => {
console.log('tag list changed:', list)
}
const onGridChange = (list : UTSJSONObject[]) => {
console.log('grid list changed:', list)
}
const onDeleteChange = (list : string[]) => {
console.log('delete list changed:', list)
}
const onIconGridChange = (list : UTSJSONObject[]) => {
console.log('icon grid list changed:', list)
}
const onImageChange = (list : string[]) => {
console.log('image list changed:', list)
}
const onHandleListChange = (list : string[]) => {
console.log('handle list changed:', list)
}
const promptAddDeleteTag = () => {
uni.showModal({
title: '新增标签',
editable: true,
placeholderText: '请输入标签内容',
confirmText: '确定',
cancelText: '取消',
success: (res) => {
if (res.confirm != true) {
return
}
const value = res.content != null ? res.content.trim() : ''
if (value.length == 0) {
uni.showToast({
title: '标签内容不能为空',
icon: 'none'
})
return
}
const nextList = data.deleteList.slice()
nextList.push(value)
data.deleteList = nextList
onDeleteChange(data.deleteList)
}
})
}
const extractChooseImagePaths = (res : ChooseImageSuccess) : string[] => {
const paths : string[] = []
for (let i = 0; i < res.tempFilePaths.length; i++) {
const path = res.tempFilePaths[i]
if (path.length > 0) {
paths.push(path)
}
}
if (paths.length > 0) {
return paths
}
for (let i = 0; i < res.tempFiles.length; i++) {
const path = res.tempFiles[i].path
if (path.length > 0) {
paths.push(path)
}
}
return paths
}
const chooseImages = () => {
const remainCount = maxImageCount - data.imageList.length
if (remainCount <= 0) {
return
}
uni.chooseImage({
count: remainCount,
success: (res : ChooseImageSuccess) => {
const selectedPaths = extractChooseImagePaths(res)
if (selectedPaths.length == 0) {
return
}
const nextList = data.imageList.slice()
for (let i = 0; i < selectedPaths.length; i++) {
if (nextList.length >= maxImageCount) {
break
}
nextList.push(selectedPaths[i])
}
data.imageList = nextList
onImageChange(data.imageList)
}
})
}
const previewImages = (index : number) => {
if (index < 0 || index >= data.imageList.length) {
return
}
uni.previewImage({
urls: data.imageList,
current: data.imageList[index]
})
}
const getGridNames = (list : UTSJSONObject[]) : string => {
const names : string[] = []
for (let i = 0; i < list.length; i++) {
const value = list[i]['name']
if (value != null) {
names.push(value as string)
}
}
return names.join(', ')
}
const getIconNames = (list : UTSJSONObject[]) : string => {
const names : string[] = []
for (let i = 0; i < list.length; i++) {
const value = list[i]['label']
if (value != null) {
names.push(value as string)
}
}
return names.join(', ')
}
defineExpose({
data,
clickItem,
promptAddDeleteTag,
chooseImages,
previewImages
})
</script>
<style>
.page-title {
font-size: 24px;
font-weight: bold;
color: var(--text-color, #333333);
margin-bottom: 20px;
text-align: center;
}
.section {
margin-bottom: 30px;
background-color: var(--list-background-color, #ffffff);
border-radius: 8px;
padding: 15px;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #007aff;
margin-bottom: 5px;
}
.section-desc {
font-size: 12px;
color: var(--text-color, #333333);
opacity: 0.7;
margin-bottom: 15px;
}
.tag-slot {
padding-right: 10px;
padding-bottom: 10px;
}
.tag-item {
display: flex;
flex-direction: column;
background-color: #007aff;
border-radius: 4px;
padding: 8px 16px;
}
.tag-text {
color: #ffffff;
font-size: 14px;
}
.tag-item-delete {
display: flex;
flex-direction: column;
background-color: #ff6b6b;
border-radius: 4px;
padding: 4px 16px;
height: 32px;
}
.tag-item-add {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fff5f5;
border-width: 1px;
border-style: dashed;
border-color: #ff6b6b;
border-radius: 4px;
padding: 4px 16px;
height: 32px;
}
.tag-plus-text {
color: #ff6b6b;
font-size: 16px;
font-weight: bold;
}
.grid-slot {
width: 100%;
padding: 5px;
}
.grid-item {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #28a745;
border-radius: 8px;
height: 80px;
}
.grid-text {
color: #ffffff;
font-size: 14px;
font-weight: bold;
}
.grid-slot-small {
width: 90%;
/* 避免scale放大1.12倍时被裁剪*/
padding: 3px;
align-self: center;
}
.grid-item-small {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #ffc107;
border-radius: 8px;
height: 60px;
}
.grid-text-small {
color: #333333;
font-size: 16px;
font-weight: bold;
}
.icon-grid-slot {
width: 100%;
padding: 5px;
}
.icon-grid-item {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #f8f9fa;
border-radius: 8px;
padding: 10px;
}
.icon-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
background-color: #e9ecef;
border-radius: 25px;
margin-bottom: 5px;
}
.icon-emoji {
font-size: 24px;
font-weight: bold;
color: #333333;
}
.icon-label {
font-size: 12px;
color: #333333;
}
.image-grid-slot {
width: 100%;
padding: 5px;
}
.image-grid-item {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 104px;
background-color: #f5f5f5;
border-radius: 12px;
overflow: hidden;
}
.image-grid-img {
width: 100%;
height: 100%;
border-radius: 12px;
}
.image-grid-plus {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 104px;
background-color: #fafafa;
border-width: 1px;
border-style: dashed;
border-color: #d9d9d9;
border-radius: 12px;
}
.image-grid-plus-icon {
font-size: 36px;
font-weight: bold;
color: #999999;
}
.handle-demo-slot {
width: 100%;
padding: 4px 0;
}
.handle-demo-item {
display: flex;
flex-direction: row;
align-items: center;
background-color: #f7f8fa;
border-width: 1px;
border-style: solid;
border-color: #e5e6eb;
border-radius: 10px;
padding: 16px 52px 16px 14px;
}
.handle-demo-index {
width: 24px;
margin-right: 12px;
color: #007aff;
font-size: 15px;
font-weight: bold;
text-align: center;
}
.handle-demo-text {
color: #333333;
font-size: 14px;
}
.handle-demo-grip {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 40px;
height: 56px;
}
.handle-demo-grip-text {
color: #999999;
font-size: 24px;
font-weight: bold;
}
.result-text {
font-size: 12px;
color: var(--text-color, #333333);
opacity: 0.7;
margin-top: 10px;
}
</style>