uview-plus组件库全面升级更新,订单结算判断支付方式是否可用代码调整,公众号关注二维码修改
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<view class="u-action-sheet-data">
|
||||
<view class="u-action-sheet-data__trigger">
|
||||
<slot name="trigger"></slot>
|
||||
<up-input
|
||||
v-if="!$slots['trigger']"
|
||||
:modelValue="current"
|
||||
disabled
|
||||
disabledColor="#ffffff"
|
||||
:placeholder="title"
|
||||
border="none"
|
||||
></up-input>
|
||||
<view @click="show = true"
|
||||
class="u-action-sheet-data__trigger__cover"></view>
|
||||
</view>
|
||||
<up-action-sheet
|
||||
:show="show"
|
||||
:actions="options"
|
||||
:title="title"
|
||||
safeAreaInsetBottom
|
||||
:description="description"
|
||||
@close="show = false"
|
||||
@select="select"
|
||||
>
|
||||
</up-action-sheet>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
valueKey: {
|
||||
type: String,
|
||||
default: 'value'
|
||||
},
|
||||
labelKey: {
|
||||
type: String,
|
||||
default: 'name'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
current: '',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.modelValue) {
|
||||
this.options.forEach((ele) => {
|
||||
if (ele[this.valueKey] == this.modelValue) {
|
||||
this.current = ele[this.labelKey]
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.options.forEach((ele) => {
|
||||
if (ele[this.valueKey] == this.modelValue) {
|
||||
this.current = ele[this.labelKey]
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hideKeyboard() {
|
||||
uni.hideKeyboard()
|
||||
},
|
||||
select(e) {
|
||||
this.$emit('update:modelValue', e[this.valueKey])
|
||||
this.current = e[this.labelKey]
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-action-sheet-data {
|
||||
&__trigger {
|
||||
position: relative;
|
||||
&__cover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 1.0
|
||||
* @version : 3.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:44:35
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/actionSheet.js
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-08-16 10:52:35
|
||||
* @FilePath : /uview-plus/libs/config/props/actionSheet.js
|
||||
*/
|
||||
export default {
|
||||
// action-sheet组件
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-08-16 10:52:35
|
||||
* @FilePath : /uview-plus/libs/config/props/props.js
|
||||
*/
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import defProps from '../../libs/config/props.js'
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
<template>
|
||||
<u-popup
|
||||
:show="show"
|
||||
@@ -8,6 +7,7 @@
|
||||
:round="round"
|
||||
>
|
||||
<view class="u-action-sheet">
|
||||
<!-- 顶部标题区域 -->
|
||||
<view
|
||||
class="u-action-sheet__header"
|
||||
v-if="title"
|
||||
@@ -17,14 +17,15 @@
|
||||
class="u-action-sheet__header__icon-wrap"
|
||||
@tap.stop="cancel"
|
||||
>
|
||||
<u-icon
|
||||
<up-icon
|
||||
name="close"
|
||||
size="17"
|
||||
color="#c8c9cc"
|
||||
bold
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 描述信息 -->
|
||||
<text
|
||||
class="u-action-sheet__description"
|
||||
:style="[{
|
||||
@@ -33,7 +34,9 @@
|
||||
v-if="description"
|
||||
>{{description}}</text>
|
||||
<slot>
|
||||
<!-- 分割线 -->
|
||||
<u-line v-if="description"></u-line>
|
||||
<!-- 操作项列表 -->
|
||||
<scroll-view scroll-y class="u-action-sheet__item-wrap" :style="{maxHeight: wrapMaxHeight}">
|
||||
<view :key="index" v-for="(item, index) in actions">
|
||||
<!-- #ifdef MP -->
|
||||
@@ -62,6 +65,7 @@
|
||||
@tap.stop="selectHandler(index)"
|
||||
:hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
|
||||
:hover-stay-time="150"
|
||||
:style="getItemHoverStyle(index)"
|
||||
>
|
||||
<template v-if="!item.loading">
|
||||
<text
|
||||
@@ -73,6 +77,7 @@
|
||||
class="u-action-sheet__item-wrap__item__subname"
|
||||
>{{ item.subname }}</text>
|
||||
</template>
|
||||
<!-- 加载状态图标 -->
|
||||
<u-loading-icon
|
||||
v-else
|
||||
custom-class="van-action-sheet__loading"
|
||||
@@ -83,15 +88,18 @@
|
||||
<!-- #ifdef MP -->
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
<!-- 选项间分割线 -->
|
||||
<u-line v-if="index !== actions.length - 1"></u-line>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</slot>
|
||||
<!-- 取消按钮前的分割区域 -->
|
||||
<u-gap
|
||||
bgColor="#eaeaec"
|
||||
height="6"
|
||||
v-if="cancelText"
|
||||
></u-gap>
|
||||
<!-- 取消按钮 -->
|
||||
<view class="u-action-sheet__item-wrap__item u-action-sheet__cancel"
|
||||
hover-class="u-action-sheet--hover" @tap="cancel" v-if="cancelText">
|
||||
<text
|
||||
@@ -168,6 +176,7 @@
|
||||
},
|
||||
emits: ["close", "select", "update:show"],
|
||||
methods: {
|
||||
// 关闭操作菜单事件处理
|
||||
closeHandler() {
|
||||
// 允许点击遮罩关闭时,才发出close事件
|
||||
if(this.closeOnClickOverlay) {
|
||||
@@ -180,6 +189,7 @@
|
||||
this.$emit('update:show', false)
|
||||
this.$emit('close')
|
||||
},
|
||||
// 选择操作项处理
|
||||
selectHandler(index) {
|
||||
const item = this.actions[index]
|
||||
if (item && !item.disabled && !item.loading) {
|
||||
@@ -190,12 +200,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
// 动态处理Hover时候第一个item的圆角
|
||||
getItemHoverStyle(index) {
|
||||
if (index === 0 && this.round && !this.title && !this.description) {
|
||||
return {
|
||||
borderTopLeftRadius: `${this.round}px`,
|
||||
borderTopRightRadius: `${this.round}px`,
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
$u-action-sheet-reset-button-width:100% !default;
|
||||
$u-action-sheet-title-font-size: 16px !default;
|
||||
$u-action-sheet-title-padding: 12px 30px !default;
|
||||
@@ -280,4 +299,4 @@
|
||||
background-color: $u-action-sheet-cancel-text-hover-background-color;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
<style scoped lang="scss">
|
||||
.agreement-content {
|
||||
width: 100%;;
|
||||
display: inline-block;
|
||||
flex-direction: column;
|
||||
.agreement-url {
|
||||
display: inline-block;
|
||||
color: blue;
|
||||
// #ifdef H5
|
||||
cursor: pointer;
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<view class="up-agreement">
|
||||
<up-modal v-model:show="show" showCancelButton @confirm="confirm" @cancel="close" confirmText="阅读并同意">
|
||||
<view class="agreement-content">
|
||||
<slot>
|
||||
我们非常重视您的个人信息和隐私保护。为了更好地保障您的个人权益,在您使用我们的产品前,
|
||||
请务必审慎阅读《<text class="agreement-url" @click="urlClick('urlProtocol')">用户协议</text>》
|
||||
和《<text class="agreement-url" @click="urlClick('urlPrivacy')">隐私政策</text>》内的所有条款,
|
||||
尤其是:1.我们对您的个人信息的收集/保存/使用/对外提供/保护等规则条款,以及您的用户权利等条款;2. 约定我们的限制责任、免责
|
||||
条款;3.其他以颜色或加粗进行标识的重要条款。如您对以上协议有任何疑问,请先不要同意,您点击“同意并继续”的行为即表示您已阅读
|
||||
完毕并同意以上协议的全部内容。
|
||||
</slot>
|
||||
</view>
|
||||
</up-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'up-agreement',
|
||||
props: {
|
||||
urlProtocol: {
|
||||
type: String,
|
||||
default: '/pages/user_agreement/agreement/info?title=用户协议'
|
||||
},
|
||||
urlPrivacy: {
|
||||
type: String,
|
||||
default: '/pages/user_agreement/agreement/info?title=隐私政策'
|
||||
},
|
||||
},
|
||||
emits: ['confirm'],
|
||||
data() {
|
||||
return {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
// #ifdef H5
|
||||
window.opener = null;
|
||||
window.close();
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.quit();
|
||||
// #endif
|
||||
},
|
||||
confirm() {
|
||||
this.show = false;
|
||||
this.$emit('confirm', 1);
|
||||
},
|
||||
showModal() {
|
||||
this.show = true;
|
||||
},
|
||||
urlClick(type) {
|
||||
uni.navigateTo({
|
||||
url: this[type]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -3,9 +3,9 @@
|
||||
* @Description :
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:47:24
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/album.js
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-08-16 16:32:24
|
||||
* @FilePath : /uview-plus/libs/config/props/album.js
|
||||
*/
|
||||
export default {
|
||||
// album 组件
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
/*
|
||||
* @Author : jry
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-08-16 16:35:24
|
||||
* @FilePath : /uview-plus/components/u-album/props.js
|
||||
*/
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import defProps from '../../libs/config/props.js'
|
||||
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 图片地址,Array<String>|Array<Object>形式
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<view class="u-album">
|
||||
<!-- 相册行容器,每行显示 rowCount 个图片 -->
|
||||
<view
|
||||
class="u-album__row"
|
||||
ref="u-album__row"
|
||||
@@ -8,13 +9,15 @@
|
||||
:key="index"
|
||||
:style="{flexWrap: autoWrap ? 'wrap' : 'nowrap'}"
|
||||
>
|
||||
<!-- 图片包装容器 -->
|
||||
<view
|
||||
class="u-album__row__wrapper"
|
||||
v-for="(item, index1) in arr"
|
||||
:key="index1"
|
||||
:style="[imageStyle(index + 1, index1 + 1)]"
|
||||
@tap="previewFullImage ? onPreviewTap($event, getSrc(item)) : ''"
|
||||
@tap="onPreviewTap($event, getSrc(item))"
|
||||
>
|
||||
<!-- 图片显示 -->
|
||||
<image
|
||||
:src="getSrc(item)"
|
||||
:mode="
|
||||
@@ -32,6 +35,7 @@
|
||||
}
|
||||
]"
|
||||
></image>
|
||||
<!-- 超出最大显示数量时的更多提示 -->
|
||||
<view
|
||||
v-if="
|
||||
showMore &&
|
||||
@@ -64,7 +68,7 @@ import { mixin } from '../../libs/mixin/mixin';
|
||||
import { addUnit, sleep } from '../../libs/function/index';
|
||||
import test from '../../libs/function/test';
|
||||
// #ifdef APP-NVUE
|
||||
// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
|
||||
// 不支持百分比单位,这里需要通过dom查询组件的宽度
|
||||
const dom = uni.requireNativePlugin('dom')
|
||||
// #endif
|
||||
|
||||
@@ -108,14 +112,20 @@ export default {
|
||||
urls: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
// 当只有一张图片时,获取图片尺寸信息
|
||||
if (newVal.length === 1) {
|
||||
this.getImageRect()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ["albumWidth"],
|
||||
computed: {
|
||||
/**
|
||||
* 计算图片样式
|
||||
* @param {Number} index1 - 行索引
|
||||
* @param {Number} index2 - 列索引
|
||||
* @returns {Object} 图片样式对象
|
||||
*/
|
||||
imageStyle() {
|
||||
return (index1, index2) => {
|
||||
const { space, rowCount, multipleSize, urls } = this,
|
||||
@@ -139,11 +149,16 @@ export default {
|
||||
return style
|
||||
}
|
||||
},
|
||||
// 将数组划分为二维数组
|
||||
/**
|
||||
* 将图片地址数组划分为二维数组,用于按行显示
|
||||
* @returns {Array} 二维数组,每个子数组代表一行图片
|
||||
*/
|
||||
showUrls() {
|
||||
if (this.autoWrap) {
|
||||
// 自动换行模式下,所有图片放在一行中显示
|
||||
return [ this.urls.slice(0, this.maxCount) ];
|
||||
} else {
|
||||
// 固定行数模式下,按 rowCount 分割图片
|
||||
const arr = []
|
||||
this.urls.map((item, index) => {
|
||||
// 限制最大展示数量
|
||||
@@ -160,18 +175,29 @@ export default {
|
||||
return arr
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 计算图片宽度
|
||||
* @returns {String} 图片宽度样式值
|
||||
*/
|
||||
imageWidth() {
|
||||
return addUnit(
|
||||
this.urls.length === 1 ? this.singleWidth : this.multipleSize, this.unit
|
||||
)
|
||||
},
|
||||
/**
|
||||
* 计算图片高度
|
||||
* @returns {String} 图片高度样式值
|
||||
*/
|
||||
imageHeight() {
|
||||
return addUnit(
|
||||
this.urls.length === 1 ? this.singleHeight : this.multipleSize, this.unit
|
||||
)
|
||||
},
|
||||
// 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
|
||||
// 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
|
||||
/**
|
||||
* 计算相册总宽度,用于外部组件对齐
|
||||
* 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
|
||||
* @returns {Number} 相册宽度
|
||||
*/
|
||||
albumWidth() {
|
||||
let width = 0
|
||||
if (this.urls.length === 1) {
|
||||
@@ -185,59 +211,100 @@ export default {
|
||||
return width
|
||||
}
|
||||
},
|
||||
emits: ['preview', 'albumWidth'],
|
||||
methods: {
|
||||
addUnit,
|
||||
// 预览图片
|
||||
/**
|
||||
* 点击图片预览
|
||||
* @param {Event} e - 点击事件对象
|
||||
* @param {String} url - 当前点击图片的地址
|
||||
*/
|
||||
onPreviewTap(e, url) {
|
||||
// 获取所有图片地址
|
||||
const urls = this.urls.map((item) => {
|
||||
return this.getSrc(item)
|
||||
})
|
||||
uni.previewImage({
|
||||
current: url,
|
||||
urls
|
||||
})
|
||||
// 是否阻止事件传播
|
||||
this.stop && this.preventEvent(e)
|
||||
if (this.previewFullImage) {
|
||||
// 使用系统默认预览图片功能
|
||||
uni.previewImage({
|
||||
current: url,
|
||||
urls
|
||||
})
|
||||
// 是否阻止事件传播
|
||||
this.stop && this.preventEvent(e)
|
||||
} else {
|
||||
// 发送自定义预览事件
|
||||
this.$emit('preview', {
|
||||
urls,
|
||||
currentIndex: urls.indexOf(url)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 获取图片的路径
|
||||
/**
|
||||
* 获取图片地址
|
||||
* @param {String|Object} item - 图片项,可以是字符串或对象
|
||||
* @returns {String} 图片地址
|
||||
*/
|
||||
getSrc(item) {
|
||||
return test.object(item)
|
||||
? (this.keyName && item[this.keyName]) || item.src
|
||||
: item
|
||||
},
|
||||
// 单图时,获取图片的尺寸
|
||||
// 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
|
||||
// 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
|
||||
/**
|
||||
* 单图时,获取图片的尺寸
|
||||
* 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
|
||||
* 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
|
||||
*/
|
||||
getImageRect() {
|
||||
const src = this.getSrc(this.urls[0])
|
||||
uni.getImageInfo({
|
||||
src,
|
||||
success: (res) => {
|
||||
let singleSize = this.singleSize;
|
||||
// 单位
|
||||
let unit = '';
|
||||
if (Number.isNaN(Number(this.singleSize))) {
|
||||
// 大小中有字符 则记录字符
|
||||
unit = this.singleSize.replace(/\d+/g, ''); // 单位
|
||||
singleSize = Number(this.singleSize.replace(/\D+/g, ''), 10); // 具体值
|
||||
}
|
||||
|
||||
// 判断图片横向还是竖向展示方式
|
||||
const isHorizotal = res.width >= res.height
|
||||
this.singleWidth = isHorizotal
|
||||
? this.singleSize
|
||||
: (res.width / res.height) * this.singleSize
|
||||
? singleSize
|
||||
: (res.width / res.height) * singleSize
|
||||
this.singleHeight = !isHorizotal
|
||||
? this.singleSize
|
||||
? singleSize
|
||||
: (res.height / res.width) * this.singleWidth
|
||||
|
||||
// 如果有单位统一设置单位
|
||||
if(unit != null && unit !== ''){
|
||||
this.singleWidth = this.singleWidth + unit
|
||||
this.singleHeight = this.singleHeight + unit
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
// 获取图片信息失败时,通过组件宽度计算
|
||||
this.getComponentWidth()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取组件的宽度
|
||||
/**
|
||||
* 获取组件的宽度,用于计算单图显示尺寸
|
||||
*/
|
||||
async getComponentWidth() {
|
||||
// 延时一定时间,以获取dom尺寸
|
||||
await sleep(30)
|
||||
// #ifndef APP-NVUE
|
||||
// H5、小程序等平台通过 $uGetRect 获取组件宽度
|
||||
this.$uGetRect('.u-album__row').then((size) => {
|
||||
this.singleWidth = size.width * this.singlePercent
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
// NVUE 平台通过 dom 插件获取组件宽度
|
||||
// 这里ref="u-album__row"所在的标签为通过for循环出来,导致this.$refs['u-album__row']是一个数组
|
||||
const ref = this.$refs['u-album__row'][0]
|
||||
ref &&
|
||||
@@ -251,8 +318,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
|
||||
.u-album {
|
||||
@include flex(column);
|
||||
|
||||
@@ -276,4 +341,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 1.0
|
||||
* @version : 3.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:48:53
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/alert.js
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-08-17 17:23:53
|
||||
* @FilePath : /uview-plus/libs/config/props/alert.js
|
||||
*/
|
||||
export default {
|
||||
// alert警告组件
|
||||
@@ -17,6 +17,10 @@ export default {
|
||||
showIcon: false,
|
||||
effect: 'light',
|
||||
center: false,
|
||||
fontSize: 14
|
||||
fontSize: 14,
|
||||
transitionMode: 'fade',
|
||||
duration: 0,
|
||||
icon: '',
|
||||
value: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
/*
|
||||
* @Author : jry
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-08-17 17:23:53
|
||||
* @FilePath : /uview-plus/libs/config/props/props.js
|
||||
*/
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import defProps from '../../libs/config/props.js'
|
||||
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 显示文字
|
||||
@@ -41,6 +50,26 @@ export const props = defineMixin({
|
||||
fontSize: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.alert.fontSize
|
||||
},
|
||||
// 动画类型
|
||||
transitionMode: {
|
||||
type: [String],
|
||||
default: () => defProps.alert.transitionMode
|
||||
},
|
||||
// 自动定时关闭毫秒
|
||||
duration: {
|
||||
type: [Number],
|
||||
default: () => defProps.alert.duration
|
||||
},
|
||||
// 自定义图标
|
||||
icon: {
|
||||
type: [String],
|
||||
default: () => defProps.alert.icon
|
||||
},
|
||||
// 是否显示
|
||||
modelValue: {
|
||||
type: [Boolean],
|
||||
default: () => defProps.alert.value
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<u-transition
|
||||
mode="fade"
|
||||
<up-transition
|
||||
:mode="transitionMode"
|
||||
:show="show"
|
||||
>
|
||||
<view
|
||||
@@ -9,22 +9,25 @@
|
||||
@tap.stop="clickHandler"
|
||||
:style="[addStyle(customStyle)]"
|
||||
>
|
||||
<!-- 左侧图标 -->
|
||||
<view
|
||||
class="u-alert__icon"
|
||||
v-if="showIcon"
|
||||
>
|
||||
<u-icon
|
||||
<up-icon
|
||||
:name="iconName"
|
||||
size="18"
|
||||
:color="iconColor"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</view>
|
||||
<!-- 内容区域 -->
|
||||
<view
|
||||
class="u-alert__content"
|
||||
:style="[{
|
||||
paddingRight: closable ? '20px' : 0
|
||||
}]"
|
||||
>
|
||||
<!-- 标题 -->
|
||||
<text
|
||||
class="u-alert__content__title"
|
||||
v-if="title"
|
||||
@@ -34,6 +37,7 @@
|
||||
}]"
|
||||
:class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
|
||||
>{{ title }}</text>
|
||||
<!-- 描述信息 -->
|
||||
<text
|
||||
class="u-alert__content__desc"
|
||||
v-if="description"
|
||||
@@ -44,19 +48,22 @@
|
||||
:class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
|
||||
>{{ description }}</text>
|
||||
</view>
|
||||
<!-- 关闭按钮 -->
|
||||
<view
|
||||
class="u-alert__close"
|
||||
v-if="closable"
|
||||
@tap.stop="closeHandler"
|
||||
>
|
||||
<u-icon
|
||||
name="close"
|
||||
:color="iconColor"
|
||||
size="15"
|
||||
></u-icon>
|
||||
<slot name="close">
|
||||
<up-icon
|
||||
name="close"
|
||||
:color="iconColor"
|
||||
size="15"
|
||||
></up-icon>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
</u-transition>
|
||||
</up-transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -70,7 +77,7 @@
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/alertTips.html
|
||||
*
|
||||
* @property {String} title 显示的文字
|
||||
* @property {String} type 使用预设的颜色 (默认 'warning' )
|
||||
* @property {String} type 使用预设的颜色 (默认 'warning' )
|
||||
* @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选
|
||||
* @property {Boolean} closable 关闭按钮(默认为叉号icon图标) (默认 false )
|
||||
* @property {Boolean} showIcon 是否显示左边的辅助图标 ( 默认 false )
|
||||
@@ -78,24 +85,34 @@
|
||||
* @property {Boolean} center 文字是否居中 (默认 false )
|
||||
* @property {String | Number} fontSize 字体大小 (默认 14 )
|
||||
* @property {Object} customStyle 定义需要用到的外部样式
|
||||
* @property {String} transitionMode 过渡动画模式 (默认 'fade' )
|
||||
* @property {String | Number} duration 自动关闭延时(毫秒),设置为0或负数则不自动关闭 (默认 0 )
|
||||
* @property {String} icon 自定义图标名称,优先级高于type默认图标
|
||||
* @property {Boolean} modelValue/v-model 绑定值,控制是否显示 (默认 true )
|
||||
* @event {Function} click 点击组件时触发
|
||||
* @event {Function} close 点击关闭按钮时触发
|
||||
* @example <u-alert :title="title" type = "warning" :closable="closable" :description = "description"></u-alert>
|
||||
* @event {Function} closed 关闭动画结束时触发
|
||||
* @example <up-alert :title="title" type = "warning" :closable="closable" :description = "description"></up-alert>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-alert',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
data() {
|
||||
return {
|
||||
// 控制组件显示隐藏
|
||||
show: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 根据不同的主题类型返回对应的图标颜色
|
||||
iconColor() {
|
||||
return this.effect === 'light' ? this.type : '#fff'
|
||||
},
|
||||
// 不同主题对应不同的图标
|
||||
iconName() {
|
||||
// 如果用户自定义了图标,则优先使用自定义图标
|
||||
if (this.icon) return this.icon;
|
||||
|
||||
switch (this.type) {
|
||||
case 'success':
|
||||
return 'checkmark-circle-fill';
|
||||
@@ -117,25 +134,50 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ["click","close"],
|
||||
emits: ["click","close", "closed", "update:modelValue"],
|
||||
watch: {
|
||||
modelValue: {
|
||||
handler(newVal) {
|
||||
this.show = newVal;
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
show: {
|
||||
handler(newVal) {
|
||||
this.$emit('update:modelValue', newVal);
|
||||
|
||||
// 如果是从显示到隐藏,且启用了自动关闭功能
|
||||
if (!newVal && this.duration > 0) {
|
||||
this.$emit('closed');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 如果设置了自动关闭时间,则在指定时间后自动关闭
|
||||
if (this.duration > 0) {
|
||||
setTimeout(() => {
|
||||
this.closeHandler();
|
||||
}, this.duration);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addUnit,
|
||||
addStyle,
|
||||
// 点击内容
|
||||
// 点击内容区域触发click事件
|
||||
clickHandler() {
|
||||
this.$emit('click')
|
||||
},
|
||||
// 点击关闭按钮
|
||||
// 点击关闭按钮触发close事件并隐藏组件
|
||||
closeHandler() {
|
||||
this.show = false
|
||||
this.$emit('close')
|
||||
this.$emit('close');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-alert {
|
||||
position: relative;
|
||||
|
||||
@@ -79,7 +79,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-avatar-group {
|
||||
@include flex;
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
<!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU -->
|
||||
<template v-if="mpAvatar && allowMp"></template>
|
||||
<!-- #endif -->
|
||||
<u-icon
|
||||
<up-icon
|
||||
v-else-if="icon"
|
||||
:name="icon"
|
||||
:size="fontSize"
|
||||
:color="color"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
<up-text
|
||||
v-else-if="text"
|
||||
:text="text"
|
||||
@@ -151,7 +151,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-avatar {
|
||||
@include flex;
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
v-if="!$slots.default && !$slots.$default"
|
||||
@click="backToTop"
|
||||
>
|
||||
<u-icon
|
||||
<up-icon
|
||||
:name="icon"
|
||||
:custom-style="iconStyle"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
<text
|
||||
v-if="text"
|
||||
class="u-back-top__text"
|
||||
@@ -111,7 +111,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
$u-back-top-flex:1 !default;
|
||||
$u-back-top-height:100% !default;
|
||||
$u-back-top-background-color:#E1E1E1 !default;
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
$u-badge-primary: $u-primary !default;
|
||||
$u-badge-error: $u-error !default;
|
||||
|
||||
1000
uni_modules/uview-plus/components/u-barcode/u-barcode.vue
Normal file
1000
uni_modules/uview-plus/components/u-barcode/u-barcode.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -51,7 +51,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
|
||||
@@ -39,13 +39,13 @@
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<u-icon
|
||||
<up-icon
|
||||
v-if="icon"
|
||||
:name="icon"
|
||||
:color="iconColorCom"
|
||||
:size="textSize * 1.35"
|
||||
:customStyle="{ marginRight: '2px' }"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
<slot>
|
||||
<text
|
||||
class="u-button__text"
|
||||
@@ -87,12 +87,12 @@
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<u-icon
|
||||
<up-icon
|
||||
v-if="icon"
|
||||
:name="icon"
|
||||
:color="iconColorCom"
|
||||
:size="textSize * 1.35"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
<text
|
||||
class="u-button__text"
|
||||
:style="[
|
||||
@@ -206,7 +206,7 @@ export default {
|
||||
},
|
||||
iconColorCom() {
|
||||
// 如果是镂空状态,设置了color就用color值,否则使用主题颜色,
|
||||
// u-icon的color能接受一个主题颜色的值
|
||||
// up-icon的color能接受一个主题颜色的值
|
||||
if (this.iconColor) return this.iconColor;
|
||||
if (this.plain) {
|
||||
return this.color ? this.color : this.type;
|
||||
@@ -306,8 +306,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
@import "./vue.scss";
|
||||
/* #endif */
|
||||
|
||||
@@ -7,15 +7,16 @@
|
||||
* @lastTime : 2021-08-20 16:52:43
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/calendar.js
|
||||
*/
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
// calendar 组件
|
||||
calendar: {
|
||||
title: '日期选择',
|
||||
title: t("up.calendar.chooseDates"),
|
||||
showTitle: true,
|
||||
showSubtitle: true,
|
||||
mode: 'single',
|
||||
startText: '开始',
|
||||
endText: '结束',
|
||||
startText: t("up.common.start"),
|
||||
endText: t("up.common.end"),
|
||||
customList: [],
|
||||
color: '#3c9cff',
|
||||
minDate: 0,
|
||||
@@ -26,8 +27,8 @@ export default {
|
||||
formatter: null,
|
||||
showLunar: false,
|
||||
showMark: true,
|
||||
confirmText: '确定',
|
||||
confirmDisabledText: '确定',
|
||||
confirmText: t("up.common.confirm"),
|
||||
confirmDisabledText: t("up.common.confirm"),
|
||||
show: false,
|
||||
closeOnClickOverlay: false,
|
||||
readonly: false,
|
||||
@@ -38,6 +39,10 @@ export default {
|
||||
allowSameDay: false,
|
||||
round: 0,
|
||||
monthNum: 3,
|
||||
weekText: ['一', '二', '三', '四', '五', '六', '日']
|
||||
weekText: [t("up.week.one"), t("up.week.two"), t("up.week.three"), t("up.week.four"), t("up.week.five"), t("up.week.six"), t("up.week.seven")],
|
||||
forbidDays: [],
|
||||
forbidDaysToast: t("up.calendar.disabled"),
|
||||
monthFormat: '',
|
||||
pageInline: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@
|
||||
},
|
||||
// 星期文本
|
||||
weekText: {
|
||||
type: Boolean,
|
||||
type: Array,
|
||||
default: () => {
|
||||
return ['一', '二', '三', '四', '五', '六', '日']
|
||||
return []
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -69,7 +69,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-calendar-header {
|
||||
display: flex;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<view class="u-calendar-month-wrapper" ref="u-calendar-month-wrapper">
|
||||
<view v-for="(item, index) in months" :key="index" :class="[`u-calendar-month-${index}`]"
|
||||
:ref="`u-calendar-month-${index}`" :id="`month-${index}`">
|
||||
<text v-if="index !== 0" class="u-calendar-month__title">{{ item.year }}年{{ item.month }}月</text>
|
||||
<text v-if="index !== 0" class="u-calendar-month__title">{{ monthTitle(item) }}</text>
|
||||
<view class="u-calendar-month__days">
|
||||
<view v-if="showMark" class="u-calendar-month__days__month-mark-wrapper">
|
||||
<text class="u-calendar-month__days__month-mark-wrapper__text">{{ item.month }}</text>
|
||||
@@ -12,11 +12,11 @@
|
||||
:class="[item1.selected && 'u-calendar-month__days__day__select--selected']">
|
||||
<view class="u-calendar-month__days__day__select" :style="[daySelectStyle(index, index1, item1)]">
|
||||
<text class="u-calendar-month__days__day__select__info"
|
||||
:class="[item1.disabled && 'u-calendar-month__days__day__select__info--disabled']"
|
||||
:class="[(item1.disabled || isForbid(item1) ) ? 'u-calendar-month__days__day__select__info--disabled' : '']"
|
||||
:style="[textStyle(item1)]">{{ item1.day }}</text>
|
||||
<text v-if="getBottomInfo(index, index1, item1)"
|
||||
class="u-calendar-month__days__day__select__buttom-info"
|
||||
:class="[item1.disabled && 'u-calendar-month__days__day__select__buttom-info--disabled']"
|
||||
:class="[(item1.disabled || isForbid(item1) ) ? 'u-calendar-month__days__day__select__buttom-info--disabled' : '']"
|
||||
:style="[textStyle(item1)]">{{ getBottomInfo(index, index1, item1) }}</text>
|
||||
<text v-if="item1.dot" class="u-calendar-month__days__day__select__dot"></text>
|
||||
</view>
|
||||
@@ -37,7 +37,8 @@
|
||||
import { colorGradient } from '../../libs/function/colorGradient';
|
||||
import test from '../../libs/function/test';
|
||||
import defProps from '../../libs/config/props';
|
||||
import dayjs from 'dayjs/esm/index'
|
||||
import dayjs from '../u-datetime-picker/dayjs.esm.min.js';
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
name: 'u-calendar-month',
|
||||
mixins: [mpMixin, mixin],
|
||||
@@ -126,6 +127,14 @@
|
||||
allowSameDay: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
forbidDays: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
forbidDaysToast: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -160,14 +169,14 @@
|
||||
// #ifdef APP-NVUE
|
||||
style.width = addUnit(dayWidth, 'px')
|
||||
// #endif
|
||||
style.height = addUnit(this.rowHeight)
|
||||
style.height = addUnit(this.rowHeight, 'px')
|
||||
if (index2 === 0) {
|
||||
// 获取当前为星期几,如果为0,则为星期天,减一为每月第一天时,需要向左偏移的item个数
|
||||
week = (week === 0 ? 7 : week) - 1
|
||||
style.marginLeft = addUnit(week * dayWidth, 'px')
|
||||
}
|
||||
if (this.mode === 'range') {
|
||||
// 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
|
||||
// 之所以需要这么写,是因为DCloud公司的iOS客户端导致的bug
|
||||
style.paddingLeft = 0
|
||||
style.paddingRight = 0
|
||||
style.paddingBottom = 0
|
||||
@@ -213,7 +222,7 @@
|
||||
style.opacity = 0.7
|
||||
}
|
||||
} else if (this.selected.length === 1) {
|
||||
// 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
|
||||
// 之所以需要这么写,是因为uni-app的iOS客户端的bug
|
||||
// 进行还原操作,否则在nvue的iOS,uni-app有bug,会导致诡异的表现
|
||||
style.borderTopLeftRadius = '3px'
|
||||
style.borderBottomLeftRadius = '3px'
|
||||
@@ -284,6 +293,7 @@
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
emits: ['monthSelected', 'updateMonthTop'],
|
||||
methods: {
|
||||
init() {
|
||||
// 初始化默认选中
|
||||
@@ -297,6 +307,20 @@
|
||||
})
|
||||
})
|
||||
},
|
||||
monthTitle(item) {
|
||||
if (uni.getLocale() == 'zh-Hans' || uni.getLocale() == 'zh-Hant') {
|
||||
return item.year + '年' + (item.month < 10 ? '0' + item.month : item.month) + '月'
|
||||
} else {
|
||||
return (item.month < 10 ? '0' + item.month : item.month) + '/' + item.year
|
||||
}
|
||||
},
|
||||
isForbid(item) {
|
||||
let date = dayjs(item.date).format("YYYY-MM-DD")
|
||||
if (this.mode !== 'range' && this.forbidDays.includes(date)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
// 判断两个日期是否相等
|
||||
dateSame(date1, date2) {
|
||||
return dayjs(date1).isSame(dayjs(date2))
|
||||
@@ -362,6 +386,12 @@
|
||||
this.item = item
|
||||
const date = dayjs(item.date).format("YYYY-MM-DD")
|
||||
if (item.disabled) return
|
||||
if (this.isForbid(item)) {
|
||||
uni.showToast({
|
||||
title: this.forbidDaysToast
|
||||
})
|
||||
return
|
||||
}
|
||||
// 对上一次选择的日期数组进行深度克隆
|
||||
let selected = deepClone(this.selected)
|
||||
if (this.mode === 'single') {
|
||||
@@ -393,7 +423,7 @@
|
||||
if(this.rangePrompt) {
|
||||
toast(this.rangePrompt)
|
||||
} else {
|
||||
toast(`选择天数不能超过 ${this.maxRange} 天`)
|
||||
toast(t("up.calendar.daysExceed", { days: this.maxRange }))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -459,7 +489,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-calendar-month-wrapper {
|
||||
margin-top: 4px;
|
||||
|
||||
@@ -147,6 +147,23 @@ export const props = defineMixin({
|
||||
weekText: {
|
||||
type: Array,
|
||||
default: defProps.calendar.weekText
|
||||
},
|
||||
forbidDays: {
|
||||
type: Array,
|
||||
default: defProps.calendar.forbidDays
|
||||
},
|
||||
forbidDaysToast:{
|
||||
type: String,
|
||||
default: defProps.calendar.forbidDaysToast
|
||||
},
|
||||
monthFormat:{
|
||||
type: String,
|
||||
default: defProps.calendar.monthFormat
|
||||
},
|
||||
// 是否页面内展示
|
||||
pageInline:{
|
||||
type: Boolean,
|
||||
default: defProps.calendar.pageInline
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
<u-popup
|
||||
:show="show"
|
||||
mode="bottom"
|
||||
closeable
|
||||
:closeable="!pageInline"
|
||||
@close="close"
|
||||
:round="round"
|
||||
:pageInline="pageInline"
|
||||
:closeOnClickOverlay="closeOnClickOverlay"
|
||||
>
|
||||
<view class="u-calendar">
|
||||
@@ -17,7 +18,7 @@
|
||||
></uHeader>
|
||||
<scroll-view
|
||||
:style="{
|
||||
height: addUnit(listHeight)
|
||||
height: addUnit(listHeight, 'px')
|
||||
}"
|
||||
scroll-y
|
||||
@scroll="onScroll"
|
||||
@@ -42,6 +43,9 @@
|
||||
:rangePrompt="rangePrompt"
|
||||
:showRangePrompt="showRangePrompt"
|
||||
:allowSameDay="allowSameDay"
|
||||
:forbidDays="forbidDays"
|
||||
:forbidDaysToast="forbidDaysToast"
|
||||
:monthFormat="monthFormat"
|
||||
ref="month"
|
||||
@monthSelected="monthSelected"
|
||||
@updateMonthTop="updateMonthTop"
|
||||
@@ -69,11 +73,11 @@ import uHeader from './header.vue'
|
||||
import uMonth from './month.vue'
|
||||
import { props } from './props.js'
|
||||
import util from './util.js'
|
||||
import dayjs from 'dayjs/esm/index'
|
||||
import dayjs from '../u-datetime-picker/dayjs.esm.min.js';
|
||||
import Calendar from '../../libs/util/calendar.js'
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin.js'
|
||||
import { mixin } from '../../libs/mixin/mixin.js'
|
||||
import { addUnit, range, error, padZero } from '../../libs/function/index';
|
||||
import { addUnit, getPx, range, error, padZero } from '../../libs/function/index';
|
||||
import test from '../../libs/function/test';
|
||||
/**
|
||||
* Calendar 日历
|
||||
@@ -184,9 +188,11 @@ export default {
|
||||
subtitle() {
|
||||
// 初始化时,this.months为空数组,所以需要特别判断处理
|
||||
if (this.months.length) {
|
||||
return `${this.months[this.monthIndex].year}年${
|
||||
this.months[this.monthIndex].month
|
||||
}月`
|
||||
if (uni.getLocale() == 'zh-Hans' || uni.getLocale() == 'zh-Hant') {
|
||||
return this.months[this.monthIndex].year + '年' + (this.months[this.monthIndex].month < 10 ? '0' + this.months[this.monthIndex].month : this.months[this.monthIndex].month) + '月'
|
||||
} else {
|
||||
return (this.months[this.monthIndex].month < 10 ? '0' + this.months[this.monthIndex].month : this.months[this.monthIndex].month) + '/' + this.months[this.monthIndex].year
|
||||
}
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
@@ -244,7 +250,13 @@ export default {
|
||||
return error('maxDate不能小于minDate时间')
|
||||
}
|
||||
// 滚动区域的高度
|
||||
this.listHeight = this.rowHeight * 5 + 30
|
||||
let bottomPadding = 0;
|
||||
if (this.pageInline) {
|
||||
bottomPadding = 0
|
||||
} else {
|
||||
bottomPadding = 30
|
||||
}
|
||||
this.listHeight = this.rowHeight * 5 + bottomPadding
|
||||
this.setMonth()
|
||||
},
|
||||
close() {
|
||||
@@ -401,8 +413,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
|
||||
.u-calendar {
|
||||
&__confirm {
|
||||
padding: 7px 18px;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import dayjs from 'dayjs/esm/index'
|
||||
import dayjs from '../u-datetime-picker/dayjs.esm.min.js';
|
||||
export default {
|
||||
methods: {
|
||||
// 设置月份数据
|
||||
|
||||
@@ -56,11 +56,11 @@
|
||||
hover-class="u-hover-class"
|
||||
:hover-stay-time="200"
|
||||
>
|
||||
<u-icon
|
||||
<up-icon
|
||||
size="28"
|
||||
name="backspace"
|
||||
color="#303133"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -222,7 +222,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
$u-car-keyboard-background-color: rgb(224, 228, 230) !default;
|
||||
$u-car-keyboard-padding:6px 0 6px !default;
|
||||
$u-car-keyboard-button-inner-width:64rpx !default;
|
||||
|
||||
40
uni_modules/uview-plus/components/u-card/card.js
Normal file
40
uni_modules/uview-plus/components/u-card/card.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* @Author : jry
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @Date : 2025-04-26 16:37:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-04-26 16:37:21
|
||||
* @FilePath : /uview-plus/libs/config/props/card.js
|
||||
*/
|
||||
export default {
|
||||
// card组件的props
|
||||
card: {
|
||||
full: false,
|
||||
title: '',
|
||||
titleColor: '#303133',
|
||||
titleSize: '15px',
|
||||
subTitle: '',
|
||||
subTitleColor: '#909399',
|
||||
subTitleSize: '13px',
|
||||
border: true,
|
||||
index: '',
|
||||
margin: '15px',
|
||||
borderRadius: '8px',
|
||||
headStyle: {},
|
||||
bodyStyle: {},
|
||||
footStyle: {},
|
||||
headBorderBottom: true,
|
||||
footBorderTop: true,
|
||||
thumb: '',
|
||||
thumbWidth: '30px',
|
||||
thumbCircle: false,
|
||||
padding: '15px',
|
||||
paddingHead: '',
|
||||
paddingBody: '',
|
||||
paddingFoot: '',
|
||||
showHead: true,
|
||||
showFoot: true,
|
||||
boxShadow: 'none'
|
||||
}
|
||||
}
|
||||
@@ -6,135 +6,129 @@ export const propsCard = defineMixin({
|
||||
// 与屏幕两侧是否留空隙
|
||||
full: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: () => defProps.card.full
|
||||
},
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: () => defProps.card.title
|
||||
},
|
||||
// 标题颜色
|
||||
titleColor: {
|
||||
type: String,
|
||||
default: '#303133'
|
||||
default: () => defProps.card.titleColor
|
||||
},
|
||||
// 标题字体大小
|
||||
titleSize: {
|
||||
type: [Number, String],
|
||||
default: '15px'
|
||||
default: () => defProps.card.titleSize
|
||||
},
|
||||
// 副标题
|
||||
subTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: () => defProps.card.subTitle
|
||||
},
|
||||
// 副标题颜色
|
||||
subTitleColor: {
|
||||
type: String,
|
||||
default: '#909399'
|
||||
default: () => defProps.card.subTitleColor
|
||||
},
|
||||
// 副标题字体大小
|
||||
subTitleSize: {
|
||||
type: [Number, String],
|
||||
default: '13'
|
||||
default: () => defProps.card.subTitleSize
|
||||
},
|
||||
// 是否显示外部边框,只对full=false时有效(卡片与边框有空隙时)
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: () => defProps.card.border
|
||||
},
|
||||
// 用于标识点击了第几个
|
||||
index: {
|
||||
type: [Number, String, Object],
|
||||
default: ''
|
||||
default: () => defProps.card.index
|
||||
},
|
||||
// 用于隔开上下左右的边距,带单位的写法,如:"30px 30px","20px 20px 30px 30px"
|
||||
margin: {
|
||||
type: String,
|
||||
default: '15px'
|
||||
default: () => defProps.card.margin
|
||||
},
|
||||
// card卡片的圆角
|
||||
borderRadius: {
|
||||
type: [Number, String],
|
||||
default: '8px'
|
||||
default: () => defProps.card.borderRadius
|
||||
},
|
||||
// 头部自定义样式,对象形式
|
||||
headStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
default: () => defProps.card.headStyle
|
||||
},
|
||||
// 主体自定义样式,对象形式
|
||||
bodyStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
default: () => defProps.card.bodyStyle
|
||||
},
|
||||
// 底部自定义样式,对象形式
|
||||
footStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
default: () => defProps.card.footStyle
|
||||
},
|
||||
// 头部是否下边框
|
||||
headBorderBottom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: () => defProps.card.headBorderBottom
|
||||
},
|
||||
// 底部是否有上边框
|
||||
footBorderTop: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: () => defProps.card.footBorderTop
|
||||
},
|
||||
// 标题左边的缩略图
|
||||
thumb: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: () => defProps.card.thumb
|
||||
},
|
||||
// 缩略图宽高
|
||||
thumbWidth: {
|
||||
type: [String, Number],
|
||||
default: '30px'
|
||||
default: () => defProps.card.thumbWidth
|
||||
},
|
||||
// 缩略图是否为圆形
|
||||
thumbCircle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: () => defProps.card.thumbCircle
|
||||
},
|
||||
// 给head,body,foot的内边距
|
||||
padding: {
|
||||
type: [String, Number],
|
||||
default: '15px'
|
||||
default: () => defProps.card.padding
|
||||
},
|
||||
paddingHead: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
default: () => defProps.card.paddingHead
|
||||
},
|
||||
paddingBody: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
default: () => defProps.card.paddingBody
|
||||
},
|
||||
paddingFoot: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
default: () => defProps.card.paddingFoot
|
||||
},
|
||||
// 是否显示头部
|
||||
showHead: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: () => defProps.card.showHead
|
||||
},
|
||||
// 是否显示尾部
|
||||
showFoot: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: () => defProps.card.showFoot
|
||||
},
|
||||
// 卡片外围阴影,字符串形式
|
||||
boxShadow: {
|
||||
type: String,
|
||||
default: 'none'
|
||||
default: () => defProps.card.boxShadow
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -136,8 +136,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
333
uni_modules/uview-plus/components/u-cascader/u-cascader.vue
Normal file
333
uni_modules/uview-plus/components/u-cascader/u-cascader.vue
Normal file
@@ -0,0 +1,333 @@
|
||||
<template>
|
||||
<up-popup :show="popupShow" mode="bottom" :popup="false"
|
||||
:mask="true" :closeable="true" :safe-area-inset-bottom="true"
|
||||
close-icon-color="#ffffff" :z-index="uZIndex"
|
||||
:maskCloseAble="maskCloseAble" @close="close">
|
||||
<view class="up-p-t-30 up-p-l-20 up-m-b-10" v-if="headerDirection =='column'">
|
||||
<up-steps v-if="popupShow" dot direction="column" v-model:current="tabsIndex">
|
||||
<up-steps-item v-for="(item, index) in genTabsList"
|
||||
@click="tabsIndex = index" :title="item.name"></up-steps-item>
|
||||
</up-steps>
|
||||
</view>
|
||||
<view class="up-p-t-20 up-m-b-10" v-else>
|
||||
<up-tabs v-if="popupShow" :list="genTabsList"
|
||||
:scrollable="true" v-model:current="tabsIndex" @change="tabsChange" ref="tabs"></up-tabs>
|
||||
</view>
|
||||
<view class="area-box">
|
||||
<view class="u-flex" :class="{ 'change':isChange }"
|
||||
:style="{transform: optionsCols == 2 && isChange ? 'translateX(-33.3333333%)' : ''}">
|
||||
<template v-for="(levelData, levelIndex) in levelList" :key="levelIndex">
|
||||
<view v-if="optionsCols == 2 || levelIndex == tabsIndex" class="area-item"
|
||||
:style="{ width: optionsCols == 2 ? '33.33333%' : '750rpx'}">
|
||||
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
|
||||
<scroll-view :scroll-y="true" style="height: 100%">
|
||||
<up-cell-group v-if="levelIndex === 0 || selectedValueIndexs[levelIndex - 1] !== undefined">
|
||||
<up-cell v-for="(item,index) in levelData"
|
||||
:title="item[labelKey]" :arrow="false"
|
||||
:index="index" :key="index"
|
||||
@click="levelChange(levelIndex, index)">
|
||||
<template v-slot:right-icon>
|
||||
<up-icon v-if="selectedValueIndexs[levelIndex] === index"
|
||||
size="17" name="checkbox-mark"></up-icon>
|
||||
</template>
|
||||
</up-cell>
|
||||
</up-cell-group>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 添加按钮区域 -->
|
||||
<view class="u-cascader-action up-flex up-flex-between">
|
||||
<view class="u-padding-20 up-flex-fill">
|
||||
<up-button @click="handleCancel" type="default">{{ t("up.common.cancel") }}</up-button>
|
||||
</view>
|
||||
<view class="u-padding-20 up-flex-fill">
|
||||
<up-button @click="handleConfirm" type="primary">{{ t("up.common.confirm") }}</up-button>
|
||||
</view>
|
||||
</view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* u-cascader 通用无限级联选择器
|
||||
* @property {String Number} z-index 弹出时的z-index值(默认1075)
|
||||
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭Picker(默认true)
|
||||
* @property {Array} data 级联数据
|
||||
* @property {Array} default-value 默认选中的值
|
||||
* @property {String} valueKey 指定选项的值为选项对象中的哪个属性值
|
||||
* @property {String} labelKey 指定选项标签为选项对象中的哪个属性值
|
||||
* @property {String} childrenKey 指定选项的子选项为选项对象中的哪个属性值
|
||||
* @property {Boolean} autoClose 是否在选择最后一级时自动关闭并触发confirm(默认false)
|
||||
*/
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
name: 'up-cascader',
|
||||
props: {
|
||||
// 通过双向绑定控制组件的弹出与收起
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 级联数据
|
||||
data: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 默认选中的值
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 指定选项的值为选项对象中的哪个属性值
|
||||
valueKey: {
|
||||
type: String,
|
||||
default: 'value'
|
||||
},
|
||||
// 指定选项标签为选项对象中的哪个属性值
|
||||
labelKey: {
|
||||
type: String,
|
||||
default: 'label'
|
||||
},
|
||||
// 指定选项的子选项为选项对象中的哪个属性值
|
||||
childrenKey: {
|
||||
type: String,
|
||||
default: 'children'
|
||||
},
|
||||
// 是否允许通过点击遮罩关闭Picker
|
||||
maskCloseAble: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 弹出的z-index值
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 是否在选择最后一级时自动关闭并触发confirm
|
||||
autoClose: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 选中项目的展示方向direction垂直方向适合文字长度过长
|
||||
headerDirection: {
|
||||
type: String,
|
||||
default: 'row'
|
||||
},
|
||||
// 选项区域列数,支持1列和2列,默认为2列
|
||||
optionsCols: {
|
||||
type: [Number],
|
||||
default: 2
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 存储每一级的数据
|
||||
levelList: [],
|
||||
// 存储每一级选中的索引
|
||||
selectedValueIndexs: [],
|
||||
tabsIndex: 0,
|
||||
popupShow: false,
|
||||
// 新增confirmValues用于存储确认的值
|
||||
confirmValues: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
handler() {
|
||||
this.initLevelList();
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
show() {
|
||||
this.popupShow = this.show;
|
||||
},
|
||||
modelValue: {
|
||||
handler() {
|
||||
this.init();
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isChange() {
|
||||
return this.tabsIndex > 1;
|
||||
},
|
||||
genTabsList() {
|
||||
let tabsList = [{
|
||||
name: "请选择"
|
||||
}];
|
||||
|
||||
// 根据选中的值动态生成tabs
|
||||
for (let i = 0; i < this.selectedValueIndexs.length; i++) {
|
||||
if (this.selectedValueIndexs[i] !== undefined && this.levelList[i]) {
|
||||
const selectedItem = this.levelList[i][this.selectedValueIndexs[i]];
|
||||
if (selectedItem) {
|
||||
tabsList[i] = {
|
||||
name: selectedItem[this.labelKey]
|
||||
};
|
||||
// 如果还有下一级,则添加"请选择"
|
||||
if (i === this.selectedValueIndexs.length - 1 &&
|
||||
selectedItem[this.childrenKey] &&
|
||||
selectedItem[this.childrenKey].length > 0) {
|
||||
tabsList.push({
|
||||
name: "请选择"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tabsList;
|
||||
},
|
||||
uZIndex() {
|
||||
// 如果用户有传递z-index值,优先使用
|
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
|
||||
}
|
||||
},
|
||||
// 新增confirm事件
|
||||
emits: ['update:modelValue', 'change', 'confirm'],
|
||||
methods: {
|
||||
t,
|
||||
init() {
|
||||
// 初始化选中值
|
||||
if (this.modelValue && this.modelValue.length > 0) {
|
||||
this.setDefaultValue();
|
||||
}
|
||||
},
|
||||
initLevelList() {
|
||||
// 初始化第一级数据
|
||||
if (this.data && this.data.length > 0) {
|
||||
this.levelList = [this.data];
|
||||
this.selectedValueIndexs = [];
|
||||
}
|
||||
},
|
||||
setDefaultValue() {
|
||||
// 根据默认值设置选中项
|
||||
// 根据modelValue获取indexs给selectedValueIndexs
|
||||
this.selectedValueIndexs = [];
|
||||
let currentLevelData = this.data;
|
||||
|
||||
for (let i = 0; i < this.modelValue.length; i++) {
|
||||
const value = this.modelValue[i];
|
||||
const index = currentLevelData.findIndex(item => item[this.valueKey] === value);
|
||||
|
||||
if (index !== -1) {
|
||||
this.selectedValueIndexs.push(index);
|
||||
// 更新下一级的数据
|
||||
if (currentLevelData[index][this.childrenKey]) {
|
||||
currentLevelData = currentLevelData[index][this.childrenKey];
|
||||
} else {
|
||||
// 如果没有子级数据,则停止处理
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// 如果找不到匹配项,则停止处理
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.$emit('update:show', false);
|
||||
},
|
||||
tabsChange(item) {
|
||||
},
|
||||
levelChange(levelIndex, index) {
|
||||
// 设置当前级的选中值
|
||||
this.$set(this.selectedValueIndexs, levelIndex, index);
|
||||
|
||||
// 清除后续级别的选中值
|
||||
for (let i = levelIndex + 1; i < this.selectedValueIndexs.length; i++) {
|
||||
this.$set(this.selectedValueIndexs, i, undefined);
|
||||
}
|
||||
|
||||
// 获取当前选中项
|
||||
const currentItem = this.levelList[levelIndex][index];
|
||||
|
||||
// 如果有子级数据,则初始化下一级
|
||||
if (currentItem && currentItem[this.childrenKey] && currentItem[this.childrenKey].length > 0) {
|
||||
// 确保levelList数组足够长
|
||||
if (this.levelList.length <= levelIndex + 1) {
|
||||
this.levelList.push(currentItem[this.childrenKey]);
|
||||
} else {
|
||||
this.$set(this.levelList, levelIndex + 1, currentItem[this.childrenKey]);
|
||||
}
|
||||
// 切换到下一级tab
|
||||
this.tabsIndex = levelIndex + 1;
|
||||
} else {
|
||||
// 没有子级数据,说明是最后一级
|
||||
if (this.autoClose) {
|
||||
// 如果启用自动关闭,则触发change事件并关闭
|
||||
this.emitChange();
|
||||
} else {
|
||||
// 否则只触发change事件,不关闭
|
||||
this.emitChange(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 修改emitChange方法,增加closePopup参数
|
||||
emitChange(closePopup = true) {
|
||||
// 构造选中结果
|
||||
const result = [];
|
||||
for (let i = 0; i < this.selectedValueIndexs.length; i++) {
|
||||
if (this.selectedValueIndexs[i] !== undefined && this.levelList[i]) {
|
||||
result.push(this.levelList[i][this.selectedValueIndexs[i]][this.valueKey]);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新confirmValues
|
||||
this.confirmValues = [...result];
|
||||
|
||||
// 触发change事件,返回value数组
|
||||
this.$emit('change', this.confirmValues);
|
||||
|
||||
// 根据参数决定是否关闭弹窗
|
||||
if (closePopup) {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
handleCancel() {
|
||||
this.close();
|
||||
},
|
||||
handleConfirm() {
|
||||
// 确认时触发confirm事件
|
||||
this.$emit('update:modelValue', this.confirmValues);
|
||||
this.$emit('confirm', this.confirmValues);
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.area-box {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 800rpx;
|
||||
|
||||
>view {
|
||||
width: 150%;
|
||||
transition: transform 0.3s ease-in-out 0s;
|
||||
transform: translateX(0);
|
||||
|
||||
&.change {
|
||||
// transform: translateX(-33.3333333%);
|
||||
}
|
||||
}
|
||||
|
||||
.area-item {
|
||||
// width: 750rpx;
|
||||
height: 800rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加按钮区域样式
|
||||
.u-cascader-action {
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
</style>
|
||||
@@ -1,49 +1,64 @@
|
||||
<template>
|
||||
<view class="u-cate-tab">
|
||||
<view class="u-cate-tab" :style="{ height: addUnit(height) }">
|
||||
<view class="u-cate-tab__wrap">
|
||||
<scroll-view class="u-cate-tab__view u-cate-tab__menu-scroll-view"
|
||||
scroll-y scroll-with-animation :scroll-top="scrollTop"
|
||||
:scroll-into-view="itemId">
|
||||
<view v-for="(item, index) in tabList" :key="index" class="u-cate-tab__item"
|
||||
:class="[current == index ? 'u-cate-tab__item-active' : '']"
|
||||
:class="[innerCurrent == index ? 'u-cate-tab__item-active' : '']"
|
||||
@tap.stop="swichMenu(index)">
|
||||
<slot name="tabItem" :item="item">
|
||||
</slot>
|
||||
<text v-if="!$slots['tabItem']" class="u-line-1">{{item[tabKeyName]}}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view :scroll-top="scrollRightTop" scroll-with-animation
|
||||
<scroll-view :scroll-top="scrollRightTop" scroll-with-animation :scroll-into-view="scrollIntoView"
|
||||
scroll-y class="u-cate-tab__right-box" @scroll="rightScroll">
|
||||
<view class="u-cate-tab__right-top">
|
||||
<slot name="rightTop" :tabList="tabList">
|
||||
</slot>
|
||||
</view>
|
||||
<view class="u-cate-tab__page-view">
|
||||
<view class="u-cate-tab__page-item" :id="'item' + index"
|
||||
v-for="(item , index) in tabList" :key="index">
|
||||
<slot name="itemList" :item="item">
|
||||
</slot>
|
||||
<template v-if="!$slots['itemList']">
|
||||
<view class="item-title">
|
||||
<text>{{item[tabKeyName]}}</text>
|
||||
</view>
|
||||
<view class="item-container">
|
||||
<template v-for="(item1, index1) in item.children" :key="index1">
|
||||
<slot name="pageItem" :pageItem="item1">
|
||||
<view class="thumb-box" >
|
||||
<image class="item-menu-image" :src="item1.icon" mode=""></image>
|
||||
<view class="item-menu-name">{{item1[itemKeyName]}}</view>
|
||||
</view>
|
||||
</slot>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<template :key="index" v-for="(item , index) in tabList">
|
||||
<view v-if="mode == 'follow' || ( mode == 'tab' && index == innerCurrent)"
|
||||
class="u-cate-tab__page-item" :id="'item' + index">
|
||||
<slot name="itemList" :item="item">
|
||||
</slot>
|
||||
<template v-if="!$slots['itemList']">
|
||||
<view class="item-title">
|
||||
<text>{{item[tabKeyName]}}</text>
|
||||
</view>
|
||||
<view class="item-container">
|
||||
<template v-for="(item1, index1) in item.children" :key="index1">
|
||||
<slot name="pageItem" :pageItem="item1">
|
||||
<view class="thumb-box" >
|
||||
<image class="item-menu-image" :src="item1.icon" mode=""></image>
|
||||
<view class="item-menu-name">{{item1[itemKeyName]}}</view>
|
||||
</view>
|
||||
</slot>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { addUnit, sleep } from '../../libs/function/index';
|
||||
export default {
|
||||
name: 'up-cate-tab',
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'follow' // follo跟随联动, tab单一显示。
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
tabList: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
@@ -57,18 +72,39 @@
|
||||
itemKeyName: {
|
||||
type: String,
|
||||
default: 'name'
|
||||
},
|
||||
current: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
tabList() {
|
||||
this.getMenuItemTop()
|
||||
}
|
||||
tabList: {
|
||||
deep: true,
|
||||
handler(newVal, oldVal) {
|
||||
// this.observer();
|
||||
sleep(30);
|
||||
this.getMenuItemTop();
|
||||
this.leftMenuStatus(this.innerCurrent);
|
||||
}
|
||||
},
|
||||
current(nval) {
|
||||
this.innerCurrent = nval;
|
||||
this.leftMenuStatus(this.innerCurrent);
|
||||
},
|
||||
height() {
|
||||
// console.log('height change');
|
||||
this.getMenuItemTop();
|
||||
this.leftMenuStatus(this.innerCurrent);
|
||||
}
|
||||
},
|
||||
emits: ['update:current'],
|
||||
data() {
|
||||
return {
|
||||
scrollTop: 0, //tab标题的滚动条位置
|
||||
scrollIntoView: '', // 滚动至哪个元素
|
||||
oldScrollTop: 0,
|
||||
current: 0, // 预设当前项的值
|
||||
innerCurrent: 0, // 预设当前项的值
|
||||
menuHeight: 0, // 左边菜单的高度
|
||||
menuItemHeight: 0, // 左边菜单item的高度
|
||||
itemId: '', // 栏目右边scroll-view用于滚动的id
|
||||
@@ -79,26 +115,32 @@
|
||||
timer: null, // 定时器
|
||||
}
|
||||
},
|
||||
onMounted() {
|
||||
mounted() {
|
||||
// this.observer();
|
||||
this.innerCurrent = this.current;
|
||||
this.leftMenuStatus(this.innerCurrent);
|
||||
this.getMenuItemTop()
|
||||
},
|
||||
methods: {
|
||||
addUnit,
|
||||
// 点击左边的栏目切换
|
||||
async swichMenu(index) {
|
||||
if(this.arr.length == 0) {
|
||||
await this.getMenuItemTop();
|
||||
if (this.mode == 'follow') {
|
||||
if(this.arr.length == 0) {
|
||||
await this.getMenuItemTop();
|
||||
}
|
||||
this.scrollIntoView = 'item' + index;
|
||||
}
|
||||
if (index == this.current) return;
|
||||
this.scrollRightTop = this.oldScrollTop;
|
||||
|
||||
if (index == this.innerCurrent) return;
|
||||
this.$nextTick(function(){
|
||||
this.scrollRightTop = this.arr[index];
|
||||
this.current = index;
|
||||
this.leftMenuStatus(index);
|
||||
this.innerCurrent = index;
|
||||
this.$emit('update:current', index);
|
||||
})
|
||||
},
|
||||
// 获取一个目标元素的高度
|
||||
getElRect(elClass, dataVal) {
|
||||
new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select('.' + elClass).fields({
|
||||
size: true
|
||||
@@ -117,54 +159,73 @@
|
||||
},
|
||||
// 观测元素相交状态
|
||||
async observer() {
|
||||
await this.$nextTick();
|
||||
// 清除之前的观察器
|
||||
if (this._observerList) {
|
||||
this._observerList.forEach(observer => {
|
||||
observer.disconnect();
|
||||
});
|
||||
}
|
||||
this._observerList = [];
|
||||
|
||||
this.tabList.map((val, index) => {
|
||||
let observer = uni.createIntersectionObserver(this);
|
||||
// 检测右边scroll-view的id为itemxx的元素与u-cate-tab__right-box的相交状态
|
||||
// 如果跟.u-cate-tab__right-box底部相交,就动态设置左边栏目的活动状态
|
||||
this._observerList.push(observer);
|
||||
// 检测相交状态
|
||||
observer.relativeTo('.u-cate-tab__right-box', {
|
||||
top: 0
|
||||
}).observe('#item' + index, res => {
|
||||
top: 10
|
||||
}).observe('#item' + index, (res) => {
|
||||
if (res.intersectionRatio > 0) {
|
||||
let id = res.id.substring(4);
|
||||
this.leftMenuStatus(id);
|
||||
console.log('res', res);
|
||||
// 修复:确保正确获取索引
|
||||
let id = res.id ? res.id.substring(4) : index;
|
||||
this.leftMenuStatus(parseInt(id));
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// 设置左边菜单的滚动状态
|
||||
async leftMenuStatus(index) {
|
||||
this.current = index;
|
||||
this.innerCurrent = index;
|
||||
this.$emit('update:current', index);
|
||||
// 如果为0,意味着尚未初始化
|
||||
if (this.menuHeight == 0 || this.menuItemHeight == 0) {
|
||||
await this.getElRect('u-cate-tab__menu-scroll-view', 'menuHeight');
|
||||
await this.getElRect('u-cate-tab__item', 'menuItemHeight');
|
||||
}
|
||||
// console.log(this.menuHeight, this.menuItemHeight)
|
||||
// 将菜单活动item垂直居中
|
||||
this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
|
||||
},
|
||||
// 获取右边菜单每个item到顶部的距离
|
||||
getMenuItemTop() {
|
||||
new Promise(resolve => {
|
||||
async getMenuItemTop() {
|
||||
// await this.$nextTick();
|
||||
// console.log('getMenuItemTop')
|
||||
return new Promise(resolve => {
|
||||
let selectorQuery = uni.createSelectorQuery().in(this);
|
||||
selectorQuery.selectAll('.u-cate-tab__page-item').boundingClientRect((rects) => {
|
||||
// 如果节点尚未生成,rects值为[](因为用selectAll,所以返回的是数组),循环调用执行
|
||||
if(!rects.length) {
|
||||
setTimeout(() => {
|
||||
this.getMenuItemTop();
|
||||
}, 10);
|
||||
}, 100);
|
||||
return ;
|
||||
}
|
||||
// console.log(rects)
|
||||
this.rects = rects;
|
||||
this.arr = [];
|
||||
rects.forEach((rect) => {
|
||||
// 这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
|
||||
this.arr.push(rect.top - rects[0].top);
|
||||
resolve();
|
||||
})
|
||||
// console.log(this.arr)
|
||||
resolve();
|
||||
}).exec()
|
||||
})
|
||||
},
|
||||
// 右边菜单滚动
|
||||
async rightScroll(e) {
|
||||
if (this.mode !== 'follow') return;
|
||||
this.oldScrollTop = e.detail.scrollTop;
|
||||
// console.log(e.detail.scrollTop)
|
||||
// console.log(JSON.stringify(this.arr))
|
||||
@@ -195,7 +256,7 @@
|
||||
return ;
|
||||
}
|
||||
}
|
||||
}, 10)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,6 +271,7 @@
|
||||
.u-cate-tab__wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
$u-cell-group-title-padding: 16px 16px 8px !default;
|
||||
$u-cell-group-title-font-size: 15px !default;
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<view class="u-cell__left-icon-wrap" v-if="$slots.icon || icon">
|
||||
<slot name="icon" v-if="$slots.icon">
|
||||
</slot>
|
||||
<u-icon v-else :name="icon"
|
||||
<up-icon v-else :name="icon"
|
||||
:custom-style="iconStyle"
|
||||
:size="size === 'large' ? 22 : 18"></u-icon>
|
||||
:size="size === 'large' ? 22 : 18"></up-icon>
|
||||
</view>
|
||||
<view class="u-cell__title">
|
||||
<!-- 将slot与默认内容用if/else分开主要是因为微信小程序不支持slot嵌套传递,这样才能解决collapse组件的slot不失效问题,label暂时未用到。 -->
|
||||
@@ -30,9 +30,9 @@
|
||||
</slot>
|
||||
<view class="u-cell__right-icon-wrap" v-if="$slots['right-icon'] || isLink"
|
||||
:class="[`u-cell__right-icon-wrap--${arrowDirection}`]">
|
||||
<u-icon v-if="rightIcon && !$slots['right-icon']" :name="rightIcon"
|
||||
<up-icon v-if="rightIcon && !$slots['right-icon']" :name="rightIcon"
|
||||
:custom-style="rightIconStyle" :color="disabled ? '#c8c9cc' : 'info'"
|
||||
:size="size === 'large' ? 18 : 16"></u-icon>
|
||||
:size="size === 'large' ? 18 : 16"></up-icon>
|
||||
<slot v-else name="right-icon">
|
||||
</slot>
|
||||
</view>
|
||||
@@ -113,7 +113,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
$u-cell-padding: 13px 15px !default;
|
||||
$u-cell-font-size: 15px !default;
|
||||
|
||||
@@ -100,8 +100,7 @@
|
||||
values.push(child.name)
|
||||
}
|
||||
})
|
||||
// 发出事件
|
||||
this.$emit('change', values)
|
||||
|
||||
// 修改通过v-model绑定的值
|
||||
// #ifdef VUE3
|
||||
this.$emit("update:modelValue", values);
|
||||
@@ -109,13 +108,14 @@
|
||||
// #ifdef VUE2
|
||||
this.$emit("input", values);
|
||||
// #endif
|
||||
// 放在最后更新,否则change事件传出去的values不会更新
|
||||
this.$emit('change', values)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-checkbox-group {
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
:style="[iconWrapStyle]"
|
||||
>
|
||||
<slot name="icon" :elIconSize="elIconSize" :elIconColor="elIconColor">
|
||||
<u-icon
|
||||
<up-icon
|
||||
class="u-checkbox__icon-wrap__icon"
|
||||
name="checkbox-mark"
|
||||
:size="elIconSize"
|
||||
@@ -177,7 +177,7 @@
|
||||
const style = {}
|
||||
if (!this.usedAlone) {
|
||||
if (this.parentData.borderBottom && this.parentData.placement === 'row') {
|
||||
error('检测到您将borderBottom设置为true,需要同时将u-checkbox-group的placement设置为column才有效')
|
||||
error('检测到您将borderBottom设置为true,需要同时将up-checkbox-group的placement设置为column才有效')
|
||||
}
|
||||
// 当父组件设置了显示下边框并且排列形式为纵向时,给内容和边框之间加上一定间隔
|
||||
if (this.parentData.borderBottom && this.parentData.placement === 'column') {
|
||||
@@ -197,13 +197,14 @@
|
||||
// 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
|
||||
this.updateParentData()
|
||||
if (!this.parent) {
|
||||
error('u-checkbox必须搭配u-checkbox-group组件使用')
|
||||
error('up-checkbox必须搭配up-checkbox-group组件使用')
|
||||
}
|
||||
let value = '';
|
||||
// #ifdef VUE2
|
||||
const value = this.parentData.value
|
||||
value = this.parentData.value
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
const value = this.parentData.modelValue
|
||||
value = this.parentData.modelValue
|
||||
// #endif
|
||||
// 设置初始化时,是否默认选中的状态,父组件u-checkbox-group的value可能是array,所以额外判断
|
||||
if (this.checked) {
|
||||
@@ -248,7 +249,9 @@
|
||||
}
|
||||
},
|
||||
emitEvent() {
|
||||
this.$emit('change', this.isChecked)
|
||||
this.$emit('change', this.isChecked, {
|
||||
name: this.name
|
||||
})
|
||||
// 双向绑定
|
||||
if (this.usedAlone) {
|
||||
this.$emit('update:checked', this.isChecked)
|
||||
@@ -281,7 +284,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
$u-checkbox-icon-wrap-margin-right:6px !default;
|
||||
$u-checkbox-icon-wrap-font-size:6px !default;
|
||||
$u-checkbox-icon-wrap-border-width:1px !default;
|
||||
|
||||
109
uni_modules/uview-plus/components/u-choose/u-choose.vue
Normal file
109
uni_modules/uview-plus/components/u-choose/u-choose.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<style scoped lang="scss">
|
||||
.up-choose {
|
||||
::v-deep .up-tag {
|
||||
font-weight: 600;
|
||||
}
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.up-choose-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.up-choose-nowrap {
|
||||
flex-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<scroll-view
|
||||
:scroll-x="wrap === false"
|
||||
:class="['up-choose', wrap ? 'up-choose-wrap' : 'up-choose-nowrap']">
|
||||
<template :key="item.id" v-for="(item,index) in options">
|
||||
<view :style="{width: width, display: 'inline-block'}">
|
||||
<slot :item="item" :index="index">
|
||||
<up-tag :type="index == currentIndex ? 'primary' : 'info'"
|
||||
size="large" :plain="index == currentIndex ? false : true"
|
||||
:class="currentIndex === index ? 'active': ''" :height="itemHeight"
|
||||
:style="{width: itemWidth, padding: itemPadding}"
|
||||
@click="change(index)">
|
||||
{{item[labelName]}}
|
||||
</up-tag>
|
||||
</slot>
|
||||
</view>
|
||||
</template>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'up-choose',
|
||||
props: {
|
||||
options:{
|
||||
type: Array,
|
||||
default: ()=>{
|
||||
return [];
|
||||
}
|
||||
},
|
||||
modelValue: {
|
||||
type: [Number,String,Array],
|
||||
default: false
|
||||
},
|
||||
type: {
|
||||
type: [String],
|
||||
default: 'radio'
|
||||
},
|
||||
itemWidth: {
|
||||
type: [String],
|
||||
default: 'auto'
|
||||
},
|
||||
itemHeight: {
|
||||
type: [String],
|
||||
default: '50px'
|
||||
},
|
||||
itemPadding: {
|
||||
type: [String],
|
||||
default: '8px'
|
||||
},
|
||||
labelName: {
|
||||
type: String,
|
||||
default: 'title'
|
||||
},
|
||||
valueName: {
|
||||
type: String,
|
||||
default: 'value'
|
||||
},
|
||||
customClick: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否换行
|
||||
wrap: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentIndex: ''
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.currentIndex = this.modelValue;
|
||||
},
|
||||
emits: ['update:modelValue', 'custom-click'],
|
||||
methods: {
|
||||
change(index){
|
||||
if (this.customClick) {
|
||||
this.$emit('custom-click', index);
|
||||
} else {
|
||||
this.currentIndex = index;
|
||||
this.$emit('update:modelValue', index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -112,7 +112,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-circle-progress {
|
||||
@include flex(row);
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<view class="u-city-locate">
|
||||
<up-index-list :indexList="indexList">
|
||||
<template #header>
|
||||
<view class="u-current-city-wrap">
|
||||
<view class="u-current-city-title">{{ t("up.cityLocate.locateCity") }}</view>
|
||||
<view class="u-current-city-item" @tap="location">
|
||||
<view class="u-location-city">{{locationCity}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template :key="index" v-for="(item, index) in cityList">
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<up-index-anchor :text="indexList[index]"></up-index-anchor>
|
||||
<!-- #endif -->
|
||||
<up-index-item>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<up-index-anchor :text="indexList[index]"></up-index-anchor>
|
||||
<!-- #endif -->
|
||||
<view class="hot-city-list" v-if="index == 0">
|
||||
<view class="" v-for="(item1, index1) in item" @tap="selectedCity(item1)">
|
||||
<view class="hot-city-item">{{ item1[nameKey] }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="item-list" v-for="(item1, index1) in item" :key="index1">
|
||||
<view class="list__item" @tap="selectedCity(item1)">
|
||||
<text class="list__item__city-name">{{item1[nameKey]}}</text>
|
||||
</view>
|
||||
<up-line></up-line>
|
||||
</view>
|
||||
</up-index-item>
|
||||
</template>
|
||||
<template #footer>
|
||||
<view class="u-safe-area-inset--bottom">
|
||||
<text class="list__footer"></text>
|
||||
</view>
|
||||
</template>
|
||||
</up-index-list>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { t } from '../../libs/i18n'
|
||||
export default{
|
||||
name: 'u-city-locate',
|
||||
props:{
|
||||
indexList: {
|
||||
type: Array,
|
||||
default: ['🔥']
|
||||
},
|
||||
cityList:{
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [
|
||||
[{
|
||||
name: '北京',
|
||||
value: 'beijing'
|
||||
},
|
||||
{
|
||||
name: '上海',
|
||||
value: 'shanghai'
|
||||
},
|
||||
{
|
||||
name: '广州',
|
||||
value: 'guangzhou'
|
||||
},
|
||||
{
|
||||
name: '深圳',
|
||||
value: 'shenzhen'
|
||||
},
|
||||
{
|
||||
name: '杭州',
|
||||
value: 'hangzhou'
|
||||
}]
|
||||
]
|
||||
}
|
||||
},
|
||||
locationType: {
|
||||
type: String,
|
||||
default: 'wgs84'
|
||||
},
|
||||
currentCity: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
nameKey: {
|
||||
type: String,
|
||||
default: 'name'
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
},
|
||||
watch:{
|
||||
currentCity(val) {
|
||||
this.locationCity = val;
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return{
|
||||
locationCity: t("up.cityLocate.locating") + '....'
|
||||
}
|
||||
},
|
||||
emits: ['location-success', 'select-city'],
|
||||
methods:{
|
||||
t,
|
||||
// 获取城市
|
||||
selectedCity(city){
|
||||
this.locationCity = city[this.nameKey];
|
||||
this.$emit('select-city', {
|
||||
locationCity: this.locationCity
|
||||
});
|
||||
},
|
||||
// 定位操作
|
||||
location(){
|
||||
let That = this;
|
||||
uni.getLocation({
|
||||
type: this.locationType,
|
||||
geocode:true,
|
||||
success(res){
|
||||
console.log(res);
|
||||
That.locationCity = res.address && res.address.city;
|
||||
That.$emit('location-success', {
|
||||
...res,
|
||||
locationCity: That.locationCity
|
||||
});
|
||||
},
|
||||
fail(){
|
||||
That.locationCity = t("up.cityLocate.fail");
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
// 页面挂载后进行异步操作
|
||||
created(){
|
||||
},
|
||||
mounted(){
|
||||
this.location();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.list__item {
|
||||
padding: 8px 1px;
|
||||
}
|
||||
.u-current-city-title {
|
||||
color: grey;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.u-current-city-item {
|
||||
height: 30px;
|
||||
}
|
||||
.hot-city-list {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
padding: 12px 0;
|
||||
.hot-city-item {
|
||||
padding: 6px 12px;
|
||||
margin: 5px;
|
||||
border: 1px solid #ededed;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -220,7 +220,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
$u-code-input-cursor-width: 1px;
|
||||
$u-code-input-cursor-height: 20px;
|
||||
$u-code-input-cursor-animation-duration: 1s;
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:55:27
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/code.js
|
||||
* @FilePath : /uview-plus/libs/config/props/code.js
|
||||
*/
|
||||
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
// code 组件
|
||||
code: {
|
||||
seconds: 60,
|
||||
startText: '获取验证码',
|
||||
changeText: 'X秒重新获取',
|
||||
endText: '重新获取',
|
||||
startText: t("up.code.send"),
|
||||
changeText: t("up.code.resendAfter"),
|
||||
endText: t("up.code.resend"),
|
||||
keepRunning: false,
|
||||
uniqueKey: ''
|
||||
}
|
||||
|
||||
@@ -128,5 +128,4 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
</style>
|
||||
|
||||
@@ -100,7 +100,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-col {
|
||||
padding: 0;
|
||||
|
||||
@@ -21,6 +21,11 @@ export default {
|
||||
name: '',
|
||||
icon: '',
|
||||
duration: 300,
|
||||
showRight: true
|
||||
showRight: true,
|
||||
titleStyle: {},
|
||||
iconStyle: {},
|
||||
rightIconStyle: {},
|
||||
cellCustomStyle: {},
|
||||
cellCustomClass: ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,13 @@ export const props = defineMixin({
|
||||
type: String,
|
||||
default: () => defProps.collapseItem.title
|
||||
},
|
||||
// 标题的样式
|
||||
titleStyle: {
|
||||
type: [Object, String],
|
||||
default: () => {
|
||||
return defProps.collapseItem.titleStyle
|
||||
}
|
||||
},
|
||||
// 标题右侧内容
|
||||
value: {
|
||||
type: String,
|
||||
@@ -62,5 +69,29 @@ export const props = defineMixin({
|
||||
type: Boolean,
|
||||
default: () => defProps.collapseItem.showRight
|
||||
},
|
||||
// 左侧图标样式
|
||||
iconStyle: {
|
||||
type: [Object, String],
|
||||
default: () => {
|
||||
return defProps.collapseItem.iconStyle
|
||||
}
|
||||
},
|
||||
// 右侧箭头图标的样式
|
||||
rightIconStyle: {
|
||||
type: [Object, String],
|
||||
default: () => {
|
||||
return defProps.collapseItem.rightIconStyle
|
||||
}
|
||||
},
|
||||
cellCustomStyle: {
|
||||
type: [Object, String],
|
||||
default: () => {
|
||||
return defProps.collapseItem.cellCustomStyle
|
||||
}
|
||||
},
|
||||
cellCustomClass: {
|
||||
type: String,
|
||||
default: () => defProps.collapseItem.cellCustomClass
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
@click="clickHandler"
|
||||
:arrowDirection="expanded ? 'up' : 'down'"
|
||||
:disabled="disabled"
|
||||
:customClass="cellCustomClass"
|
||||
:customStyle="cellCustomStyle"
|
||||
>
|
||||
<!-- 微信小程序不支持,因为微信中不支持 <slot name="title" #title />的写法 -->
|
||||
<template #title>
|
||||
@@ -22,7 +24,7 @@
|
||||
</template>
|
||||
<template #icon>
|
||||
<slot name="icon">
|
||||
<u-icon v-if="!$slots.icon && icon" :size="22" :name="icon"></u-icon>
|
||||
<up-icon v-if="!$slots.icon && icon" :size="22" :name="icon"></up-icon>
|
||||
</slot>
|
||||
</template>
|
||||
<template #value>
|
||||
@@ -34,7 +36,7 @@
|
||||
</template>
|
||||
<template #right-icon>
|
||||
<template v-if="showRight">
|
||||
<u-icon v-if="!$slots['right-icon']" :size="16" name="arrow-right"></u-icon>
|
||||
<up-icon v-if="!$slots['right-icon']" :size="16" name="arrow-right"></up-icon>
|
||||
<slot name="right-icon">
|
||||
</slot>
|
||||
</template>
|
||||
@@ -223,7 +225,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-collapse-item {
|
||||
|
||||
|
||||
@@ -87,5 +87,4 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
</style>
|
||||
|
||||
1095
uni_modules/uview-plus/components/u-color-picker/u-color-picker.vue
Normal file
1095
uni_modules/uview-plus/components/u-color-picker/u-color-picker.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,11 +8,11 @@
|
||||
class="u-notice__left-icon"
|
||||
v-if="icon"
|
||||
>
|
||||
<u-icon
|
||||
<up-icon
|
||||
:name="icon"
|
||||
:color="color"
|
||||
size="19"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</view>
|
||||
</slot>
|
||||
<swiper
|
||||
@@ -40,19 +40,19 @@
|
||||
class="u-notice__right-icon"
|
||||
v-if="['link', 'closable'].includes(mode)"
|
||||
>
|
||||
<u-icon
|
||||
<up-icon
|
||||
v-if="mode === 'link'"
|
||||
name="arrow-right"
|
||||
:size="17"
|
||||
:color="color"
|
||||
></u-icon>
|
||||
<u-icon
|
||||
></up-icon>
|
||||
<up-icon
|
||||
v-if="mode === 'closable'"
|
||||
name="close"
|
||||
:size="16"
|
||||
:color="color"
|
||||
@click="close"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -128,7 +128,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-notice {
|
||||
@include flex;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<view @click="handleClick">
|
||||
<slot>复制</slot>
|
||||
<slot>{{ t("up.common.copy") }}</slot>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
name: "up-copy",
|
||||
props: {
|
||||
@@ -17,16 +18,17 @@ export default {
|
||||
},
|
||||
notice: {
|
||||
type: String,
|
||||
default: '复制成功'
|
||||
default: t("up.common.copy") + t("up.common.success")
|
||||
}
|
||||
},
|
||||
emits: ['success'],
|
||||
methods: {
|
||||
t,
|
||||
handleClick() {
|
||||
let content = this.content;
|
||||
if (!content) {
|
||||
uni.showToast({
|
||||
title: '暂无',
|
||||
title: t("up.common.none"),
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
@@ -42,7 +44,7 @@ export default {
|
||||
success: function() {
|
||||
if (that.alertStyle == 'modal') {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
title: "up.common.tip",
|
||||
content: that.notice
|
||||
});
|
||||
} else {
|
||||
@@ -55,7 +57,7 @@ export default {
|
||||
},
|
||||
fail:function(){
|
||||
uni.showToast({
|
||||
title: '复制失败',
|
||||
title: t("up.common.copy") + t("up.common.fail"),
|
||||
icon: 'none',
|
||||
duration:3000,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<view class="u-count-down">
|
||||
<slot>
|
||||
<slot :days="timeData.days" :hours="timeData.hours"
|
||||
:minutes="timeData.minutes" :seconds="timeData.seconds">
|
||||
<text class="u-count-down__text">{{ formattedTime }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
@@ -112,6 +113,7 @@
|
||||
this.remainTime = remain
|
||||
// 根据剩余的毫秒时间,得出该有天,小时,分钟等的值,返回一个对象
|
||||
const timeData = parseTimeData(remain)
|
||||
this.timeData = timeData;
|
||||
this.$emit('change', timeData)
|
||||
// 得出格式化后的时间
|
||||
this.formattedTime = parseFormat(this.format, timeData)
|
||||
@@ -151,7 +153,6 @@
|
||||
lang="scss"
|
||||
scoped
|
||||
>
|
||||
@import "../../libs/css/components.scss";
|
||||
$u-count-down-text-color:$u-content-color !default;
|
||||
$u-count-down-text-font-size:15px !default;
|
||||
$u-count-down-text-line-height:22px !default;
|
||||
|
||||
@@ -178,8 +178,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-count-num {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-flex;
|
||||
|
||||
406
uni_modules/uview-plus/components/u-coupon/u-coupon.vue
Normal file
406
uni_modules/uview-plus/components/u-coupon/u-coupon.vue
Normal file
@@ -0,0 +1,406 @@
|
||||
<template>
|
||||
<view class="up-coupon" :class="[`up-coupon--${shape}`, `up-coupon--${type}`, `up-coupon--${size}`, {'up-coupon--disabled': disabled}]"
|
||||
:style="[couponStyle]" @click="handleClick">
|
||||
<view class="up-coupon__content">
|
||||
<!-- 左侧金额区域 -->
|
||||
<view class="up-coupon__amount">
|
||||
<slot name="unit" :unit="unit" :unitPosition="unitPosition" v-if="unitPosition === 'left'">
|
||||
<text class="up-coupon__amount-unit" v-if="unitPosition === 'left'">{{ unit }}</text>
|
||||
</slot>
|
||||
<slot name="amount" :amount="amount">
|
||||
<text class="up-coupon__amount-value">{{ amount }}</text>
|
||||
</slot>
|
||||
<slot name="unit" :unit="unit" :unitPosition="unitPosition" v-if="unitPosition === 'right'">
|
||||
<text class="up-coupon__amount-unit" v-if="unitPosition === 'right'">{{ unit }}</text>
|
||||
</slot>
|
||||
<slot name="limit" :limit="limit">
|
||||
<text class="up-coupon__amount-limit" v-if="limit">{{ limit }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
|
||||
<!-- 中间描述区域 -->
|
||||
<view class="up-coupon__info">
|
||||
<slot name="title" :title="title">
|
||||
<text class="up-coupon__info-title">{{ title }}</text>
|
||||
</slot>
|
||||
<slot name="desc" :desc="desc">
|
||||
<text class="up-coupon__info-desc" v-if="desc">{{ desc }}</text>
|
||||
</slot>
|
||||
<slot name="time" :time="time">
|
||||
<text class="up-coupon__info-time" v-if="time">{{ time }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
|
||||
<!-- 右侧操作区域 -->
|
||||
<view class="up-coupon__action u-padding-right-20">
|
||||
<slot name="action" :actionText="actionText" :circle="circle">
|
||||
<up-tag type="error" :bgColor="type ? 'transparent' : '#eb433d'"
|
||||
:borderColor="type ? '#eee' : '#eb433d'" borderRadius="6px"
|
||||
size="medium" class="up-coupon__action-text"
|
||||
:shape="circle ? 'circle': 'circle'">{{ actionText }}</up-tag>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 红包绳子效果 -->
|
||||
<view v-if="shape === 'envelope'" class="up-coupon__rope"></view>
|
||||
|
||||
<!-- 默认插槽,可用于添加额外内容 -->
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'up-coupon',
|
||||
props: {
|
||||
// 金额
|
||||
amount: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
// 金额单位
|
||||
unit: {
|
||||
type: String,
|
||||
default: '¥'
|
||||
},
|
||||
// 单位位置
|
||||
unitPosition: {
|
||||
type: String,
|
||||
default: 'left'
|
||||
},
|
||||
// 使用限制
|
||||
limit: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: '优惠券'
|
||||
},
|
||||
// 描述
|
||||
desc: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 有效期
|
||||
time: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 操作按钮文字
|
||||
actionText: {
|
||||
type: String,
|
||||
default: '使用'
|
||||
},
|
||||
// 形状:coupon-优惠券, envelope-红包, card-卡片
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'coupon'
|
||||
},
|
||||
// 尺寸:small, medium, large
|
||||
size: {
|
||||
type: String,
|
||||
default: 'medium'
|
||||
},
|
||||
// 是否圆形按钮
|
||||
circle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否禁用
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 文字颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 内置背景类型
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
couponStyle() {
|
||||
const style = {};
|
||||
if (this.bgColor) style.background = this.bgColor;
|
||||
if (this.color) style.color = this.color;
|
||||
return style;
|
||||
},
|
||||
dotCount() {
|
||||
// 根据尺寸计算锯齿数量
|
||||
const map = {
|
||||
small: 8,
|
||||
medium: 10,
|
||||
large: 12
|
||||
};
|
||||
return map[this.size] || 10;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
if (this.disabled) return;
|
||||
this.$emit('click');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.up-coupon {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 8rpx;
|
||||
background: #ffebf0;
|
||||
color: $u-main-color;
|
||||
|
||||
&--coupon {
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -24rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: -24rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
&--envelope {
|
||||
border-radius: 16rpx;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 20rpx;
|
||||
background: repeating-linear-gradient(-45deg, #ffd000, #ffd000 10rpx, #ffa000 10rpx, #ffa000 20rpx);
|
||||
}
|
||||
}
|
||||
|
||||
&--card {
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
width: 100%;
|
||||
|
||||
&--small {
|
||||
// width: 520rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
&--medium {
|
||||
// width: 600rpx;
|
||||
height: 180rpx;
|
||||
}
|
||||
|
||||
&--large {
|
||||
// width: 700rpx;
|
||||
height: 220rpx;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
padding: 0 30rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&__amount {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding-left: 10rpx;
|
||||
padding-right: 30rpx;
|
||||
border-right: 1px dashed #ccc;
|
||||
|
||||
&-unit {
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&-value {
|
||||
font-size: 56rpx;
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
line-height: 1;
|
||||
margin: 10rpx 0;
|
||||
}
|
||||
|
||||
&-limit {
|
||||
font-size: 24rpx;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding-left: 30rpx;
|
||||
|
||||
&-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
&-desc {
|
||||
font-size: 24rpx;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
&-time {
|
||||
font-size: 20rpx;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
&__action {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__dots {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__dot {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
margin: 0 -16rpx;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
&__rope {
|
||||
position: absolute;
|
||||
top: -40rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(to right, #ffd000, #ffa000);
|
||||
border-radius: 40rpx 40rpx 0 0;
|
||||
z-index: 1;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -20rpx;
|
||||
width: 20rpx;
|
||||
height: 40rpx;
|
||||
background: linear-gradient(to bottom, #ffd000, #ffa000);
|
||||
border-radius: 10rpx 0 0 10rpx;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -20rpx;
|
||||
width: 20rpx;
|
||||
height: 40rpx;
|
||||
background: linear-gradient(to bottom, #ffd000, #ffa000);
|
||||
border-radius: 0 10rpx 10rpx 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 不同主题样式
|
||||
&--primary {
|
||||
background: linear-gradient(90deg, #43afff, #3b8cff);
|
||||
color: #fff;
|
||||
.up-coupon__amount {
|
||||
border-right: 1px dashed #eee;
|
||||
}
|
||||
.up-coupon__amount-value {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&--success {
|
||||
background: linear-gradient(90deg, #67dda9, #19be6b);
|
||||
color: #fff !important;
|
||||
.up-coupon__amount {
|
||||
border-right: 1px dashed #eee;
|
||||
}
|
||||
.up-coupon__amount-value {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&--warning {
|
||||
background: linear-gradient(90deg, #ff9739, #ff6a39);
|
||||
color: #fff;
|
||||
.up-coupon__amount {
|
||||
border-right: 1px dashed #eee;
|
||||
}
|
||||
.up-coupon__amount-value {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&--error {
|
||||
background: linear-gradient(90deg, #ff7070, #ff4747);
|
||||
color: #fff;
|
||||
.up-coupon__amount {
|
||||
border-right: 1px dashed #eee;
|
||||
}
|
||||
.up-coupon__amount-value {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1228
uni_modules/uview-plus/components/u-cropper/u-cropper.vue
Normal file
1228
uni_modules/uview-plus/components/u-cropper/u-cropper.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@
|
||||
* @lastTime : 2021-08-20 16:57:48
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/datetimePicker.js
|
||||
*/
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
// datetimePicker 组件
|
||||
datetimePicker: {
|
||||
@@ -26,12 +27,18 @@ export default {
|
||||
formatter: null,
|
||||
loading: false,
|
||||
itemHeight: 44,
|
||||
cancelText: '取消',
|
||||
confirmText: '确认',
|
||||
cancelText: t("up.common.cancel"),
|
||||
confirmText: t("up.common.confirm"),
|
||||
cancelColor: '#909193',
|
||||
confirmColor: '#3c9cff',
|
||||
visibleItemCount: 5,
|
||||
closeOnClickOverlay: false,
|
||||
defaultIndex: []
|
||||
defaultIndex: [],
|
||||
inputBorder: 'surround',
|
||||
disabled: false,
|
||||
disabledColor: '',
|
||||
placeholder: t("up.common.pleaseChoose"),
|
||||
inputProps: {},
|
||||
pageInline: false
|
||||
}
|
||||
}
|
||||
|
||||
7
uni_modules/uview-plus/components/u-datetime-picker/dayjs.esm.min.js
vendored
Normal file
7
uni_modules/uview-plus/components/u-datetime-picker/dayjs.esm.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -5,15 +5,29 @@ export const props = defineMixin({
|
||||
// 是否显示input
|
||||
hasInput: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
default: false
|
||||
},
|
||||
inputProps: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
inputBorder: {
|
||||
type: String,
|
||||
default: () => defProps.input.inputBorder
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
default: () => defProps.input.disabled
|
||||
},
|
||||
disabledColor:{
|
||||
type: String,
|
||||
default: () => defProps.input.disabledColor
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: () => '请选择'
|
||||
default: () => defProps.input.placeholder
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
@@ -149,6 +163,11 @@ export const props = defineMixin({
|
||||
defaultIndex: {
|
||||
type: Array,
|
||||
default: () => defProps.datetimePicker.defaultIndex
|
||||
},
|
||||
// 是否页面内展示
|
||||
pageInline:{
|
||||
type: Boolean,
|
||||
default: () => defProps.datetimePicker.pageInline
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
<template>
|
||||
<view class="u-datetime-picker">
|
||||
<view v-if="hasInput" class="u-datetime-picker__has-input"
|
||||
@click="onShowByClickInput"
|
||||
@click="onShowByClickInput"
|
||||
>
|
||||
<slot name="trigger" :value="inputValue">
|
||||
<up-input
|
||||
:placeholder="placeholder"
|
||||
:readonly="!!showByClickInput"
|
||||
border="surround"
|
||||
v-model="inputValue"
|
||||
:disabled="disabled"
|
||||
v-bind="inputPropsInner"
|
||||
></up-input>
|
||||
<div class="input-cover">
|
||||
</div>
|
||||
<cover-view class="input-cover">
|
||||
</cover-view>
|
||||
</slot>
|
||||
</view>
|
||||
<u-picker
|
||||
ref="picker"
|
||||
:show="show || (hasInput && showByClickInput)"
|
||||
:show="pageInline || show || (hasInput && showByClickInput)"
|
||||
:popupMode="popupMode"
|
||||
:closeOnClickOverlay="closeOnClickOverlay"
|
||||
:columns="columns"
|
||||
@@ -31,6 +29,7 @@
|
||||
:cancelColor="cancelColor"
|
||||
:confirmColor="confirmColor"
|
||||
:toolbarRightSlot="toolbarRightSlot"
|
||||
:pageInline="pageInline"
|
||||
@close="close"
|
||||
@cancel="cancel"
|
||||
@confirm="confirm"
|
||||
@@ -60,7 +59,7 @@
|
||||
import { props } from './props';
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin';
|
||||
import { mixin } from '../../libs/mixin/mixin';
|
||||
import dayjs from 'dayjs/esm/index';
|
||||
import dayjs from './dayjs.esm.min.js';
|
||||
import { range, error, padZero } from '../../libs/function/index';
|
||||
import test from '../../libs/function/test';
|
||||
/**
|
||||
@@ -111,6 +110,12 @@
|
||||
watch: {
|
||||
show(newValue, oldValue) {
|
||||
if (newValue) {
|
||||
// #ifdef VUE3
|
||||
this.innerValue = this.correctValue(this.modelValue)
|
||||
// #endif
|
||||
// #ifdef VUE2
|
||||
this.innerValue = this.correctValue(this.value)
|
||||
// #endif
|
||||
this.updateColumnValue(this.innerValue)
|
||||
}
|
||||
},
|
||||
@@ -133,7 +138,17 @@
|
||||
computed: {
|
||||
// 如果以下这些变量发生了变化,意味着需要重新初始化各列的值
|
||||
propsChange() {
|
||||
return [this.mode, this.maxDate, this.minDate, this.minHour, this.maxHour, this.minMinute, this.maxMinute, this.filter, ]
|
||||
return [this.mode, this.maxDate, this.minDate, this.minHour, this.maxHour, this.minMinute, this.maxMinute, this.filter, this.modelValue]
|
||||
},
|
||||
// input的props
|
||||
inputPropsInner() {
|
||||
return {
|
||||
border: this.inputBorder,
|
||||
placeholder: this.placeholder,
|
||||
disabled: this.disabled,
|
||||
disabledColor: this.disabledColor,
|
||||
...this.inputProps
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -162,6 +177,9 @@
|
||||
case 'year-month':
|
||||
format = 'YYYY-MM'
|
||||
break;
|
||||
case 'datehour':
|
||||
format = 'YYYY-MM-DD HH'
|
||||
break;
|
||||
case 'datetime':
|
||||
format = 'YYYY-MM-DD HH:mm'
|
||||
break;
|
||||
@@ -428,7 +446,10 @@
|
||||
},
|
||||
// 根据minDate、maxDate、minHour、maxHour等边界值,判断各列的开始和结束边界值
|
||||
getBoundary(type, innerValue) {
|
||||
const value = new Date(innerValue)
|
||||
let value = new Date(innerValue)
|
||||
if(isNaN(value.getTime())){
|
||||
value = new Date()
|
||||
}
|
||||
const boundary = new Date(this[`${type}Date`])
|
||||
const year = dayjs(boundary).year()
|
||||
let month = 1
|
||||
@@ -467,14 +488,13 @@
|
||||
if(!this.disabled){
|
||||
this.showByClickInput = !this.showByClickInput
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
.u-datetime-picker {
|
||||
flex: 1;
|
||||
&__has-input {
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
v-if="dot"
|
||||
class="u-divider__dot"
|
||||
>●</text>
|
||||
<text
|
||||
v-else-if="text"
|
||||
class="u-divider__text"
|
||||
:style="[textStyle]"
|
||||
>{{text}}</text>
|
||||
<slot>
|
||||
<text
|
||||
v-if="!dot && text"
|
||||
class="u-divider__text"
|
||||
:style="[textStyle]"
|
||||
>{{text}}</text>
|
||||
</slot>
|
||||
<u-line
|
||||
:color="lineColor"
|
||||
:customStyle="rightLineStyle"
|
||||
@@ -95,7 +97,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
$u-divider-margin:15px 0 !default;
|
||||
$u-divider-text-margin:0 15px !default;
|
||||
$u-divider-dot-font-size:12px !default;
|
||||
|
||||
367
uni_modules/uview-plus/components/u-dragsort/u-dragsort.vue
Normal file
367
uni_modules/uview-plus/components/u-dragsort/u-dragsort.vue
Normal file
@@ -0,0 +1,367 @@
|
||||
<template>
|
||||
<view class="u-dragsort"
|
||||
:class="[direction == 'horizontal' ? 'u-dragsort--horizontal' : '', direction == 'all' ? 'u-dragsort--all' : '']">
|
||||
<movable-area class="u-dragsort-area" :style="movableAreaStyle">
|
||||
<movable-view v-for="(item, index) in list" :key="item.id" :id="`u-dragsort-item-${index}`"
|
||||
class="u-dragsort-item" :class="{ 'dragging': dragIndex === index }"
|
||||
:direction="direction === 'all' ? 'all' : direction" :x="item.x" :y="item.y" :inertia="false"
|
||||
:disabled="!draggable || (item.draggable === false)" @change="onChange(index, $event)"
|
||||
@touchstart="onTouchStart(index)" @touchend="onTouchEnd" @touchcancel="onTouchEnd">
|
||||
<view class="u-dragsort-item-content">
|
||||
<slot :item="item" :index="index">
|
||||
{{ item.label }}
|
||||
</slot>
|
||||
</view>
|
||||
</movable-view>
|
||||
</movable-area>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin';
|
||||
import { mixin } from '../../libs/mixin/mixin';
|
||||
import { addStyle, addUnit, sleep } from '../../libs/function/index';
|
||||
export default {
|
||||
name: 'u-dragsort',
|
||||
// #ifdef MP
|
||||
mixins: [mpMixin, mixin,],
|
||||
// #endif
|
||||
// #ifndef MP
|
||||
mixins: [mixin],
|
||||
// #endif
|
||||
props: {
|
||||
initialList: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => []
|
||||
},
|
||||
draggable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
default: 'vertical',
|
||||
validator: value => ['vertical', 'horizontal', 'all'].includes(value)
|
||||
},
|
||||
// 新增列数属性,用于all模式
|
||||
columns: {
|
||||
type: Number,
|
||||
default: 3
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
dragIndex: -1,
|
||||
itemHeight: 40,
|
||||
itemWidth: 80,
|
||||
areaWidth: 0, // 可拖动区域宽度
|
||||
areaHeight: 0, // 可拖动区域高度
|
||||
originalPositions: [], // 保存原始位置
|
||||
currentPosition: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
movableAreaStyle() {
|
||||
if (this.direction === 'vertical') {
|
||||
return {
|
||||
height: `${this.list.length * this.itemHeight}px`,
|
||||
width: '100%'
|
||||
};
|
||||
} else if (this.direction === 'horizontal') {
|
||||
return {
|
||||
height: '100%',
|
||||
width: `${this.list.length * this.itemWidth}px`
|
||||
};
|
||||
} else {
|
||||
// all模式,计算网格布局所需的高度
|
||||
const rows = Math.ceil(this.list.length / this.columns);
|
||||
return {
|
||||
height: `${rows * this.itemHeight}px`,
|
||||
width: '100%'
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ['drag-end'],
|
||||
async mounted() {
|
||||
await this.$nextTick();
|
||||
this.initList();
|
||||
this.calculateItemSize();
|
||||
this.calculateAreaSize();
|
||||
},
|
||||
methods: {
|
||||
initList() {
|
||||
// 初始化列表项的位置
|
||||
this.list = this.initialList.map((item, index) => {
|
||||
let x = 0, y = 0;
|
||||
|
||||
if (this.direction === 'horizontal') {
|
||||
x = index * this.itemWidth;
|
||||
y = 0;
|
||||
} else if (this.direction === 'vertical') {
|
||||
x = 0;
|
||||
y = index * this.itemHeight;
|
||||
} else {
|
||||
// all模式,网格布局
|
||||
const col = index % this.columns;
|
||||
const row = Math.floor(index / this.columns);
|
||||
x = col * this.itemWidth;
|
||||
y = row * this.itemHeight;
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
x,
|
||||
y
|
||||
};
|
||||
});
|
||||
// 保存初始位置
|
||||
this.saveOriginalPositions();
|
||||
},
|
||||
saveOriginalPositions() {
|
||||
// 保存当前位置作为原始位置
|
||||
this.originalPositions = this.list.map(item => ({
|
||||
x: item.x,
|
||||
y: item.y
|
||||
}));
|
||||
},
|
||||
async calculateItemSize() {
|
||||
// 计算项目尺寸
|
||||
await sleep(30);
|
||||
return new Promise((resolve) => {
|
||||
uni.createSelectorQuery()
|
||||
.in(this)
|
||||
.select('.u-dragsort-item-content')
|
||||
.boundingClientRect(res => {
|
||||
if (res) {
|
||||
this.itemHeight = res.height || 40;
|
||||
this.itemWidth = res.width || 80;
|
||||
|
||||
// 更新所有项目的位置
|
||||
this.updatePositions();
|
||||
// 保存原始位置
|
||||
this.saveOriginalPositions();
|
||||
}
|
||||
resolve(res);
|
||||
})
|
||||
.exec();
|
||||
});
|
||||
},
|
||||
async calculateAreaSize() {
|
||||
// 计算可拖动区域尺寸
|
||||
await sleep(30);
|
||||
return new Promise((resolve) => {
|
||||
uni.createSelectorQuery()
|
||||
.in(this)
|
||||
.select('.u-dragsort-area')
|
||||
.boundingClientRect(res => {
|
||||
if (res) {
|
||||
this.areaWidth = res.width || 300;
|
||||
this.areaHeight = res.height || 300;
|
||||
}
|
||||
resolve(res);
|
||||
})
|
||||
.exec();
|
||||
});
|
||||
},
|
||||
updatePositions() {
|
||||
// 更新所有项目的位置
|
||||
this.list.forEach((item, index) => {
|
||||
if (this.direction === 'vertical') {
|
||||
item.y = index * this.itemHeight;
|
||||
item.x = 0;
|
||||
} else if (this.direction === 'horizontal') {
|
||||
item.x = index * this.itemWidth;
|
||||
item.y = 0;
|
||||
} else {
|
||||
// all模式,网格布局
|
||||
const col = index % this.columns;
|
||||
const row = Math.floor(index / this.columns);
|
||||
item.x = col * this.itemWidth;
|
||||
item.y = row * this.itemHeight;
|
||||
}
|
||||
});
|
||||
},
|
||||
onTouchStart(index) {
|
||||
this.dragIndex = index;
|
||||
// 保存当前位置作为原始位置
|
||||
this.saveOriginalPositions();
|
||||
},
|
||||
onChange(index, event) {
|
||||
if (!event.detail.source || event.detail.source !== 'touch') return;
|
||||
|
||||
this.currentPosition.x = event.detail.x;
|
||||
this.currentPosition.y = event.detail.y;
|
||||
|
||||
// all模式下使用更智能的位置计算
|
||||
if (this.direction === 'all') {
|
||||
this.handleAllModeChange(index);
|
||||
} else {
|
||||
// 原有的垂直和水平模式逻辑
|
||||
let itemSize = 0;
|
||||
let targetIndex = -1;
|
||||
|
||||
if (this.direction === 'vertical') {
|
||||
itemSize = this.itemHeight;
|
||||
targetIndex = Math.max(0, Math.min(
|
||||
Math.round(this.currentPosition.y / itemSize),
|
||||
this.list.length - 1
|
||||
));
|
||||
} else if (this.direction === 'horizontal') {
|
||||
itemSize = this.itemWidth;
|
||||
targetIndex = Math.max(0, Math.min(
|
||||
Math.round(this.currentPosition.x / itemSize),
|
||||
this.list.length - 1
|
||||
));
|
||||
}
|
||||
|
||||
// 如果位置发生变化,则重新排序
|
||||
if (targetIndex !== index) {
|
||||
this.reorderItems(index, targetIndex);
|
||||
}
|
||||
}
|
||||
},
|
||||
handleAllModeChange(index) {
|
||||
// 在all模式下,根据当前位置计算最近的网格位置
|
||||
const col = Math.max(0, Math.min(Math.round(this.currentPosition.x / this.itemWidth), this.columns - 1));
|
||||
const row = Math.max(0, Math.round(this.currentPosition.y / this.itemHeight));
|
||||
|
||||
// 计算目标索引
|
||||
let targetIndex = row * this.columns + col;
|
||||
targetIndex = Math.max(0, Math.min(targetIndex, this.list.length - 1));
|
||||
|
||||
// 如果位置发生变化,则重新排序
|
||||
if (targetIndex !== index) {
|
||||
this.reorderItems(index, targetIndex);
|
||||
}
|
||||
},
|
||||
reorderItems(fromIndex, toIndex) {
|
||||
const movedItem = this.list.splice(fromIndex, 1)[0];
|
||||
this.list.splice(toIndex, 0, movedItem);
|
||||
|
||||
// 震动反馈
|
||||
if (uni.vibrateShort) {
|
||||
uni.vibrateShort();
|
||||
}
|
||||
|
||||
// 更新当前拖拽项目的新索引
|
||||
this.dragIndex = toIndex;
|
||||
|
||||
// 更新所有项目的位置
|
||||
this.updatePositions();
|
||||
|
||||
// 保存当前位置作为原始位置
|
||||
this.saveOriginalPositions();
|
||||
},
|
||||
onTouchEnd() {
|
||||
// 0.001是为了解决拖动过快等某些极限场景下位置还原不生效问题
|
||||
if (this.direction === 'horizontal') {
|
||||
this.list[this.dragIndex].x = this.currentPosition.x + 0.001;
|
||||
} else if (this.direction === 'vertical' || this.direction === 'all') {
|
||||
this.list[this.dragIndex].y = this.currentPosition.y + 0.001;
|
||||
this.list[this.dragIndex].x = this.currentPosition.x + 0.001;
|
||||
}
|
||||
|
||||
// 重置到位置,需要延迟触发动,否则无效。
|
||||
sleep(50).then(() => {
|
||||
this.list.forEach((item, index) => {
|
||||
item.x = this.originalPositions[index].x;
|
||||
item.y = this.originalPositions[index].y;
|
||||
});
|
||||
this.dragIndex = -1;
|
||||
this.$emit('drag-end', [...this.list]);
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
initialList: {
|
||||
handler() {
|
||||
this.$nextTick(() => {
|
||||
this.initList();
|
||||
});
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
direction: {
|
||||
handler() {
|
||||
this.$nextTick(() => {
|
||||
this.initList();
|
||||
this.calculateItemSize();
|
||||
this.calculateAreaSize();
|
||||
});
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
handler() {
|
||||
if (this.direction === 'all') {
|
||||
this.$nextTick(() => {
|
||||
this.initList();
|
||||
this.updatePositions();
|
||||
this.saveOriginalPositions();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.u-dragsort {
|
||||
width: 100%;
|
||||
|
||||
.u-dragsort-area {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.u-dragsort-item {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
&.dragging {
|
||||
z-index: 1000;
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.u-dragsort-item-content {
|
||||
padding: 0px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 6px;
|
||||
border-radius: 8rpx;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
&.u-dragsort--horizontal {
|
||||
.u-dragsort-area {
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.u-dragsort-item {
|
||||
display: flex;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.u-dragsort--all {
|
||||
.u-dragsort-area {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.u-dragsort-item {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -114,7 +114,6 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../libs/css/components.scss";
|
||||
.u-dropdown-item__scroll {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
@@ -8,24 +8,24 @@
|
||||
<view class="u-dropdown__menu__item" v-for="(item, index) in menuList" :key="index" @tap.stop="menuClick(index)">
|
||||
<view class="u-flex u-flex-row">
|
||||
<text class="u-dropdown__menu__item__text" :style="{
|
||||
color: item.disabled ? '#c0c4cc' : (index === current || highlightIndex == index) ? activeColor : inactiveColor,
|
||||
color: item.disabled ? '#c0c4cc' : (index === current || highlightIndexList.includes(index)) ? activeColor : inactiveColor,
|
||||
fontSize: addUnit(titleSize)
|
||||
}">{{item.title}}</text>
|
||||
<view class="u-dropdown__menu__item__arrow" :class="{
|
||||
'u-dropdown__menu__item__arrow--rotate': index === current
|
||||
}">
|
||||
<u-icon :custom-style="{display: 'flex'}" :name="menuIcon" :size="addUnit(menuIconSize)" :color="index === current || highlightIndex == index ? activeColor : '#c0c4cc'"></u-icon>
|
||||
<up-icon :custom-style="{display: 'flex'}" :name="menuIcon" :size="addUnit(menuIconSize)" :color="index === current || highlightIndexList.includes(index) ? activeColor : '#c0c4cc'"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-dropdown__content" :style="[contentStyle, {
|
||||
transition: `opacity ${duration / 1000}s linear`,
|
||||
top: addUnit(height),
|
||||
height: contentHeight + 'px'
|
||||
transition: `opacity ${duration / 1000}s, z-index ${duration / 1000}s linear`,
|
||||
top: addUnit(height)
|
||||
}]"
|
||||
@tap="maskClick" @touchmove.stop.prevent>
|
||||
<view @tap.stop.prevent class="u-dropdown__content__popup" :style="[popupStyle]">
|
||||
<view @tap.stop.prevent class="u-dropdown__content__popup" :style="[popupStyle, {
|
||||
}]">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view class="u-dropdown__content__mask"></view>
|
||||
@@ -71,8 +71,8 @@
|
||||
zIndex: -1,
|
||||
opacity: 0
|
||||
},
|
||||
// 让某个菜单保持高亮的状态
|
||||
highlightIndex: 99999,
|
||||
// 让某些菜单保持高亮的状态
|
||||
highlightIndexList: [],
|
||||
contentHeight: 0
|
||||
}
|
||||
},
|
||||
@@ -129,6 +129,7 @@
|
||||
// 展开时,设置下拉内容的样式
|
||||
this.contentStyle = {
|
||||
zIndex: 11,
|
||||
height: this.contentHeight + 'px'
|
||||
}
|
||||
// 标记展开状态以及当前展开项的索引
|
||||
this.active = true;
|
||||
@@ -147,10 +148,11 @@
|
||||
this.active = false;
|
||||
this.current = 99999;
|
||||
// 下拉内容的样式进行调整,不透明度设置为0
|
||||
this.contentStyle = {
|
||||
zIndex: -1,
|
||||
opacity: 0
|
||||
}
|
||||
this.contentStyle.zIndex = -1;
|
||||
this.contentStyle.opacity = 0;
|
||||
setTimeout(() => {
|
||||
this.contentStyle.height = 0;
|
||||
}, this.duration)
|
||||
},
|
||||
// 点击遮罩
|
||||
maskClick() {
|
||||
@@ -158,9 +160,13 @@
|
||||
if (!this.closeOnClickMask) return;
|
||||
this.close();
|
||||
},
|
||||
// 外部手动设置某个菜单高亮
|
||||
highlight(index = undefined) {
|
||||
this.highlightIndex = index !== undefined ? index : 99999;
|
||||
// 外部手动设置某些菜单高亮
|
||||
highlight(indexParams = undefined) {
|
||||
if (Array.isArray(indexParams)) {
|
||||
this.highlightIndexList = [...indexParams];
|
||||
return;
|
||||
}
|
||||
this.highlightIndexList = indexParams !== undefined ? [indexParams] : [];
|
||||
},
|
||||
// 获取下拉菜单内容的高度
|
||||
getContentHeight() {
|
||||
@@ -181,7 +187,6 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-dropdown {
|
||||
flex: 1;
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
:style="[emptyStyle]"
|
||||
v-if="show"
|
||||
>
|
||||
<u-icon
|
||||
<up-icon
|
||||
v-if="!isSrc"
|
||||
:name="mode === 'message' ? 'chat' : `empty-${mode}`"
|
||||
:size="iconSize"
|
||||
:color="iconColor"
|
||||
margin-top="14"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
<image
|
||||
v-else
|
||||
:style="{
|
||||
@@ -35,6 +35,7 @@
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin';
|
||||
import { mixin } from '../../libs/mixin/mixin';
|
||||
import { addUnit, addStyle, deepMerge } from '../../libs/function/index';
|
||||
import { t } from '../../libs/i18n'
|
||||
/**
|
||||
* empty 内容为空
|
||||
* @description 该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个"没有内容"的场景, 我们精心挑选了十几个场景的图标,方便您使用。
|
||||
@@ -62,21 +63,21 @@
|
||||
data() {
|
||||
return {
|
||||
icons: {
|
||||
car: '购物车为空',
|
||||
page: '页面不存在',
|
||||
search: '没有搜索结果',
|
||||
address: '没有收货地址',
|
||||
wifi: '没有WiFi',
|
||||
order: '订单为空',
|
||||
coupon: '没有优惠券',
|
||||
favor: '暂无收藏',
|
||||
permission: '无权限',
|
||||
history: '无历史记录',
|
||||
news: '无新闻列表',
|
||||
message: '消息列表为空',
|
||||
list: '列表为空',
|
||||
data: '数据为空',
|
||||
comment: '暂无评论',
|
||||
car: t("up.empty.car"),
|
||||
page: t("up.empty.page"),
|
||||
search: t("up.empty.search"),
|
||||
address: t("up.empty.address"),
|
||||
wifi: t("up.empty.wifi"),
|
||||
order: t("up.empty.order"),
|
||||
coupon: t("up.empty.coupon"),
|
||||
favor: t("up.empty.favor"),
|
||||
permission: t("up.empty.permission"),
|
||||
history: t("up.empty.history"),
|
||||
news: t("up.empty.news"),
|
||||
message: t("up.empty.message"),
|
||||
list: t("up.empty.list"),
|
||||
data: t("up.empty.data"),
|
||||
comment: t("up.empty.comment"),
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -107,7 +108,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
$u-empty-text-margin-top:20rpx !default;
|
||||
$u-empty-slot-margin-top:20rpx !default;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<view class="u-float-button__main" @click="clickHandler" :style="{
|
||||
backgroundColor: backgroundColor,
|
||||
color: color,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
@@ -24,10 +25,11 @@
|
||||
bottom: height
|
||||
}">
|
||||
<slot name="list">
|
||||
<template v-for="item in list">
|
||||
<template :key="index" v-for="(item, index) in list">
|
||||
<view class="u-float-button__item" :style="{
|
||||
backgroundColor: item?.backgroundColor ? item?.backgroundColor : backgroundColor,
|
||||
color: item?.color ? item?.color : color,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
@@ -147,8 +149,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
|
||||
.u-float-button {
|
||||
z-index: 999;
|
||||
.show-list {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
v-if="required || leftIcon || label"
|
||||
:style="{
|
||||
width: addUnit(labelWidth || parentData.labelWidth),
|
||||
marginBottom: parentData.labelPosition === 'left' ? 0 : '5px',
|
||||
marginBottom: (labelPosition || parentData.labelPosition) === 'left' ? 0 : '5px',
|
||||
}"
|
||||
>
|
||||
<!-- 为了块对齐 -->
|
||||
@@ -29,10 +29,10 @@
|
||||
class="u-form-item__body__left__content__icon"
|
||||
v-if="leftIcon"
|
||||
>
|
||||
<u-icon
|
||||
<up-icon
|
||||
:name="leftIcon"
|
||||
:custom-style="leftIconStyle"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</view>
|
||||
<text
|
||||
class="u-form-item__body__left__content__label"
|
||||
@@ -62,7 +62,7 @@
|
||||
v-if="!!message && parentData.errorType === 'message'"
|
||||
class="u-form-item__body__right__message"
|
||||
:style="{
|
||||
marginLeft: addUnit(parentData.labelPosition === 'top' ? 0 : (labelWidth || parentData.labelWidth))
|
||||
marginLeft: addUnit((labelPosition || parentData.labelPosition) === 'top' ? 0 : (labelWidth || parentData.labelWidth))
|
||||
}"
|
||||
>{{ message }}</text>
|
||||
</slot>
|
||||
@@ -185,7 +185,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-form-item {
|
||||
@include flex(column);
|
||||
@@ -240,10 +239,8 @@
|
||||
|
||||
&__slot {
|
||||
flex: 1;
|
||||
/* #ifndef MP */
|
||||
@include flex;
|
||||
align-items: center;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
&__icon {
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
* @property {String | Number} labelWidth 提示文字的宽度,单位px ( 默认 45 )
|
||||
* @property {String} labelAlign lable字体的对齐方式 ( 默认 ‘left' )
|
||||
* @property {Object} labelStyle lable的样式,对象形式
|
||||
* @example <up-formlabelPosition="left" :model="model1" :rules="rules" ref="form1"></up-form>
|
||||
* @example <up-form labelPosition="left" :model="model1" :rules="rules" ref="form1"></up-form>
|
||||
*/
|
||||
export default {
|
||||
name: "u-form",
|
||||
@@ -126,7 +126,7 @@
|
||||
});
|
||||
},
|
||||
// 对部分表单字段进行校验
|
||||
async validateField(value, callback, event = null) {
|
||||
async validateField(value, callback, event = null,options) {
|
||||
// $nextTick是必须的,否则model的变更,可能会延后于此方法的执行
|
||||
this.$nextTick(() => {
|
||||
// 校验错误信息,返回给回调方法,用于存放所有form-item的错误信息
|
||||
@@ -191,9 +191,11 @@
|
||||
errorsRes.push(...errors);
|
||||
childErrors.push(...errors);
|
||||
}
|
||||
child.message =
|
||||
childErrors[0]?.message ? childErrors[0].message : null;
|
||||
|
||||
//没有配置,或者配置了showErrorMsg为true时候,才修改子组件message,默认没有配置
|
||||
if(!options||options?.showErrorMsg==true){
|
||||
child.message =
|
||||
childErrors[0]?.message ? childErrors[0].message : null;
|
||||
}
|
||||
if (i == (rules.length - 1)) {
|
||||
resolve(errorsRes)
|
||||
}
|
||||
@@ -217,8 +219,12 @@
|
||||
});
|
||||
});
|
||||
},
|
||||
// 校验全部数据
|
||||
validate(callback) {
|
||||
/**
|
||||
* 校验全部数据
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.showErrorMsg -是否显示校验信息,
|
||||
*/
|
||||
validate(options) {
|
||||
// 开发环境才提示,生产环境不会提示
|
||||
if (process.env.NODE_ENV === 'development' && Object.keys(this.formRules).length === 0) {
|
||||
error('未设置rules,请看文档说明!如果已经设置,请刷新页面。');
|
||||
@@ -240,7 +246,7 @@
|
||||
} else {
|
||||
resolve(true)
|
||||
}
|
||||
});
|
||||
},null,options);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -37,5 +37,4 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
</style>
|
||||
|
||||
434
uni_modules/uview-plus/components/u-goods-sku/u-goods-sku.vue
Normal file
434
uni_modules/uview-plus/components/u-goods-sku/u-goods-sku.vue
Normal file
@@ -0,0 +1,434 @@
|
||||
<template>
|
||||
<view class="up-goods-sku">
|
||||
<view @click="open">
|
||||
<slot name="trigger"></slot>
|
||||
</view>
|
||||
<up-popup
|
||||
v-model:show="show"
|
||||
mode="bottom"
|
||||
:closeable="pageInline ? false : closeable"
|
||||
:pageInline="pageInline"
|
||||
:border-radius="20"
|
||||
@close="close"
|
||||
>
|
||||
<view class="up-goods-sku-container" :style="{padding: pageInline ? '0px' : ''}">
|
||||
<view class="up-goods-sku__header">
|
||||
<slot name="header">
|
||||
<view class="up-goods-sku__header__image">
|
||||
<image :src="goodsInfo.image || goodsInfo.picture" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="up-goods-sku__header__info">
|
||||
<view class="up-goods-sku__header__info__price">
|
||||
<text class="up-goods-sku__header__info__price__symbol">¥</text>
|
||||
<text class="up-goods-sku__header__info__price__value">{{ price }}</text>
|
||||
</view>
|
||||
<view class="up-goods-sku__header__info__stock">{{ t('up.goodsSku.stock') }} {{ stock }} {{ t('up.goodsSku.amount') }}</view>
|
||||
<view class="up-goods-sku__header__info__selected">{{ t('up.goodsSku.choosed') }}: {{ selectedSkuText }}</view>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
|
||||
<scroll-view class="up-goods-sku__content" scroll-y>
|
||||
<view v-for="(treeItem, index) in skuTree" :key="index" class="up-goods-sku__content__item">
|
||||
<view class="up-goods-sku__content__item__title">{{ treeItem.label }}</view>
|
||||
<view class="up-goods-sku__content__item__list">
|
||||
<view
|
||||
v-for="(leafItem, leafIndex) in treeItem.children"
|
||||
:key="leafIndex"
|
||||
class="up-goods-sku__content__item__list__item"
|
||||
:class="{
|
||||
'up-goods-sku__content__item__list__item--active': isSelected(treeItem.name, leafItem.id),
|
||||
'up-goods-sku__content__item__list__item--disabled': isDisabled(treeItem.name, leafItem.id)
|
||||
}"
|
||||
@click="onSkuClick(treeItem.name, leafItem)"
|
||||
>
|
||||
<text>{{ leafItem.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="up-goods-sku__content__count">
|
||||
<view class="up-goods-sku__content__count__title">{{ t('"up.goodsSku.buyAmount"') }}</view>
|
||||
<view class="up-goods-sku__content__count__control">
|
||||
<up-number-box
|
||||
v-model="buyNum"
|
||||
:min="1"
|
||||
:max="maxBuyNum"
|
||||
:disabled="!canBuy"
|
||||
@change="onNumChange"
|
||||
></up-number-box>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="up-goods-sku__footer">
|
||||
<up-button
|
||||
type="primary"
|
||||
:disabled="!canBuy"
|
||||
@click="onConfirm"
|
||||
>
|
||||
{{ confirmText }}
|
||||
</up-button>
|
||||
</view>
|
||||
</view>
|
||||
</up-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
name: 'up-goods-sku',
|
||||
props: {
|
||||
// 商品信息
|
||||
goodsInfo: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
// SKU树形结构
|
||||
skuTree: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// SKU列表
|
||||
skuList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 最大购买数量
|
||||
maxBuy: {
|
||||
type: Number,
|
||||
default: 999
|
||||
},
|
||||
// 确认按钮文字
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: '确定'
|
||||
},
|
||||
// 是否显示关闭弹窗按钮
|
||||
closeable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否页面内联模式
|
||||
pageInline: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
// 已选择的SKU
|
||||
selectedSku: {},
|
||||
// 购买数量
|
||||
buyNum: 1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 当前价格
|
||||
price() {
|
||||
const selectedSkuComb = this.getSelectedSkuComb()
|
||||
if (selectedSkuComb) {
|
||||
return selectedSkuComb.price || selectedSkuComb.price_fee
|
||||
}
|
||||
return this.goodsInfo.price || this.goodsInfo.price_fee || 0
|
||||
},
|
||||
// 当前库存
|
||||
stock() {
|
||||
const selectedSkuComb = this.getSelectedSkuComb()
|
||||
if (selectedSkuComb) {
|
||||
return selectedSkuComb.stock || selectedSkuComb.quantity
|
||||
}
|
||||
return this.goodsInfo.stock || this.goodsInfo.quantity || 0
|
||||
},
|
||||
// 最大购买数量
|
||||
maxBuyNum() {
|
||||
const stock = this.stock
|
||||
return stock > this.maxBuy ? this.maxBuy : stock
|
||||
},
|
||||
// 是否可以购买
|
||||
canBuy() {
|
||||
const selectedSkuCount = Object.keys(this.selectedSku).length
|
||||
const skuTreeCount = this.skuTree.length
|
||||
return selectedSkuCount === skuTreeCount && this.buyNum > 0 && this.stock > 0
|
||||
},
|
||||
// 已选SKU文字描述
|
||||
selectedSkuText() {
|
||||
const selected = []
|
||||
Object.keys(this.selectedSku).forEach(key => {
|
||||
const value = this.selectedSku[key]
|
||||
if (value) {
|
||||
this.skuTree.forEach(treeItem => {
|
||||
if (treeItem.name === key) {
|
||||
treeItem.children.forEach(leafItem => {
|
||||
if (leafItem.id === value) {
|
||||
selected.push(leafItem.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return selected.join(', ')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
emits: ['open', 'confirm', 'close'],
|
||||
created() {
|
||||
if (this.pageInline) {
|
||||
this.show = true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
t,
|
||||
// 判断SKU是否被选中
|
||||
isSelected(skuKey, skuValueId) {
|
||||
return this.selectedSku[skuKey] === skuValueId
|
||||
},
|
||||
// 判断SKU是否禁用
|
||||
isDisabled(skuKey, skuValueId) {
|
||||
// 构造一个临时的已选中SKU对象
|
||||
const tempSelected = { ...this.selectedSku, [skuKey]: skuValueId }
|
||||
|
||||
// 检查是否还有未选择的SKU维度
|
||||
const selectedCount = Object.keys(tempSelected).filter(key => tempSelected[key]).length
|
||||
const totalSkuCount = this.skuTree.length
|
||||
|
||||
// 如果所有SKU都已选择,则检查组合是否存在
|
||||
if (selectedCount === totalSkuCount) {
|
||||
return !this.getSkuComb(tempSelected)
|
||||
}
|
||||
|
||||
// 检查当前选择的SKU是否会导致无法组成有效组合
|
||||
for (let i = 0; i < this.skuList.length; i++) {
|
||||
const sku = this.skuList[i]
|
||||
let match = true
|
||||
|
||||
// 检查已选中的SKU是否匹配
|
||||
for (const key in tempSelected) {
|
||||
if (tempSelected[key] && sku[key] !== tempSelected[key]) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
// SKU点击事件
|
||||
onSkuClick(skuKey, skuValue) {
|
||||
// 如果是禁用状态,直接返回
|
||||
if (this.isDisabled(skuKey, skuValue.id)) {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果已选中,则取消选中
|
||||
if (this.selectedSku[skuKey] === skuValue.id) {
|
||||
this.$set(this.selectedSku, skuKey, '')
|
||||
} else {
|
||||
this.$set(this.selectedSku, skuKey, skuValue.id)
|
||||
}
|
||||
},
|
||||
// 数量改变事件
|
||||
onNumChange(e) {
|
||||
this.buyNum = e.value
|
||||
},
|
||||
// 获取选中的SKU组合
|
||||
getSelectedSkuComb() {
|
||||
return this.getSkuComb(this.selectedSku)
|
||||
},
|
||||
// 根据已选SKU获取组合信息
|
||||
getSkuComb(selectedSku) {
|
||||
const selected = { ...selectedSku }
|
||||
|
||||
// 过滤掉空值
|
||||
Object.keys(selected).forEach(key => {
|
||||
if (!selected[key]) {
|
||||
delete selected[key]
|
||||
}
|
||||
})
|
||||
|
||||
// 检查是否所有SKU都已选择
|
||||
if (Object.keys(selected).length !== this.skuTree.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
// 查找匹配的SKU组合
|
||||
for (let i = 0; i < this.skuList.length; i++) {
|
||||
const sku = this.skuList[i]
|
||||
let match = true
|
||||
|
||||
for (const key in selected) {
|
||||
if (sku[key] !== selected[key]) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
return sku
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
// 重置选择
|
||||
reset() {
|
||||
this.selectedSku = {}
|
||||
this.buyNum = 1
|
||||
},
|
||||
open() {
|
||||
this.show = true;
|
||||
this.$emit('open')
|
||||
},
|
||||
// 关闭弹窗
|
||||
close() {
|
||||
this.false = true;
|
||||
this.$emit('close')
|
||||
},
|
||||
// 确认选择
|
||||
onConfirm() {
|
||||
if (!this.canBuy) {
|
||||
return
|
||||
}
|
||||
|
||||
const selectedSkuComb = this.getSelectedSkuComb()
|
||||
this.$emit('confirm', {
|
||||
sku: selectedSkuComb,
|
||||
goodsInfo: this.goodsInfo,
|
||||
num: this.buyNum,
|
||||
selectedText: this.selectedSkuText
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.up-goods-sku {
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
|
||||
.up-goods-sku-container {
|
||||
padding: 4rpx 30rpx;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 30rpx 0;
|
||||
position: relative;
|
||||
|
||||
&__image {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
margin-right: 20rpx;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
flex: 1;
|
||||
|
||||
&__price {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&__symbol {
|
||||
font-size: 24rpx;
|
||||
color: #fa3534;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-size: 36rpx;
|
||||
color: #fa3534;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
&__stock {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
&__selected {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
max-height: 600rpx;
|
||||
padding: 0 30rpx 30rpx 0;
|
||||
|
||||
&__item {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
&__title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
&__list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&__item {
|
||||
padding: 10rpx 20rpx;
|
||||
border: 2rpx solid #eee;
|
||||
border-radius: 10rpx;
|
||||
margin-right: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
|
||||
&--active {
|
||||
border-color: #fa3534;
|
||||
color: #fa3534;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
color: #ccc;
|
||||
border-color: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__count {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 20rpx;
|
||||
|
||||
&__title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
padding: 20rpx 0rpx 40rpx 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -172,7 +172,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
$u-grid-item-hover-class-opcatiy:.5 !default;
|
||||
$u-grid-item-margin-top:1rpx !default;
|
||||
$u-grid-item-border-right-width:0.5px !default;
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
$u-grid-width:100% !default;
|
||||
.u-grid {
|
||||
/* #ifdef APP-NVUE */
|
||||
@@ -106,7 +105,7 @@
|
||||
// 在uni-app中应尽量避免使用flex布局以外的方式,因为nvue/uvue等方案都支持flex布局
|
||||
// 这里使用grid布局使用为目前20240409uni-app在抖音小程序开启virtualHost时有bug,存在事件失效问题。
|
||||
/* #ifndef APP-NVUE */
|
||||
display: grid;
|
||||
display: grid !important;
|
||||
grid-gap: v-bind(gap);
|
||||
grid-template-columns: repeat(v-bind(col), 1fr);
|
||||
/* #endif */
|
||||
|
||||
@@ -35,24 +35,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef APP-NVUE
|
||||
// nvue通过weex的dom模块引入字体,相关文档地址如下:
|
||||
// https://weex.apache.org/zh/docs/modules/dom.html#addrule
|
||||
const fontUrl = 'https://at.alicdn.com/t/font_2225171_8kdcwk4po24.ttf'
|
||||
const domModule = weex.requireModule('dom')
|
||||
domModule.addRule('fontFace', {
|
||||
'fontFamily': "uicon-iconfont",
|
||||
'src': `url('${fontUrl}')`
|
||||
})
|
||||
// #endif
|
||||
|
||||
// 引入图标名称,已经对应的unicode
|
||||
import icons from './icons'
|
||||
import icons from './icons';
|
||||
import { props } from './props';
|
||||
import config from '../../libs/config/config';
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin';
|
||||
import { mixin } from '../../libs/mixin/mixin';
|
||||
import { addUnit, addStyle } from '../../libs/function/index';
|
||||
import config from '../../libs/config/config';
|
||||
import fontUtil from './util';
|
||||
/**
|
||||
* icon 图标
|
||||
* @description 基于字体的图标集,包含了大多数常见场景的图标。
|
||||
@@ -82,18 +72,12 @@
|
||||
export default {
|
||||
name: 'u-icon',
|
||||
beforeCreate() {
|
||||
// #ifdef APP-NVUE
|
||||
if (this.customFontFamily) {
|
||||
domModule.addRule('fontFace', {
|
||||
'fontFamily': `${this.customPrefix}-${this.customFontFamily}`,
|
||||
'src': `url('${this.customFontUrl}')`
|
||||
})
|
||||
if (!fontUtil.params.loaded) {
|
||||
fontUtil.loadFont();
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
emits: ['click'],
|
||||
@@ -102,7 +86,7 @@
|
||||
uClasses() {
|
||||
let classes = []
|
||||
classes.push(this.customPrefix + '-' + this.name)
|
||||
// uView的自定义图标类名为u-iconfont
|
||||
// uview-plus内置图标类名为u-iconfont
|
||||
if (this.customPrefix == 'uicon') {
|
||||
classes.push('u-iconfont')
|
||||
} else {
|
||||
@@ -127,6 +111,9 @@
|
||||
// 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中
|
||||
top: addUnit(this.top)
|
||||
}
|
||||
if (this.customPrefix !== 'uicon') {
|
||||
style.fontFamily = this.customPrefix
|
||||
}
|
||||
// 非主题色值时,才当作颜色值
|
||||
if (this.color && !config.type.includes(this.color)) style.color = this.color
|
||||
|
||||
@@ -147,7 +134,7 @@
|
||||
icon() {
|
||||
// 使用自定义图标的时候页面上会把name属性也展示出来,所以在这里处理一下
|
||||
if (this.customPrefix !== "uicon") {
|
||||
return this.customIcons[this.customPrefix + '-' + this.name] || this.name;
|
||||
return config.customIcons[this.name] || this.name;
|
||||
}
|
||||
// 如果内置的图标中找不到对应的图标,就直接返回name值,因为用户可能传入的是unicode代码
|
||||
return icons['uicon-' + this.name] || this.name
|
||||
@@ -166,8 +153,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
// 变量定义
|
||||
$u-icon-primary: $u-primary !default;
|
||||
$u-icon-success: $u-success !default;
|
||||
@@ -176,13 +161,12 @@
|
||||
$u-icon-error: $u-error !default;
|
||||
$u-icon-label-line-height:1 !default;
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
// 非nvue下加载字体
|
||||
/* #ifdef APP || MP-QQ || MP-TOUTIAO || MP-BAIDU || MP-KUAISHOU || MP-XHS */
|
||||
// 2025/04/09在App/微信/支付宝/鸿蒙元服务已改用uni.loadFontFace加载字体
|
||||
@font-face {
|
||||
font-family: 'uicon-iconfont';
|
||||
src: url('https://at.alicdn.com/t/font_2225171_8kdcwk4po24.ttf') format('truetype');
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.u-icon {
|
||||
|
||||
68
uni_modules/uview-plus/components/u-icon/util.js
Normal file
68
uni_modules/uview-plus/components/u-icon/util.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import config from '../../libs/config/config';
|
||||
|
||||
let params = {
|
||||
loaded: false
|
||||
};
|
||||
// 加载字体方法
|
||||
const loadFont = () => {
|
||||
// console.log('加载字体图标');
|
||||
// 全局加载不稳定,默认关闭,需要开启可以配置loadFontOnce。
|
||||
if (config.loadFontOnce) {
|
||||
params.loaded = true;
|
||||
}
|
||||
// #ifdef APP-NVUE
|
||||
// nvue通过weex的dom模块引入字体,相关文档地址如下:
|
||||
// https://weex.apache.org/zh/docs/modules/dom.html#addrule
|
||||
const domModule = weex.requireModule('dom');
|
||||
domModule.addRule('fontFace', {
|
||||
'fontFamily': "uicon-iconfont",
|
||||
'src': `url('${config.iconUrl}')`
|
||||
});
|
||||
if (config.customIcon.family) {
|
||||
domModule.addRule('fontFace', {
|
||||
'fontFamily': config.customIcon.family,
|
||||
'src': `url('${config.customIcon.url}')`
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP || H5 || MP-WEIXIN || MP-ALIPAY
|
||||
uni.loadFontFace({
|
||||
global: true, // 是否全局生效。微信小程序 '2.10.0'起支持全局生效,需在 app.vue 中调用。
|
||||
family: 'uicon-iconfont',
|
||||
source: 'url("' + config.iconUrl + '")',
|
||||
success() {
|
||||
// console.log('内置字体图标加载成功');
|
||||
},
|
||||
fail() {
|
||||
// console.error('内置字体图标加载出错');
|
||||
}
|
||||
});
|
||||
if (config.customIcon.family) {
|
||||
uni.loadFontFace({
|
||||
global: true, // 是否全局生效。微信小程序 '2.10.0'起支持全局生效,需在 app.vue 中调用。
|
||||
family: config.customIcon.family,
|
||||
source: 'url("' + config.customIcon.url + '")',
|
||||
success() {
|
||||
// console.log('扩展字体图标加载成功');
|
||||
},
|
||||
fail() {
|
||||
// console.error('扩展字体图标加载出错');
|
||||
}
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
// if (this.customFontFamily) {
|
||||
// domModule.addRule('fontFace', {
|
||||
// 'fontFamily': `${this.customPrefix}-${this.customFontFamily}`,
|
||||
// 'src': `url('${this.customFontUrl}')`
|
||||
// })
|
||||
// }
|
||||
// #endif
|
||||
return true;
|
||||
};
|
||||
|
||||
export default {
|
||||
params: params,
|
||||
loadFont
|
||||
}
|
||||
@@ -36,22 +36,25 @@
|
||||
}"
|
||||
>
|
||||
<slot name="loading">
|
||||
<u-icon
|
||||
<up-icon
|
||||
:name="loadingIcon"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</slot>
|
||||
</view>
|
||||
<view
|
||||
v-if="showError && isError && !loading"
|
||||
class="u-image__error"
|
||||
:style="{
|
||||
borderRadius: shape == 'circle' ? '50%' : addUnit(radius)
|
||||
borderRadius: shape == 'circle' ? '50%' : addUnit(radius),
|
||||
backgroundColor: this.bgColor,
|
||||
width: addUnit(width),
|
||||
height: addUnit(height)
|
||||
}"
|
||||
>
|
||||
<slot name="error">
|
||||
<u-icon
|
||||
<up-icon
|
||||
:name="errorIcon"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
@@ -132,12 +135,12 @@
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
if (this.loading || this.isError || this.width == '100%' || this.mode != 'heightFix') {
|
||||
style.width = this.width;
|
||||
style.width = addUnit(this.width);
|
||||
} else {
|
||||
style.width = 'fit-content';
|
||||
}
|
||||
if (this.loading || this.isError || this.height == '100%' || this.mode != 'widthFix') {
|
||||
style.height = this.height;
|
||||
style.height = addUnit(this.height);
|
||||
} else {
|
||||
style.height = 'fit-content';
|
||||
}
|
||||
@@ -153,12 +156,12 @@
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
if (this.loading || this.isError || this.width == '100%' || this.mode != 'heightFix') {
|
||||
style.width = this.width;
|
||||
style.width = addUnit(this.width);
|
||||
} else {
|
||||
style.width = 'fit-content';
|
||||
}
|
||||
if (this.loading || this.isError || this.height == '100%' || this.mode != 'widthFix') {
|
||||
style.height = this.height;
|
||||
style.height = addUnit(this.height);
|
||||
} else {
|
||||
style.height = 'fit-content';
|
||||
}
|
||||
@@ -220,17 +223,15 @@
|
||||
// 移除图片的背景色
|
||||
removeBgColor() {
|
||||
// 淡入动画过渡完成后,将背景设置为透明色,否则png图片会看到灰色的背景
|
||||
this.backgroundStyle = {
|
||||
backgroundColor: this.bgColor || '#ffffff'
|
||||
};
|
||||
// this.backgroundStyle = {
|
||||
// backgroundColor: this.bgColor || '#ffffff'
|
||||
// };
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
|
||||
$u-image-error-top:0px !default;
|
||||
$u-image-error-left:0px !default;
|
||||
$u-image-error-width:100% !default;
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
text: '',
|
||||
color: '#606266',
|
||||
size: 14,
|
||||
bgColor: '#dedede',
|
||||
bgColor: '#f1f1f1',
|
||||
height: 32
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<!-- #endif -->
|
||||
<view
|
||||
class="u-index-anchor u-border-bottom"
|
||||
:class="{ 'u-index-anchor--sticky': parentSticky }"
|
||||
:ref="`u-index-anchor-${text}`"
|
||||
:style="{
|
||||
height: addUnit(height),
|
||||
@@ -16,7 +17,7 @@
|
||||
fontSize: addUnit(size),
|
||||
color: color
|
||||
}"
|
||||
>{{ text }}</text>
|
||||
>{{ text.name || text }}</text>
|
||||
</view>
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
</header>
|
||||
@@ -69,15 +70,24 @@
|
||||
return error('u-index-anchor必须要搭配u-index-item组件使用')
|
||||
}
|
||||
// 设置u-index-item的id为anchor的text标识符,因为非nvue下滚动列表需要依赖scroll-view滚动到元素的特性
|
||||
indexListItem.id = this.text.charCodeAt(0)
|
||||
if (typeof this.text == 'string') {
|
||||
indexListItem.id = this.text.charCodeAt(0)
|
||||
} else {
|
||||
indexListItem.id = this.text.name.charCodeAt(0)
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
parentSticky() {
|
||||
const indexList = $parent.call(this, "u-index-list");
|
||||
return indexList ? indexList.sticky : true;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-index-anchor {
|
||||
position: sticky;
|
||||
@@ -87,6 +97,11 @@
|
||||
padding-left: 15px;
|
||||
z-index: 1;
|
||||
|
||||
&--sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&__text {
|
||||
@include flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -85,6 +85,5 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
</style>
|
||||
|
||||
@@ -15,6 +15,7 @@ export default {
|
||||
indexList: [],
|
||||
sticky: true,
|
||||
customNavHeight: 0,
|
||||
safeBottomFix: false
|
||||
safeBottomFix: false,
|
||||
itemMargin: '0rpx'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,5 +32,10 @@ export const props = defineMixin({
|
||||
type: Boolean,
|
||||
default: () => defProps.indexList.safeBottomFix
|
||||
},
|
||||
//自定义下边距
|
||||
itemMargin: {
|
||||
type: String,
|
||||
default: () => defProps.indexList.itemMargin
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<text
|
||||
class="u-index-list__letter__item__index"
|
||||
:style="{color: activeIndex === index ? '#fff' : inactiveColor}"
|
||||
>{{ item }}</text>
|
||||
>{{ item.key || item }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<u-transition
|
||||
@@ -87,7 +87,7 @@
|
||||
width: addUnit(indicatorHeight)
|
||||
}"
|
||||
>
|
||||
<text class="u-index-list__indicator__text">{{ uIndexList[activeIndex] }}</text>
|
||||
<text class="u-index-list__indicator__text">{{ uIndexList[activeIndex]?.key || uIndexList[activeIndex] }}</text>
|
||||
</view>
|
||||
</u-transition>
|
||||
</view>
|
||||
@@ -284,8 +284,10 @@
|
||||
return new Promise(resolve => {
|
||||
// 延时一定时间,以获取dom尺寸
|
||||
// #ifndef APP-NVUE
|
||||
this.$uGetRect('.u-index-list__scroll-view').then(size => {
|
||||
resolve(size)
|
||||
this.$nextTick(() => {
|
||||
this.$uGetRect('.u-index-list__scroll-view').then(size => {
|
||||
resolve(size)
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
|
||||
@@ -389,9 +391,14 @@
|
||||
// 如果偏移量太小,前后得出的会是同一个索引字母,为了防抖,进行返回
|
||||
if (currentIndex === this.activeIndex) return
|
||||
this.activeIndex = currentIndex
|
||||
this.$emit('select', this.uIndexList[currentIndex])
|
||||
// #ifndef APP-NVUE || MP-WEIXIN
|
||||
// 在非nvue中,由于anchor和item都在u-index-item中,所以需要对index-item进行偏移
|
||||
this.scrollIntoView = `u-index-item-${this.uIndexList[currentIndex].charCodeAt(0)}`
|
||||
if (typeof this.uIndexList[currentIndex] == 'string') {
|
||||
this.scrollIntoView = `u-index-item-${this.uIndexList[currentIndex].charCodeAt(0)}`
|
||||
} else {
|
||||
this.scrollIntoView = `u-index-item-${this.uIndexList[currentIndex].name.charCodeAt(0)}`
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
@@ -406,12 +413,13 @@
|
||||
const anchors = this.anchors
|
||||
// 由于list组件无法获取cell的top值,这里通过header slot和各个item之间的height,模拟出类似非nvue下的位置信息
|
||||
let children = this.children.map((item, index) => {
|
||||
const childHeight = item.height + getPx(this.itemMargin)
|
||||
const child = {
|
||||
height: item.height,
|
||||
height: childHeight,
|
||||
top: top
|
||||
}
|
||||
// 进行累加,给下一个item提供计算依据
|
||||
top = top + item.height
|
||||
top = top + childHeight
|
||||
// #ifdef APP-NVUE
|
||||
// 只有nvue下,需要将锚点的高度也累加,非nvue下锚点高度是包含在index-item中的。
|
||||
top = top + anchors[index].height
|
||||
@@ -419,7 +427,7 @@
|
||||
return child
|
||||
})
|
||||
// console.log('this.children[currentIndex].top', children[currentIndex].top)
|
||||
if (children[currentIndex]?.top) {
|
||||
if (children[currentIndex]?.top || children[currentIndex].top === 0) {
|
||||
this.scrollTop = children[currentIndex].top - getPx(customNavHeight)
|
||||
}
|
||||
// #endif
|
||||
@@ -489,12 +497,13 @@
|
||||
const anchors = this.anchors
|
||||
// 由于list组件无法获取cell的top值,这里通过header slot和各个item之间的height,模拟出类似非nvue下的位置信息
|
||||
children = this.children.map((item, index) => {
|
||||
const childHeight = item.height + getPx(this.itemMargin)
|
||||
const child = {
|
||||
height: item.height,
|
||||
height: childHeight,
|
||||
top: top
|
||||
}
|
||||
// 进行累加,给下一个item提供计算依据
|
||||
top = top + item.height
|
||||
top = top + childHeight
|
||||
// #ifdef APP-NVUE
|
||||
// 只有nvue下,需要将锚点的高度也累加,非nvue下锚点高度是包含在index-item中的。
|
||||
top = top + anchors[index].height
|
||||
@@ -531,7 +540,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-index-list {
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
* @Description :
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 17:13:55
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/input.js
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-08-20 10:21:55
|
||||
* @FilePath : /uview-plus/libs/config/props/input.js
|
||||
*/
|
||||
export default {
|
||||
// index 组件
|
||||
@@ -43,6 +43,8 @@ export default {
|
||||
border: 'surround',
|
||||
readonly: false,
|
||||
shape: 'square',
|
||||
formatter: null
|
||||
formatter: null,
|
||||
cursorColor: '',
|
||||
passwordVisibilityToggle: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,12 @@ export const props = defineMixin({
|
||||
// 是否显示清除控件
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: () => defProps.input.clearable
|
||||
default: false
|
||||
},
|
||||
// 是否仅在聚焦时显示清除控件
|
||||
onlyClearableOnFocused: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否密码类型
|
||||
password: {
|
||||
@@ -193,6 +198,16 @@ export const props = defineMixin({
|
||||
ignoreCompositionEvent: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 光标颜色
|
||||
cursorColor: {
|
||||
type: String,
|
||||
default: () => defProps.input.cursorColor
|
||||
},
|
||||
// 密码类型可见性切换
|
||||
passwordVisibilityToggle: {
|
||||
type: Boolean,
|
||||
default: () => defProps.input.passwordVisibilityToggle
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
v-if="prefixIcon || $slots.prefix"
|
||||
>
|
||||
<slot name="prefix">
|
||||
<u-icon
|
||||
<up-icon
|
||||
:name="prefixIcon"
|
||||
size="18"
|
||||
:customStyle="prefixIconStyle"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</slot>
|
||||
</view>
|
||||
<view class="u-input__content__field-wrapper" @tap="clickHandler">
|
||||
@@ -21,7 +21,7 @@
|
||||
ref="input-native"
|
||||
class="u-input__content__field-wrapper__field"
|
||||
:style="[inputStyle]"
|
||||
:type="type"
|
||||
:type="showPassword && 'password' == type ? 'text' : type"
|
||||
:focus="focus"
|
||||
:cursor="cursor"
|
||||
:value="innerValue"
|
||||
@@ -34,11 +34,12 @@
|
||||
:confirm-type="confirmType"
|
||||
:confirm-hold="confirmHold"
|
||||
:hold-keyboard="holdKeyboard"
|
||||
:cursor-color="cursorColor"
|
||||
:cursor-spacing="cursorSpacing"
|
||||
:adjust-position="adjustPosition"
|
||||
:selection-end="selectionEnd"
|
||||
:selection-start="selectionStart"
|
||||
:password="password || type === 'password' || false"
|
||||
:password="isPassword"
|
||||
:ignoreCompositionEvent="ignoreCompositionEvent"
|
||||
@input="onInput"
|
||||
@blur="onBlur"
|
||||
@@ -53,23 +54,32 @@
|
||||
v-if="isShowClear"
|
||||
@click="onClear"
|
||||
>
|
||||
<u-icon
|
||||
<up-icon
|
||||
name="close"
|
||||
size="11"
|
||||
color="#ffffff"
|
||||
customStyle="line-height: 12px"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</view>
|
||||
<view
|
||||
class="u-input__content__subfix-password-shower"
|
||||
v-if="(type == 'password' || password) && passwordVisibilityToggle"
|
||||
>
|
||||
<up-icon @click="showPassword = !showPassword"
|
||||
:name="showPassword ? 'eye-off' : 'eye-fill'"
|
||||
size="18"
|
||||
></up-icon>
|
||||
</view>
|
||||
<view
|
||||
class="u-input__content__subfix-icon"
|
||||
v-if="suffixIcon || $slots.suffix"
|
||||
>
|
||||
<slot name="suffix">
|
||||
<u-icon
|
||||
<up-icon
|
||||
:name="suffixIcon"
|
||||
size="18"
|
||||
:customStyle="suffixIconStyle"
|
||||
></u-icon>
|
||||
></up-icon>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
@@ -105,6 +115,7 @@ import { addStyle, addUnit, deepMerge, formValidate, $parent, sleep, os } from '
|
||||
* @property {Boolean} autoBlur 键盘收起时,是否自动失去焦点,目前仅App3.0.0+有效 ( 默认 false )
|
||||
* @property {Boolean} disableDefaultPadding 是否去掉 iOS 下的默认内边距,仅微信小程序,且type=textarea时有效 ( 默认 false )
|
||||
* @property {String | Number} cursor 指定focus时光标的位置( 默认 140 )
|
||||
* @property {String } cursorColor 光标颜色
|
||||
* @property {String | Number} cursorSpacing 输入框聚焦时底部与键盘的距离 ( 默认 30 )
|
||||
* @property {String | Number} selectionStart 光标起始位置,自动聚集时有效,需与selection-end搭配使用 ( 默认 -1 )
|
||||
* @property {String | Number} selectionEnd 光标结束位置,自动聚集时有效,需与selection-start搭配使用 ( 默认 -1 )
|
||||
@@ -140,7 +151,8 @@ export default {
|
||||
// value绑定值的变化是由内部还是外部引起的
|
||||
changeFromInner: false,
|
||||
// 过滤处理方法
|
||||
innerFormatter: value => value
|
||||
innerFormatter: value => value,
|
||||
showPassword: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@@ -176,10 +188,32 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 是否密码
|
||||
isPassword() {
|
||||
let ret = false;
|
||||
if(this.password) {
|
||||
ret = true;
|
||||
} else if (this.type == 'password') {
|
||||
ret = true;
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
if (this.showPassword) {
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
// 是否显示清除控件
|
||||
isShowClear() {
|
||||
const { clearable, readonly, focused, innerValue } = this;
|
||||
return !!clearable && !readonly && !!focused && innerValue !== "";
|
||||
const { clearable, readonly, focused, innerValue, onlyClearableOnFocused } = this;
|
||||
if (!clearable || readonly) {
|
||||
return false;
|
||||
}
|
||||
if (onlyClearableOnFocused) {
|
||||
return !!focused && innerValue !== "";
|
||||
} else {
|
||||
return innerValue !== "";
|
||||
}
|
||||
},
|
||||
// 组件的类名
|
||||
inputClass() {
|
||||
@@ -206,7 +240,7 @@ export default {
|
||||
if (this.border === "none") {
|
||||
style.padding = "0";
|
||||
} else {
|
||||
// 由于uni-app的iOS开发者能力有限,导致需要分开写才有效
|
||||
// 由于uni-app的iOS端限制,导致需要分开写才有效
|
||||
style.paddingTop = "6px";
|
||||
style.paddingBottom = "6px";
|
||||
style.paddingLeft = "9px";
|
||||
@@ -334,8 +368,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-input {
|
||||
@include flex(row);
|
||||
align-items: center;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* @lastTime : 2021-08-20 17:07:49
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/keyboard.js
|
||||
*/
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
// 键盘组件
|
||||
keyboard: {
|
||||
@@ -23,8 +24,8 @@ export default {
|
||||
show: false,
|
||||
overlay: true,
|
||||
zIndex: 10075,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
cancelText: t("up.common.cancel"),
|
||||
confirmText: t("up.common.confirm"),
|
||||
autoChange: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-keyboard {
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
<script>
|
||||
import {
|
||||
addUnit,
|
||||
guid
|
||||
guid,
|
||||
rpx2px
|
||||
} from '../../libs/function/index.js';
|
||||
/**
|
||||
* lazyLoad 懒加载
|
||||
@@ -114,7 +115,7 @@
|
||||
// 将threshold从rpx转为px
|
||||
getThreshold() {
|
||||
// 先取绝对值,因为threshold可能是负数,最后根据this.threshold是正数或者负数,重新还原
|
||||
let thresholdPx = uni.upx2px(Math.abs(this.threshold));
|
||||
let thresholdPx = rpx2px(Math.abs(this.threshold));
|
||||
return this.threshold < 0 ? -thresholdPx : thresholdPx;
|
||||
},
|
||||
// 计算图片的高度,可能为auto,带%,或者直接数值
|
||||
@@ -223,6 +224,10 @@
|
||||
if (res.intersectionRatio > 0) {
|
||||
// 懒加载状态改变
|
||||
this.isShow = true;
|
||||
// 图片为空时显示错误
|
||||
if (!this.image) {
|
||||
this.loadError();
|
||||
}
|
||||
// 如果图片已经加载,去掉监听,减少性能的消耗
|
||||
this.disconnectObserver('contentObserver');
|
||||
}
|
||||
@@ -238,8 +243,6 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-wrap {
|
||||
background-color: #eee;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -14,6 +14,7 @@ export default {
|
||||
inactiveColor: '#ececec',
|
||||
percentage: 0,
|
||||
showText: true,
|
||||
height: 12
|
||||
height: 12,
|
||||
fromRight: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,11 @@ export const props = defineMixin({
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.lineProgress.height
|
||||
},
|
||||
// 是否从右往左加载
|
||||
fromRight: {
|
||||
type: Boolean,
|
||||
default: () => defProps.lineProgress.fromRight
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -62,6 +62,11 @@
|
||||
style.width = this.lineWidth
|
||||
style.backgroundColor = this.activeColor
|
||||
style.height = addUnit(this.height)
|
||||
if (this.fromRight) {
|
||||
style.right = 0;
|
||||
} else {
|
||||
style.left = 0;
|
||||
}
|
||||
return style
|
||||
},
|
||||
innserPercentage() {
|
||||
@@ -108,7 +113,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-line-progress {
|
||||
align-items: stretch;
|
||||
@@ -127,7 +131,6 @@
|
||||
&__line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
align-items: center;
|
||||
@include flex(row);
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-line {
|
||||
/* #ifndef APP-NVUE */
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/link.js
|
||||
*/
|
||||
import config from '../../libs/config/config'
|
||||
import { t } from '../../libs/i18n'
|
||||
|
||||
const {
|
||||
color
|
||||
@@ -19,7 +20,7 @@ export default {
|
||||
fontSize: 15,
|
||||
underLine: false,
|
||||
href: '',
|
||||
mpTips: '链接已复制,请在浏览器打开',
|
||||
mpTips: t("up.link.copyed"),
|
||||
lineColor: '',
|
||||
text: ''
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
$u-link-line-height:1 !default;
|
||||
|
||||
.u-link {
|
||||
|
||||
@@ -113,7 +113,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-list-item {}
|
||||
</style>
|
||||
|
||||
@@ -174,7 +174,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-list {
|
||||
@include flex(column);
|
||||
|
||||
@@ -193,7 +193,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
$u-loading-icon-color: #c8c9cc !default;
|
||||
$u-loading-icon-text-margin-left:4px !default;
|
||||
$u-loading-icon-text-color:$u-content-color !default;
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
* @lastTime : 2021-08-20 17:00:23
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/loadingPage.js
|
||||
*/
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
// loading-page组件
|
||||
loadingPage: {
|
||||
loadingText: '正在加载',
|
||||
loadingText: t("up.common.loading2"),
|
||||
image: '',
|
||||
loadingMode: 'circle',
|
||||
loading: false,
|
||||
|
||||
@@ -83,8 +83,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
$text-color: rgb(200, 200, 200) !default;
|
||||
$text-size: 19px !default;
|
||||
$u-loading-icon-margin-bottom: 10px !default;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* @lastTime : 2021-08-20 17:15:26
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/loadmore.js
|
||||
*/
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
// loadmore 组件
|
||||
loadmore: {
|
||||
@@ -17,9 +18,9 @@ export default {
|
||||
iconSize: 17,
|
||||
color: '#606266',
|
||||
loadingIcon: 'spinner',
|
||||
loadmoreText: '加载更多',
|
||||
loadingText: '正在加载...',
|
||||
nomoreText: '没有更多了',
|
||||
loadmoreText: t("up.loadmoe.loadmore"),
|
||||
loadingText: t("up.common.loading2") + '...',
|
||||
nomoreText: t("up.loadmoe.nomore"),
|
||||
isDot: false,
|
||||
iconColor: '#b7b7b7',
|
||||
marginTop: 10,
|
||||
|
||||
@@ -123,7 +123,6 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-loadmore {
|
||||
@include flex(row);
|
||||
|
||||
File diff suppressed because one or more lines are too long
300
uni_modules/uview-plus/components/u-markdown/u-markdown.vue
Normal file
300
uni_modules/uview-plus/components/u-markdown/u-markdown.vue
Normal file
@@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<view class="up-markdown" :class="theme">
|
||||
<up-parse :content="parsedContent" :previewImg="previewImg"></up-parse>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { marked } from './marked.esm.js';
|
||||
|
||||
export default {
|
||||
name: 'up-markdown',
|
||||
props: {
|
||||
// markdown内容
|
||||
content: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 是否启用图片预览
|
||||
previewImg: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否显示代码块行号
|
||||
showLineNumber: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 主题样式 'light' | 'dark'
|
||||
theme: {
|
||||
type: String,
|
||||
default: 'light'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
parsedContent: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
content: {
|
||||
handler(newVal) {
|
||||
this.parseMarkdown(newVal);
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 解析markdown内容
|
||||
parseMarkdown(content) {
|
||||
if (!content) {
|
||||
this.parsedContent = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用marked解析markdown
|
||||
let parsed = marked(content);
|
||||
|
||||
// 处理代码块
|
||||
parsed = this.handleCodeBlock(parsed);
|
||||
|
||||
// 应用主题样式
|
||||
parsed = this.applyTheme(parsed);
|
||||
|
||||
this.parsedContent = parsed;
|
||||
},
|
||||
|
||||
// 处理代码块
|
||||
handleCodeBlock(html) {
|
||||
// 添加代码块样式和行号
|
||||
return html.replace(/<pre><code([^>]*)>([^<]+)<\/code><\/pre>/g, (match, lang, code) => {
|
||||
const language = lang.match(/class="language-([^"]+)"/);
|
||||
const langClass = language ? `language-${language[1]}` : '';
|
||||
|
||||
let result = `<pre class="up-markdown-code ${langClass}">`;
|
||||
|
||||
if (this.showLineNumber) {
|
||||
// 添加行号
|
||||
const lines = code.split('\n').filter(line => line.trim() !== '');
|
||||
result += '<span class="up-markdown-line-numbers">';
|
||||
lines.push('');
|
||||
lines.forEach((_, index) => {
|
||||
result += `<span class="up-markdown-line-number">${index + 1}</span>`;
|
||||
});
|
||||
result += '</span>';
|
||||
}
|
||||
|
||||
result += `<code class='code-lang ${langClass}'>${code}</code></pre>`;
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
// 应用主题样式
|
||||
applyTheme(html) {
|
||||
// 可以根据theme属性添加不同的样式类
|
||||
return html;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.up-markdown {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
padding: 16px;
|
||||
word-wrap: break-word;
|
||||
|
||||
/* 标题样式 */
|
||||
::v-deep h1 {
|
||||
font-size: 32px;
|
||||
margin: 8px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::v-deep h2 {
|
||||
font-size: 24px;
|
||||
margin: 8px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::v-deep h3 {
|
||||
font-size: 18px;
|
||||
margin: 7px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::v-deep h4 {
|
||||
font-size: 16px;
|
||||
margin: 7px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::v-deep h5 {
|
||||
font-size: 13px;
|
||||
margin: 6px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::v-deep h6 {
|
||||
font-size: 10px;
|
||||
margin: 5px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 段落样式 */
|
||||
::v-deep p {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
/* 链接样式 */
|
||||
::v-deep a {
|
||||
color: #007AFF;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
/* 列表样式 */
|
||||
::v-deep ul,
|
||||
::v-deep ol {
|
||||
margin: 16px 0;
|
||||
padding-left: 32px;
|
||||
|
||||
li {
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep ul li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
::v-deep ol li {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
/* 引用样式 */
|
||||
::v-deep blockquote {
|
||||
margin: 8px 0;
|
||||
padding: 0 10px;
|
||||
border-left: 4px solid #ccc;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 代码样式 */
|
||||
::v-deep &-code {
|
||||
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||||
font-size: 14px;
|
||||
background-color: #f6f8fa;
|
||||
padding: 3px 6px;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
}
|
||||
::v-deep .code-lang {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
::v-deep pre {
|
||||
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||||
font-size: 14px;
|
||||
background-color: #f6f8fa;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
border-radius: 6px;
|
||||
margin: 16px 0;
|
||||
|
||||
::v-deep code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
::v-deep table {
|
||||
border-collapse: collapse;
|
||||
margin: 16px 0;
|
||||
width: 100%;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid #dfe2e5;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
tr:nth-child(2n) {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
}
|
||||
|
||||
/* 图片样式 */
|
||||
::v-deep img {
|
||||
max-width: 100%;
|
||||
box-sizing: content-box;
|
||||
background-color: #fff;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
/* 分割线样式 */
|
||||
::v-deep hr {
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: 24px 0;
|
||||
background-color: #e1e4e8;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* 深色主题 */
|
||||
&.dark {
|
||||
color: #ccc;
|
||||
background-color: #1e1e1e;
|
||||
|
||||
::v-deep &-code {
|
||||
background-color: #2d2d2d;
|
||||
color: #dcdcdc;
|
||||
}
|
||||
|
||||
::v-deep pre {
|
||||
background-color: #2d2d2d;
|
||||
color: #dcdcdc;
|
||||
}
|
||||
|
||||
::v-deep blockquote {
|
||||
margin: 8px 0;
|
||||
padding: 0 10px;
|
||||
border-left: 4px solid #ccc;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
::v-deep a {
|
||||
color: #4da6ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 代码块行号样式 */
|
||||
::v-deep .up-markdown-line-numbers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding-right: 10px;
|
||||
margin-right: 10px;
|
||||
border-right: 1px solid #ddd;
|
||||
user-select: none;
|
||||
|
||||
.up-markdown-line-number {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -7,14 +7,15 @@
|
||||
* @lastTime : 2021-08-20 17:15:59
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/modal.js
|
||||
*/
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
// modal 组件
|
||||
modal: {
|
||||
show: false,
|
||||
title: '',
|
||||
content: '',
|
||||
confirmText: '确认',
|
||||
cancelText: '取消',
|
||||
confirmText: t("up.common.confirm"),
|
||||
cancelText: t("up.common.cancel"),
|
||||
showConfirmButton: true,
|
||||
showCancelButton: false,
|
||||
confirmColor: '#2979ff',
|
||||
@@ -26,6 +27,10 @@ export default {
|
||||
negativeTop: 0,
|
||||
width: '650rpx',
|
||||
confirmButtonShape: '',
|
||||
contentTextAlign: 'left'
|
||||
duration: 400,
|
||||
contentTextAlign: 'left',
|
||||
asyncCloseTip: t("up.common.inOperatio") + '...',
|
||||
asyncCancelClose: false,
|
||||
contentStyle: {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,10 +82,30 @@ export const props = defineMixin({
|
||||
type: String,
|
||||
default: () => defProps.modal.confirmButtonShape
|
||||
},
|
||||
// 弹窗动画过度时间
|
||||
duration: {
|
||||
type: [Number],
|
||||
default: defProps.modal.duration
|
||||
},
|
||||
// 文案对齐方式
|
||||
contentTextAlign: {
|
||||
type: String,
|
||||
default: () => defProps.modal.contentTextAlign
|
||||
},
|
||||
// 异步确定时如果点击了取消时候的提示文案
|
||||
asyncCloseTip: {
|
||||
type: String,
|
||||
default: () => defProps.modal.asyncCloseTip
|
||||
},
|
||||
// 是否异步关闭,只对取消按钮有效
|
||||
asyncCancelClose: {
|
||||
type: Boolean,
|
||||
default: () => defProps.modal.asyncCancelClose
|
||||
},
|
||||
// 内容样式
|
||||
contentStyle: {
|
||||
type: Object,
|
||||
default: () => defProps.modal.contentStyle
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user