lijuncheng
4 months ago
19 changed files with 1183 additions and 206 deletions
@ -1,11 +1,11 @@ |
|||||
VITE_BASE_URL=http://172.22.32.9:81/api/admin-api |
VITE_BASE_URL=http://172.21.32.14:81/api/admin-api |
||||
VITE_BASE_URL_IMAGE=http://172.22.32.9:81/admin-api |
VITE_BASE_URL_IMAGE=http://172.21.32.14:81/admin-api |
||||
|
|
||||
# 租户配置 |
# 租户配置 |
||||
VITE_TENANT='[{"text":"成都1397","value":2},{"text":"长春2379","value":3}]' |
VITE_TENANT='[{"text":"长春1379","value":1}]' |
||||
|
|
||||
# 是否是测试环境 |
# 是否是测试环境 |
||||
VITE_isDevelopment=false |
VITE_isDevelopment=false |
||||
|
|
||||
# 积木报表请求路径 |
# 积木报表请求路径 |
||||
VITE_JMREPORT_BASE_URL='http://172.22.32.9:90' |
VITE_JMREPORT_BASE_URL='http://172.21.32.14:90' |
||||
|
@ -1,7 +1,36 @@ |
|||||
<template> |
<template> |
||||
|
<view class=""> |
||||
|
<view class=""> |
||||
|
<com-blank-view @goScan='openFg' v-if="detailSource.length==0"></com-blank-view> |
||||
|
</view> |
||||
|
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
|
import comBlankView from '@/mycomponents/common/comBlankView.vue' |
||||
|
export default { |
||||
|
components: { |
||||
|
comBlankView |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
detailSource:[] |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
onShow() { |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
|
||||
|
}, |
||||
|
methods: { |
||||
|
openFg(){ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style> |
||||
|
@ -1,7 +1,36 @@ |
|||||
<template> |
<template> |
||||
|
<view class=""> |
||||
|
<view class=""> |
||||
|
<com-blank-view @goScan='openFg' v-if="detailSource.length==0"></com-blank-view> |
||||
|
</view> |
||||
|
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
|
import comBlankView from '@/mycomponents/common/comBlankView.vue' |
||||
|
export default { |
||||
|
components: { |
||||
|
comBlankView |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
detailSource:[] |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
onShow() { |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
|
||||
|
}, |
||||
|
methods: { |
||||
|
openFg(){ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style> |
||||
|
@ -0,0 +1,8 @@ |
|||||
|
## 1.0.11(2023-11-15) |
||||
|
reqFun只允许传入Promise |
||||
|
## 1.0.10(2023-11-13) |
||||
|
去除默认值 |
||||
|
## 1.0.1(2023-11-13) |
||||
|
更新插件依赖 |
||||
|
## 1.0.0(2023-11-13) |
||||
|
第一次发布 |
@ -0,0 +1,22 @@ |
|||||
|
export default { |
||||
|
mode: 'radio', //radio checkbox 单选、多选
|
||||
|
showType: 'text', //暂时不用 text image 纯文字、图文
|
||||
|
selected: '', //已选中的
|
||||
|
dataList: null, //如果dataList传入了数组则直接使用传入的数组渲染,无需再配置proxyConfig
|
||||
|
proxyConfig: { //组件内部代理请求数据配置
|
||||
|
reqFun: Promise.resolve(), //请求方法
|
||||
|
localPaging: false //前端本地分页,如果使用的是dataList默认是本地分页
|
||||
|
}, |
||||
|
page: { //如果是组件内部代理请求会把page的值加入到请求参数里
|
||||
|
pageIndex: 1, //当前页
|
||||
|
pageSize: 20 //页大小
|
||||
|
}, |
||||
|
search: { |
||||
|
type: 'local', //搜索的类型,local本地搜索, remote向服务器请求。只有使用proxyConfig.reqFun请求才能配置为remote
|
||||
|
}, |
||||
|
fields: { //显示的key,value
|
||||
|
label: 'name', |
||||
|
value: 'code', |
||||
|
image: 'image', //暂时不用
|
||||
|
} |
||||
|
} |
@ -0,0 +1,74 @@ |
|||||
|
/**深度合并对象*/ |
||||
|
export const deepMerge = function(target = {}, source = {}) { |
||||
|
target = deepClone(target); //深度克隆
|
||||
|
if (typeof target !== 'object' || typeof source !== 'object') return false; //判断类型
|
||||
|
for (let prop in source) { |
||||
|
if (!source.hasOwnProperty(prop)) continue; //检测属性是否为对象的自有属性
|
||||
|
if (prop in target) { //prop 中是否有 target 属性
|
||||
|
if (typeof target[prop] !== 'object') { |
||||
|
target[prop] = source[prop]; |
||||
|
} else { |
||||
|
if (typeof source[prop] !== 'object') { |
||||
|
target[prop] = source[prop]; |
||||
|
} else { |
||||
|
if (target[prop].concat && source[prop].concat) { |
||||
|
target[prop] = target[prop].concat(source[prop]); //合并
|
||||
|
} else { |
||||
|
target[prop] = deepMerge(target[prop], source[prop]); //递归
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
target[prop] = source[prop]; |
||||
|
} |
||||
|
} |
||||
|
return target; |
||||
|
} |
||||
|
|
||||
|
const isArray = function(arr) { |
||||
|
return Object.prototype.toString.call(arr) === '[object Array]'; |
||||
|
} |
||||
|
|
||||
|
/**深度合并对象*/ |
||||
|
export const deepClone = function(obj) { |
||||
|
// 对常见的“非”值,直接返回原来值
|
||||
|
if ([null, undefined, NaN, false].includes(obj)) return obj; |
||||
|
if (typeof obj !== "object" && typeof obj !== 'function') { |
||||
|
//原始类型直接返回
|
||||
|
return obj; |
||||
|
} |
||||
|
let o = isArray(obj) ? [] : {}; |
||||
|
for (let i in obj) { |
||||
|
if (obj.hasOwnProperty(i)) { |
||||
|
o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i]; |
||||
|
} |
||||
|
} |
||||
|
return o; |
||||
|
}; |
||||
|
|
||||
|
let timeout = null |
||||
|
/** |
||||
|
* 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数 |
||||
|
* |
||||
|
* @param {Function} func 要执行的回调函数 |
||||
|
* @param {Number} wait 延时的时间 |
||||
|
* @param {Boolean} immediate 是否立即执行 |
||||
|
* @return null |
||||
|
*/ |
||||
|
export const debounce = function(func, wait = 500, immediate = false) { |
||||
|
// 清除定时器
|
||||
|
if (timeout !== null) clearTimeout(timeout) |
||||
|
// 立即执行,此类情况一般用不到
|
||||
|
if (immediate) { |
||||
|
const callNow = !timeout |
||||
|
timeout = setTimeout(() => { |
||||
|
timeout = null |
||||
|
}, wait) |
||||
|
if (callNow) typeof func === 'function' && func() |
||||
|
} else { |
||||
|
// 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
|
||||
|
timeout = setTimeout(() => { |
||||
|
typeof func === 'function' && func() |
||||
|
}, wait) |
||||
|
} |
||||
|
} |
@ -0,0 +1,130 @@ |
|||||
|
<template> |
||||
|
<view class="wz-list"> |
||||
|
<view :class="item.check?'check-item':'list-item'" v-for="(item,index) in list" :key="index" |
||||
|
@click="$emit('checkList',item)"> |
||||
|
<view class="" v-if="isShowIcon"> |
||||
|
<uni-icons v-if="item.check" type="checkbox-filled" color="#007aff" size="24"></uni-icons> |
||||
|
<uni-icons v-else type="circle" color="#999" size="24"></uni-icons> |
||||
|
</view> |
||||
|
|
||||
|
({{index+1}}) |
||||
|
<view class="uni-flex" style="flex-direction: column; font-size: 32rpx;"> |
||||
|
<view class="list-item-text">ERP料号 : {{item.itemCode}}</view> |
||||
|
<view class="list-item-text">名称 : {{item.itemName}}</view> |
||||
|
<view class="list-item-text">描述 : {{item.itemDesc1}}</view> |
||||
|
</view> |
||||
|
|
||||
|
</view> |
||||
|
<view class="loadmore" @click="$emit('loadmore')"> |
||||
|
<view v-if="page.loading == 'loading'" class="icon"> |
||||
|
<uni-icons type="spinner-cycle" size="24"></uni-icons> |
||||
|
</view> |
||||
|
<view class="">{{loadmore[page.loading]}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'wz-list', |
||||
|
props: { |
||||
|
list: { |
||||
|
type: Array, |
||||
|
default: () => [] |
||||
|
}, |
||||
|
isShowIcon: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
page: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return { |
||||
|
loading: 'loading', //loading|loadmore|nomore |
||||
|
isEmpty: false |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
fields: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return { |
||||
|
label: 'name', |
||||
|
value: 'code', |
||||
|
image: 'image' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
loadmore: { |
||||
|
loading: '正在加载', |
||||
|
loadmore: '点击或上拉加载更多', |
||||
|
nomore: '没有更多了' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
onLoad() { |
||||
|
|
||||
|
}, |
||||
|
methods: { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.wz-list { |
||||
|
@mixin wz-line ($line) { |
||||
|
overflow: hidden; |
||||
|
word-break: break-all; |
||||
|
text-overflow: ellipsis; |
||||
|
display: -webkit-box; // 弹性伸缩盒 |
||||
|
-webkit-box-orient: vertical; // 设置伸缩盒子元素排列方式 |
||||
|
-webkit-line-clamp: $line; |
||||
|
} |
||||
|
|
||||
|
.list-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
color: $uni-text-color-grey; |
||||
|
padding: $uni-spacing-col-base; |
||||
|
margin-bottom: $uni-spacing-col-sm; |
||||
|
background-color: #fff; |
||||
|
|
||||
|
.list-item-text { |
||||
|
@include wz-line(2); |
||||
|
margin-left: 4px; |
||||
|
margin-bottom: 1px |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.check-item { |
||||
|
@extend .list-item; |
||||
|
color: $uni-text-color; |
||||
|
} |
||||
|
|
||||
|
.loadmore { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
font-size: $uni-font-size-sm; |
||||
|
padding: 8px; |
||||
|
|
||||
|
@keyframes rotateInfinite { |
||||
|
form { |
||||
|
transform: rotate(0deg); |
||||
|
} |
||||
|
|
||||
|
to { |
||||
|
transform: rotate(360deg); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.icon { |
||||
|
animation: rotateInfinite infinite 1s linear; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,90 @@ |
|||||
|
<template> |
||||
|
<view class="wz-search"> |
||||
|
<view class="search-input"> |
||||
|
<uni-icons color="#808080" size="22" type="search"></uni-icons> |
||||
|
<view class="right"> |
||||
|
<input :value="value" :placeholder="placeholder" @input="change" /> |
||||
|
</view> |
||||
|
<uni-icons v-if="value || value === 0 || value === '0'" color="#808080" size="22" type="clear" |
||||
|
@click="clear"> |
||||
|
</uni-icons> |
||||
|
</view> |
||||
|
<view class="search-button"> |
||||
|
<button class="button" @click="searchData">搜索</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'wz-search', |
||||
|
props: { |
||||
|
value: {}, |
||||
|
placeholder: { |
||||
|
type: String, |
||||
|
default: '请输入搜索关键字' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
searchVal: '' |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
change({ |
||||
|
detail |
||||
|
}) { |
||||
|
this.$emit('input', detail.value) |
||||
|
}, |
||||
|
clear() { |
||||
|
this.$emit('clear') |
||||
|
this.$emit('input', '') |
||||
|
}, |
||||
|
searchData() { |
||||
|
this.$emit('search', this.value) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.wz-search { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
font-size: $uni-font-size-base; |
||||
|
|
||||
|
.search-input { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 32px; |
||||
|
padding: 0 8px; |
||||
|
border: 1px solid $uni-border-color; |
||||
|
border-radius: 100px; |
||||
|
|
||||
|
.right { |
||||
|
flex: 1; |
||||
|
width: 100%; |
||||
|
margin: 0 4px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.search-button { |
||||
|
margin-left: 8px; |
||||
|
|
||||
|
.button { |
||||
|
padding: 0; |
||||
|
font-size: inherit; |
||||
|
background-color: transparent; |
||||
|
color: inherit; |
||||
|
width: 100%; |
||||
|
height: 32px; |
||||
|
line-height: 32px; |
||||
|
|
||||
|
&::after { |
||||
|
border: 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,357 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<u-popup ref="popup" v-model="showPopup" border-radius="15" style="width: 100%;" mode="center"> |
||||
|
<view class="select-popup-content"> |
||||
|
<view class="uni-flex" style="display: flex; width: 100%;justify-content: center; "> |
||||
|
<view class="" style=" font-size: 35rpx; margin: 20rpx; font-weight: bold;"> |
||||
|
{{title}} |
||||
|
</view> |
||||
|
</view> |
||||
|
<u-line></u-line> |
||||
|
|
||||
|
<view class="popup-search"> |
||||
|
<wz-search v-model="searchValue" @search="searchData('search')" @clear="searchData('clear')" @input="change"/> |
||||
|
</view> |
||||
|
<scroll-view class="popup-scroll-y" :scroll-top="scroll.newTop" scroll-y |
||||
|
:style="{'--popup-scroll-height': scrollH}" @scrolltolower="scrolltolower" @scroll="viewScroll"> |
||||
|
<wz-list :list="dataList" :fields="popConfig.fields" :page="pageStatus" @checkList="checkList" |
||||
|
@loadmore="scrolltolower" :isShowIcon="false" /> |
||||
|
</scroll-view> |
||||
|
<view v-if="popConfig.mode =='checkbox'" class="footer-button"> |
||||
|
<view class="button-item"> |
||||
|
<button class="button" @click="close">取消</button> |
||||
|
</view> |
||||
|
<view class="button-item"> |
||||
|
<button class="button" type="primary" @click="checkboxConfirm">确定</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</u-popup> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import wzSearch from "./wz-search.vue" |
||||
|
import wzList from "./wz-list.vue" |
||||
|
import { |
||||
|
deepMerge, |
||||
|
deepClone, |
||||
|
debounce |
||||
|
} from "./util/index.js" |
||||
|
import defaultConfig from "./util/defaultConfig.js" |
||||
|
let allDataList = null |
||||
|
/** |
||||
|
* 下拉选择弹窗 |
||||
|
* @function open 打开弹窗 |
||||
|
* @property {String} scrollH 滚动内容的高度,不包含搜索框和多选时的底部按钮栏。需要带单位 |
||||
|
* @property {String} popType = popup弹出方向默认bottom 请查看uni-popup文档 |
||||
|
*/ |
||||
|
export default { |
||||
|
name: "wz-select-popup", |
||||
|
components: { |
||||
|
wzSearch, |
||||
|
wzList |
||||
|
}, |
||||
|
props: { |
||||
|
scrollH: { |
||||
|
type: String, |
||||
|
default: '50vh' |
||||
|
}, |
||||
|
popType: { |
||||
|
type: String, |
||||
|
default: 'bottom' |
||||
|
}, |
||||
|
title: { |
||||
|
type: String, |
||||
|
default: '物料查询' |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
dataList: [], |
||||
|
searchValue: '', |
||||
|
pageStatus: { |
||||
|
loading: '', //loading|loadmore|nomore |
||||
|
isEmpty: false |
||||
|
}, |
||||
|
scroll: { |
||||
|
oldTop: 0, |
||||
|
newTop: 0 |
||||
|
}, |
||||
|
popConfig: {}, |
||||
|
showPopup:false |
||||
|
}; |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.popConfig = deepClone(defaultConfig) |
||||
|
}, |
||||
|
destroyed() { |
||||
|
allDataList = null |
||||
|
}, |
||||
|
methods: { |
||||
|
viewScrollTop() { |
||||
|
// 解决view层不同步的问题 |
||||
|
this.scroll.newTop = this.scroll.oldTop |
||||
|
this.$nextTick(function() { |
||||
|
this.scroll.newTop = 0 |
||||
|
}); |
||||
|
}, |
||||
|
viewScroll(e) { |
||||
|
debounce(() => { |
||||
|
this.scroll.oldTop = e.detail.scrollTop |
||||
|
}, 100) |
||||
|
}, |
||||
|
change(data){ |
||||
|
console.log("输入",data) |
||||
|
this.searchData("search") |
||||
|
}, |
||||
|
searchData(type) { |
||||
|
const { |
||||
|
search, |
||||
|
fields, |
||||
|
proxyConfig |
||||
|
} = this.popConfig |
||||
|
this.pageStatus.loading == 'loading' |
||||
|
this.pageStatus.isEmpty = false |
||||
|
this.popConfig.page.pageIndex = 1 |
||||
|
this.dataList = [] |
||||
|
this.viewScrollTop() |
||||
|
if (search.type === 'local' && proxyConfig.localPaging) { |
||||
|
if (type === 'clear') { |
||||
|
this.listInit(allDataList) |
||||
|
return |
||||
|
} |
||||
|
this.dataList = allDataList?.filter(v => v[fields.label].includes(this.searchValue)) |
||||
|
} else { |
||||
|
this.proxyRequest() |
||||
|
} |
||||
|
}, |
||||
|
/** |
||||
|
* 打开弹窗选择器 |
||||
|
* @function |
||||
|
* @param {Object} configs 弹出层配置 |
||||
|
* @prmam {String} configs.mode 选择模式radio单选 | checkbox多选 默认是radio |
||||
|
* @param {String | Array} configs.selected 已选中的value,单选传字符串,多选传数组 |
||||
|
* |
||||
|
* @param {Array} dataList 如果dataList传入了数组,则直接使用传入的数组渲染,无需再配置proxyConfig |
||||
|
* |
||||
|
* @param {Object} configs.proxyConfig 组件内部代理请求数据配置 |
||||
|
* @param {Function} proxyConfig.reqFun 请求数据的方法 |
||||
|
* @param {Boolean} proxyConfig.localPaging 是否本地分页 |
||||
|
* |
||||
|
* @param {Object} configs.search //搜索相关参数 |
||||
|
* @param {String} search.type = local组件内部排序搜索 | remote请求接口,会把输入的搜索文字传入proxyConfig.reqFun |
||||
|
* |
||||
|
* @param {Object} configs.page 分页的页码参数 |
||||
|
* @param {Number} page.pageIndex 当前页 |
||||
|
* @param {Number} page.pageSize 页大小 |
||||
|
* |
||||
|
* @param {Object} configs.fields 传入选项数据的key value |
||||
|
* @param {String} fields.label 用于显示文字的key |
||||
|
* @param {String} fields.value 用于取值的key |
||||
|
*/ |
||||
|
open(configs = {}) { |
||||
|
const { |
||||
|
dataList, |
||||
|
...config |
||||
|
} = configs || {} |
||||
|
|
||||
|
if (config) { |
||||
|
this.popConfig = deepMerge(this.popConfig, config) |
||||
|
} |
||||
|
this.dataList=[] |
||||
|
this.searchValue="" |
||||
|
this.listInit(dataList) |
||||
|
this.showPopup =true; |
||||
|
}, |
||||
|
/**列表数据初始化*/ |
||||
|
listInit(dataList) { |
||||
|
|
||||
|
this.pageStatus.loading = 'loading' |
||||
|
//如果dataList传入了数组,则不再调用proxyConfig.reqFun请求方法 |
||||
|
if (dataList && dataList instanceof Array) { |
||||
|
console.warn('直接使用传入的dataList') |
||||
|
allDataList = deepClone(dataList) |
||||
|
this.popConfig.proxyConfig.localPaging = true |
||||
|
this.getLocalPagingData() |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if (this.popConfig.proxyConfig.reqFun instanceof Function) { |
||||
|
// if (Object.prototype.toString.call(this.popConfig.proxyConfig.reqFun) != '[object Promise]') { |
||||
|
// throw 'proxyConfig.reqFun需要return Promise.resolve(列表数据)' |
||||
|
// } |
||||
|
this.proxyRequest() |
||||
|
} |
||||
|
}, |
||||
|
/**组件代理请求*/ |
||||
|
proxyRequest() { |
||||
|
console.log("请求proxyRequest") |
||||
|
const { |
||||
|
proxyConfig, |
||||
|
page |
||||
|
} = this.popConfig |
||||
|
this.pageStatus.loading = 'loading' |
||||
|
|
||||
|
proxyConfig.reqFun({ |
||||
|
...page, |
||||
|
searchValue: this.searchValue |
||||
|
}).then(list => { |
||||
|
list.items.forEach(res=>{ |
||||
|
res.code =res.itemCode |
||||
|
res.name =res.itemName; |
||||
|
}) |
||||
|
|
||||
|
if (proxyConfig.localPaging) { //前端本地分页 |
||||
|
allDataList = list.items |
||||
|
this.getLocalPagingData() |
||||
|
return |
||||
|
} |
||||
|
this.pageStatus.loading = 'loadmore' |
||||
|
|
||||
|
// if (!list.items || list.items.length != page.pageSize) { |
||||
|
// this.pageStatus.loading = 'nomore' |
||||
|
// this.pageStatus.isEmpty = true |
||||
|
// } |
||||
|
if (!list.items || list.items.length ==0) { |
||||
|
this.pageStatus.loading = 'nomore' |
||||
|
this.pageStatus.isEmpty = true |
||||
|
} |
||||
|
|
||||
|
this.handleSelected(list.items) |
||||
|
this.dataList = this.dataList.concat(list.items) |
||||
|
}) |
||||
|
}, |
||||
|
close() { |
||||
|
this.showPopup =false; |
||||
|
this.$emit('close') |
||||
|
setTimeout(() => { |
||||
|
this.allDataList = null |
||||
|
this.pageStatus.isEmpty = false |
||||
|
this.pageStatus.loading = 'loading' |
||||
|
this.popConfig = defaultConfig |
||||
|
this.dataList = [] |
||||
|
}, 500) |
||||
|
}, |
||||
|
/**上拉触底*/ |
||||
|
scrolltolower() { |
||||
|
if (!this.pageStatus.isEmpty && this.pageStatus.loading != 'loading') { |
||||
|
this.popConfig.page.pageIndex++ |
||||
|
if (this.popConfig.proxyConfig.localPaging) { |
||||
|
this.getLocalPagingData() |
||||
|
} else { |
||||
|
this.proxyRequest() |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
/**前端本地分页*/ |
||||
|
getLocalPagingData() { |
||||
|
const { |
||||
|
pageIndex, |
||||
|
pageSize |
||||
|
} = this.popConfig.page |
||||
|
const list = allDataList.slice(pageSize * (pageIndex - 1), pageSize + (pageSize * (pageIndex - 1))) |
||||
|
this.pageStatus.loading = 'loadmore' |
||||
|
this.handleSelected(list) |
||||
|
this.dataList = this.dataList.concat(list) |
||||
|
if (!list || list.length != pageSize) { |
||||
|
this.pageStatus.loading = 'nomore' |
||||
|
this.pageStatus.isEmpty = true |
||||
|
} |
||||
|
}, |
||||
|
/**多选点击确定*/ |
||||
|
checkboxConfirm() { |
||||
|
const list = this.dataList.filter(v => v.check) |
||||
|
const data = { |
||||
|
names: [], |
||||
|
values: [] |
||||
|
} |
||||
|
const { |
||||
|
fields, |
||||
|
mode |
||||
|
} = this.popConfig |
||||
|
list.forEach(v => { |
||||
|
data.names.push(v[fields.label]) |
||||
|
data.values.push(v[fields.value]) |
||||
|
}) |
||||
|
this.$emit('select', mode, { |
||||
|
...data, |
||||
|
origin: list, |
||||
|
}) |
||||
|
this.close() |
||||
|
}, |
||||
|
/**选择*/ |
||||
|
checkList(data, index) { |
||||
|
const { |
||||
|
mode, |
||||
|
fields |
||||
|
} = this.popConfig |
||||
|
if (mode == 'radio') { |
||||
|
this.dataList.forEach(v => { |
||||
|
v.check = v[fields.value] == data[fields.value] ? true : false |
||||
|
}) |
||||
|
this.$emit('select', mode, data) |
||||
|
this.close() |
||||
|
} else { |
||||
|
data.check = !data.check |
||||
|
} |
||||
|
}, |
||||
|
/**处理已选中的选项*/ |
||||
|
handleSelected(list) { |
||||
|
const { |
||||
|
selected, |
||||
|
fields |
||||
|
} = this.popConfig |
||||
|
list?.forEach(item => { |
||||
|
const valueCode = item.itemCode |
||||
|
const isChecked = selected instanceof Array ? selected.includes(valueCode) : selected == |
||||
|
valueCode |
||||
|
item.check = isChecked ? true : false |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.select-popup-content { |
||||
|
background-color: #fff; |
||||
|
|
||||
|
.popup-search { |
||||
|
font-size: $uni-font-size-base; |
||||
|
padding: 4px $uni-spacing-row-base; |
||||
|
border-bottom: 1px solid $uni-border-color; |
||||
|
} |
||||
|
|
||||
|
.footer-button { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
text-align: center; |
||||
|
|
||||
|
.button-item { |
||||
|
flex: 1; |
||||
|
padding: 4px 16px; |
||||
|
|
||||
|
.button { |
||||
|
width: 100%; |
||||
|
height: 36px; |
||||
|
line-height: 36px; |
||||
|
font-size: $uni-font-size-base; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.popup-scroll-y { |
||||
|
height: var(--popup-scroll-height); |
||||
|
padding: $uni-spacing-col-sm $uni-spacing-col-base; |
||||
|
box-sizing: border-box; |
||||
|
background-color: $uni-bg-color-grey; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,82 @@ |
|||||
|
{ |
||||
|
"id": "wz-select-popup", |
||||
|
"displayName": "popup弹窗选择器", |
||||
|
"version": "1.0.11", |
||||
|
"description": "popup弹窗选择器支持单选,多选,组件内部代理请求,自定义label,value显示,分页加载,", |
||||
|
"keywords": [ |
||||
|
"wz-select-popup" |
||||
|
], |
||||
|
"repository": "", |
||||
|
"engines": { |
||||
|
"HBuilderX": "^3.96" |
||||
|
}, |
||||
|
"dcloudext": { |
||||
|
"type": "component-vue", |
||||
|
"sale": { |
||||
|
"regular": { |
||||
|
"price": "0.00" |
||||
|
}, |
||||
|
"sourcecode": { |
||||
|
"price": "0.00" |
||||
|
} |
||||
|
}, |
||||
|
"contact": { |
||||
|
"qq": "" |
||||
|
}, |
||||
|
"declaration": { |
||||
|
"ads": "无", |
||||
|
"data": "插件不采集任何数据", |
||||
|
"permissions": "无" |
||||
|
}, |
||||
|
"npmurl": "" |
||||
|
}, |
||||
|
"uni_modules": { |
||||
|
"dependencies": ["uni-icons", "uni-popup"], |
||||
|
"encrypt": [], |
||||
|
"platforms": { |
||||
|
"cloud": { |
||||
|
"tcb": "y", |
||||
|
"aliyun": "y" |
||||
|
}, |
||||
|
"client": { |
||||
|
"Vue": { |
||||
|
"vue2": "y", |
||||
|
"vue3": "u" |
||||
|
}, |
||||
|
"App": { |
||||
|
"app-vue": "y", |
||||
|
"app-nvue": "u", |
||||
|
"app-uvue": "n" |
||||
|
}, |
||||
|
"H5-mobile": { |
||||
|
"Safari": "u", |
||||
|
"Android Browser": "u", |
||||
|
"微信浏览器(Android)": "y", |
||||
|
"QQ浏览器(Android)": "u" |
||||
|
}, |
||||
|
"H5-pc": { |
||||
|
"Chrome": "y", |
||||
|
"IE": "u", |
||||
|
"Edge": "y", |
||||
|
"Firefox": "u", |
||||
|
"Safari": "u" |
||||
|
}, |
||||
|
"小程序": { |
||||
|
"微信": "y", |
||||
|
"阿里": "u", |
||||
|
"百度": "u", |
||||
|
"字节跳动": "u", |
||||
|
"QQ": "u", |
||||
|
"钉钉": "u", |
||||
|
"快手": "u", |
||||
|
"飞书": "u", |
||||
|
"京东": "u" |
||||
|
}, |
||||
|
"快应用": { |
||||
|
"华为": "u", |
||||
|
"联盟": "u" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,132 @@ |
|||||
|
# wz-select-popup |
||||
|
|
||||
|
## 示例 |
||||
|
|
||||
|
```vue |
||||
|
<template> |
||||
|
<view> |
||||
|
<view class="" style="margin-bottom: 16px;"> |
||||
|
<view style="margin-bottom: 8px;">names:{{userForm.names}}</view> |
||||
|
<view style="margin-bottom: 8px;">values:{{userForm.values}}</view> |
||||
|
<view style="margin-bottom: 8px;">origin:{{userForm.origin}}</view> |
||||
|
</view> |
||||
|
<button @click="open">打开</button> |
||||
|
<wz-select-popup ref="wzSelectPopup" @select="selectCheckbox" /> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { |
||||
|
simulationReqGetList, |
||||
|
getDataList |
||||
|
} from "@/common/mock.js" |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
userForm: { |
||||
|
names: [], |
||||
|
values: [], |
||||
|
origin:[] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
open() { |
||||
|
this.$refs.wzSelectPopup.open({ |
||||
|
mode: 'checkbox', //radio checkbox 单选、多选 |
||||
|
// dataList:[], //如果dataList传入了数组则直接使用传入的数组渲染,无需再配置proxyConfig |
||||
|
selected: this.userForm.values, //已选中的数据 |
||||
|
proxyConfig: { //组件内部代理请求数据配置 |
||||
|
reqFun: this.reqGetList, //请求方法,在方法中请返回Promise.resolve([]) |
||||
|
localPaging: false //前端本地分页 |
||||
|
}, |
||||
|
search: { |
||||
|
type: 'local', //local本地数据搜索 | remote请求接口 |
||||
|
}, |
||||
|
fields: { |
||||
|
label: 'name', |
||||
|
value: 'code' |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
selectCheckbox(mode, data) { |
||||
|
console.log(mode, data) |
||||
|
this.userForm.names = data.names |
||||
|
this.userForm.values = data.values |
||||
|
this.userForm.origin = data.origin |
||||
|
}, |
||||
|
reqGetList(data) { |
||||
|
return simulationReqGetList({ |
||||
|
pageIndex: data.pageIndex, |
||||
|
pageSize: data.pageSize, |
||||
|
name: data.searchValue |
||||
|
}) |
||||
|
}, |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
|
||||
|
</style> |
||||
|
``` |
||||
|
|
||||
|
## 属性props |
||||
|
|
||||
|
| 属性 | 类型 | 默认值 | 说明 | |
||||
|
| ------- | ------ | ------ | ------------------------------------------------------------ | |
||||
|
| scrollH | String | 50vh | 滚动内容的高度,不包含搜索框和多选时的底部按钮栏。需要带单位 | |
||||
|
|
||||
|
|
||||
|
## 方法methods |
||||
|
|
||||
|
| 方法名 | 说明 | 参数 | |
||||
|
| ------ | -------- | ----------------------------- | |
||||
|
| open | 打开弹窗 | [configs](#open方法的configs) | |
||||
|
| close | 关闭弹窗 | | |
||||
|
|
||||
|
### open方法的configs |
||||
|
|
||||
|
| 参数名 | 说明 | 类型 | 默认值 | |
||||
|
| ----------- | ------------------------------------------------------------ | ------------- | -------------------------- | |
||||
|
| mode | 选择模式radio单选 ,checkbox多选 | String | radio | |
||||
|
| selected | 已选中的value, 单选传字符串,多选传数组 | String\|Array | [] | |
||||
|
| dataList | 传入了数组,则直接使用传入的数组渲染,无需再配置proxyConfig | Array | null | |
||||
|
| proxyConfig | 组件内部代理请求数据配置 | Object | [proxyConfig](#proxyConfig的属性) | |
||||
|
| search | 搜索配置 | Object | [search](#search的属性) | |
||||
|
| page | 分页配置,如果是组件内部代理请求会把page的值加入到proxyConfig.reqFun请求参数里 | Object | [page](#page的属性) | |
||||
|
| fields | 显示和取值的label,value | Object | [fields](#fields的属性) | |
||||
|
|
||||
|
#### proxyConfig的属性 |
||||
|
|
||||
|
| 参数名 | 说明 | 类型 | 默认值 | 返回值 | |
||||
|
| ----------- | --------------------------------------------------------- | ------- | ------------------- | --------------------------------------- | |
||||
|
| reqFun | 向服务器请求数据的Promise方法,会返回页码以及搜索框的value | Promise | Promise.resolve([]) | ({pageIndex, pageSize,searchValue})=>{} | |
||||
|
| localPaging | 是否本地分页 | Boolean | false | | |
||||
|
|
||||
|
#### search的属性 |
||||
|
|
||||
|
| 参数名 | 说明 | 类型 | 默认值 | |
||||
|
| ------ | ------------------------------------------------------------ | ------ | ------ | |
||||
|
| type | 搜索的类型,local本地搜索, remote向服务器请求。只有使用proxyConfig.reqFun请求才能配置为remote | String | local | |
||||
|
|
||||
|
#### page的属性 |
||||
|
|
||||
|
| 参数名 | 说明 | 类型 | 默认值 | |
||||
|
| --------- | ------ | ------ | ------ | |
||||
|
| pageIndex | 当前页 | Number | 1 | |
||||
|
| pageSize | 页大小 | Number | 20 | |
||||
|
|
||||
|
#### fields的属性 |
||||
|
|
||||
|
| 参数名 | 说明 | 类型 | 默认值 | |
||||
|
| ------ | ------------ | ------ | ------ | |
||||
|
| label | 显示的字段名 | String | name | |
||||
|
| value | 取值的字段名 | String | code | |
||||
|
|
||||
|
## 事件enevt |
||||
|
|
||||
|
| 名称 | 触发时机 | 返回值 | |
||||
|
| ------ | ------------------------------------------ | ------------------------------------------------------------ | |
||||
|
| select | 单选点击列表时触发,多选点击确定按钮时触发 | mode等于radio时:(mode, {check,name,code})=>{}<br/>mode等于checkbox时:(mode, {names,values,origin})=>{} | |
||||
|
| close | popup关闭 | void | |
Loading…
Reference in new issue