388 lines
9.6 KiB
Vue
388 lines
9.6 KiB
Vue
<!--
|
||
组件作用: 弹层, 列表支持 单选, 多选。
|
||
|
||
使用方法:
|
||
|
||
<JeepayPopupListSelect ref="jeepayPopupListSelect" :reqTableDataFunc="reqTableDataFunc" :fields="{ key: 'articleId', left: 'articleId', right: 'title'}"/>
|
||
jeepayPopupListSelect.value.open().then((selected) => {
|
||
console.log(selected);
|
||
jeepayPopupListSelect.value.close() //自行关闭
|
||
})
|
||
|
||
@author terrfly
|
||
@site https://www.jeequan.com
|
||
@date 2022/11/26 16:24
|
||
-->
|
||
<template>
|
||
<uni-popup ref="popupRef" type="bottom" @maskClick="close" @change="change" mask-background-color="rgba(0,0,0,.5)" :safe-area="false">
|
||
<view class="card-wrapper">
|
||
<view class="selected-title" v-if="title">
|
||
{{ title }}
|
||
</view>
|
||
<view class="search-wrap" v-if="props.searchInputName">
|
||
<view class="input-search">
|
||
<uni-easyinput
|
||
v-model="vdata.searchData[props.searchInputName]"
|
||
prefixIcon="search"
|
||
:inputBorder="false"
|
||
:clearable="false"
|
||
:styles="{
|
||
backgroundColor: 'transparent'
|
||
}"
|
||
type="text"
|
||
placeholder="搜索"
|
||
placeholderStyle=" color: rgba(0,0,0,0.85);font-size: 30rpx;"
|
||
/>
|
||
<view class="search-button" @tap="searchFunc">搜索</view>
|
||
</view>
|
||
<!-- <view class="add-btn" hover-class="touch-button" v-if="props.addUse" @tap="">新增应用</view> -->
|
||
</view>
|
||
<!-- 数据渲染 -->
|
||
<JTableList ref="jeepayTableListRef" :reqTableDataFunc="reqTableDataFuncWrapper" :searchData="vdata.searchData" :initData="false" height="406rpx">
|
||
<template #tableBody="{ record }">
|
||
<view class="store" @tap="selectFunc(record)">
|
||
<template v-if="isCheckbox">
|
||
<view class="more-selected" :class="{ 'more-disabled': record.hasDirector, 'more-active': hasSelected(record) }">
|
||
<image :src="record.hasDirector ? '/static/iconImg/icon-disable.svg' : '/static/iconImg/icon-check.svg'" mode="scaleToFill" />
|
||
</view>
|
||
</template>
|
||
<view v-else :class="{ 'store-dot-def': true, 'active-dot': hasSelected(record) }"></view>
|
||
<slot name="content" :record="record">
|
||
<view class="store-inner-slot">
|
||
<view class="left">
|
||
<!-- <text>{{ record[props.fields.left] }}</text> -->
|
||
<text class="tit">{{ record[props.fields.left] }}</text>
|
||
<text class="ino" v-if="record.intro">{{ record.intro }}</text>
|
||
</view>
|
||
<view class="right">{{ record[props.fields.right] }}</view>
|
||
</view>
|
||
</slot>
|
||
</view>
|
||
</template>
|
||
</JTableList>
|
||
|
||
<!-- <button v-if="vdata.hasNext" @tap="jeepayTableListRef.addNext()">下一页</button> -->
|
||
|
||
<view class="footer-wrapper">
|
||
<view class="footer-main">
|
||
<view class="footer-button">
|
||
<view class="flex-center" hover-class="touch-button" @tap="close">取消</view>
|
||
<view @tap="confirmFunc" class="confirm flex-center" hover-class="touch-button">确认</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</uni-popup>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { inject, nextTick, reactive, ref, render, watch } from 'vue';
|
||
|
||
const jeepayTableListRef = ref();
|
||
|
||
// 定义组件参数
|
||
const props = defineProps({
|
||
configMode: { type: String, default: '' }, // 搜索时仅展示商户号
|
||
// 请求业务数据, 参数可自行控制。
|
||
reqTableDataFunc: { type: Function, default: () => {} },
|
||
|
||
// 搜索输入框的name , 不传入则不显示搜索。
|
||
searchInputName: { type: String },
|
||
|
||
// 约定的字段 , 若不合适请通过插槽自行插入。
|
||
fields: { type: Object, default: { key: 'id', left: 'left', right: 'right' } },
|
||
|
||
// 是否多选框, 默认单选。
|
||
isCheckbox: { type: Boolean, default: false },
|
||
// 标题有则显示无则隐藏
|
||
title: [String, Number],
|
||
// 是否显示增加引用按钮
|
||
addUse: { type: Boolean, default: false }
|
||
});
|
||
let changePageMetaOverflowFunc = inject('changePageMetaOverflowFunc');
|
||
const emits = defineEmits(['confirm']);
|
||
|
||
const popupRef = ref();
|
||
|
||
console.log(props, 'propsprops');
|
||
const vdata = reactive({
|
||
searchData: {}, //当前页的搜索条件
|
||
|
||
hasNext: false, // 是否包含下一页数据
|
||
|
||
selectedList: [], // 选择的值
|
||
|
||
promiseObject: {}
|
||
});
|
||
|
||
// 点击搜索
|
||
function searchFunc() {
|
||
jeepayTableListRef.value.refTable(true);
|
||
}
|
||
|
||
/** reqTableDataFunc 处理函数 */
|
||
function reqTableDataFuncWrapper(params) {
|
||
return props.reqTableDataFunc(params).then(({ bizData }) => {
|
||
vdata.hasNext = bizData.hasNext; // 是否包含下一页
|
||
return Promise.resolve({ bizData });
|
||
});
|
||
}
|
||
|
||
// 是否选中
|
||
function hasSelected(record) {
|
||
return vdata.selectedList.filter((r) => r[props.fields.key] == record[props.fields.key]).length > 0;
|
||
}
|
||
|
||
/** 选择函数 **/
|
||
function selectFunc(record) {
|
||
// 判断是否已存在
|
||
if (hasSelected(record)) {
|
||
// 多选需删除
|
||
if (props.isCheckbox) {
|
||
vdata.selectedList.splice(
|
||
vdata.selectedList.findIndex((r) => r[props.fields.key] == record[props.fields.key]),
|
||
1
|
||
);
|
||
}
|
||
|
||
// 单选无需操作
|
||
|
||
return false;
|
||
}
|
||
|
||
// 多选直接添加
|
||
if (props.isCheckbox) {
|
||
if (record.hasDirector) return; //如果被其他店长绑定 禁止选中
|
||
return vdata.selectedList.push(record);
|
||
} else {
|
||
// 单选
|
||
|
||
return (vdata.selectedList = [record]);
|
||
}
|
||
}
|
||
|
||
// 点击确定事件
|
||
function confirmFunc() {
|
||
// 多选
|
||
if (props.isCheckbox) {
|
||
emits('confirm', vdata.selectedList);
|
||
return vdata.promiseObject.resolve(vdata.selectedList);
|
||
}
|
||
|
||
// 单选 仅返回第一个即可。
|
||
emits('confirm', vdata.selectedList.length > 0 ? vdata.selectedList[0] : null);
|
||
vdata.promiseObject.resolve(vdata.selectedList.length > 0 ? vdata.selectedList[0] : null);
|
||
}
|
||
|
||
// 显示弹层, 并且返回一个promise
|
||
// promise.then(selected)
|
||
// 注意: 此Promise 只会回调一次, 如需要验证是否选中正确, 需要使用@confirm事件。
|
||
// 如果对拿到的值不做校验,获取到然后关闭。 那么可以直接使用.then() 然后立马关闭弹层。
|
||
function open(defaultVal) {
|
||
// 清空数据
|
||
vdata.selectedList = [];
|
||
vdata.searchData[props.searchInputName] = '';
|
||
|
||
// 默认选中。
|
||
if (defaultVal) {
|
||
if (Array.isArray(defaultVal)) {
|
||
vdata.selectedList = defaultVal;
|
||
} else {
|
||
vdata.selectedList = [defaultVal];
|
||
}
|
||
}
|
||
|
||
popupRef.value.open();
|
||
|
||
nextTick(() => {
|
||
jeepayTableListRef.value.refTable(true);
|
||
uni.hideTabBar(); // 可能报错, 在nextTick中, 不影响其他业务。
|
||
});
|
||
|
||
return new Promise((resolve, reject) => {
|
||
vdata.promiseObject.resolve = resolve;
|
||
vdata.promiseObject.reject = reject;
|
||
});
|
||
}
|
||
|
||
// 关闭弹层
|
||
function close() {
|
||
popupRef.value.close();
|
||
uni.showTabBar();
|
||
}
|
||
const change = (e) => {
|
||
if (changePageMetaOverflowFunc) {
|
||
changePageMetaOverflowFunc(!e.show);
|
||
}
|
||
};
|
||
// 将表格事件暴露出去 https://www.jianshu.com/p/39d14c25c987
|
||
defineExpose({ open, close });
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.card-wrapper {
|
||
border-radius: 32rpx 32rpx 0 0;
|
||
background-color: #fff;
|
||
overflow: hidden;
|
||
min-height: 70vh;
|
||
max-height: 80vh;
|
||
overflow-y: auto;
|
||
.selected-title {
|
||
height: 110rpx;
|
||
line-height: 110rpx;
|
||
text-align: center;
|
||
font-size: 30rpx;
|
||
font-weight: 500;
|
||
border-bottom: 1rpx solid #ededed;
|
||
}
|
||
.search-wrap {
|
||
padding: 30rpx;
|
||
display: flex;
|
||
.input-search {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
padding-left: 10rpx;
|
||
border-radius: 10rpx;
|
||
background-color: #f7f7f7;
|
||
.search-button {
|
||
padding: 24rpx 30rpx;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
color: #2980fd;
|
||
}
|
||
}
|
||
.add-btn {
|
||
border-radius: 10rpx;
|
||
color: #fff;
|
||
padding: 0 20upx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: $jeepay-bg-primary;
|
||
margin-left: 30rpx;
|
||
}
|
||
}
|
||
.store {
|
||
position: relative;
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
align-items: center;
|
||
margin: 40rpx;
|
||
font-size: 30rpx;
|
||
background-color: #f5f5f5;
|
||
padding: 20rpx;
|
||
border-radius: 12rpx;
|
||
.more-selected {
|
||
flex-shrink: 0;
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
border-radius: 6rpx;
|
||
margin-right: 20rpx;
|
||
border: 2rpx solid rgba(217, 217, 217, 1);
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
.more-disabled {
|
||
background-color: #f7f7f7;
|
||
}
|
||
.more-active {
|
||
background-color: #2980fd;
|
||
}
|
||
.store-dot-def {
|
||
position: relative;
|
||
margin-right: 20rpx;
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
background-color: #d7d8d9;
|
||
border-radius: 50%;
|
||
&::after {
|
||
content: '';
|
||
display: block;
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
width: 50%;
|
||
height: 50%;
|
||
border-radius: 50%;
|
||
background-color: #fff;
|
||
}
|
||
}
|
||
.active-dot {
|
||
background-color: #2980fd;
|
||
}
|
||
|
||
.store-inner-slot {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
width: 100%;
|
||
|
||
.left {
|
||
display: flex;
|
||
flex-direction: column;
|
||
.ino {
|
||
color: #999;
|
||
padding-top: 12upx;
|
||
font-size: 24upx;
|
||
}
|
||
}
|
||
.right {
|
||
color: #999999;
|
||
}
|
||
}
|
||
}
|
||
.all-store::after {
|
||
content: '';
|
||
display: block;
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 40rpx;
|
||
right: 40rpx;
|
||
height: 1rpx;
|
||
background-color: #ededed;
|
||
}
|
||
.footer-wrapper {
|
||
height: 186rpx;
|
||
.footer-main {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: env(safe-area-inset-bottom);
|
||
border-top: 1rpx solid #ededed;
|
||
background-color: #fff;
|
||
.tips {
|
||
margin: 20rpx;
|
||
text-align: center;
|
||
font-size: 27rpx;
|
||
color: #a6a6a6;
|
||
}
|
||
.footer-button {
|
||
padding: 0 30rpx;
|
||
margin-top: 30rpx;
|
||
padding-bottom: 30rpx;
|
||
display: flex;
|
||
|
||
justify-content: space-between;
|
||
view {
|
||
width: 330rpx;
|
||
height: 110rpx;
|
||
font-size: 33rpx;
|
||
font-weight: 500;
|
||
color: rgba(0, 0, 0, 0.5);
|
||
border-radius: 20rpx;
|
||
background-color: #f7f7f7;
|
||
}
|
||
.confirm {
|
||
color: #fff;
|
||
background: $jeepay-bg-primary;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|