feat:还原更新框架

This commit is contained in:
2026-07-23 14:49:48 +08:00
parent d5a6a16ea5
commit 75c7434f79
529 changed files with 43714 additions and 68201 deletions
@@ -1,43 +1,31 @@
/*
* @Author : LQ
* @Description :
* @version : 3.0
* @version : 1.0
* @Date : 2021-08-20 16:44:21
* @LastAuthor : jry
* @lastTime : 2025-12-19 08:55:21
* @FilePath : /uview-plus/libs/config/props/picker.js
* @LastAuthor : LQ
* @lastTime : 2021-08-20 17:18:20
* @FilePath : /u-view2.0/uview-ui/libs/config/props/picker.js
*/
import { t } from '../../libs/i18n';
export default {
// picker
picker: {
show: false,
popupMode: 'bottom',
popupMode: 'bottom',
showToolbar: true,
title: '',
columns: [],
loading: false,
itemHeight: 44,
cancelText: t('up.common.cancel'),
confirmText: t('up.common.confirm'),
cancelText: '取消',
confirmText: '确定',
cancelColor: '#909193',
confirmColor: '',
confirmColor: '#3c9cff',
visibleItemCount: 5,
keyName: 'text',
valueName: 'value',
closeOnClickOverlay: false,
defaultIndex: [],
immediateChange: true,
zIndex: 10076,
disabled: false,
disabledColor: '',
placeholder: t('up.common.pleaseChoose'),
inputProps: {},
bgColor: '',
round: 0,
duration: 300,
overlayOpacity: 0.5,
pageInline: false
immediateChange: true,
zIndex: 10076,
}
};
}
@@ -1,169 +1,117 @@
import { defineMixin } from '../../libs/vue'
import PickerDefaultProps from './picker'
import { registerComponentProps } from '../../libs/config/props.js'
const defProps = registerComponentProps(PickerDefaultProps)
export const props = defineMixin({
props: {
modelValue: {
type: Array,
default: () => []
},
hasInput: {
type: Boolean,
default: false
},
inputProps: {
type: Object,
default: () => {
return {}
}
},
inputBorder: {
type: String,
default: () => defProps.input.inputBorder
},
disabled: {
type: Boolean,
default: () => defProps.picker.disabled
},
disabledColor:{
type: String,
default: () => defProps.picker.disabledColor
},
placeholder: {
type: String,
default: () => defProps.picker.placeholder
},
// 是否展示picker弹窗
show: {
type: Boolean,
default: () => defProps.picker.show
},
// 弹出的方向,可选值为 top bottom right left center
popupMode: {
type: String,
default: () => defProps.picker.popupMode
},
// 是否展示顶部的操作栏
showToolbar: {
type: Boolean,
default: () => defProps.picker.showToolbar
},
// 顶部标题
title: {
type: String,
default: () => defProps.picker.title
},
// 对象数组,设置每一列的数据
columns: {
type: Array,
default: () => defProps.picker.columns
},
// 是否显示加载中状态
loading: {
type: Boolean,
default: () => defProps.picker.loading
},
// 各列中,单个选项的高度
itemHeight: {
type: [String, Number],
default: () => defProps.picker.itemHeight
},
// 取消按钮的文字
cancelText: {
type: String,
default: () => defProps.picker.cancelText
},
// 确认按钮的文字
confirmText: {
type: String,
default: () => defProps.picker.confirmText
},
// 取消按钮的颜色
cancelColor: {
type: String,
default: () => defProps.picker.cancelColor
},
// 确认按钮的颜色
confirmColor: {
type: String,
default: () => defProps.picker.confirmColor
},
// 每列中可见选项的数量
visibleItemCount: {
type: [String, Number],
default: () => defProps.picker.visibleItemCount
},
// 选项对象中,需要展示的属性键名
keyName: {
type: String,
default: () => defProps.picker.keyName
},
// 选项对象中,需要获取的属性值键名
valueName: {
type: String,
default: () => defProps.picker.valueName
},
// 是否允许点击遮罩关闭选择器
closeOnClickOverlay: {
type: Boolean,
default: () => defProps.picker.closeOnClickOverlay
},
// 各列的默认索引
defaultIndex: {
type: Array,
default: () => defProps.picker.defaultIndex
},
// 是否在手指松开时立即触发 change 事件。若不开启则会在滚动动画结束后触发 change 事件,只在微信2.21.1及以上有效
immediateChange: {
type: Boolean,
default: () => defProps.picker.immediateChange
},
// 工具栏右侧插槽是否开启
toolbarRightSlot: {
type: Boolean,
default: false
},
// 层级
zIndex: {
type: [String, Number],
default: () => defProps.picker.zIndex
},
// 弹窗背景色,设置为transparent可去除白色背景
bgColor: {
type: String,
default: () => defProps.picker.bgColor
},
// 是否显示圆角
round: {
type: [Boolean, String, Number],
default: () => defProps.picker.round
},
// 动画时长,单位ms
duration: {
type: [String, Number],
default: () => defProps.picker.duration
},
// 遮罩的透明度,0-1之间
overlayOpacity: {
type: [Number, String],
default: () => defProps.picker.overlayOpacity
},
// 是否页面内展示
pageInline:{
type: Boolean,
default: () => defProps.picker.pageInline
},
// 蒙层样式样式
maskClass: {
type: String,
defualt: ''
},
// 蒙层样式样式
maskStyle: {
type: String,
defualt: ''
}
}
})
import { defineMixin } from '../../libs/vue'
import defProps from '../../libs/config/props.js'
export const props = defineMixin({
props: {
modelValue: {
type: Array,
default: () => []
},
//是否禁用
disabled: {
type: Boolean,
default: false
},
disabledColor:{
type: String,
default: () => defProps.input.disabledColor
},
hasInput: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: () => '请选择'
},
// 是否展示picker弹窗
show: {
type: Boolean,
default: () => defProps.picker.show
},
// 弹出的方向,可选值为 top bottom right left center
popupMode: {
type: String,
default: () => defProps.picker.popupMode
},
// 是否展示顶部的操作栏
showToolbar: {
type: Boolean,
default: () => defProps.picker.showToolbar
},
// 顶部标题
title: {
type: String,
default: () => defProps.picker.title
},
// 对象数组,设置每一列的数据
columns: {
type: Array,
default: () => defProps.picker.columns
},
// 是否显示加载中状态
loading: {
type: Boolean,
default: () => defProps.picker.loading
},
// 各列中,单个选项的高度
itemHeight: {
type: [String, Number],
default: () => defProps.picker.itemHeight
},
// 取消按钮的文字
cancelText: {
type: String,
default: () => defProps.picker.cancelText
},
// 确认按钮的文字
confirmText: {
type: String,
default: () => defProps.picker.confirmText
},
// 取消按钮的颜色
cancelColor: {
type: String,
default: () => defProps.picker.cancelColor
},
// 确认按钮的颜色
confirmColor: {
type: String,
default: () => defProps.picker.confirmColor
},
// 每列中可见选项的数量
visibleItemCount: {
type: [String, Number],
default: () => defProps.picker.visibleItemCount
},
// 选项对象中,需要展示的属性键名
keyName: {
type: String,
default: () => defProps.picker.keyName
},
// 是否允许点击遮罩关闭选择器
closeOnClickOverlay: {
type: Boolean,
default: () => defProps.picker.closeOnClickOverlay
},
// 各列的默认索引
defaultIndex: {
type: Array,
default: () => defProps.picker.defaultIndex
},
// 是否在手指松开时立即触发 change 事件。若不开启则会在滚动动画结束后触发 change 事件,只在微信2.21.1及以上有效
immediateChange: {
type: Boolean,
default: () => defProps.picker.immediateChange
},
// 工具栏右侧插槽是否开启
toolbarRightSlot: {
type: Boolean,
default: false
},
// 层级
zIndex: {
type: [String, Number],
default: () => defProps.picker.zIndex
},
}
})
@@ -1,27 +1,15 @@
<template>
<view class="u-picker-wraper">
<view v-if="hasInput" class="u-picker-input cursor-pointer" @click="onShowByClickInput">
<slot :value="inputLabel">
</slot>
<slot name="trigger" :value="inputLabel">
</slot>
<up-input
v-if="!$slots['default'] && !$slots['$default'] && !$slots['trigger']"
:readonly="true"
v-model="inputLabel"
v-bind="inputPropsInner">
</up-input>
<cover-view class="input-cover"></cover-view>
<view class="u-picker-warrper">
<view v-if="hasInput" class="u-picker-input cursor-pointer" @click="onShowByClickInput">
<slot>
<up-input :disabled="disabled" :disabledColor="disabledColor" :placeholder="placeholder" :readonly="true" border="surround" v-model="inputLabel"></up-input>
<div class="input-cover"></div>
</slot>
</view>
<u-popup
:show="show || (hasInput && showByClickInput)"
:mode="popupMode"
:zIndex="zIndex"
:bgColor="bgColor"
:round="round"
:duration="duration"
:pageInline="pageInline"
:overlayOpacity="overlayOpacity"
@close="closeHandler"
>
<view class="u-picker">
@@ -43,13 +31,11 @@
<slot name="toolbar-bottom"></slot>
<picker-view
class="u-picker__view"
:mask-class="maskClass"
:mask-style="maskStyleInner"
:indicatorStyle="`height: ${addUnit(itemHeight, 'px')}`"
:indicatorStyle="`height: ${addUnit(itemHeight)}`"
:value="innerIndex"
:immediateChange="immediateChange"
:style="{
height: `${addUnit(visibleItemCount * itemHeight, 'px')}`
height: `${addUnit(visibleItemCount * itemHeight)}`
}"
@change="changeHandler"
>
@@ -65,8 +51,8 @@
v-for="(item1, index1) in item"
:key="index1"
:style="{
height: addUnit(itemHeight, 'px'),
lineHeight: addUnit(itemHeight, 'px'),
height: addUnit(itemHeight),
lineHeight: addUnit(itemHeight),
fontWeight: index1 === innerIndex[index] ? 'bold' : 'normal',
display: 'block'
}"
@@ -103,10 +89,6 @@
* @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭选择器(默认 false )
* @property {Array} defaultIndex 各列的默认索引
* @property {Boolean} immediateChange 是否在手指松开时立即触发change事件(默认 true )
* @property {String | Number} round 圆角值(默认 0)
* @property {String } bgColor 背景色值(默认 '' )
* @property {String | Number} duration 动画时长,单位ms (默认 300 )
* @property {String | Number} overlayDuration 遮罩层动画时长,单位ms (默认 350 )
* @event {Function} close 关闭选择器时触发
* @event {Function} cancel 点击取消按钮触发
* @event {Function} change 当选择值变化时触发
@@ -135,6 +117,19 @@ export default {
}
},
watch: {
// 监听默认索引的变化,重新设置对应的值
defaultIndex: {
immediate: true,
deep:true,
handler(n,o) {
// 修复uniapp调用子组件直接:defaultIndex="[0]"这样写
// v-model的值变化时候导致defaultIndexwatch也会执行的问题
//单纯vue不会出现
if (!o || n.join("/") != o.join("/")) {
this.setIndexs(n, true)
}
}
},
// 监听columns参数的变化
columns: {
immediate: true,
@@ -143,113 +138,40 @@ export default {
this.setColumns(n)
}
},
// 监听默认索引的变化,重新设置对应的值
defaultIndex: {
immediate: true,
deep:true,
handler(n,o) {
// 修复uniapp调用子组件直接:defaultIndex="[0]"这样写
// v-model的值变化时候导致defaultIndexwatch也会执行的问题
//单纯vue不会出现
if (!o || n.join("/") != o.join("/")) {
this.setIndexs(n, true)
}
}
},
modelValue: {
immediate: true,
deep:true,
handler(n,o) {
// 修复uniapp调用子组件直接:defaultIndex="[0]"这样写
// v-model的值变化时候导致defaultIndexwatch也会执行的问题
//单纯vue不会出现
if (!o || n.join("/") != o.join("/")) {
let arr = [];
if (n != null) {
n.forEach((element, index) => {
let currentCols = this.getColumnValues(index)
if(!Array.isArray(currentCols) && currentCols.length===0) {
return
}
if (typeof currentCols[0] === 'object') {
currentCols.forEach((item, index2) => {
if (item[this.valueName] == element) {
arr.push(index2)
}
})
} else {
currentCols.forEach((item, index2) => {
if (item == element) {
arr.push(index2)
}
})
}
});
// alert(arr)
if (arr.length == 0 && this.defaultIndex) {
} else {
this.setIndexs(arr, true)
}
}
}
}
}
},
emits: ['close', 'cancel', 'confirm', 'change', 'update:modelValue', 'update:show'],
computed: {
// input的props
inputPropsInner() {
return {
border: this.inputBorder,
placeholder: this.placeholder,
disabled: this.disabled,
disabledColor: this.disabledColor,
...this.inputProps
}
},
//已选&&已确认的值显示在input上面的文案
inputLabel() {
let firstItem = this.innerColumns[0] && this.innerColumns[0][0];
// //区分是不是对象数组
if (firstItem && Object.prototype.toString.call(firstItem) === '[object Object]') {
let res = this.innerColumns[0].filter(item => this.modelValue.includes(item[this.valueName]))
res = res.map(item => item[this.keyName]);
return res.join("/");
} else {
//用户确定的值,才显示到输入框
return this.modelValue.join("/");
}
},
//已选,待确认的值
inputValue() {
let items = this.innerColumns.map((item, index) => item[this.innerIndex[index]])
let res = []
//区分是不是对象数组
if (items[0] && Object.prototype.toString.call(items[0]) === '[object Object]') {
//对象数组返回属性值集合
items.forEach(element => {
res.push(element && element[this.valueName])
});
} else {
//非对象数组返回元素集合
items.forEach((element, index) => {
res.push(element)
});
}
return res
},
maskStyleInner() {
// 显式传入时,始终以外部传参为准(即使传空字符串)
if (this.upHasProp('maskStyle')) {
return this.maskStyle || ''
}
if (this.upThemeIsDark) {
const topBottomStrong = 'rgba(28, 28, 30, 0.95)'
const topBottomWeak = 'rgba(28, 28, 30, 0.6)'
return `background-image: linear-gradient(180deg, ${topBottomStrong}, ${topBottomWeak}), linear-gradient(0deg, ${topBottomStrong}, ${topBottomWeak});`
}
return ''
//已选&&已确认的值显示在input上面的文案
inputLabel() {
let firstItem = this.innerColumns[0] && this.innerColumns[0][0];
// //区分是不是对象数组
if (firstItem && Object.prototype.toString.call(firstItem) === '[object Object]') {
let res = this.innerColumns[0].filter(item => this.modelValue.includes(item['id']))
res = res.map(item => item[this.keyName]);
return res.join("/");
} else {
//用户确定的值,才显示到输入框
return this.modelValue.join("/");
}
},
//已选,待确认的值
inputValue() {
let items = this.innerColumns.map((item, index) => item[this.innerIndex[index]])
let res = []
//区分是不是对象数组
if (items[0] && Object.prototype.toString.call(items[0]) === '[object Object]') {
//对象数组返回id集合
items.forEach(element => {
res.push(element && element['id'])
});
} else {
//非对象数组返回元素集合
items.forEach((element, index) => {
res.push(element)
});
}
return res
}
},
methods: {
@@ -274,7 +196,6 @@ export default {
if (this.hasInput) {
this.showByClickInput = false
}
this.setDefault()
this.$emit('update:show', false)
this.$emit('close')
}
@@ -284,13 +205,14 @@ export default {
if (this.hasInput) {
this.showByClickInput = false
}
this.setDefault()
this.$emit('update:show', false)
this.$emit('cancel')
},
setDefault() {
let arr = [0]
if (this.lastIndex.length == 0) {
// 点击工具栏的确定按钮
confirm() {
//如果用户有没有触发过change
if (!this.currentActiveValue.length) {
let arr = [0]
//如果有默认值&&默认值的数组长度是正确的,就用默认值
if (Array.isArray(this.defaultIndex) && this.defaultIndex.length == this.innerColumns.length) {
arr = [...this.defaultIndex];
@@ -298,23 +220,13 @@ export default {
//否则默认都选中第一个
arr = Array(this.innerColumns.length).fill(0);
}
} else {
arr = deepClone(this.lastIndex)
}
this.setLastIndex(arr)
this.setIndexs(arr)
},
// 点击工具栏的确定按钮
confirm() {
// 如果用户有没有触发过change
if (!this.currentActiveValue.length) {
this.setDefault()
this.setLastIndex(arr)
this.setIndexs(arr)
}
this.$emit('update:modelValue', this.inputValue)
if (this.hasInput) {
this.showByClickInput = false
}
this.setLastIndex(this.innerIndex)
this.$emit('update:show', false)
this.$emit('confirm', {
indexs: this.innerIndex,
@@ -334,7 +246,7 @@ export default {
// 通过对比前后两次的列索引,得出当前变化的是哪一列
for (let i = 0; i < value.length; i++) {
let item = value[i]
if (item !== undefined && item !== (this.lastIndex[i] || 0)) { // 把undefined转为合法假值0
if (item !== (this.lastIndex[i] || 0)) { // 把undefined转为合法假值0
// 设置columnIndex为当前变化列的索引
columnIndex = i
// index则为变化列中的变化项的索引
@@ -345,14 +257,13 @@ export default {
this.columnIndex = columnIndex
const values = this.innerColumns
// 将当前的各项变化索引,设置为"上一次"的索引变化值
// this.setLastIndex(value)
this.setLastIndex(value)
this.setIndexs(value)
//如果是非自带输入框才会在change时候触发v-model绑值的变化
//否则会非常的奇怪,用户未确认,值就变了
// if (!this.hasInput) {
// this.$emit('update:modelValue', this.inputValue)
// }
if (!this.hasInput) {
this.$emit('update:modelValue', this.inputValue)
}
this.$emit('change', {
// #ifndef MP-WEIXIN || MP-LARK
// 微信小程序不能传递this,会因为循环引用而报错
@@ -407,25 +318,10 @@ export default {
// 设置整体各列的columns的值
setColumns(columns) {
// console.log(columns)
const prevColumns = this.innerColumns
this.innerColumns = deepClone(columns)
// 如果在设置各列数据时,没有被设置默认的各列索引defaultIndex,那么用0去填充它,数组长度为列的数量
if (this.innerIndex.length === 0) {
this.innerIndex = new Array(columns.length).fill(0)
} else {
// 修复异步columns加载时defaultIndex位置不更新的问题 (issue #841)
// 当某列从空数组变为有数据时,picker-view不会因为:value未变化而重新滚动到指定位置
// 需要通过先清空再赋值的方式强制触发picker-view的滚动更新
const hasColumnsChangedFromEmpty = columns.some(
(col, i) => col && col.length > 0 && (!prevColumns[i] || prevColumns[i].length === 0)
)
if (hasColumnsChangedFromEmpty) {
const targetIndex = deepClone(this.innerIndex)
this.innerIndex = []
this.$nextTick(() => {
this.innerIndex = targetIndex
})
}
}
},
// 获取各列选中值对应的索引
@@ -446,20 +342,21 @@ export default {
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
.u-picker {
position: relative;
&-input {
position: relative;
.input-cover {
opacity: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
&-input {
position: relative;
.input-cover {
opacity: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index:1;
}
z-index:1;
}
}
&__view {
@@ -498,7 +395,7 @@ export default {
@include flex;
justify-content: center;
align-items: center;
background-color: var(--up-card-bg-color, #ffffff);
background-color: rgba(255, 255, 255, 0.87);
z-index: 1000;
}
}