feat:还原更新框架
This commit is contained in:
@@ -12,19 +12,7 @@
|
||||
|
||||
## 说明
|
||||
|
||||
【文档有广告,不喜勿入谢谢】
|
||||
|
||||
uview-plus,是uni-app全面兼容vue3/nvue/鸿蒙/uni-app-x(已经发布https://ext.dcloud.net.cn/plugin?name=uview-ultra)的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水。uview-plus是基于uView2.x移植的支持vue3的版本,感谢uView。
|
||||
|
||||
## 版本
|
||||
|
||||
### uview-plus v3 (uni-app版本)
|
||||
1. 保持选项式API,尽可能兼容原有uview2各种API,比如mixins,方便大家升级更容易;
|
||||
2. 新手友好,保持原汁原味js,二次开发方便,未使用typescript重写,但带有独立的类型申明包;
|
||||
|
||||
### uview-ultra v4 (uni-app-x版本)
|
||||
1. 尽可能保持原有组件的API一致,方便大家升级;
|
||||
2. 使用uts + 组合式API架构级全新重构,但仍然保持源码的简易,不封装过于复杂,便于修改。
|
||||
uview-plus,是uni-app全面兼容vue3/nvue/鸿蒙/uni-app-x的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水。uview-plus是基于uView2.x移植的支持vue3的版本,感谢uView。
|
||||
|
||||
## 可视化设计
|
||||
|
||||
@@ -44,13 +32,7 @@ uview-plus现已推出免费可视化设计,可以方便的进行页面可视
|
||||
您可以通过**微信**扫码,查看最佳的演示效果。
|
||||
<br>
|
||||
<br>
|
||||
|
||||
| H5 | 微信小程序 | 安卓 | 鸿蒙Next(已上架) |
|
||||
| --- | --- | --- | --- |
|
||||
| <img src="https://uview-plus.jiangruyi.com/common/h5_qrcode.png" width="160" height="160" /> | <img src="https://uview-plus.jiangruyi.com/common/weixin_mini_qrcode.jpg" width="160" height="160" /> | <img src="https://uview-plus.jiangruyi.com/common/android_qrcode.png" width="160" height="160" /> | <img src="https://uview-plus.jiangruyi.com/common/hos_qrcode.jpg" width="160" height="160" /> |
|
||||
<br>
|
||||
<img src="https://uview-plus.jiangruyi.com/common/hos.png">
|
||||
|
||||
<img src="https://uview-plus.jiangruyi.com/common/h5_qrcode.png" width="220" height="220" >
|
||||
|
||||
## 链接
|
||||
|
||||
@@ -59,6 +41,9 @@ uview-plus现已推出免费可视化设计,可以方便的进行页面可视
|
||||
- [升级指南](https://uview-plus.jiangruyi.com/components/changeGuide.html)
|
||||
- [关于我们](https://uview-plus.jiangruyi.com/cooperation/about.html)
|
||||
|
||||
## 交流反馈
|
||||
|
||||
欢迎加入我们的QQ群交流反馈:[点此跳转](https://uview-plus.jiangruyi.com/components/addQQGroup.html)
|
||||
|
||||
## 关于PR
|
||||
|
||||
@@ -87,4 +72,3 @@ uview-plus现已推出免费可视化设计,可以方便的进行页面可视
|
||||
## 版权信息
|
||||
uview-plus遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uview-plus应用到您的产品中。
|
||||
|
||||
Root 组件基于上游项目 `uni-ku/root` 二次开发,已按 MIT 要求保留其原始授权文件:`libs/root/LICENSE`。
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,109 +0,0 @@
|
||||
<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 : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-08-16 10:52:35
|
||||
* @FilePath : /uview-plus/libs/config/props/actionSheet.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:44:35
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/actionSheet.js
|
||||
*/
|
||||
export default {
|
||||
// action-sheet组件
|
||||
@@ -14,8 +14,6 @@ export default {
|
||||
title: '',
|
||||
description: '',
|
||||
actions: [],
|
||||
nameKey: 'name',
|
||||
subnameKey: 'subnameKey',
|
||||
index: '',
|
||||
cancelText: '',
|
||||
closeOnClickAction: true,
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
/*
|
||||
* @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 ActionSheetDefaultProps from './actionSheet'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
import defProps from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(ActionSheetDefaultProps)
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 操作菜单是否展示 (默认false)
|
||||
@@ -33,16 +23,6 @@ export const props = defineMixin({
|
||||
type: Array,
|
||||
default: () => defProps.actionSheet.actions
|
||||
},
|
||||
// 选项名称key
|
||||
nameKey: {
|
||||
type: String,
|
||||
default: () => defProps.actionSheet.nameKey
|
||||
},
|
||||
// 选项子名称key
|
||||
subnameKey: {
|
||||
type: String,
|
||||
default: () => defProps.actionSheet.subnameKey
|
||||
},
|
||||
// 取消按钮的文字,不为空时显示按钮
|
||||
cancelText: {
|
||||
type: String,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
<template>
|
||||
<u-popup
|
||||
:show="show"
|
||||
@@ -7,39 +8,32 @@
|
||||
:round="round"
|
||||
>
|
||||
<view class="u-action-sheet">
|
||||
<!-- 顶部标题区域 -->
|
||||
<view
|
||||
class="u-action-sheet__header"
|
||||
v-if="title"
|
||||
>
|
||||
<text
|
||||
class="u-action-sheet__header__title u-line-1"
|
||||
:style="titleDynamicStyle"
|
||||
>{{title}}</text>
|
||||
<text class="u-action-sheet__header__title u-line-1">{{title}}</text>
|
||||
<view
|
||||
class="u-action-sheet__header__icon-wrap"
|
||||
@tap.stop="cancel"
|
||||
>
|
||||
<up-icon
|
||||
<u-icon
|
||||
name="close"
|
||||
size="17"
|
||||
:color="closeIconColor"
|
||||
color="#c8c9cc"
|
||||
bold
|
||||
></up-icon>
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 描述信息 -->
|
||||
<text
|
||||
class="u-action-sheet__description"
|
||||
:style="[{
|
||||
marginTop: `${title && description ? 0 : '18px'}`
|
||||
}, descriptionDynamicStyle]"
|
||||
}]"
|
||||
v-if="description"
|
||||
>{{description}}</text>
|
||||
<slot>
|
||||
<!-- 分割线 -->
|
||||
<u-line v-if="description" :color="dividerColor"></u-line>
|
||||
<!-- 操作项列表 -->
|
||||
<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 -->
|
||||
@@ -68,20 +62,17 @@
|
||||
@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
|
||||
class="u-action-sheet__item-wrap__item__name"
|
||||
:style="[itemStyle(index)]"
|
||||
>{{ item[nameKey] }}</text>
|
||||
>{{ item.name }}</text>
|
||||
<text
|
||||
v-if="item[subnameKey]"
|
||||
v-if="item.subname"
|
||||
class="u-action-sheet__item-wrap__item__subname"
|
||||
:style="[subnameStyle(index)]"
|
||||
>{{ item[subnameKey] }}</text>
|
||||
>{{ item.subname }}</text>
|
||||
</template>
|
||||
<!-- 加载状态图标 -->
|
||||
<u-loading-icon
|
||||
v-else
|
||||
custom-class="van-action-sheet__loading"
|
||||
@@ -92,25 +83,21 @@
|
||||
<!-- #ifdef MP -->
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
<!-- 选项间分割线 -->
|
||||
<u-line v-if="index !== actions.length - 1" :color="dividerColor"></u-line>
|
||||
<u-line v-if="index !== actions.length - 1"></u-line>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</slot>
|
||||
<!-- 取消按钮前的分割区域 -->
|
||||
<u-gap
|
||||
:bgColor="cancelGapColor"
|
||||
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
|
||||
@touchmove.stop.prevent
|
||||
:hover-stay-time="150"
|
||||
class="u-action-sheet__cancel-text"
|
||||
:style="cancelTextDynamicStyle"
|
||||
>{{cancelText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -127,7 +114,7 @@
|
||||
/**
|
||||
* ActionSheet 操作菜单
|
||||
* @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/actionSheet.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/actionSheet.html
|
||||
*
|
||||
* @property {Boolean} show 操作菜单是否展示 (默认 false )
|
||||
* @property {String} title 操作菜单标题
|
||||
@@ -167,54 +154,20 @@
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
titleDynamicStyle() {
|
||||
return {
|
||||
color: this.upThemeVar('--up-main-color', '#303133')
|
||||
}
|
||||
},
|
||||
descriptionDynamicStyle() {
|
||||
return {
|
||||
color: this.upThemeVar('--up-tips-color', '#909193')
|
||||
}
|
||||
},
|
||||
closeIconColor() {
|
||||
return this.upThemeVar('--up-content-color', '#606266')
|
||||
},
|
||||
dividerColor() {
|
||||
return this.upThemeVar('--up-border-color', this.upThemeIsDark ? '#3a3a3c' : '#dadbde')
|
||||
},
|
||||
cancelGapColor() {
|
||||
return this.upThemeVar('--up-gap-bg-color', this.upThemeIsDark ? '#111111' : '#eaeaec')
|
||||
},
|
||||
cancelTextDynamicStyle() {
|
||||
return {
|
||||
color: this.upThemeVar('--up-main-color', '#303133')
|
||||
}
|
||||
},
|
||||
// 操作项目的样式
|
||||
itemStyle() {
|
||||
return (index) => {
|
||||
const style = {
|
||||
color: this.upThemeVar('--up-main-color', '#303133')
|
||||
};
|
||||
let style = {};
|
||||
if (this.actions[index].color) style.color = this.actions[index].color
|
||||
if (this.actions[index].fontSize) style.fontSize = addUnit(this.actions[index].fontSize)
|
||||
// 选项被禁用的样式
|
||||
if (this.actions[index].disabled) style.color = this.upThemeVar('--up-light-color', '#c0c4cc')
|
||||
if (this.actions[index].disabled) style.color = '#c0c4cc'
|
||||
return style;
|
||||
}
|
||||
},
|
||||
subnameStyle() {
|
||||
return (index) => ({
|
||||
color: this.actions[index].disabled
|
||||
? this.upThemeVar('--up-light-color', '#c0c4cc')
|
||||
: this.upThemeVar('--up-tips-color', '#909193')
|
||||
})
|
||||
}
|
||||
},
|
||||
emits: ["close", "select", "update:show"],
|
||||
methods: {
|
||||
// 关闭操作菜单事件处理
|
||||
closeHandler() {
|
||||
// 允许点击遮罩关闭时,才发出close事件
|
||||
if(this.closeOnClickOverlay) {
|
||||
@@ -227,7 +180,6 @@
|
||||
this.$emit('update:show', false)
|
||||
this.$emit('close')
|
||||
},
|
||||
// 选择操作项处理
|
||||
selectHandler(index) {
|
||||
const item = this.actions[index]
|
||||
if (item && !item.disabled && !item.loading) {
|
||||
@@ -238,39 +190,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
// 动态处理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;
|
||||
$u-action-sheet-title-color: var(--up-main-color, #303133) !default;
|
||||
$u-action-sheet-title-color: $u-main-color !default;
|
||||
$u-action-sheet-header-icon-wrap-right:15px !default;
|
||||
$u-action-sheet-header-icon-wrap-top:15px !default;
|
||||
$u-action-sheet-description-font-size:13px !default;
|
||||
$u-action-sheet-description-color: var(--up-tips-color, #909193) !default;
|
||||
$u-action-sheet-description-color:14px !default;
|
||||
$u-action-sheet-description-margin: 18px 15px !default;
|
||||
$u-action-sheet-item-wrap-item-padding:17px !default;
|
||||
$u-action-sheet-item-wrap-name-font-size:16px !default;
|
||||
$u-action-sheet-item-wrap-subname-font-size:13px !default;
|
||||
$u-action-sheet-item-wrap-subname-color: var(--up-tips-color, #909193) !default;
|
||||
$u-action-sheet-item-wrap-subname-color: #c0c4cc !default;
|
||||
$u-action-sheet-item-wrap-subname-margin-top:10px !default;
|
||||
$u-action-sheet-cancel-text-font-size:16px !default;
|
||||
$u-action-sheet-cancel-text-color: var(--up-main-color, #303133) !default;
|
||||
$u-action-sheet-cancel-text-color:$u-content-color !default;
|
||||
$u-action-sheet-cancel-text-font-size:15px !default;
|
||||
$u-action-sheet-cancel-text-hover-background-color: var(--up-hover-bg-color, rgb(242, 243, 245)) !default;
|
||||
$u-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
|
||||
|
||||
.u-reset-button {
|
||||
width: $u-action-sheet-reset-button-width;
|
||||
@@ -296,9 +239,8 @@
|
||||
}
|
||||
|
||||
&__description {
|
||||
display: block;
|
||||
font-size: $u-action-sheet-description-font-size;
|
||||
color: $u-action-sheet-description-color;
|
||||
color: $u-tips-color;
|
||||
margin: $u-action-sheet-description-margin;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -314,7 +256,7 @@
|
||||
|
||||
&__name {
|
||||
font-size: $u-action-sheet-item-wrap-name-font-size;
|
||||
color: var(--up-main-color, #303133);
|
||||
color: $u-main-color;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<style scoped lang="scss">
|
||||
.agreement-content {
|
||||
width: 100%;;
|
||||
display: inline-block;
|
||||
flex-direction: column;
|
||||
color: var(--up-main-color, #303133);
|
||||
.agreement-url {
|
||||
display: inline-block;
|
||||
color: var(--up-primary, #2979ff);
|
||||
// #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>
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-08-16 16:32:24
|
||||
* @FilePath : /uview-plus/libs/config/props/album.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:47:24
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/album.js
|
||||
*/
|
||||
export default {
|
||||
// album 组件
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
/*
|
||||
* @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 AlbumDefaultProps from './album'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(AlbumDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 图片地址,Array<String>|Array<Object>形式
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<view class="u-album">
|
||||
<!-- 相册行容器,每行显示 rowCount 个图片 -->
|
||||
<view
|
||||
class="u-album__row"
|
||||
ref="u-album__row"
|
||||
@@ -9,15 +8,13 @@
|
||||
: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="onPreviewTap($event, getSrc(item))"
|
||||
@tap="previewFullImage ? onPreviewTap($event, getSrc(item)) : ''"
|
||||
>
|
||||
<!-- 图片显示 -->
|
||||
<image
|
||||
:src="getSrc(item)"
|
||||
:mode="
|
||||
@@ -35,7 +32,6 @@
|
||||
}
|
||||
]"
|
||||
></image>
|
||||
<!-- 超出最大显示数量时的更多提示 -->
|
||||
<view
|
||||
v-if="
|
||||
showMore &&
|
||||
@@ -68,14 +64,14 @@ import { mixin } from '../../libs/mixin/mixin';
|
||||
import { addUnit, sleep } from '../../libs/function/index';
|
||||
import test from '../../libs/function/test';
|
||||
// #ifdef APP-NVUE
|
||||
// 不支持百分比单位,这里需要通过dom查询组件的宽度
|
||||
// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
|
||||
const dom = uni.requireNativePlugin('dom')
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* Album 相册
|
||||
* @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/album.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/album.html
|
||||
*
|
||||
* @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式
|
||||
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
|
||||
@@ -112,20 +108,14 @@ 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,
|
||||
@@ -149,16 +139,11 @@ 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) => {
|
||||
// 限制最大展示数量
|
||||
@@ -175,29 +160,18 @@ 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长度等变化时,重新计算图片的宽度
|
||||
* @returns {Number} 相册宽度
|
||||
*/
|
||||
// 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
|
||||
// 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
|
||||
albumWidth() {
|
||||
let width = 0
|
||||
if (this.urls.length === 1) {
|
||||
@@ -211,100 +185,59 @@ 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)
|
||||
})
|
||||
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
|
||||
? singleSize
|
||||
: (res.width / res.height) * singleSize
|
||||
? this.singleSize
|
||||
: (res.width / res.height) * this.singleSize
|
||||
this.singleHeight = !isHorizotal
|
||||
? singleSize
|
||||
? this.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 &&
|
||||
@@ -318,6 +251,8 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
|
||||
.u-album {
|
||||
@include flex(column);
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-08-17 17:23:53
|
||||
* @FilePath : /uview-plus/libs/config/props/alert.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:48:53
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/alert.js
|
||||
*/
|
||||
export default {
|
||||
// alert警告组件
|
||||
@@ -17,10 +17,6 @@ export default {
|
||||
showIcon: false,
|
||||
effect: 'light',
|
||||
center: false,
|
||||
fontSize: 14,
|
||||
transitionMode: 'fade',
|
||||
duration: 0,
|
||||
icon: '',
|
||||
value: true
|
||||
fontSize: 14
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
/*
|
||||
* @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 AlertDefaultProps from './alert'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(AlertDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 显示文字
|
||||
@@ -52,26 +41,6 @@ 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>
|
||||
<up-transition
|
||||
:mode="transitionMode"
|
||||
<u-transition
|
||||
mode="fade"
|
||||
:show="show"
|
||||
>
|
||||
<view
|
||||
@@ -9,25 +9,22 @@
|
||||
@tap.stop="clickHandler"
|
||||
:style="[addStyle(customStyle)]"
|
||||
>
|
||||
<!-- 左侧图标 -->
|
||||
<view
|
||||
class="u-alert__icon"
|
||||
v-if="showIcon"
|
||||
>
|
||||
<up-icon
|
||||
<u-icon
|
||||
:name="iconName"
|
||||
size="18"
|
||||
:color="iconColor"
|
||||
></up-icon>
|
||||
></u-icon>
|
||||
</view>
|
||||
<!-- 内容区域 -->
|
||||
<view
|
||||
class="u-alert__content"
|
||||
:style="[{
|
||||
paddingRight: closable ? '20px' : 0
|
||||
}]"
|
||||
>
|
||||
<!-- 标题 -->
|
||||
<text
|
||||
class="u-alert__content__title"
|
||||
v-if="title"
|
||||
@@ -37,7 +34,6 @@
|
||||
}]"
|
||||
:class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
|
||||
>{{ title }}</text>
|
||||
<!-- 描述信息 -->
|
||||
<text
|
||||
class="u-alert__content__desc"
|
||||
v-if="description"
|
||||
@@ -48,22 +44,19 @@
|
||||
: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"
|
||||
>
|
||||
<slot name="close">
|
||||
<up-icon
|
||||
<u-icon
|
||||
name="close"
|
||||
:color="iconColor"
|
||||
size="15"
|
||||
></up-icon>
|
||||
</slot>
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</up-transition>
|
||||
</u-transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -74,7 +67,7 @@
|
||||
/**
|
||||
* Alert 警告提示
|
||||
* @description 警告提示,展现需要关注的信息。
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/alertTips.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/alertTips.html
|
||||
*
|
||||
* @property {String} title 显示的文字
|
||||
* @property {String} type 使用预设的颜色 (默认 'warning' )
|
||||
@@ -85,34 +78,24 @@
|
||||
* @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 点击关闭按钮时触发
|
||||
* @event {Function} closed 关闭动画结束时触发
|
||||
* @example <up-alert :title="title" type = "warning" :closable="closable" :description = "description"></up-alert>
|
||||
* @example <u-alert :title="title" type = "warning" :closable="closable" :description = "description"></u-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';
|
||||
@@ -134,50 +117,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
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);
|
||||
}
|
||||
},
|
||||
emits: ["click","close"],
|
||||
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;
|
||||
@@ -195,7 +153,7 @@
|
||||
}
|
||||
|
||||
&--primary--light {
|
||||
background-color: var(--up-primary-light, #ecf5ff);
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
&--error--dark {
|
||||
@@ -203,7 +161,7 @@
|
||||
}
|
||||
|
||||
&--error--light {
|
||||
background-color: var(--up-error-light, #FEF0F0);
|
||||
background-color: #FEF0F0;
|
||||
}
|
||||
|
||||
&--success--dark {
|
||||
@@ -211,7 +169,7 @@
|
||||
}
|
||||
|
||||
&--success--light {
|
||||
background-color: var(--up-success-light, #f5fff0);
|
||||
background-color: #f5fff0;
|
||||
}
|
||||
|
||||
&--warning--dark {
|
||||
@@ -219,7 +177,7 @@
|
||||
}
|
||||
|
||||
&--warning--light {
|
||||
background-color: var(--up-warning-light, #FDF6EC);
|
||||
background-color: #FDF6EC;
|
||||
}
|
||||
|
||||
&--info--dark {
|
||||
@@ -227,7 +185,7 @@
|
||||
}
|
||||
|
||||
&--info--light {
|
||||
background-color: var(--up-info-light, #f4f4f5);
|
||||
background-color: #f4f4f5;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
@@ -239,22 +197,24 @@
|
||||
flex: 1;
|
||||
|
||||
&__title {
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
color: $u-main-color;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&__desc {
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
color: $u-main-color;
|
||||
font-size: 14px;
|
||||
flex-wrap: wrap;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&__title--dark,
|
||||
&__desc--dark {
|
||||
color: var(--up-white, #FFFFFF);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
&__text--primary--light,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/avatarGroup.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:49:55
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/avatarGroup.js
|
||||
*/
|
||||
export default {
|
||||
// avatarGroup 组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import AvatarGroupDefaultProps from './avatarGroup'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(AvatarGroupDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 头像图片组
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
/**
|
||||
* AvatarGroup 头像组
|
||||
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/avatar.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/avatar.html
|
||||
*
|
||||
* @property {Array} urls 头像图片组 (默认 [] )
|
||||
* @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 )
|
||||
@@ -79,6 +79,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-avatar-group {
|
||||
@include flex;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/avatar.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:49:22
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/avatar.js
|
||||
*/
|
||||
export default {
|
||||
// avatar 组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import AvatarDefaultProps from './avatar'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(AvatarDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
import test from '../../libs/function/test';
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
<!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU -->
|
||||
<template v-if="mpAvatar && allowMp"></template>
|
||||
<!-- #endif -->
|
||||
<up-icon
|
||||
<u-icon
|
||||
v-else-if="icon"
|
||||
:name="icon"
|
||||
:size="fontSize"
|
||||
:color="color"
|
||||
></up-icon>
|
||||
></u-icon>
|
||||
<up-text
|
||||
v-else-if="text"
|
||||
:text="text"
|
||||
@@ -63,7 +63,7 @@
|
||||
/**
|
||||
* Avatar 头像
|
||||
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/avatar.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/avatar.html
|
||||
*
|
||||
* @property {String} src 头像路径,如加载失败,将会显示默认头像(不能为相对路径)
|
||||
* @property {String} shape 头像形状 ( circle (默认) | square)
|
||||
@@ -151,6 +151,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-avatar {
|
||||
@include flex;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/backtop.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:50:18
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/backtop.js
|
||||
*/
|
||||
export default {
|
||||
// backtop组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import BackTopDefaultProps from './backtop'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(BackTopDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 返回顶部的形状,circle-圆形,square-方形
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
v-if="!$slots.default && !$slots.$default"
|
||||
@click="backToTop"
|
||||
>
|
||||
<up-icon
|
||||
<u-icon
|
||||
:name="icon"
|
||||
:custom-style="iconStyle"
|
||||
></up-icon>
|
||||
></u-icon>
|
||||
<text
|
||||
v-if="text"
|
||||
class="u-back-top__text"
|
||||
@@ -111,6 +111,7 @@
|
||||
</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;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/badge.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-23 19:51:50
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/badge.js
|
||||
*/
|
||||
export default {
|
||||
// 徽标数组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import BadgeDefaultProps from './badge'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(BadgeDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 是否显示圆点
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
$u-badge-primary: $u-primary !default;
|
||||
$u-badge-error: $u-error !default;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* @Author : ly
|
||||
* @Description :
|
||||
* @version : 4.0
|
||||
* @Date : 2026-02-02 23:54:58
|
||||
* @LastAuthor : ly
|
||||
* @lastTime : 2026-02-02 23:54:58
|
||||
* @FilePath : /uview-ultra/components/up-box/box.js
|
||||
*/
|
||||
export default {
|
||||
// box 组件
|
||||
box: {
|
||||
bgColors: ['#EEFCFF', '#FCF8FF', '#FDF8F2'],
|
||||
height: '160px',
|
||||
borderRadius: '6px',
|
||||
gap: '15px',
|
||||
leftIcon: '',
|
||||
leftTitle: '左',
|
||||
rightTopIcon: '',
|
||||
rightTopTitle: '右上',
|
||||
rightBottomIcon: '',
|
||||
rightBottomTitle: '右下'
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,27 @@
|
||||
import { defineMixin } from '../../libs/vue.js'
|
||||
import BoxDefaultProps from './box.js'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(BoxDefaultProps)
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import defProps from '../../libs/config/props.js'
|
||||
|
||||
export const propsBox = defineMixin({
|
||||
props: {
|
||||
// 背景色
|
||||
bgColors: {
|
||||
type: [Array],
|
||||
default: () => defProps.box.bgColors
|
||||
default: ['#EEFCFF', '#FCF8FF', '#FDF8F2']
|
||||
},
|
||||
// 高度
|
||||
height: {
|
||||
type: [String],
|
||||
default: () => defProps.box.height
|
||||
default: "160px"
|
||||
},
|
||||
// 圆角
|
||||
borderRadius: {
|
||||
type: [String],
|
||||
default: () => defProps.box.borderRadius
|
||||
default: "6px"
|
||||
},
|
||||
// 间隔
|
||||
gap: {
|
||||
type: [String],
|
||||
default: () => defProps.box.gap
|
||||
},
|
||||
// 左侧图标
|
||||
leftIcon: {
|
||||
type: [String],
|
||||
default: () => defProps.box.leftIcon
|
||||
},
|
||||
// 左侧文案
|
||||
leftTitle: {
|
||||
type: [String],
|
||||
default: () => defProps.box.leftTitle
|
||||
},
|
||||
// 右上图标
|
||||
rightTopIcon: {
|
||||
type: [String],
|
||||
default: () => defProps.box.rightTopIcon
|
||||
},
|
||||
// 右上文案
|
||||
rightTopTitle: {
|
||||
type: [String],
|
||||
default: () => defProps.box.rightTopTitle
|
||||
},
|
||||
// 右下图标
|
||||
rightBottomIcon: {
|
||||
type: [String],
|
||||
default: () => defProps.box.rightBottomIcon
|
||||
},
|
||||
// 右下文案
|
||||
rightBottomTitle: {
|
||||
type: [String],
|
||||
default: () => defProps.box.rightBottomTitle
|
||||
default: "15px"
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,31 +1,16 @@
|
||||
<template>
|
||||
<view class="u-box" :style="[{height: height}, addStyle(customStyle)]">
|
||||
<view class="u-box__left" :style="{borderRadius: borderRadius, backgroundColor: bgColors[0]}">
|
||||
<slot name="left">
|
||||
<view class="flex flex-row items-center justify-center">
|
||||
<u-icon size="36" :name="leftIcon"></u-icon>
|
||||
<text class="ml-2 text-16px">{{leftTitle}}</text>
|
||||
</view>
|
||||
</slot>
|
||||
<slot name="left">左</slot>
|
||||
</view>
|
||||
<view class="u-box__gap" :style="{width: gap, height: height}"></view>
|
||||
<view class="u-box__right">
|
||||
<view class="u-box__right-top" :style="{borderRadius: borderRadius, backgroundColor: bgColors[1]}">
|
||||
<slot name="rightTop">
|
||||
<view class="flex flex-row items-center justify-center">
|
||||
<u-icon size="36" :name="rightTopIcon"></u-icon>
|
||||
<text class="ml-2 text-15px">{{rightTopTitle}}</text>
|
||||
</view>
|
||||
</slot>
|
||||
<slot name="rightTop">右上</slot>
|
||||
</view>
|
||||
<view class="u-box__right-gap" :style="{height: gap}"></view>
|
||||
<view class="u-box__right-bottom" :style="{borderRadius: borderRadius, backgroundColor: bgColors[2]}">
|
||||
<slot name="rightBottom">
|
||||
<view class="flex flex-row items-center justify-center">
|
||||
<u-icon size="36" :name="rightBottomIcon"></u-icon>
|
||||
<text class="ml-2 text-15px">{{rightBottomTitle}}</text>
|
||||
</view>
|
||||
</slot>
|
||||
<slot name="rightBottom">右下</slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -66,6 +51,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/button.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:51:27
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/button.js
|
||||
*/
|
||||
export default {
|
||||
// button组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import ButtonDefaultProps from './button'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(ButtonDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 是否细边框
|
||||
|
||||
@@ -39,13 +39,13 @@
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<up-icon
|
||||
<u-icon
|
||||
v-if="icon"
|
||||
:name="icon"
|
||||
:color="iconColorCom"
|
||||
:size="textSize * 1.35"
|
||||
:customStyle="{ marginRight: '2px' }"
|
||||
></up-icon>
|
||||
></u-icon>
|
||||
<slot>
|
||||
<text
|
||||
class="u-button__text"
|
||||
@@ -87,12 +87,12 @@
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<up-icon
|
||||
<u-icon
|
||||
v-if="icon"
|
||||
:name="icon"
|
||||
:color="iconColorCom"
|
||||
:size="textSize * 1.35"
|
||||
></up-icon>
|
||||
></u-icon>
|
||||
<text
|
||||
class="u-button__text"
|
||||
:style="[
|
||||
@@ -117,11 +117,11 @@ import { mixin } from '../../libs/mixin/mixin';
|
||||
import { props } from "./props";
|
||||
import { addStyle } from '../../libs/function/index';
|
||||
import { throttle } from '../../libs/function/throttle';
|
||||
import { getThemeVar } from '../../libs/theme/runtime.js';
|
||||
import color from '../../libs/config/color';
|
||||
/**
|
||||
* button 按钮
|
||||
* @description Button 按钮
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/button.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/button.html
|
||||
*
|
||||
* @property {Boolean} hairline 是否显示按钮的细边框 (默认 true )
|
||||
* @property {String} type 按钮的预置样式,info,primary,error,warning,success (默认 'info' )
|
||||
@@ -192,67 +192,26 @@ export default {
|
||||
);
|
||||
}
|
||||
},
|
||||
isDarkTheme() {
|
||||
return this.$u.theme && this.$u.theme.mode === 'dark';
|
||||
},
|
||||
nvueMainColor() {
|
||||
return this.resolveNvueColor(this.$u.color.mainColor, this.isDarkTheme ? '#f5f5f5' : '#303133');
|
||||
},
|
||||
nvueBorderColor() {
|
||||
return this.resolveNvueColor(this.$u.color.borderColor, this.isDarkTheme ? '#3a3a3c' : '#dadbde');
|
||||
},
|
||||
themeTypeColor() {
|
||||
const fallbackMap = {
|
||||
primary: '#3c9cff',
|
||||
success: '#5ac725',
|
||||
warning: '#f9ae3d',
|
||||
error: '#f56c6c',
|
||||
info: '#909399',
|
||||
};
|
||||
const type = fallbackMap[this.type] ? this.type : 'info';
|
||||
return this.resolveNvueColor(
|
||||
this.upThemeVar(`--up-${type}`, fallbackMap[type]),
|
||||
fallbackMap[type]
|
||||
);
|
||||
},
|
||||
nvueInfoBackgroundColor() {
|
||||
return this.upThemeVar(
|
||||
'--up-button-info-background-color',
|
||||
this.upThemeVar(
|
||||
'--up-card-bg-color',
|
||||
this.isDarkTheme ? '#1c1c1e' : '#ffffff'
|
||||
)
|
||||
);
|
||||
},
|
||||
nvuePlainBackgroundColor() {
|
||||
return this.upThemeVar(
|
||||
'--up-button-plain-background-color',
|
||||
this.upThemeVar(
|
||||
'--up-card-bg-color',
|
||||
this.isDarkTheme ? '#1c1c1e' : '#ffffff'
|
||||
)
|
||||
);
|
||||
},
|
||||
loadingColor() {
|
||||
if (this.plain) {
|
||||
// 如果有设置color值,则用color值,否则使用type主题颜色
|
||||
return this.color
|
||||
? this.color
|
||||
: this.themeTypeColor;
|
||||
: color[`u-${this.type}`];
|
||||
}
|
||||
if (this.type === "info") {
|
||||
return this.isDarkTheme ? "#9ca3af" : "#c9c9c9";
|
||||
return "#c9c9c9";
|
||||
}
|
||||
return "rgb(200, 200, 200)";
|
||||
},
|
||||
iconColorCom() {
|
||||
// 如果是镂空状态,设置了color就用color值,否则使用主题颜色,
|
||||
// up-icon的color能接受一个主题颜色的值
|
||||
// u-icon的color能接受一个主题颜色的值
|
||||
if (this.iconColor) return this.iconColor;
|
||||
if (this.plain) {
|
||||
return this.color ? this.color : this.type;
|
||||
} else {
|
||||
return this.type === "info" ? this.nvueMainColor : "#ffffff";
|
||||
return this.type === "info" ? "#000000" : "#ffffff";
|
||||
}
|
||||
},
|
||||
baseColor() {
|
||||
@@ -281,30 +240,7 @@ export default {
|
||||
style.borderWidth = "1px";
|
||||
style.borderStyle = "solid";
|
||||
}
|
||||
return style;
|
||||
}
|
||||
// #ifdef APP-NVUE
|
||||
const typeColor = this.themeTypeColor;
|
||||
if (this.plain) {
|
||||
style.color = this.type === 'info' ? this.nvueMainColor : typeColor;
|
||||
style.backgroundColor = this.nvuePlainBackgroundColor;
|
||||
style.borderColor = this.type === 'info' ? this.nvueBorderColor : typeColor;
|
||||
style.borderWidth = this.hairline ? '0.5px' : '1px';
|
||||
style.borderStyle = 'solid';
|
||||
} else {
|
||||
style.borderWidth = this.hairline ? '0.5px' : '1px';
|
||||
style.borderStyle = 'solid';
|
||||
if (this.type === 'info') {
|
||||
style.color = this.nvueMainColor;
|
||||
style.backgroundColor = this.nvueInfoBackgroundColor;
|
||||
style.borderColor = this.nvueBorderColor;
|
||||
} else {
|
||||
style.color = '#ffffff';
|
||||
style.backgroundColor = typeColor;
|
||||
style.borderColor = typeColor;
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
return style;
|
||||
},
|
||||
// nvue版本按钮的字体不会继承父组件的颜色,需要对每一个text组件进行单独的设置
|
||||
@@ -312,12 +248,10 @@ export default {
|
||||
let style = {};
|
||||
// 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
|
||||
if (this.type === "info") {
|
||||
style.color = this.nvueMainColor;
|
||||
style.color = "#323233";
|
||||
}
|
||||
if (this.color) {
|
||||
style.color = this.plain ? this.color : "white";
|
||||
} else if (this.plain && this.type !== 'info') {
|
||||
style.color = this.themeTypeColor;
|
||||
}
|
||||
style.fontSize = this.textSize + "px";
|
||||
return style;
|
||||
@@ -337,14 +271,6 @@ export default {
|
||||
'error', 'opensetting', 'launchapp', 'agreeprivacyauthorization'],
|
||||
methods: {
|
||||
addStyle,
|
||||
resolveNvueColor(colorValue, fallbackColor) {
|
||||
if (typeof colorValue !== 'string') return fallbackColor;
|
||||
if (colorValue.indexOf('var(') > -1) return fallbackColor;
|
||||
return colorValue;
|
||||
},
|
||||
upThemeVar(varName, fallbackColor) {
|
||||
return getThemeVar(varName, fallbackColor, this.$u);
|
||||
},
|
||||
clickHandler(e: any) {
|
||||
// 非禁止并且非加载中,才能点击
|
||||
if (!this.disabled && !this.loading) {
|
||||
@@ -380,6 +306,8 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
@import "./vue.scss";
|
||||
/* #endif */
|
||||
@@ -406,9 +334,9 @@ $u-button-mini-height: 22px !default;
|
||||
$u-button-mini-font-size: 10px !default;
|
||||
$u-button-mini-min-width: 50px !default;
|
||||
$u-button-disabled-opacity: 0.5 !default;
|
||||
$u-button-info-color: $u-main-color !default;
|
||||
$u-button-info-background-color: var(--up-button-info-background-color, var(--up-card-bg-color, #fff)) !default;
|
||||
$u-button-info-border-color: $u-border-color !default;
|
||||
$u-button-info-color: #323233 !default;
|
||||
$u-button-info-background-color: #fff !default;
|
||||
$u-button-info-border-color: #ebedf0 !default;
|
||||
$u-button-info-border-width: 1px !default;
|
||||
$u-button-info-border-style: solid !default;
|
||||
$u-button-success-color: #fff !default;
|
||||
@@ -441,7 +369,7 @@ $u-button-square-border-top-left-radius: 3px !default;
|
||||
$u-button-square-border-bottom-left-radius: 3px !default;
|
||||
$u-button-square-border-bottom-right-radius: 3px !default;
|
||||
$u-button-icon-min-width: 1em !default;
|
||||
$u-button-plain-background-color: var(--up-button-plain-background-color, var(--up-card-bg-color, #fff)) !default;
|
||||
$u-button-plain-background-color: #fff !default;
|
||||
$u-button-hairline-border-width: 0.5px !default;
|
||||
|
||||
.u-button {
|
||||
|
||||
@@ -15,7 +15,7 @@ $u-button-plain-u-button-error-color:$u-error;
|
||||
$u-button-plain-u-button-warning-color:$u-warning;
|
||||
|
||||
.u-button {
|
||||
width: auto;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
|
||||
&__text {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* @Author : uview-plus
|
||||
* @Description : calendarStrip default props
|
||||
* @version : 3.0
|
||||
* @Date : 2026-05-29 14:40:00
|
||||
*/
|
||||
import { t } from '../../libs/i18n'
|
||||
|
||||
export default {
|
||||
calendarStrip: {
|
||||
modelValue: null,
|
||||
minDate: 0,
|
||||
maxDate: 0,
|
||||
color: '#3c9cff',
|
||||
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')
|
||||
],
|
||||
fullCalendar: true,
|
||||
fullCalendarProps: {},
|
||||
fullMonthNum: 24,
|
||||
pullDownThreshold: 40,
|
||||
collapseAfterSelect: true,
|
||||
readonly: false,
|
||||
showToday: true,
|
||||
monthFormat: '',
|
||||
expandHint: '下拉展开月历',
|
||||
collapseHint: '上拉收起月历'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CalendarStripDefaultProps from './calendarStrip'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CalendarStripDefaultProps)
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number, Date, null],
|
||||
default: () => defProps.calendarStrip.modelValue
|
||||
},
|
||||
minDate: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.calendarStrip.minDate
|
||||
},
|
||||
maxDate: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.calendarStrip.maxDate
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: () => defProps.calendarStrip.color
|
||||
},
|
||||
weekText: {
|
||||
type: Array,
|
||||
default: () => defProps.calendarStrip.weekText
|
||||
},
|
||||
fullCalendar: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendarStrip.fullCalendar
|
||||
},
|
||||
fullCalendarProps: {
|
||||
type: Object,
|
||||
default: () => defProps.calendarStrip.fullCalendarProps
|
||||
},
|
||||
fullMonthNum: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.calendarStrip.fullMonthNum
|
||||
},
|
||||
pullDownThreshold: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.calendarStrip.pullDownThreshold
|
||||
},
|
||||
collapseAfterSelect: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendarStrip.collapseAfterSelect
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendarStrip.readonly
|
||||
},
|
||||
showToday: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendarStrip.showToday
|
||||
},
|
||||
monthFormat: {
|
||||
type: String,
|
||||
default: () => defProps.calendarStrip.monthFormat
|
||||
},
|
||||
expandHint: {
|
||||
type: String,
|
||||
default: () => defProps.calendarStrip.expandHint
|
||||
},
|
||||
collapseHint: {
|
||||
type: String,
|
||||
default: () => defProps.calendarStrip.collapseHint
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,507 +0,0 @@
|
||||
<template>
|
||||
<view class="u-calendar-strip">
|
||||
<view v-if="!fullCalendar || !innerShowFull">
|
||||
<view class="u-calendar-strip__header u-border-bottom">
|
||||
<text
|
||||
class="u-calendar-strip__header__switch"
|
||||
:class="{ 'u-calendar-strip__header__switch--disabled': switchPrevDisabled }"
|
||||
@tap="prevMonth"
|
||||
>‹</text>
|
||||
<text class="u-calendar-strip__header__title">{{ monthLabel }}</text>
|
||||
<text
|
||||
class="u-calendar-strip__header__switch"
|
||||
:class="{ 'u-calendar-strip__header__switch--disabled': switchNextDisabled }"
|
||||
@tap="nextMonth"
|
||||
>›</text>
|
||||
<text
|
||||
v-if="fullCalendar"
|
||||
class="u-calendar-strip__header__toggle"
|
||||
@click="toggleFull('button')"
|
||||
>▾</text>
|
||||
</view>
|
||||
<view
|
||||
class="u-calendar-strip__scroll-wrap"
|
||||
@touchstart="onTouchStart"
|
||||
@touchend="onTouchEnd"
|
||||
>
|
||||
<scroll-view
|
||||
class="u-calendar-strip__scroll"
|
||||
scroll-x
|
||||
enable-flex
|
||||
:show-scrollbar="false"
|
||||
:scroll-into-view="scrollIntoView"
|
||||
>
|
||||
<view class="u-calendar-strip__scroll__inner">
|
||||
<view
|
||||
v-for="(item, index) in monthDays"
|
||||
:key="index"
|
||||
class="u-calendar-strip__day"
|
||||
:id="getDateId(item.date)"
|
||||
:class="[
|
||||
item.disabled && 'u-calendar-strip__day--disabled',
|
||||
item.selected && 'u-calendar-strip__day--selected',
|
||||
item.today && showToday && !item.selected && 'u-calendar-strip__day--today',
|
||||
]"
|
||||
:style="[dayStyle(item)]"
|
||||
@tap="onDayTap(item)"
|
||||
>
|
||||
<text class="u-calendar-strip__day__date">{{ item.day }}</text>
|
||||
<text class="u-calendar-strip__day__week">{{ getWeekLabel(item.week) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="fullCalendar && innerShowFull"
|
||||
class="u-calendar-strip__panel-wrap"
|
||||
@touchstart="onTouchStart"
|
||||
@touchend="onTouchEnd"
|
||||
>
|
||||
<view class="u-calendar-strip__panel">
|
||||
<u-calendar
|
||||
:show="true"
|
||||
:pageInline="true"
|
||||
:showTitle="false"
|
||||
:showConfirm="false"
|
||||
:closeOnClickOverlay="false"
|
||||
:monthSwitch="true"
|
||||
mode="single"
|
||||
:defaultDate="innerSelectedDate"
|
||||
:minDate="panelMinDate"
|
||||
:maxDate="panelMaxDate"
|
||||
:monthNum="panelMonthNum"
|
||||
:readonly="readonly"
|
||||
:showToday="showToday"
|
||||
:color="color"
|
||||
v-bind="fullCalendarProps"
|
||||
@confirm="onPanelConfirm"
|
||||
></u-calendar>
|
||||
</view>
|
||||
<view class="u-calendar-strip__hint u-calendar-strip__hint--panel">
|
||||
<text class="u-calendar-strip__hint__text" @click="toggleFull('hint')">{{ collapseHint }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { props } from './props'
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin'
|
||||
import { mixin } from '../../libs/mixin/mixin'
|
||||
import dayjs from '../u-datetime-picker/dayjs.esm.min.js'
|
||||
import test from '../../libs/function/test'
|
||||
|
||||
/**
|
||||
* CalendarStrip 单行日历
|
||||
* @description 单行横向日期日历,支持切月、下拉展开完整月历
|
||||
*/
|
||||
export default {
|
||||
name: 'u-calendar-strip',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
data() {
|
||||
return {
|
||||
innerSelectedDate: '',
|
||||
currentMonth: '',
|
||||
scrollIntoView: '',
|
||||
innerShowFull: false,
|
||||
touchStartX: 0,
|
||||
touchStartY: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
innerMaxDate() {
|
||||
return test.number(this.maxDate) ? Number(this.maxDate) : this.maxDate
|
||||
},
|
||||
innerMinDate() {
|
||||
return test.number(this.minDate) ? Number(this.minDate) : this.minDate
|
||||
},
|
||||
rangeChange() {
|
||||
return [this.innerMinDate, this.innerMaxDate]
|
||||
},
|
||||
hasMaxDate() {
|
||||
return !!this.innerMaxDate && dayjs(this.innerMaxDate).isValid()
|
||||
},
|
||||
hasMinDate() {
|
||||
return !!this.innerMinDate && dayjs(this.innerMinDate).isValid()
|
||||
},
|
||||
minDateDay() {
|
||||
if (!this.hasMinDate) return ''
|
||||
return dayjs(this.innerMinDate).format('YYYY-MM-DD')
|
||||
},
|
||||
maxDateDay() {
|
||||
if (!this.hasMaxDate) return ''
|
||||
return dayjs(this.innerMaxDate).format('YYYY-MM-DD')
|
||||
},
|
||||
todayDate() {
|
||||
return dayjs().format('YYYY-MM-DD')
|
||||
},
|
||||
monthLabel() {
|
||||
if (!this.currentMonth) return ''
|
||||
const date = dayjs(`${this.currentMonth}-01`)
|
||||
if (this.monthFormat) return date.format(this.monthFormat)
|
||||
if (uni.getLocale() == 'zh-Hans' || uni.getLocale() == 'zh-Hant') {
|
||||
return date.format('YYYY年MM月')
|
||||
}
|
||||
return date.format('MM/YYYY')
|
||||
},
|
||||
monthDays() {
|
||||
if (!this.currentMonth) return []
|
||||
const start = dayjs(`${this.currentMonth}-01`)
|
||||
const days = start.daysInMonth()
|
||||
return new Array(days).fill(0).map((item, index) => {
|
||||
const date = start.date(index + 1).format('YYYY-MM-DD')
|
||||
return {
|
||||
day: index + 1,
|
||||
date,
|
||||
week: start.date(index + 1).day(),
|
||||
disabled: this.isDateDisabled(date),
|
||||
selected: this.dateSame(date, this.innerSelectedDate),
|
||||
today: this.dateSame(date, this.todayDate)
|
||||
}
|
||||
})
|
||||
},
|
||||
switchPrevDisabled() {
|
||||
if (!this.hasMinDate || !this.currentMonth) return false
|
||||
const current = dayjs(`${this.currentMonth}-01`)
|
||||
const minMonth = dayjs(this.minDateDay).startOf('month')
|
||||
return current.isSame(minMonth, 'month') || current.isBefore(minMonth, 'month')
|
||||
},
|
||||
switchNextDisabled() {
|
||||
if (!this.hasMaxDate || !this.currentMonth) return false
|
||||
const current = dayjs(`${this.currentMonth}-01`)
|
||||
const maxMonth = dayjs(this.maxDateDay).startOf('month')
|
||||
return current.isSame(maxMonth, 'month') || current.isAfter(maxMonth, 'month')
|
||||
},
|
||||
panelMinDate() {
|
||||
if (this.hasMinDate) return this.minDateDay
|
||||
const monthNum = Math.max(1, Number(this.fullMonthNum) || 24)
|
||||
const anchor = this.currentMonth || dayjs().format('YYYY-MM')
|
||||
return dayjs(`${anchor}-01`).subtract(monthNum - 1, 'month').startOf('month').format('YYYY-MM-DD')
|
||||
},
|
||||
panelMaxDate() {
|
||||
if (this.hasMaxDate) return this.maxDateDay
|
||||
const monthNum = Math.max(1, Number(this.fullMonthNum) || 24)
|
||||
const anchor = this.currentMonth || dayjs().format('YYYY-MM')
|
||||
return dayjs(`${anchor}-01`).add(monthNum - 1, 'month').endOf('month').format('YYYY-MM-DD')
|
||||
},
|
||||
panelMonthNum() {
|
||||
return this.getMonths(this.panelMinDate, this.panelMaxDate)
|
||||
},
|
||||
pullHintText() {
|
||||
return this.innerShowFull ? this.collapseHint : this.expandHint
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(n) {
|
||||
this.syncByValue(n, false, 'sync')
|
||||
}
|
||||
},
|
||||
rangeChange() {
|
||||
this.syncByValue(this.innerSelectedDate || this.modelValue, true, 'range')
|
||||
}
|
||||
},
|
||||
emits: ['change', 'confirm', 'monthChange', 'toggleFull', 'update:modelValue'],
|
||||
methods: {
|
||||
dateSame(date1, date2) {
|
||||
if (!date1 || !date2) return false
|
||||
return dayjs(date1).isSame(dayjs(date2), 'day')
|
||||
},
|
||||
normalizeDate(value) {
|
||||
if (!value) return ''
|
||||
const parsed = dayjs(value)
|
||||
if (!parsed.isValid()) return ''
|
||||
return parsed.format('YYYY-MM-DD')
|
||||
},
|
||||
clampDate(date) {
|
||||
let next = this.normalizeDate(date)
|
||||
if (!next) return ''
|
||||
if (this.hasMinDate && dayjs(next).isBefore(dayjs(this.minDateDay), 'day')) {
|
||||
next = this.minDateDay
|
||||
}
|
||||
if (this.hasMaxDate && dayjs(next).isAfter(dayjs(this.maxDateDay), 'day')) {
|
||||
next = this.maxDateDay
|
||||
}
|
||||
return next
|
||||
},
|
||||
isDateDisabled(date) {
|
||||
if (!date) return true
|
||||
if (this.hasMinDate && dayjs(date).isBefore(dayjs(this.minDateDay), 'day')) {
|
||||
return true
|
||||
}
|
||||
if (this.hasMaxDate && dayjs(date).isAfter(dayjs(this.maxDateDay), 'day')) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
getDateId(date) {
|
||||
return `u-calendar-strip-day-${dayjs(date).format('YYYYMMDD')}`
|
||||
},
|
||||
getWeekLabel(week) {
|
||||
const index = week === 0 ? 6 : week - 1
|
||||
return this.weekText[index] || ''
|
||||
},
|
||||
dayStyle(item) {
|
||||
const style = {}
|
||||
if (item.selected) {
|
||||
style.backgroundColor = this.color
|
||||
}
|
||||
if (!item.selected && item.today && this.showToday) {
|
||||
style.borderColor = this.color
|
||||
}
|
||||
return style
|
||||
},
|
||||
scrollToDate(date) {
|
||||
if (!date) {
|
||||
this.scrollIntoView = ''
|
||||
return
|
||||
}
|
||||
const target = this.getDateId(date)
|
||||
this.scrollIntoView = ''
|
||||
this.$nextTick(() => {
|
||||
this.scrollIntoView = target
|
||||
})
|
||||
},
|
||||
syncByValue(value, emit = false, scene = 'sync') {
|
||||
let next = this.clampDate(value)
|
||||
if (!next) {
|
||||
next = this.clampDate(this.todayDate)
|
||||
}
|
||||
if (!next) return
|
||||
this.setSelectedDate(next, scene, emit)
|
||||
},
|
||||
setSelectedDate(date, scene = 'tap', emit = true) {
|
||||
const next = this.clampDate(date)
|
||||
if (!next || this.isDateDisabled(next)) return
|
||||
const prevDate = this.innerSelectedDate
|
||||
const prevMonth = this.currentMonth
|
||||
this.innerSelectedDate = next
|
||||
this.currentMonth = dayjs(next).format('YYYY-MM')
|
||||
this.scrollToDate(next)
|
||||
if (!emit) return
|
||||
const payload = {
|
||||
date: next,
|
||||
month: this.currentMonth,
|
||||
scene
|
||||
}
|
||||
if (!this.dateSame(prevDate, next)) {
|
||||
this.$emit('update:modelValue', next)
|
||||
}
|
||||
this.$emit('change', payload)
|
||||
this.$emit('confirm', payload)
|
||||
if (prevMonth !== this.currentMonth) {
|
||||
this.$emit('monthChange', {
|
||||
month: this.currentMonth,
|
||||
scene
|
||||
})
|
||||
}
|
||||
},
|
||||
getMonths(minDate, maxDate) {
|
||||
const minYear = dayjs(minDate).year()
|
||||
const minMonth = dayjs(minDate).month() + 1
|
||||
const maxYear = dayjs(maxDate).year()
|
||||
const maxMonth = dayjs(maxDate).month() + 1
|
||||
return Math.max(1, (maxYear - minYear) * 12 + (maxMonth - minMonth) + 1)
|
||||
},
|
||||
findFirstEnabledDate(month) {
|
||||
const start = dayjs(`${month}-01`)
|
||||
const days = start.daysInMonth()
|
||||
for (let i = 1; i <= days; i++) {
|
||||
const date = start.date(i).format('YYYY-MM-DD')
|
||||
if (!this.isDateDisabled(date)) {
|
||||
return date
|
||||
}
|
||||
}
|
||||
return ''
|
||||
},
|
||||
switchMonth(step = 0) {
|
||||
if (step < 0 && this.switchPrevDisabled) return
|
||||
if (step > 0 && this.switchNextDisabled) return
|
||||
const baseMonth = this.currentMonth || dayjs(this.todayDate).format('YYYY-MM')
|
||||
const target = dayjs(`${baseMonth}-01`).add(step, 'month')
|
||||
const targetMonth = target.format('YYYY-MM')
|
||||
const selectedDay = dayjs(this.innerSelectedDate || this.todayDate).date()
|
||||
const dayInTargetMonth = Math.min(selectedDay, target.daysInMonth())
|
||||
let next = target.date(dayInTargetMonth).format('YYYY-MM-DD')
|
||||
next = this.clampDate(next)
|
||||
if (!next || !dayjs(next).isSame(target, 'month') || this.isDateDisabled(next)) {
|
||||
next = this.findFirstEnabledDate(targetMonth)
|
||||
}
|
||||
if (!next) return
|
||||
this.setSelectedDate(next, 'switch', true)
|
||||
},
|
||||
prevMonth() {
|
||||
this.switchMonth(-1)
|
||||
},
|
||||
nextMonth() {
|
||||
this.switchMonth(1)
|
||||
},
|
||||
onDayTap(item) {
|
||||
if (this.readonly || item.disabled) return
|
||||
this.setSelectedDate(item.date, 'tap', true)
|
||||
},
|
||||
setFullVisible(show, source = 'button') {
|
||||
if (!this.fullCalendar) return
|
||||
if (this.innerShowFull === show) return
|
||||
this.innerShowFull = show
|
||||
this.$emit('toggleFull', {
|
||||
show,
|
||||
source
|
||||
})
|
||||
},
|
||||
toggleFull(source = 'button') {
|
||||
this.setFullVisible(!this.innerShowFull, source)
|
||||
},
|
||||
onPanelConfirm(e) {
|
||||
if (!Array.isArray(e) || !e.length) return
|
||||
this.setSelectedDate(e[0], 'full', true)
|
||||
if (this.collapseAfterSelect) {
|
||||
this.setFullVisible(false, 'auto')
|
||||
}
|
||||
},
|
||||
onTouchStart(event) {
|
||||
if (!this.fullCalendar) return
|
||||
const point = event?.changedTouches?.[0] || event?.touches?.[0]
|
||||
if (!point) return
|
||||
this.touchStartX = point.clientX
|
||||
this.touchStartY = point.clientY
|
||||
},
|
||||
onTouchEnd(event) {
|
||||
if (!this.fullCalendar) return
|
||||
const point = event?.changedTouches?.[0] || event?.touches?.[0]
|
||||
if (!point) return
|
||||
const deltaX = point.clientX - this.touchStartX
|
||||
const deltaY = point.clientY - this.touchStartY
|
||||
const threshold = Number(this.pullDownThreshold) || 40
|
||||
if (Math.abs(deltaY) < threshold || Math.abs(deltaY) <= Math.abs(deltaX)) {
|
||||
return
|
||||
}
|
||||
if (deltaY > 0 && !this.innerShowFull) {
|
||||
this.setFullVisible(true, 'pull-down')
|
||||
}
|
||||
if (deltaY < 0 && this.innerShowFull) {
|
||||
this.setFullVisible(false, 'pull-up')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-calendar-strip {
|
||||
background-color: var(--up-card-bg-color, #ffffff);
|
||||
border-radius: 10px;
|
||||
margin: 0 8px;
|
||||
|
||||
&__header {
|
||||
@include flex;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
height: 40px;
|
||||
|
||||
&__switch {
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
line-height: 40px;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
|
||||
&--disabled {
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
}
|
||||
|
||||
&__toggle {
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
line-height: 40px;
|
||||
color: var(--up-content-color, $u-content-color);
|
||||
}
|
||||
}
|
||||
|
||||
&__scroll-wrap {
|
||||
padding: 10px 0 6px;
|
||||
}
|
||||
|
||||
&__scroll {
|
||||
&__inner {
|
||||
@include flex;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__day {
|
||||
width: 54px;
|
||||
height: 62px;
|
||||
margin-right: 8px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
@include flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
&__date {
|
||||
font-size: 18px;
|
||||
line-height: 22px;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
}
|
||||
|
||||
&__week {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--up-content-color, $u-content-color);
|
||||
}
|
||||
|
||||
&--selected {
|
||||
.u-calendar-strip__day__date,
|
||||
.u-calendar-strip__day__week {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
&--today {
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
&__hint {
|
||||
padding: 4px 0 10px;
|
||||
text-align: center;
|
||||
|
||||
&__text {
|
||||
font-size: 12px;
|
||||
color: var(--up-tips-color, $u-tips-color);
|
||||
}
|
||||
}
|
||||
|
||||
&__panel {
|
||||
padding: 0 8px 8px;
|
||||
}
|
||||
|
||||
&__panel-wrap {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
&__hint--panel {
|
||||
padding: 0 0 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,22 +1,21 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/calendar.js
|
||||
* @LastAuthor : LQ
|
||||
* @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: t("up.calendar.chooseDates"),
|
||||
title: '日期选择',
|
||||
showTitle: true,
|
||||
showSubtitle: true,
|
||||
mode: 'single',
|
||||
startText: t("up.common.start"),
|
||||
endText: t("up.common.end"),
|
||||
startText: '开始',
|
||||
endText: '结束',
|
||||
customList: [],
|
||||
color: '#3c9cff',
|
||||
minDate: 0,
|
||||
@@ -27,8 +26,8 @@ export default {
|
||||
formatter: null,
|
||||
showLunar: false,
|
||||
showMark: true,
|
||||
confirmText: t("up.common.confirm"),
|
||||
confirmDisabledText: t("up.common.confirm"),
|
||||
confirmText: '确定',
|
||||
confirmDisabledText: '确定',
|
||||
show: false,
|
||||
closeOnClickOverlay: false,
|
||||
readonly: false,
|
||||
@@ -37,27 +36,10 @@ export default {
|
||||
rangePrompt: '',
|
||||
showRangePrompt: true,
|
||||
allowSameDay: false,
|
||||
rangeResultMode: 'all',
|
||||
enableTime: false,
|
||||
timePrecision: 'minute',
|
||||
defaultTime: '',
|
||||
round: 0,
|
||||
overlay: true,
|
||||
duration: 300,
|
||||
overlayStyle: {},
|
||||
overlayOpacity: 0.5,
|
||||
zIndex: 10075,
|
||||
safeAreaInsetBottom: true,
|
||||
safeAreaInsetTop: false,
|
||||
bgColor: '',
|
||||
monthNum: 3,
|
||||
monthSwitch: false,
|
||||
showToday: true,
|
||||
todayColor: '',
|
||||
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")],
|
||||
weekText: ['一', '二', '三', '四', '五', '六', '日'],
|
||||
forbidDays: [],
|
||||
forbidDaysToast: t("up.calendar.disabled"),
|
||||
monthFormat: '',
|
||||
pageInline: false
|
||||
forbidDaysToast: '该日期已禁用',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,42 +4,10 @@
|
||||
class="u-calendar-header__title"
|
||||
v-if="showTitle"
|
||||
>{{ title }}</text>
|
||||
<view
|
||||
class="u-calendar-header__subtitle-wrap"
|
||||
<text
|
||||
class="u-calendar-header__subtitle"
|
||||
v-if="showSubtitle"
|
||||
>
|
||||
<text
|
||||
v-if="showSwitch"
|
||||
class="u-calendar-header__switch"
|
||||
:class="{ 'u-calendar-header__switch--disabled': prevYearDisabled }"
|
||||
@click="prevYear"
|
||||
>«</text>
|
||||
<text
|
||||
v-if="showSwitch"
|
||||
class="u-calendar-header__switch"
|
||||
:class="{ 'u-calendar-header__switch--disabled': prevDisabled }"
|
||||
@click="prev"
|
||||
>‹</text>
|
||||
<text class="u-calendar-header__subtitle">{{ subtitle }}</text>
|
||||
<text
|
||||
v-if="showSwitch"
|
||||
class="u-calendar-header__switch"
|
||||
:class="{ 'u-calendar-header__switch--disabled': nextDisabled }"
|
||||
@click="next"
|
||||
>›</text>
|
||||
<text
|
||||
v-if="showSwitch"
|
||||
class="u-calendar-header__switch"
|
||||
:class="{ 'u-calendar-header__switch--disabled': nextYearDisabled }"
|
||||
@click="nextYear"
|
||||
>»</text>
|
||||
<text
|
||||
v-if="showToday"
|
||||
class="u-calendar-header__today"
|
||||
:class="{ 'u-calendar-header__today--disabled': todayDisabled }"
|
||||
@click="today"
|
||||
>{{ todayText }}</text>
|
||||
</view>
|
||||
>{{ subtitle }}</text>
|
||||
<view class="u-calendar-header__weekdays">
|
||||
<text class="u-calendar-header__weekdays__weekday">{{ weekText[0] }}</text>
|
||||
<text class="u-calendar-header__weekdays__weekday">{{ weekText[1] }}</text>
|
||||
@@ -83,49 +51,9 @@
|
||||
weekText: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
return ['一', '二', '三', '四', '五', '六', '日']
|
||||
}
|
||||
},
|
||||
// 是否显示月份切换按钮
|
||||
showSwitch: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 上个月按钮是否禁用
|
||||
prevDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 下个月按钮是否禁用
|
||||
nextDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 上一年按钮是否禁用
|
||||
prevYearDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 下一年按钮是否禁用
|
||||
nextYearDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示今天按钮
|
||||
showToday: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 今天按钮文案
|
||||
todayText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 今天按钮是否禁用
|
||||
todayDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -133,36 +61,15 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
prev() {
|
||||
if (!this.prevDisabled) {
|
||||
this.$emit('prev')
|
||||
}
|
||||
},
|
||||
next() {
|
||||
if (!this.nextDisabled) {
|
||||
this.$emit('next')
|
||||
}
|
||||
},
|
||||
prevYear() {
|
||||
if (!this.prevYearDisabled) {
|
||||
this.$emit('prevYear')
|
||||
}
|
||||
},
|
||||
nextYear() {
|
||||
if (!this.nextYearDisabled) {
|
||||
this.$emit('nextYear')
|
||||
}
|
||||
},
|
||||
today() {
|
||||
if (!this.todayDisabled) {
|
||||
this.$emit('today')
|
||||
}
|
||||
name() {
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-calendar-header {
|
||||
display: flex;
|
||||
@@ -171,41 +78,20 @@
|
||||
|
||||
&__title {
|
||||
font-size: 16px;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
color: $u-main-color;
|
||||
text-align: center;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__subtitle-wrap {
|
||||
@include flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
font-size: 14px;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
&__switch {
|
||||
width: 44px;
|
||||
color: $u-main-color;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
font-size: 22px;
|
||||
line-height: 40px;
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
|
||||
&--disabled {
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
|
||||
&__weekdays {
|
||||
@@ -214,27 +100,11 @@
|
||||
|
||||
&__weekday {
|
||||
font-size: 13px;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
color: $u-main-color;
|
||||
line-height: 30px;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&__today {
|
||||
margin-left: 4px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
padding: 0 10px;
|
||||
border-radius: 14px;
|
||||
font-size: 13px;
|
||||
color: var(--up-primary, $u-primary);
|
||||
border: 1px solid var(--up-primary, $u-primary);
|
||||
flex-shrink: 0;
|
||||
|
||||
&--disabled {
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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">{{ monthTitle(item) }}</text>
|
||||
<text v-if="index !== 0" class="u-calendar-month__title">{{ item.year }}年{{ item.month }}月</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>
|
||||
@@ -33,12 +33,11 @@
|
||||
// #endif
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin';
|
||||
import { mixin } from '../../libs/mixin/mixin';
|
||||
import { addUnit, deepClone, toast, sleep, getWindowInfo } from '../../libs/function/index';
|
||||
import { addUnit, deepClone, toast, sleep } from '../../libs/function/index';
|
||||
import { colorGradient } from '../../libs/function/colorGradient';
|
||||
import test from '../../libs/function/test';
|
||||
import defProps from '../../libs/config/props';
|
||||
import dayjs from '../u-datetime-picker/dayjs.esm.min.js';
|
||||
import { t } from '../../libs/i18n'
|
||||
import dayjs from 'dayjs/esm/index'
|
||||
export default {
|
||||
name: 'u-calendar-month',
|
||||
mixins: [mpMixin, mixin],
|
||||
@@ -135,16 +134,6 @@
|
||||
forbidDaysToast: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 今天日期,用于独立高亮
|
||||
todayDate: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 今天日期的独立高亮颜色
|
||||
todayColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -173,24 +162,17 @@
|
||||
return (index1, index2, item) => {
|
||||
const style = {}
|
||||
let week = item.week
|
||||
// 隐藏挂载场景下节点宽度可能拿到0,兜底使用窗口宽度避免首日偏移归零
|
||||
const wrapperWidth = this.width > 0 ? this.width : (getWindowInfo().windowWidth || 0)
|
||||
// 不进行四舍五入的形式保留2位小数
|
||||
const dayWidth = Number(parseFloat(wrapperWidth / 7).toFixed(3).slice(0, -1))
|
||||
const dayWidth = Number(parseFloat(this.width / 7).toFixed(3).slice(0, -1))
|
||||
// 得出每个日期的宽度
|
||||
// #ifdef APP-NVUE
|
||||
style.width = addUnit(dayWidth, 'px')
|
||||
// #endif
|
||||
style.height = addUnit(this.rowHeight, 'px')
|
||||
style.height = addUnit(this.rowHeight)
|
||||
if (index2 === 0) {
|
||||
// 获取当前为星期几,如果为0,则为星期天,减一为每月第一天时,需要向左偏移的item个数
|
||||
week = (week === 0 ? 7 : week) - 1
|
||||
// #ifdef APP-NVUE
|
||||
style.marginLeft = addUnit(week * dayWidth, 'px')
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
style.marginLeft = `${(week / 7) * 100}%`
|
||||
// #endif
|
||||
}
|
||||
if (this.mode === 'range') {
|
||||
// 之所以需要这么写,是因为DCloud公司的iOS客户端导致的bug
|
||||
@@ -210,10 +192,6 @@
|
||||
if (this.selected.some(item => this.dateSame(item, date))) {
|
||||
style.backgroundColor = this.color
|
||||
}
|
||||
if (this.todayDate && this.dateSame(date, this.todayDate)) {
|
||||
style.border = `1px solid ${this.resolvedTodayColor}`
|
||||
style.boxSizing = 'border-box'
|
||||
}
|
||||
if (this.mode === 'single') {
|
||||
if (date === this.selected[0]) {
|
||||
// 因为需要对nvue的兼容,只能这么写,无法缩写,也无法通过类名控制等等
|
||||
@@ -238,13 +216,12 @@
|
||||
// 处于第一和最后一个之间的日期,背景色设置为浅色,通过将对应颜色进行等分,再取其尾部的颜色值
|
||||
if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
|
||||
.selected[len]))) {
|
||||
const rangeEndColor = this.upThemeVar('--up-card-bg-color', this.upThemeIsDark ? '#1c1c1e' : '#ffffff')
|
||||
style.backgroundColor = colorGradient(this.color, rangeEndColor, 100)[90]
|
||||
style.backgroundColor = colorGradient(this.color, '#ffffff', 100)[90]
|
||||
// 增加一个透明度,让范围区间的背景色也能看到底部的mark水印字符
|
||||
style.opacity = this.upThemeIsDark ? 0.85 : 0.7
|
||||
style.opacity = 0.7
|
||||
}
|
||||
} else if (this.selected.length === 1) {
|
||||
// 之所以需要这么写,是因为uni-app的iOS客户端的bug
|
||||
// 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
|
||||
// 进行还原操作,否则在nvue的iOS,uni-app有bug,会导致诡异的表现
|
||||
style.borderTopLeftRadius = '3px'
|
||||
style.borderBottomLeftRadius = '3px'
|
||||
@@ -260,9 +237,6 @@
|
||||
return style
|
||||
}
|
||||
},
|
||||
resolvedTodayColor() {
|
||||
return this.todayColor || this.color
|
||||
},
|
||||
// 某个日期是否被选中
|
||||
textStyle() {
|
||||
return (item) => {
|
||||
@@ -280,9 +254,6 @@
|
||||
style.color = this.color
|
||||
}
|
||||
}
|
||||
if (this.todayDate && this.dateSame(date, this.todayDate) && !this.isSelectedDate(date)) {
|
||||
style.color = this.resolvedTodayColor
|
||||
}
|
||||
return style
|
||||
}
|
||||
},
|
||||
@@ -321,7 +292,6 @@
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
emits: ['monthSelected', 'updateMonthTop'],
|
||||
methods: {
|
||||
init() {
|
||||
// 初始化默认选中
|
||||
@@ -335,13 +305,6 @@
|
||||
})
|
||||
})
|
||||
},
|
||||
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)) {
|
||||
@@ -353,21 +316,16 @@
|
||||
dateSame(date1, date2) {
|
||||
return dayjs(date1).isSame(dayjs(date2))
|
||||
},
|
||||
isSelectedDate(date) {
|
||||
return this.selected.some(item => this.dateSame(item, date))
|
||||
},
|
||||
// 获取月份数据区域的宽度,因为nvue不支持百分比,所以无法通过css设置每个日期item的宽度
|
||||
getWrapperWidth() {
|
||||
// #ifdef APP-NVUE
|
||||
dom.getComponentRect(this.$refs['u-calendar-month-wrapper'], res => {
|
||||
const width = res && res.size ? Number(res.size.width) : 0
|
||||
this.width = width > 0 ? width : (getWindowInfo().windowWidth || 0)
|
||||
this.width = res.size.width
|
||||
})
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
this.$uGetRect('.u-calendar-month-wrapper').then(size => {
|
||||
const width = size ? Number(size.width) : 0
|
||||
this.width = width > 0 ? width : (getWindowInfo().windowWidth || 0)
|
||||
this.width = size.width
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
@@ -392,7 +350,7 @@
|
||||
// 获取每个月份区域的尺寸
|
||||
getMonthRectByPromise(el) {
|
||||
// #ifndef APP-NVUE
|
||||
// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://uview-plus.jiangruyi.com/js/getRect.html
|
||||
// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://ijry.github.io/uview-plus/js/getRect.html
|
||||
// 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同
|
||||
return new Promise(resolve => {
|
||||
this.$uGetRect(`.${el}`).then(size => {
|
||||
@@ -456,7 +414,7 @@
|
||||
if(this.rangePrompt) {
|
||||
toast(this.rangePrompt)
|
||||
} else {
|
||||
toast(t("up.calendar.daysExceed", { days: this.maxRange }))
|
||||
toast(`选择天数不能超过 ${this.maxRange} 天`)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -516,24 +474,13 @@
|
||||
setSelected(selected, event = true) {
|
||||
this.selected = selected
|
||||
event && this.$emit('monthSelected', this.selected,'tap')
|
||||
},
|
||||
selectDate(date) {
|
||||
const targetDate = dayjs(date).format("YYYY-MM-DD")
|
||||
for (let monthIndex = 0; monthIndex < this.months.length; monthIndex++) {
|
||||
const dayIndex = this.months[monthIndex].date.findIndex(item => {
|
||||
return this.dateSame(item.date, targetDate)
|
||||
})
|
||||
if (dayIndex !== -1) {
|
||||
this.clickHandler(monthIndex, dayIndex, this.months[monthIndex].date[dayIndex])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-calendar-month-wrapper {
|
||||
margin-top: 4px;
|
||||
@@ -547,7 +494,7 @@
|
||||
font-size: 14px;
|
||||
line-height: 42px;
|
||||
height: 42px;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
color: $u-main-color;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -569,7 +516,7 @@
|
||||
|
||||
&__text {
|
||||
font-size: 155px;
|
||||
color: var(--up-calendar-month-mark-color, rgba(231, 232, 234, 0.83));
|
||||
color: rgba(231, 232, 234, 0.83);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,7 +547,7 @@
|
||||
}
|
||||
|
||||
&__buttom-info {
|
||||
color: var(--up-content-color, $u-content-color);
|
||||
color: $u-content-color;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
@@ -614,21 +561,20 @@
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
color: var(--up-disabled-color, #cacbcd);
|
||||
color: #cacbcd;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
|
||||
&--selected {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
color: var(--up-disabled-color, #cacbcd);
|
||||
color: #cacbcd;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,5 +605,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CalendarDefaultProps from './calendar'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
import defProps from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CalendarDefaultProps)
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 日历顶部标题
|
||||
@@ -100,46 +98,6 @@ export const props = defineMixin({
|
||||
type: Boolean,
|
||||
default: () => defProps.calendar.show
|
||||
},
|
||||
// 是否显示遮罩
|
||||
overlay: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendar.overlay
|
||||
},
|
||||
// 动画时长,单位ms
|
||||
duration: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.calendar.duration
|
||||
},
|
||||
// 自定义遮罩的样式
|
||||
overlayStyle: {
|
||||
type: [Object, String],
|
||||
default: () => defProps.calendar.overlayStyle
|
||||
},
|
||||
// 遮罩的透明度,0-1之间
|
||||
overlayOpacity: {
|
||||
type: [Number, String],
|
||||
default: () => defProps.calendar.overlayOpacity
|
||||
},
|
||||
// 层级
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.calendar.zIndex
|
||||
},
|
||||
// 是否为iPhoneX留出底部安全距离
|
||||
safeAreaInsetBottom: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendar.safeAreaInsetBottom
|
||||
},
|
||||
// 是否留出顶部安全距离(状态栏高度)
|
||||
safeAreaInsetTop: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendar.safeAreaInsetTop
|
||||
},
|
||||
// 弹窗背景色,设置为transparent可去除白色背景
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: () => defProps.calendar.bgColor
|
||||
},
|
||||
// 是否允许点击遮罩关闭日历
|
||||
closeOnClickOverlay: {
|
||||
type: Boolean,
|
||||
@@ -174,26 +132,6 @@ export const props = defineMixin({
|
||||
allowSameDay: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendar.allowSameDay
|
||||
},
|
||||
// 区间模式下确认返回值格式,all-返回区间内所有日期,boundary-仅返回起止日期
|
||||
rangeResultMode: {
|
||||
type: String,
|
||||
default: () => defProps.calendar.rangeResultMode
|
||||
},
|
||||
// 是否开启时分秒选择
|
||||
enableTime: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendar.enableTime
|
||||
},
|
||||
// 时间精度:hour-仅时,minute-时分,second-时分秒
|
||||
timePrecision: {
|
||||
type: String,
|
||||
default: () => defProps.calendar.timePrecision
|
||||
},
|
||||
// 默认时间,格式支持 HH / HH:mm / HH:mm:ss
|
||||
defaultTime: {
|
||||
type: String,
|
||||
default: () => defProps.calendar.defaultTime
|
||||
},
|
||||
// 圆角值
|
||||
round: {
|
||||
@@ -205,21 +143,6 @@ export const props = defineMixin({
|
||||
type: [Number, String],
|
||||
default: 3
|
||||
},
|
||||
// 是否启用非滚动的单月切换模式
|
||||
monthSwitch: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendar.monthSwitch
|
||||
},
|
||||
// 是否显示今天按钮
|
||||
showToday: {
|
||||
type: Boolean,
|
||||
default: () => defProps.calendar.showToday
|
||||
},
|
||||
// 今天日期的独立高亮颜色,默认跟随主题色
|
||||
todayColor: {
|
||||
type: String,
|
||||
default: () => defProps.calendar.todayColor
|
||||
},
|
||||
// 星期文案
|
||||
weekText: {
|
||||
type: Array,
|
||||
@@ -233,14 +156,5 @@ export const props = defineMixin({
|
||||
type: String,
|
||||
default: defProps.calendar.forbidDaysToast
|
||||
},
|
||||
monthFormat:{
|
||||
type: String,
|
||||
default: defProps.calendar.monthFormat
|
||||
},
|
||||
// 是否页面内展示
|
||||
pageInline:{
|
||||
type: Boolean,
|
||||
default: defProps.calendar.pageInline
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,18 +2,9 @@
|
||||
<u-popup
|
||||
:show="show"
|
||||
mode="bottom"
|
||||
:overlay="overlay"
|
||||
:duration="duration"
|
||||
:overlayStyle="overlayStyle"
|
||||
:overlayOpacity="overlayOpacity"
|
||||
:zIndex="zIndex"
|
||||
:safeAreaInsetBottom="safeAreaInsetBottom"
|
||||
:safeAreaInsetTop="safeAreaInsetTop"
|
||||
:bgColor="bgColor"
|
||||
:closeable="!pageInline"
|
||||
closeable
|
||||
@close="close"
|
||||
:round="round"
|
||||
:pageInline="pageInline"
|
||||
:closeOnClickOverlay="closeOnClickOverlay"
|
||||
>
|
||||
<view class="u-calendar">
|
||||
@@ -23,46 +14,10 @@
|
||||
:showSubtitle="showSubtitle"
|
||||
:showTitle="showTitle"
|
||||
:weekText="weekText"
|
||||
:showSwitch="monthSwitch"
|
||||
:showToday="showToday"
|
||||
:todayText="todayText"
|
||||
:todayDisabled="todayDisabled"
|
||||
:prevDisabled="switchPrevDisabled"
|
||||
:nextDisabled="switchNextDisabled"
|
||||
:prevYearDisabled="switchPrevYearDisabled"
|
||||
:nextYearDisabled="switchNextYearDisabled"
|
||||
@prev="prevMonth"
|
||||
@next="nextMonth"
|
||||
@prevYear="prevYear"
|
||||
@nextYear="nextYear"
|
||||
@today="jumpToToday"
|
||||
></uHeader>
|
||||
<view v-if="showTimePanel" class="u-calendar__time-panel">
|
||||
<view v-if="mode === 'single'" class="u-calendar__time-row">
|
||||
<text class="u-calendar__time-date">{{ singleDateLabel }}</text>
|
||||
<view class="u-calendar__time-trigger" @click="openTimePicker('single')">
|
||||
<text class="u-calendar__time-text">{{ singleTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="mode === 'range' && rangeResultMode === 'boundary'">
|
||||
<view class="u-calendar__time-row">
|
||||
<text class="u-calendar__time-date">{{ rangeStartDateLabel }}</text>
|
||||
<view class="u-calendar__time-trigger" @click="openTimePicker('start')">
|
||||
<text class="u-calendar__time-text">{{ rangeStartTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-calendar__time-row">
|
||||
<text class="u-calendar__time-date">{{ rangeEndDateLabel }}</text>
|
||||
<view class="u-calendar__time-trigger" @click="openTimePicker('end')">
|
||||
<text class="u-calendar__time-text">{{ rangeEndTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view
|
||||
v-if="!monthSwitch"
|
||||
:style="{
|
||||
height: addUnit(listHeight, 'px')
|
||||
height: addUnit(listHeight)
|
||||
}"
|
||||
scroll-y
|
||||
@scroll="onScroll"
|
||||
@@ -89,48 +44,11 @@
|
||||
:allowSameDay="allowSameDay"
|
||||
:forbidDays="forbidDays"
|
||||
:forbidDaysToast="forbidDaysToast"
|
||||
:monthFormat="monthFormat"
|
||||
:todayDate="todayDate"
|
||||
:todayColor="todayColor"
|
||||
ref="month"
|
||||
@monthSelected="monthSelected"
|
||||
@updateMonthTop="onUpdateMonthTop"
|
||||
@updateMonthTop="updateMonthTop"
|
||||
></uMonth>
|
||||
</scroll-view>
|
||||
<view
|
||||
v-else
|
||||
:style="{
|
||||
height: addUnit(listHeight, 'px')
|
||||
}"
|
||||
>
|
||||
<uMonth
|
||||
:color="color"
|
||||
:rowHeight="rowHeight"
|
||||
:showMark="showMark"
|
||||
:months="currentMonths"
|
||||
:mode="mode"
|
||||
:maxCount="maxCount"
|
||||
:startText="startText"
|
||||
:endText="endText"
|
||||
:defaultDate="defaultDate"
|
||||
:minDate="innerMinDate"
|
||||
:maxDate="innerMaxDate"
|
||||
:maxMonth="monthNum"
|
||||
:readonly="readonly"
|
||||
:maxRange="maxRange"
|
||||
:rangePrompt="rangePrompt"
|
||||
:showRangePrompt="showRangePrompt"
|
||||
:allowSameDay="allowSameDay"
|
||||
:forbidDays="forbidDays"
|
||||
:forbidDaysToast="forbidDaysToast"
|
||||
:monthFormat="monthFormat"
|
||||
:todayDate="todayDate"
|
||||
:todayColor="todayColor"
|
||||
ref="month"
|
||||
@monthSelected="monthSelected"
|
||||
@updateMonthTop="onUpdateMonthTop"
|
||||
></uMonth>
|
||||
</view>
|
||||
<slot name="footer" v-if="showConfirm">
|
||||
<view class="u-calendar__confirm">
|
||||
<u-button
|
||||
@@ -146,32 +64,6 @@
|
||||
</slot>
|
||||
</view>
|
||||
</u-popup>
|
||||
<u-popup
|
||||
:show="timePickerShow"
|
||||
mode="center"
|
||||
:round="8"
|
||||
:closeOnClickOverlay="true"
|
||||
@close="closeTimePicker"
|
||||
>
|
||||
<view class="u-calendar__time-picker">
|
||||
<view class="u-calendar__time-picker__header">
|
||||
<text class="u-calendar__time-picker__cancel" @click="closeTimePicker">取消</text>
|
||||
<text class="u-calendar__time-picker__title">选择时间</text>
|
||||
<text class="u-calendar__time-picker__confirm" @click="confirmTimePicker">确定</text>
|
||||
</view>
|
||||
<picker-view class="u-calendar__time-picker__body" :value="timePickerValue" @change="onTimePickerChange">
|
||||
<picker-view-column>
|
||||
<view v-for="(item, index) in hourOptions" :key="`h-${index}`" class="u-calendar__time-picker__item">{{ item }}</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column v-if="timePrecision !== 'hour'">
|
||||
<view v-for="(item, index) in minuteOptions" :key="`m-${index}`" class="u-calendar__time-picker__item">{{ item }}</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column v-if="timePrecision === 'second'">
|
||||
<view v-for="(item, index) in secondOptions" :key="`s-${index}`" class="u-calendar__time-picker__item">{{ item }}</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -179,17 +71,16 @@ import uHeader from './header.vue'
|
||||
import uMonth from './month.vue'
|
||||
import { props } from './props.js'
|
||||
import util from './util.js'
|
||||
import dayjs from '../u-datetime-picker/dayjs.esm.min.js';
|
||||
import dayjs from 'dayjs/esm/index'
|
||||
import Calendar from '../../libs/util/calendar.js'
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin.js'
|
||||
import { mixin } from '../../libs/mixin/mixin.js'
|
||||
import { addUnit, getPx, range, error, padZero } from '../../libs/function/index';
|
||||
import { addUnit, range, error, padZero } from '../../libs/function/index';
|
||||
import test from '../../libs/function/test';
|
||||
import { t } from '../../libs/i18n'
|
||||
/**
|
||||
* Calendar 日历
|
||||
* @description 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中.
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/calendar.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/calendar.html
|
||||
*
|
||||
* @property {String} title 标题内容 (默认 日期选择 )
|
||||
* @property {Boolean} showTitle 是否显示标题 (默认 true )
|
||||
@@ -210,28 +101,14 @@ import { t } from '../../libs/i18n'
|
||||
* @property {String} confirmText 确定按钮的文字 (默认 '确定' )
|
||||
* @property {String} confirmDisabledText 确认按钮处于禁用状态时的文字 (默认 '确定' )
|
||||
* @property {Boolean} show 是否显示日历弹窗 (默认 false )
|
||||
* @property {Boolean} overlay 是否显示遮罩 (默认 true )
|
||||
* @property {String | Number} duration 动画时长,单位ms (默认 300 )
|
||||
* @property {Object | String} overlayStyle 自定义遮罩的样式
|
||||
* @property {Number | String} overlayOpacity 遮罩透明度,0-1之间 (默认 0.5 )
|
||||
* @property {String | Number} zIndex 弹出层的z-index值 (默认 10075 )
|
||||
* @property {Boolean} safeAreaInsetBottom 是否留出底部安全距离 (默认 true )
|
||||
* @property {Boolean} safeAreaInsetTop 是否留出顶部安全距离(状态栏高度) (默认 false )
|
||||
* @property {String} bgColor 弹窗背景色,设置为transparent可去除白色背景
|
||||
* @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭日历 (默认 false )
|
||||
* @property {Boolean} readonly 是否为只读状态,只读状态下禁止选择日期 (默认 false )
|
||||
* @property {String | Number} maxRange 日期区间最多可选天数,默认无限制,mode = range时有效
|
||||
* @property {String} rangePrompt 范围选择超过最多可选天数时的提示文案,mode = range时有效
|
||||
* @property {Boolean} showRangePrompt 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 (默认 true )
|
||||
* @property {Boolean} allowSameDay 是否允许日期范围的起止时间为同一天,mode = range时有效 (默认 false )
|
||||
* @property {String} rangeResultMode 区间模式下确认返回值格式,all-返回区间内所有日期,boundary-仅返回起止日期 (默认 'all' )
|
||||
* @property {Boolean} enableTime 是否开启时分秒选择 (默认 false )
|
||||
* @property {String} timePrecision 时间精度,hour-仅时,minute-时分,second-时分秒 (默认 'minute' )
|
||||
* @property {String} defaultTime 默认时间,支持HH/HH:mm/HH:mm:ss,不传则按00:00补齐
|
||||
* @property {Number|String} round 圆角值,默认无圆角 (默认 0 )
|
||||
* @property {Number|String} monthNum 最多展示的月份数量 (默认 3 )
|
||||
* @property {Boolean} monthSwitch 是否启用非滚动的单月切换模式 (默认 false )
|
||||
* @property {String} todayColor 今天日期的独立高亮颜色,默认跟随主题色
|
||||
* @property {Array} weekText 星期文案 (默认 ['一', '二', '三', '四', '五', '六', '日'] )
|
||||
*
|
||||
* @event {Function()} confirm 点击确定按钮时触发 选择日期相关的返回参数
|
||||
@@ -259,15 +136,6 @@ export default {
|
||||
scrollIntoView: '',
|
||||
scrollIntoViewScroll: '',
|
||||
scrollTop:0,
|
||||
timePickerShow: false,
|
||||
timePickerTarget: 'single',
|
||||
timePickerValue: [0, 0, 0],
|
||||
hourOptions: [],
|
||||
minuteOptions: [],
|
||||
secondOptions: [],
|
||||
singleTime: '00:00',
|
||||
rangeStartTime: '00:00',
|
||||
rangeEndTime: '00:00',
|
||||
// 过滤处理方法
|
||||
innerFormatter: (value) => value
|
||||
}
|
||||
@@ -285,13 +153,6 @@ export default {
|
||||
this.setMonth()
|
||||
}
|
||||
},
|
||||
timePrecision() {
|
||||
this.initTimeOptions()
|
||||
this.initTimeValues()
|
||||
},
|
||||
defaultTime() {
|
||||
this.initTimeValues()
|
||||
},
|
||||
// 打开弹窗时,设置月份数据
|
||||
show: {
|
||||
immediate: true,
|
||||
@@ -318,28 +179,6 @@ export default {
|
||||
? Number(this.minDate)
|
||||
: this.minDate
|
||||
},
|
||||
todayDate() {
|
||||
return dayjs().format('YYYY-MM-DD')
|
||||
},
|
||||
todayText() {
|
||||
return t('up.calendar.today')
|
||||
},
|
||||
todayDisabled() {
|
||||
const today = dayjs(this.todayDate)
|
||||
const minDate = this.innerMinDate
|
||||
? dayjs(this.innerMinDate)
|
||||
: null
|
||||
const maxDate = this.innerMaxDate
|
||||
? dayjs(this.innerMaxDate)
|
||||
: null
|
||||
if (minDate && today.isBefore(minDate, 'day')) {
|
||||
return true
|
||||
}
|
||||
if (maxDate && today.isAfter(maxDate, 'day')) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
// 多个条件的变化,会引起选中日期的变化,这里统一管理监听
|
||||
selectedChange() {
|
||||
return [this.innerMinDate, this.innerMaxDate, this.defaultDate]
|
||||
@@ -347,53 +186,13 @@ export default {
|
||||
subtitle() {
|
||||
// 初始化时,this.months为空数组,所以需要特别判断处理
|
||||
if (this.months.length) {
|
||||
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
|
||||
}
|
||||
return `${this.months[this.monthIndex].year}年${
|
||||
this.months[this.monthIndex].month
|
||||
}月`
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
currentMonths() {
|
||||
if (this.monthSwitch && this.months.length) {
|
||||
return [this.months[this.monthIndex]]
|
||||
}
|
||||
return this.months
|
||||
},
|
||||
switchPrevDisabled() {
|
||||
return this.monthIndex <= 0
|
||||
},
|
||||
switchNextDisabled() {
|
||||
return this.monthIndex >= this.months.length - 1
|
||||
},
|
||||
switchPrevYearDisabled() {
|
||||
return this.monthIndex - 12 < 0
|
||||
},
|
||||
switchNextYearDisabled() {
|
||||
return this.monthIndex + 12 > this.months.length - 1
|
||||
},
|
||||
showTimePanel() {
|
||||
if (!this.enableTime) return false
|
||||
if (this.mode === 'single') return true
|
||||
if (this.mode === 'range' && this.rangeResultMode === 'boundary') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
singleDateLabel() {
|
||||
return this.selected[0] || '--'
|
||||
},
|
||||
rangeStartDateLabel() {
|
||||
return this.selected[0] || '--'
|
||||
},
|
||||
rangeEndDateLabel() {
|
||||
if (this.selected.length >= 2) {
|
||||
return this.selected[this.selected.length - 1]
|
||||
}
|
||||
return '--'
|
||||
},
|
||||
buttonDisabled() {
|
||||
// 如果为range类型,且选择的日期个数不足1个时,让底部的按钮出于disabled状态
|
||||
if (this.mode === 'range') {
|
||||
@@ -414,130 +213,6 @@ export default {
|
||||
emits: ["confirm", "close"],
|
||||
methods: {
|
||||
addUnit,
|
||||
padTime(num) {
|
||||
return String(num).padStart(2, '0')
|
||||
},
|
||||
initTimeOptions() {
|
||||
this.hourOptions = Array.from({ length: 24 }, (_, i) => this.padTime(i))
|
||||
this.minuteOptions = Array.from({ length: 60 }, (_, i) => this.padTime(i))
|
||||
this.secondOptions = Array.from({ length: 60 }, (_, i) => this.padTime(i))
|
||||
},
|
||||
parseTimeValue(value = '') {
|
||||
const raw = String(value || '').trim()
|
||||
const parts = raw.length ? raw.split(':') : []
|
||||
const getNumber = (index, max) => {
|
||||
const current = Number(parts[index] || 0)
|
||||
if (Number.isNaN(current)) return 0
|
||||
return Math.max(0, Math.min(max, current))
|
||||
}
|
||||
const hour = getNumber(0, 23)
|
||||
const minute = getNumber(1, 59)
|
||||
const second = getNumber(2, 59)
|
||||
return [hour, minute, second]
|
||||
},
|
||||
getDefaultTimeValue() {
|
||||
const [hour, minute, second] = this.parseTimeValue(this.defaultTime)
|
||||
if (this.timePrecision === 'hour') {
|
||||
return this.padTime(hour)
|
||||
}
|
||||
if (this.timePrecision === 'second') {
|
||||
return `${this.padTime(hour)}:${this.padTime(minute)}:${this.padTime(second)}`
|
||||
}
|
||||
return `${this.padTime(hour)}:${this.padTime(minute)}`
|
||||
},
|
||||
initTimeValues() {
|
||||
const value = this.getDefaultTimeValue()
|
||||
this.singleTime = value
|
||||
this.rangeStartTime = value
|
||||
this.rangeEndTime = value
|
||||
},
|
||||
timeToPickerValue(value = '') {
|
||||
const [hour, minute, second] = this.parseTimeValue(value)
|
||||
return [hour, minute, second]
|
||||
},
|
||||
pickerValueToTime(value = []) {
|
||||
const hour = Number(value[0] || 0)
|
||||
const minute = Number(value[1] || 0)
|
||||
const second = Number(value[2] || 0)
|
||||
if (this.timePrecision === 'hour') {
|
||||
return this.padTime(hour)
|
||||
}
|
||||
if (this.timePrecision === 'second') {
|
||||
return `${this.padTime(hour)}:${this.padTime(minute)}:${this.padTime(second)}`
|
||||
}
|
||||
return `${this.padTime(hour)}:${this.padTime(minute)}`
|
||||
},
|
||||
openTimePicker(target) {
|
||||
this.timePickerTarget = target
|
||||
let currentValue = this.singleTime
|
||||
if (target === 'start') currentValue = this.rangeStartTime
|
||||
if (target === 'end') currentValue = this.rangeEndTime
|
||||
this.timePickerValue = this.timeToPickerValue(currentValue)
|
||||
this.timePickerShow = true
|
||||
},
|
||||
onTimePickerChange(e) {
|
||||
this.timePickerValue = e.detail.value
|
||||
},
|
||||
closeTimePicker() {
|
||||
this.timePickerShow = false
|
||||
},
|
||||
confirmTimePicker() {
|
||||
const value = this.pickerValueToTime(this.timePickerValue)
|
||||
if (this.timePickerTarget === 'single') this.singleTime = value
|
||||
if (this.timePickerTarget === 'start') this.rangeStartTime = value
|
||||
if (this.timePickerTarget === 'end') this.rangeEndTime = value
|
||||
this.timePickerShow = false
|
||||
},
|
||||
timeToSecond(timeText = '') {
|
||||
const [hour, minute, second] = this.parseTimeValue(timeText)
|
||||
return hour * 3600 + minute * 60 + second
|
||||
},
|
||||
validateSameDayRangeTime() {
|
||||
if (!this.enableTime || this.mode !== 'range' || this.rangeResultMode !== 'boundary') {
|
||||
return true
|
||||
}
|
||||
if (this.selected.length < 2) return true
|
||||
const startDate = this.selected[0]
|
||||
const endDate = this.selected[this.selected.length - 1]
|
||||
if (startDate !== endDate) return true
|
||||
const startSeconds = this.timeToSecond(this.rangeStartTime)
|
||||
const endSeconds = this.timeToSecond(this.rangeEndTime)
|
||||
if (endSeconds < startSeconds) {
|
||||
uni.showToast({
|
||||
title: '结束时间不能早于开始时间',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
appendTime(dateText, timeText) {
|
||||
return `${dateText} ${timeText}`
|
||||
},
|
||||
getConfirmValue(selected = this.selected) {
|
||||
let result = selected
|
||||
if (
|
||||
this.mode === 'range' &&
|
||||
this.rangeResultMode === 'boundary' &&
|
||||
selected.length >= 2
|
||||
) {
|
||||
const len = selected.length - 1
|
||||
result = [selected[0], selected[len]]
|
||||
}
|
||||
if (!this.showTimePanel || !this.enableTime) {
|
||||
return result
|
||||
}
|
||||
if (this.mode === 'single' && result.length >= 1) {
|
||||
return [this.appendTime(result[0], this.singleTime)]
|
||||
}
|
||||
if (this.mode === 'range' && this.rangeResultMode === 'boundary' && result.length >= 2) {
|
||||
return [
|
||||
this.appendTime(result[0], this.rangeStartTime),
|
||||
this.appendTime(result[1], this.rangeEndTime)
|
||||
]
|
||||
}
|
||||
return result
|
||||
},
|
||||
// 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用
|
||||
setFormatter(e) {
|
||||
this.innerFormatter = e
|
||||
@@ -556,8 +231,7 @@ export default {
|
||||
return
|
||||
}
|
||||
if( scene === 'tap') {
|
||||
if (!this.validateSameDayRangeTime()) return
|
||||
this.$emit('confirm', this.getConfirmValue())
|
||||
this.$emit('confirm', this.selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -572,15 +246,7 @@ export default {
|
||||
return error('maxDate不能小于minDate时间')
|
||||
}
|
||||
// 滚动区域的高度
|
||||
let bottomPadding = 0;
|
||||
if (this.pageInline) {
|
||||
bottomPadding = 0
|
||||
} else {
|
||||
bottomPadding = 30
|
||||
}
|
||||
this.listHeight = this.rowHeight * (this.monthSwitch ? 6 : 5) + bottomPadding
|
||||
this.initTimeOptions()
|
||||
this.initTimeValues()
|
||||
this.listHeight = this.rowHeight * 5 + 30
|
||||
this.setMonth()
|
||||
},
|
||||
close() {
|
||||
@@ -589,8 +255,7 @@ export default {
|
||||
// 点击确定按钮
|
||||
confirm() {
|
||||
if (!this.buttonDisabled) {
|
||||
if (!this.validateSameDayRangeTime()) return
|
||||
this.$emit('confirm', this.getConfirmValue())
|
||||
this.$emit('confirm', this.selected)
|
||||
}
|
||||
},
|
||||
// 获得两个日期之间的月份数
|
||||
@@ -675,67 +340,6 @@ export default {
|
||||
year: dayjs(minDate).add(i, 'month').year()
|
||||
})
|
||||
}
|
||||
if (this.monthSwitch) {
|
||||
this.monthIndex = this.getDefaultMonthIndex()
|
||||
}
|
||||
},
|
||||
getDefaultMonthIndex() {
|
||||
let selected = dayjs().format('YYYY-MM')
|
||||
if (this.defaultDate) {
|
||||
if (!test.array(this.defaultDate)) {
|
||||
selected = dayjs(this.defaultDate).format('YYYY-MM')
|
||||
} else if (this.defaultDate.length) {
|
||||
selected = dayjs(this.defaultDate[0]).format('YYYY-MM')
|
||||
}
|
||||
}
|
||||
const index = this.months.findIndex(({ year, month }) => {
|
||||
return `${year}-${padZero(month)}` === selected
|
||||
})
|
||||
return index === -1 ? 0 : index
|
||||
},
|
||||
prevMonth() {
|
||||
if (!this.switchPrevDisabled) {
|
||||
this.monthIndex -= 1
|
||||
}
|
||||
},
|
||||
nextMonth() {
|
||||
if (!this.switchNextDisabled) {
|
||||
this.monthIndex += 1
|
||||
}
|
||||
},
|
||||
prevYear() {
|
||||
if (!this.switchPrevYearDisabled) {
|
||||
this.monthIndex -= 12
|
||||
}
|
||||
},
|
||||
nextYear() {
|
||||
if (!this.switchNextYearDisabled) {
|
||||
this.monthIndex += 12
|
||||
}
|
||||
},
|
||||
jumpToToday() {
|
||||
if (this.todayDisabled) {
|
||||
return
|
||||
}
|
||||
const targetMonth = dayjs(this.todayDate).format('YYYY-MM')
|
||||
const selectToday = () => {
|
||||
if (this.mode === 'range') {
|
||||
return
|
||||
}
|
||||
this.$refs.month && this.$refs.month.selectDate(this.todayDate)
|
||||
}
|
||||
if (this.monthSwitch) {
|
||||
const todayMonthIndex = this.months.findIndex(({ year, month }) => {
|
||||
return `${year}-${padZero(month)}` === targetMonth
|
||||
})
|
||||
if (todayMonthIndex !== -1) {
|
||||
this.monthIndex = todayMonthIndex
|
||||
this.$nextTick(selectToday)
|
||||
}
|
||||
return
|
||||
}
|
||||
this.scrollIntoDefaultMonth(targetMonth)
|
||||
this.$nextTick(selectToday)
|
||||
},
|
||||
// 滚动到默认设置的月份
|
||||
scrollIntoDefaultMonth(selected) {
|
||||
@@ -750,7 +354,6 @@ export default {
|
||||
if (_index !== -1) {
|
||||
// #ifndef MP-WEIXIN
|
||||
this.$nextTick(() => {
|
||||
this.scrollIntoView = ''
|
||||
this.scrollIntoView = `month-${_index}`
|
||||
this.scrollIntoViewScroll = this.scrollIntoView
|
||||
})
|
||||
@@ -773,12 +376,6 @@ export default {
|
||||
}
|
||||
},
|
||||
// 更新月份的top值
|
||||
onUpdateMonthTop(topArr = []) {
|
||||
if (this.monthSwitch) {
|
||||
return
|
||||
}
|
||||
this.updateMonthTop(topArr)
|
||||
},
|
||||
updateMonthTop(topArr = []) {
|
||||
// 设置对应月份的top值,用于onScroll方法更新月份
|
||||
topArr.map((item, index) => {
|
||||
@@ -806,88 +403,9 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
|
||||
.u-calendar {
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
background-color: var(--up-card-bg-color, #ffffff);
|
||||
|
||||
&__time-panel {
|
||||
padding: 8px 16px 0;
|
||||
}
|
||||
|
||||
&__time-row {
|
||||
height: 34px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
&__time-date {
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
&__time-trigger {
|
||||
min-width: 92px;
|
||||
height: 30px;
|
||||
padding: 0 10px;
|
||||
border-radius: 15px;
|
||||
border: 1px solid #dcdfe6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__time-text {
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
&__time-picker {
|
||||
width: 320px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__time-picker__header {
|
||||
height: 44px;
|
||||
padding: 0 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
&__time-picker__cancel {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
&__time-picker__title {
|
||||
font-size: 15px;
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__time-picker__confirm {
|
||||
font-size: 14px;
|
||||
color: #3c9cff;
|
||||
}
|
||||
|
||||
&__time-picker__body {
|
||||
width: 320px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
&__time-picker__item {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
&__confirm {
|
||||
padding: 7px 18px;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import dayjs from '../u-datetime-picker/dayjs.esm.min.js';
|
||||
import dayjs from 'dayjs/esm/index'
|
||||
export default {
|
||||
methods: {
|
||||
// 设置月份数据
|
||||
|
||||
@@ -1,734 +0,0 @@
|
||||
<template>
|
||||
<view
|
||||
class="u-canvas"
|
||||
:id="rootId"
|
||||
:style="{
|
||||
width: useRootHeightAndWidth ? '100%' : actualWidth + unit,
|
||||
height: useRootHeightAndWidth ? '100%' : actualHeight + unit
|
||||
}"
|
||||
>
|
||||
<!-- #ifdef MP || H5 -->
|
||||
<canvas
|
||||
class="u-canvas__canvas"
|
||||
:id="canvasId"
|
||||
:canvas-id="canvasId"
|
||||
type="2d"
|
||||
:disable-scroll="disableScroll"
|
||||
:style="{ width: actualWidth + unit, height: actualHeight + unit }"
|
||||
@touchstart="onTouchStart"
|
||||
@touchmove="onTouchMove"
|
||||
@touchend="onTouchEnd"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<canvas
|
||||
class="u-canvas__canvas"
|
||||
:id="canvasId"
|
||||
:canvas-id="canvasId"
|
||||
:disable-scroll="disableScroll"
|
||||
:style="{ width: actualWidth + unit, height: actualHeight + unit }"
|
||||
@touchstart="onTouchStart"
|
||||
@touchmove="onTouchMove"
|
||||
@touchend="onTouchEnd"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<gcanvas
|
||||
class="u-canvas__canvas"
|
||||
ref="gcanvas"
|
||||
:style="{ width: actualWidth + unit, height: actualHeight + unit }"
|
||||
@touchstart="onTouchStart"
|
||||
@touchmove="onTouchMove"
|
||||
@touchend="onTouchEnd"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef APP-NVUE
|
||||
import {
|
||||
enable,
|
||||
WeexBridge,
|
||||
Image as GImage
|
||||
} from '../../libs/util/gcanvas/index.js';
|
||||
// #endif
|
||||
|
||||
export default {
|
||||
name: 'u-canvas',
|
||||
emits: ['ready', 'touchstart', 'touchmove', 'touchend'],
|
||||
props: {
|
||||
canvasId: {
|
||||
type: String,
|
||||
default: () => `u-canvas${Math.floor(Math.random() * 1000000)}`
|
||||
},
|
||||
width: {
|
||||
type: [String, Number],
|
||||
default: 300
|
||||
},
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: 300
|
||||
},
|
||||
unit: {
|
||||
type: String,
|
||||
default: 'px'
|
||||
},
|
||||
useRootHeightAndWidth: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#ffffff'
|
||||
},
|
||||
disableScroll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rootId: `u-canvas-root-${Math.floor(Math.random() * 1000000)}`,
|
||||
ctx: null,
|
||||
widthLocal: this.parseSize(this.width),
|
||||
heightLocal: this.parseSize(this.height),
|
||||
fontSize: 12,
|
||||
fontFamily: 'sans-serif',
|
||||
fontWeight: 'normal',
|
||||
dpr: 1
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
actualWidth() {
|
||||
return this.useRootHeightAndWidth ? this.widthLocal : this.parseSize(this.width);
|
||||
},
|
||||
actualHeight() {
|
||||
return this.useRootHeightAndWidth ? this.heightLocal : this.parseSize(this.height);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
width() {
|
||||
this.refresh();
|
||||
},
|
||||
height() {
|
||||
this.refresh();
|
||||
},
|
||||
bgColor() {
|
||||
this.clearCanvas();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this._canvasNode = null;
|
||||
this._selectorResult = null;
|
||||
this._canvasElement = null;
|
||||
this._imageCache = Object.create(null);
|
||||
this._isNvue = false;
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.initCanvas();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
parseSize(value) {
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
if (typeof value !== 'string') {
|
||||
return 0;
|
||||
}
|
||||
if (value.endsWith('rpx') || value.endsWith('upx')) {
|
||||
return uni.upx2px(parseFloat(value));
|
||||
}
|
||||
if (value.endsWith('px')) {
|
||||
return parseFloat(value) || 0;
|
||||
}
|
||||
return parseFloat(value) || 0;
|
||||
},
|
||||
onTouchStart(event) {
|
||||
this.$emit('touchstart', event);
|
||||
},
|
||||
onTouchMove(event) {
|
||||
this.$emit('touchmove', event);
|
||||
},
|
||||
onTouchEnd(event) {
|
||||
this.$emit('touchend', event);
|
||||
},
|
||||
async getCanvasNode(id = this.canvasId, isCanvas = true) {
|
||||
return new Promise((resolve) => {
|
||||
try {
|
||||
// #ifdef APP-NVUE
|
||||
setTimeout(() => {
|
||||
const gcanvas = this.$refs.gcanvas;
|
||||
if (!gcanvas) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
this._isNvue = true;
|
||||
resolve(enable(gcanvas, { bridge: WeexBridge }));
|
||||
}, 100);
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-NVUE
|
||||
uni.createSelectorQuery()
|
||||
.in(this)
|
||||
.select(`#${id}`)
|
||||
.fields(
|
||||
{
|
||||
node: isCanvas,
|
||||
size: true
|
||||
},
|
||||
(res) => {
|
||||
resolve(res || false);
|
||||
}
|
||||
)
|
||||
.exec();
|
||||
// #endif
|
||||
} catch (error) {
|
||||
console.error('获取画布节点失败:', error);
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
},
|
||||
getCanvasElement() {
|
||||
return this._canvasElement || null;
|
||||
},
|
||||
getRawContext() {
|
||||
return this.ctx;
|
||||
},
|
||||
getCanvasContext() {
|
||||
// #ifdef APP-PLUS
|
||||
return uni.createCanvasContext(this.canvasId, this);
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
return this._canvasElement && typeof this._canvasElement.getContext === 'function'
|
||||
? this._canvasElement.getContext('2d')
|
||||
: null;
|
||||
// #endif
|
||||
|
||||
// #ifdef MP || H5
|
||||
return this._canvasElement && typeof this._canvasElement.getContext === 'function'
|
||||
? this._canvasElement.getContext('2d')
|
||||
: null;
|
||||
// #endif
|
||||
},
|
||||
async setNewSize() {
|
||||
const rootNode = await this.getCanvasNode(this.rootId, false);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
if (rootNode.width) {
|
||||
this.widthLocal = rootNode.width;
|
||||
}
|
||||
if (rootNode.height) {
|
||||
this.heightLocal = rootNode.height;
|
||||
}
|
||||
},
|
||||
async initCanvas(force = false) {
|
||||
try {
|
||||
if (this.useRootHeightAndWidth) {
|
||||
await this.setNewSize();
|
||||
}
|
||||
if (this.ctx && !force) {
|
||||
this.$emit('ready', {
|
||||
width: this.actualWidth,
|
||||
height: this.actualHeight
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
this._canvasNode = await this.getCanvasNode(this.canvasId);
|
||||
if (!this._canvasNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._selectorResult = this._canvasNode;
|
||||
this._canvasElement = this._canvasNode.node || this._canvasNode;
|
||||
this.dpr = uni.getSystemInfoSync().pixelRatio || 1;
|
||||
|
||||
// #ifdef MP || H5
|
||||
if (this._canvasElement) {
|
||||
this._canvasElement.width = Math.ceil(this.actualWidth * this.dpr);
|
||||
this._canvasElement.height = Math.ceil(this.actualHeight * this.dpr);
|
||||
}
|
||||
// #endif
|
||||
|
||||
this.ctx = this.getCanvasContext();
|
||||
if (!this.ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// #ifdef MP || H5
|
||||
if (typeof this.ctx.setTransform === 'function') {
|
||||
this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);
|
||||
} else if (typeof this.ctx.scale === 'function') {
|
||||
this.ctx.scale(this.dpr, this.dpr);
|
||||
}
|
||||
// #endif
|
||||
|
||||
this.applyFont();
|
||||
this.clearCanvas();
|
||||
this.$emit('ready', {
|
||||
width: this.actualWidth,
|
||||
height: this.actualHeight
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('初始化Canvas失败:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
refresh() {
|
||||
return this.initCanvas(true);
|
||||
},
|
||||
getWidth() {
|
||||
return this.actualWidth;
|
||||
},
|
||||
getHeight() {
|
||||
return this.actualHeight;
|
||||
},
|
||||
clearCanvas() {
|
||||
if (!this.ctx) return;
|
||||
this.clearRect(0, 0, this.actualWidth, this.actualHeight);
|
||||
if (this.bgColor && this.bgColor !== 'transparent') {
|
||||
this.beginPath();
|
||||
this.rect(0, 0, this.actualWidth, this.actualHeight);
|
||||
this.setFillStyle(this.bgColor);
|
||||
this.fill();
|
||||
}
|
||||
this.draw();
|
||||
},
|
||||
callContext(method, ...args) {
|
||||
if (this.ctx && typeof this.ctx[method] === 'function') {
|
||||
return this.ctx[method](...args);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
rect(x, y, width, height) {
|
||||
return this.callContext('rect', x, y, width, height);
|
||||
},
|
||||
clearRect(x, y, width, height) {
|
||||
return this.callContext('clearRect', x, y, width, height);
|
||||
},
|
||||
fillRect(x, y, width, height) {
|
||||
return this.callContext('fillRect', x, y, width, height);
|
||||
},
|
||||
strokeRect(x, y, width, height) {
|
||||
return this.callContext('strokeRect', x, y, width, height);
|
||||
},
|
||||
fill() {
|
||||
return this.callContext('fill');
|
||||
},
|
||||
stroke() {
|
||||
return this.callContext('stroke');
|
||||
},
|
||||
beginPath() {
|
||||
return this.callContext('beginPath');
|
||||
},
|
||||
closePath() {
|
||||
return this.callContext('closePath');
|
||||
},
|
||||
moveTo(x, y) {
|
||||
return this.callContext('moveTo', x, y);
|
||||
},
|
||||
lineTo(x, y) {
|
||||
return this.callContext('lineTo', x, y);
|
||||
},
|
||||
arc(x, y, radius, startAngle, endAngle, anticlockwise = false) {
|
||||
return this.callContext('arc', x, y, radius, startAngle, endAngle, anticlockwise);
|
||||
},
|
||||
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
||||
return this.callContext('bezierCurveTo', cp1x, cp1y, cp2x, cp2y, x, y);
|
||||
},
|
||||
quadraticCurveTo(cpx, cpy, x, y) {
|
||||
return this.callContext('quadraticCurveTo', cpx, cpy, x, y);
|
||||
},
|
||||
clip() {
|
||||
return this.callContext('clip');
|
||||
},
|
||||
save() {
|
||||
return this.callContext('save');
|
||||
},
|
||||
restore() {
|
||||
return this.callContext('restore');
|
||||
},
|
||||
translate(x, y) {
|
||||
return this.callContext('translate', x, y);
|
||||
},
|
||||
rotate(angle) {
|
||||
return this.callContext('rotate', angle);
|
||||
},
|
||||
scale(x, y) {
|
||||
return this.callContext('scale', x, y);
|
||||
},
|
||||
setFillStyle(color) {
|
||||
if (!this.ctx) return;
|
||||
if (typeof this.ctx.setFillStyle === 'function') {
|
||||
this.ctx.setFillStyle(color);
|
||||
} else {
|
||||
this.ctx.fillStyle = color;
|
||||
}
|
||||
},
|
||||
setStrokeStyle(color) {
|
||||
if (!this.ctx) return;
|
||||
if (typeof this.ctx.setStrokeStyle === 'function') {
|
||||
this.ctx.setStrokeStyle(color);
|
||||
} else {
|
||||
this.ctx.strokeStyle = color;
|
||||
}
|
||||
},
|
||||
setLineWidth(width) {
|
||||
if (!this.ctx) return;
|
||||
if (typeof this.ctx.setLineWidth === 'function') {
|
||||
this.ctx.setLineWidth(width);
|
||||
} else {
|
||||
this.ctx.lineWidth = width;
|
||||
}
|
||||
},
|
||||
setLineCap(lineCap = 'round') {
|
||||
if (!this.ctx) return;
|
||||
if (typeof this.ctx.setLineCap === 'function') {
|
||||
this.ctx.setLineCap(lineCap);
|
||||
} else {
|
||||
this.ctx.lineCap = lineCap;
|
||||
}
|
||||
},
|
||||
setLineJoin(lineJoin = 'round') {
|
||||
if (!this.ctx) return;
|
||||
if (typeof this.ctx.setLineJoin === 'function') {
|
||||
this.ctx.setLineJoin(lineJoin);
|
||||
} else {
|
||||
this.ctx.lineJoin = lineJoin;
|
||||
}
|
||||
},
|
||||
setTextAlign(align = 'left') {
|
||||
if (!this.ctx) return;
|
||||
if (typeof this.ctx.setTextAlign === 'function') {
|
||||
this.ctx.setTextAlign(align);
|
||||
} else {
|
||||
this.ctx.textAlign = align;
|
||||
}
|
||||
},
|
||||
setTextBaseline(baseline = 'alphabetic') {
|
||||
if (!this.ctx) return;
|
||||
if (typeof this.ctx.setTextBaseline === 'function') {
|
||||
this.ctx.setTextBaseline(baseline);
|
||||
} else {
|
||||
this.ctx.textBaseline = baseline;
|
||||
}
|
||||
},
|
||||
setFontSize(fontSize) {
|
||||
this.fontSize = Number(fontSize) || this.fontSize;
|
||||
if (this.ctx && typeof this.ctx.setFontSize === 'function') {
|
||||
this.ctx.setFontSize(this.fontSize);
|
||||
}
|
||||
this.applyFont();
|
||||
},
|
||||
setFont(font) {
|
||||
if (!this.ctx) return;
|
||||
if ('font' in this.ctx) {
|
||||
this.ctx.font = font;
|
||||
return;
|
||||
}
|
||||
const matched = String(font).match(/(\d+(?:\.\d+)?)px/);
|
||||
if (matched) {
|
||||
this.fontSize = Number(matched[1]);
|
||||
}
|
||||
this.applyFont();
|
||||
},
|
||||
setGlobalAlpha(alpha) {
|
||||
if (!this.ctx) return;
|
||||
if (typeof this.ctx.setGlobalAlpha === 'function') {
|
||||
this.ctx.setGlobalAlpha(alpha);
|
||||
} else {
|
||||
this.ctx.globalAlpha = alpha;
|
||||
}
|
||||
},
|
||||
setShadow(offsetX = 0, offsetY = 0, blur = 0, color = 'rgba(0,0,0,0)') {
|
||||
if (!this.ctx) return;
|
||||
if (typeof this.ctx.setShadow === 'function') {
|
||||
this.ctx.setShadow(offsetX, offsetY, blur, color);
|
||||
return;
|
||||
}
|
||||
this.ctx.shadowOffsetX = offsetX;
|
||||
this.ctx.shadowOffsetY = offsetY;
|
||||
this.ctx.shadowBlur = blur;
|
||||
this.ctx.shadowColor = color;
|
||||
},
|
||||
setLineStyle(lineColor, lineWidth) {
|
||||
this.setLineCap('round');
|
||||
this.setLineJoin('round');
|
||||
this.setStrokeStyle(lineColor);
|
||||
this.setLineWidth(lineWidth);
|
||||
},
|
||||
applyFont() {
|
||||
if (!this.ctx) return;
|
||||
const font = `${this.fontWeight === 'normal' ? '' : `${this.fontWeight} `}${this.fontSize}px ${this.fontFamily}`.trim();
|
||||
if ('font' in this.ctx) {
|
||||
this.ctx.font = font;
|
||||
} else if (typeof this.ctx.setFont === 'function') {
|
||||
this.ctx.setFont(font);
|
||||
} else if (typeof this.ctx.setFontSize === 'function') {
|
||||
this.ctx.setFontSize(this.fontSize);
|
||||
}
|
||||
},
|
||||
fillText(text, x, y) {
|
||||
return this.callContext('fillText', String(text), x, y);
|
||||
},
|
||||
measureText(text) {
|
||||
if (this.ctx && typeof this.ctx.measureText === 'function') {
|
||||
return this.ctx.measureText(String(text));
|
||||
}
|
||||
return {
|
||||
width: String(text).length * this.fontSize * 0.6
|
||||
};
|
||||
},
|
||||
createLinearGradient(x0, y0, x1, y1) {
|
||||
if (this.ctx && typeof this.ctx.createLinearGradient === 'function') {
|
||||
return this.ctx.createLinearGradient(x0, y0, x1, y1);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
createRadialGradient(x0, y0, r0, x1, y1, r1) {
|
||||
if (this.ctx && typeof this.ctx.createRadialGradient === 'function') {
|
||||
return this.ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
loadImage(src) {
|
||||
if (this._imageCache[src]) {
|
||||
return Promise.resolve(this._imageCache[src]);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let image = null;
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
image = new GImage();
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
const canvas = this.getCanvasElement();
|
||||
if (canvas && typeof canvas.createImage === 'function') {
|
||||
image = canvas.createImage();
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
image = new Image();
|
||||
image.crossOrigin = 'anonymous';
|
||||
// #endif
|
||||
|
||||
if (!image) {
|
||||
resolve(src);
|
||||
return;
|
||||
}
|
||||
|
||||
image.onload = () => {
|
||||
this._imageCache[src] = image;
|
||||
resolve(image);
|
||||
};
|
||||
image.onerror = reject;
|
||||
image.src = src;
|
||||
});
|
||||
},
|
||||
async drawImage(source, ...args) {
|
||||
if (!this.ctx || typeof this.ctx.drawImage !== 'function') {
|
||||
return false;
|
||||
}
|
||||
if (typeof source !== 'string' || (typeof this.ctx.setFillStyle === 'function' && !this._isNvue)) {
|
||||
this.ctx.drawImage(source, ...args);
|
||||
return true;
|
||||
}
|
||||
const image = await this.loadImage(source);
|
||||
this.ctx.drawImage(image, ...args);
|
||||
return true;
|
||||
},
|
||||
draw(isLastDraw = false, callback) {
|
||||
if (this.ctx && typeof this.ctx.draw === 'function') {
|
||||
return this.ctx.draw(isLastDraw, callback);
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
setTimeout(callback, 0);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
toTempFilePath(options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const width = options.width || this.actualWidth;
|
||||
const height = options.height || this.actualHeight;
|
||||
const request = {
|
||||
x: options.x || 0,
|
||||
y: options.y || 0,
|
||||
width,
|
||||
height,
|
||||
destWidth: options.destWidth || width,
|
||||
destHeight: options.destHeight || height,
|
||||
fileType: options.fileType || 'png',
|
||||
quality: options.quality === undefined ? 1 : options.quality
|
||||
};
|
||||
const success = (res) => {
|
||||
if (typeof options.success === 'function') options.success(res);
|
||||
resolve(res);
|
||||
};
|
||||
const fail = (err) => {
|
||||
if (typeof options.fail === 'function') options.fail(err);
|
||||
reject(err);
|
||||
};
|
||||
const complete = (res) => {
|
||||
if (typeof options.complete === 'function') options.complete(res);
|
||||
};
|
||||
|
||||
// #ifdef H5
|
||||
const canvas = this.getCanvasElement() || (this.ctx && this.ctx.canvas);
|
||||
if (canvas && typeof canvas.toDataURL === 'function') {
|
||||
try {
|
||||
let exportCanvas = canvas;
|
||||
if (
|
||||
request.x !== 0 ||
|
||||
request.y !== 0 ||
|
||||
request.width !== this.actualWidth ||
|
||||
request.height !== this.actualHeight ||
|
||||
request.destWidth !== request.width ||
|
||||
request.destHeight !== request.height
|
||||
) {
|
||||
exportCanvas = document.createElement('canvas');
|
||||
exportCanvas.width = request.destWidth;
|
||||
exportCanvas.height = request.destHeight;
|
||||
const exportCtx = exportCanvas.getContext('2d');
|
||||
exportCtx.drawImage(
|
||||
canvas,
|
||||
request.x * this.dpr,
|
||||
request.y * this.dpr,
|
||||
request.width * this.dpr,
|
||||
request.height * this.dpr,
|
||||
0,
|
||||
0,
|
||||
request.destWidth,
|
||||
request.destHeight
|
||||
);
|
||||
}
|
||||
const mime = request.fileType === 'jpg' || request.fileType === 'jpeg'
|
||||
? 'image/jpeg'
|
||||
: 'image/png';
|
||||
const res = {
|
||||
tempFilePath: exportCanvas.toDataURL(mime, request.quality)
|
||||
};
|
||||
success(res);
|
||||
complete(res);
|
||||
return;
|
||||
} catch (error) {
|
||||
fail(error);
|
||||
complete(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
if (this.ctx && typeof this.ctx.toTempFilePath === 'function') {
|
||||
this.ctx.toTempFilePath(
|
||||
request.x,
|
||||
request.y,
|
||||
request.width,
|
||||
request.height,
|
||||
request.destWidth,
|
||||
request.destHeight,
|
||||
request.fileType,
|
||||
request.quality,
|
||||
(res) => {
|
||||
success(res);
|
||||
complete(res);
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
// #endif
|
||||
|
||||
const canvasNode = this.getCanvasElement();
|
||||
const uniOptions = {
|
||||
...request,
|
||||
canvasId: this.canvasId,
|
||||
success,
|
||||
fail,
|
||||
complete
|
||||
};
|
||||
if (canvasNode) {
|
||||
uniOptions.canvas = canvasNode;
|
||||
}
|
||||
uni.canvasToTempFilePath(uniOptions, this);
|
||||
});
|
||||
},
|
||||
async exportImage(fileType = 'png', quality = 1) {
|
||||
const res = await this.toTempFilePath({
|
||||
fileType,
|
||||
quality,
|
||||
width: this.actualWidth,
|
||||
height: this.actualHeight,
|
||||
destWidth: this.actualWidth,
|
||||
destHeight: this.actualHeight
|
||||
});
|
||||
return res.tempFilePath || res.apFilePath;
|
||||
},
|
||||
getImageData(options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = {
|
||||
canvasId: this.canvasId,
|
||||
x: options.x || 0,
|
||||
y: options.y || 0,
|
||||
width: options.width || this.actualWidth,
|
||||
height: options.height || this.actualHeight,
|
||||
success: (res) => {
|
||||
if (typeof options.success === 'function') options.success(res);
|
||||
resolve(res);
|
||||
},
|
||||
fail: (err) => {
|
||||
if (typeof options.fail === 'function') options.fail(err);
|
||||
reject(err);
|
||||
},
|
||||
complete: options.complete
|
||||
};
|
||||
uni.canvasGetImageData(request, this);
|
||||
});
|
||||
},
|
||||
putImageData(options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = {
|
||||
canvasId: this.canvasId,
|
||||
x: options.x || 0,
|
||||
y: options.y || 0,
|
||||
width: options.width || this.actualWidth,
|
||||
height: options.height || this.actualHeight,
|
||||
data: options.data,
|
||||
success: (res) => {
|
||||
if (typeof options.success === 'function') options.success(res);
|
||||
resolve(res);
|
||||
},
|
||||
fail: (err) => {
|
||||
if (typeof options.fail === 'function') options.fail(err);
|
||||
reject(err);
|
||||
},
|
||||
complete: options.complete
|
||||
};
|
||||
uni.canvasPutImageData(request, this);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.u-canvas {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.u-canvas__canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/carKeyboard.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:53:20
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/carKeyboard.js
|
||||
*/
|
||||
export default {
|
||||
// 车牌号键盘
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CarKeyboardDefaultProps from './carKeyboard'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
import defProps from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CarKeyboardDefaultProps)
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 是否打乱键盘按键的顺序
|
||||
|
||||
@@ -56,11 +56,11 @@
|
||||
hover-class="u-hover-class"
|
||||
:hover-stay-time="200"
|
||||
>
|
||||
<up-icon
|
||||
<u-icon
|
||||
size="28"
|
||||
name="backspace"
|
||||
:color="upThemeVar('--up-main-color', '#303133')"
|
||||
></up-icon>
|
||||
color="#303133"
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -222,25 +222,26 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$u-car-keyboard-background-color: var(--up-bg-color, rgb(224, 228, 230)) !default;
|
||||
@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;
|
||||
$u-car-keyboard-button-inner-background-color:var(--up-card-bg-color, #ffffff) !default;
|
||||
$u-car-keyboard-button-inner-background-color:#FFFFFF !default;
|
||||
$u-car-keyboard-button-height:80rpx !default;
|
||||
$u-car-keyboard-button-inner-box-shadow:0 1px 0px #999992 !default;
|
||||
$u-car-keyboard-button-border-radius:4px !default;
|
||||
$u-car-keyboard-button-inner-margin:8rpx 5rpx !default;
|
||||
$u-car-keyboard-button-text-font-size:16px !default;
|
||||
$u-car-keyboard-button-text-color:var(--up-main-color, #303133) !default;
|
||||
$u-car-keyboard-button-text-color:$u-main-color !default;
|
||||
$u-car-keyboard-center-inner-margin: 0 4rpx !default;
|
||||
$u-car-keyboard-special-button-width:134rpx !default;
|
||||
$u-car-keyboard-lang-font-size:16px !default;
|
||||
$u-car-keyboard-lang-color:var(--up-main-color, #303133) !default;
|
||||
$u-car-keyboard-lang-color:$u-main-color !default;
|
||||
$u-car-keyboard-active-color:$u-primary !default;
|
||||
$u-car-keyboard-line-font-size:15px !default;
|
||||
$u-car-keyboard-line-color:var(--up-main-color, #303133) !default;
|
||||
$u-car-keyboard-line-color:$u-main-color !default;
|
||||
$u-car-keyboard-line-margin:0 1px !default;
|
||||
$u-car-keyboard-u-hover-class-background-color:var(--up-border-color, #BBBCC6) !default;
|
||||
$u-car-keyboard-u-hover-class-background-color:#BBBCC6 !default;
|
||||
|
||||
.u-keyboard {
|
||||
@include flex(column);
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* @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'
|
||||
}
|
||||
}
|
||||
@@ -1,136 +1,140 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CardDefaultProps from './card'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
import defProps from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CardDefaultProps)
|
||||
export const propsCard = defineMixin({
|
||||
props: {
|
||||
// 与屏幕两侧是否留空隙
|
||||
full: {
|
||||
type: Boolean,
|
||||
default: () => defProps.card.full
|
||||
default: false
|
||||
},
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: () => defProps.card.title
|
||||
default: ''
|
||||
},
|
||||
// 标题颜色
|
||||
titleColor: {
|
||||
type: String,
|
||||
default: () => defProps.card.titleColor
|
||||
default: '#303133'
|
||||
},
|
||||
// 标题字体大小
|
||||
titleSize: {
|
||||
type: [Number, String],
|
||||
default: () => defProps.card.titleSize
|
||||
default: '15px'
|
||||
},
|
||||
// 副标题
|
||||
subTitle: {
|
||||
type: String,
|
||||
default: () => defProps.card.subTitle
|
||||
default: ''
|
||||
},
|
||||
// 副标题颜色
|
||||
subTitleColor: {
|
||||
type: String,
|
||||
default: () => defProps.card.subTitleColor
|
||||
default: '#909399'
|
||||
},
|
||||
// 副标题字体大小
|
||||
subTitleSize: {
|
||||
type: [Number, String],
|
||||
default: () => defProps.card.subTitleSize
|
||||
default: '13'
|
||||
},
|
||||
// 是否显示外部边框,只对full=false时有效(卡片与边框有空隙时)
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: () => defProps.card.border
|
||||
default: true
|
||||
},
|
||||
// 用于标识点击了第几个
|
||||
index: {
|
||||
type: [Number, String, Object],
|
||||
default: () => defProps.card.index
|
||||
default: ''
|
||||
},
|
||||
// 用于隔开上下左右的边距,带单位的写法,如:"30px 30px","20px 20px 30px 30px"
|
||||
margin: {
|
||||
type: String,
|
||||
default: () => defProps.card.margin
|
||||
default: '15px'
|
||||
},
|
||||
// card卡片的圆角
|
||||
borderRadius: {
|
||||
type: [Number, String],
|
||||
default: () => defProps.card.borderRadius
|
||||
default: '8px'
|
||||
},
|
||||
// 头部自定义样式,对象形式
|
||||
headStyle: {
|
||||
type: Object,
|
||||
default: () => defProps.card.headStyle
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 主体自定义样式,对象形式
|
||||
bodyStyle: {
|
||||
type: Object,
|
||||
default: () => defProps.card.bodyStyle
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 底部自定义样式,对象形式
|
||||
footStyle: {
|
||||
type: Object,
|
||||
default: () => defProps.card.footStyle
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 头部是否下边框
|
||||
headBorderBottom: {
|
||||
type: Boolean,
|
||||
default: () => defProps.card.headBorderBottom
|
||||
default: true
|
||||
},
|
||||
// 底部是否有上边框
|
||||
footBorderTop: {
|
||||
type: Boolean,
|
||||
default: () => defProps.card.footBorderTop
|
||||
default: true
|
||||
},
|
||||
// 标题左边的缩略图
|
||||
thumb: {
|
||||
type: String,
|
||||
default: () => defProps.card.thumb
|
||||
default: ''
|
||||
},
|
||||
// 缩略图宽高
|
||||
thumbWidth: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.card.thumbWidth
|
||||
default: '30px'
|
||||
},
|
||||
// 缩略图是否为圆形
|
||||
thumbCircle: {
|
||||
type: Boolean,
|
||||
default: () => defProps.card.thumbCircle
|
||||
default: false
|
||||
},
|
||||
// 给head,body,foot的内边距
|
||||
padding: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.card.padding
|
||||
default: '15px'
|
||||
},
|
||||
paddingHead: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.card.paddingHead
|
||||
default: ''
|
||||
},
|
||||
paddingBody: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.card.paddingBody
|
||||
default: ''
|
||||
},
|
||||
paddingFoot: {
|
||||
type: [String, Number],
|
||||
default: () => defProps.card.paddingFoot
|
||||
default: ''
|
||||
},
|
||||
// 是否显示头部
|
||||
showHead: {
|
||||
type: Boolean,
|
||||
default: () => defProps.card.showHead
|
||||
default: true
|
||||
},
|
||||
// 是否显示尾部
|
||||
showFoot: {
|
||||
type: Boolean,
|
||||
default: () => defProps.card.showFoot
|
||||
default: true
|
||||
},
|
||||
// 卡片外围阴影,字符串形式
|
||||
boxShadow: {
|
||||
type: String,
|
||||
default: () => defProps.card.boxShadow
|
||||
default: 'none'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -136,11 +136,13 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-size: 28rpx;
|
||||
background-color: var(--up-card-bg-color, #ffffff);
|
||||
background-color: #ffffff;
|
||||
box-sizing: border-box;
|
||||
|
||||
&-full {
|
||||
|
||||
@@ -1,371 +0,0 @@
|
||||
<template>
|
||||
<up-popup :show="popupShow" mode="bottom" :popup="false"
|
||||
:mask="true" :closeable="closeable" :safe-area-inset-bottom="true"
|
||||
:close-icon-color="closeIconColor" :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">
|
||||
<view v-for="(item, index) in genTabsList" @click="toFatherIndex(index)">
|
||||
<up-steps-item
|
||||
:title="item.name"/>
|
||||
</view>
|
||||
</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" :style="[levelPaneStyle, { 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)
|
||||
* @property {Boolean} closeable 是否显示关闭图标(默认true)
|
||||
*/
|
||||
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
|
||||
},
|
||||
// 是否显示关闭图标
|
||||
closeable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 存储每一级的数据
|
||||
levelList: [],
|
||||
// 存储每一级选中的索引
|
||||
selectedValueIndexs: [],
|
||||
tabsIndex: 0,
|
||||
popupShow: false,
|
||||
// 新增confirmValues用于存储确认的值
|
||||
confirmValues: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
handler() {
|
||||
this.initLevelList();
|
||||
this.setDefaultValue();
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
show() {
|
||||
this.popupShow = this.show;
|
||||
},
|
||||
modelValue: {
|
||||
handler() {
|
||||
// 初始化选中值
|
||||
this.setDefaultValue();
|
||||
},
|
||||
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;
|
||||
},
|
||||
closeIconColor() {
|
||||
return 'var(--up-main-color, #303133)';
|
||||
},
|
||||
levelPaneStyle() {
|
||||
return {
|
||||
backgroundColor: 'var(--up-bg-color, #f7f7f7)'
|
||||
};
|
||||
}
|
||||
},
|
||||
// 新增confirm事件
|
||||
emits: ['update:modelValue', 'update:show', 'change', 'confirm', 'cancel'],
|
||||
methods: {
|
||||
t,
|
||||
getSelectedValues() {
|
||||
const result = [];
|
||||
for (let i = 0; i < this.selectedValueIndexs.length; i++) {
|
||||
const selectedIndex = this.selectedValueIndexs[i];
|
||||
if (selectedIndex === undefined) continue;
|
||||
if (!this.levelList[i] || !this.levelList[i][selectedIndex]) continue;
|
||||
result.push(this.levelList[i][selectedIndex][this.valueKey]);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
initLevelList() {
|
||||
// 初始化第一级数据
|
||||
if (this.data && this.data.length > 0) {
|
||||
this.levelList = [this.data];
|
||||
this.selectedValueIndexs = [];
|
||||
}
|
||||
},
|
||||
setDefaultValue() {
|
||||
// 检查data是否为空
|
||||
if (!this.data || this.data.length == 0) return;
|
||||
// 检查modelValue是否为空
|
||||
if (!this.modelValue || this.modelValue.length == 0) {
|
||||
this.confirmValues = [];
|
||||
return;
|
||||
}
|
||||
// 根据默认值设置选中项
|
||||
// 根据modelValue获取indexs给selectedValueIndexs
|
||||
this.selectedValueIndexs = [];
|
||||
this.levelList = []; // 设置层级数据为空
|
||||
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);
|
||||
this.levelList[i] = currentLevelData; // 设置每一层级的数据
|
||||
|
||||
if (index !== -1) {
|
||||
this.selectedValueIndexs.push(index);
|
||||
// 更新下一级的数据
|
||||
if (currentLevelData[index][this.childrenKey]) {
|
||||
currentLevelData = currentLevelData[index][this.childrenKey];
|
||||
} else {
|
||||
// 如果没有子级数据,则停止处理
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// 如果找不到匹配项,则停止处理
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 同步确认值,避免“仅回显未改动时确认返回空数组”
|
||||
this.confirmValues = this.getSelectedValues();
|
||||
},
|
||||
close() {
|
||||
this.$emit('cancel');
|
||||
this.$emit('update:show', false);
|
||||
},
|
||||
tabsChange(item) {
|
||||
},
|
||||
levelChange(levelIndex, index) {
|
||||
// 设置当前级的选中值
|
||||
this.$set(this.selectedValueIndexs, levelIndex, index);
|
||||
|
||||
// 清除后续级别的选中值
|
||||
this.selectedValueIndexs.splice(levelIndex + 1);
|
||||
this.tabsIndex = Math.min(this.tabsIndex, levelIndex);
|
||||
|
||||
// 清除后续级别的列表
|
||||
this.levelList.splice(levelIndex + 1);
|
||||
|
||||
// 获取当前选中项
|
||||
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();
|
||||
this.handleConfirm();
|
||||
} else {
|
||||
// 否则只触发change事件,不关闭
|
||||
this.emitChange(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 修改emitChange方法,增加closePopup参数
|
||||
emitChange(closePopup = true) {
|
||||
// 构造选中结果
|
||||
const result = this.getSelectedValues();
|
||||
|
||||
// 更新confirmValues
|
||||
this.confirmValues = [...result];
|
||||
|
||||
// 触发change事件,返回value数组
|
||||
this.$emit('change', this.confirmValues);
|
||||
|
||||
// 根据参数决定是否关闭弹窗
|
||||
if (closePopup) {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
handleCancel() {
|
||||
this.close();
|
||||
},
|
||||
handleConfirm() {
|
||||
const values = this.confirmValues.length ? this.confirmValues : this.getSelectedValues();
|
||||
this.confirmValues = [...values];
|
||||
// 确认时触发confirm事件
|
||||
this.$emit('update:modelValue', values);
|
||||
this.$emit('confirm', values);
|
||||
this.close();
|
||||
},
|
||||
// 跳转父节点
|
||||
toFatherIndex(index){
|
||||
this.tabsIndex = index;
|
||||
},
|
||||
}
|
||||
}
|
||||
</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 var(--up-border-color, #eee);
|
||||
}
|
||||
</style>
|
||||
@@ -1,27 +1,22 @@
|
||||
<template>
|
||||
<view class="u-cate-tab" :style="{ height: addUnit(height) }">
|
||||
<view class="u-cate-tab">
|
||||
<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="[innerCurrent == index ? 'u-cate-tab__item-active' : '']"
|
||||
:class="[current == 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-into-view="scrollIntoView"
|
||||
<scroll-view :scroll-top="scrollRightTop" scroll-with-animation
|
||||
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">
|
||||
<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">
|
||||
<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']">
|
||||
@@ -40,25 +35,15 @@
|
||||
</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: () => {
|
||||
@@ -72,43 +57,18 @@
|
||||
itemKeyName: {
|
||||
type: String,
|
||||
default: 'name'
|
||||
},
|
||||
current: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
tabList: {
|
||||
deep: true,
|
||||
handler(newVal, oldVal) {
|
||||
// this.observer();
|
||||
sleep(30).then(() => {
|
||||
this.getMenuItemTop();
|
||||
this.leftMenuStatus(this.innerCurrent);
|
||||
})
|
||||
tabList() {
|
||||
this.getMenuItemTop()
|
||||
}
|
||||
},
|
||||
current(nval) {
|
||||
this.innerCurrent = nval;
|
||||
this.leftMenuStatus(this.innerCurrent);
|
||||
sleep(30).then(() => {
|
||||
this.swichMenu(this.innerCurrent)
|
||||
})
|
||||
},
|
||||
height() {
|
||||
// console.log('height change');
|
||||
this.getMenuItemTop();
|
||||
this.leftMenuStatus(this.innerCurrent);
|
||||
}
|
||||
},
|
||||
emits: ['update:current'],
|
||||
data() {
|
||||
return {
|
||||
scrollTop: 0, //tab标题的滚动条位置
|
||||
scrollIntoView: '', // 滚动至哪个元素
|
||||
oldScrollTop: 0,
|
||||
innerCurrent: 0, // 预设当前项的值
|
||||
current: 0, // 预设当前项的值
|
||||
menuHeight: 0, // 左边菜单的高度
|
||||
menuItemHeight: 0, // 左边菜单item的高度
|
||||
itemId: '', // 栏目右边scroll-view用于滚动的id
|
||||
@@ -119,38 +79,26 @@
|
||||
timer: null, // 定时器
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// this.observer();
|
||||
this.innerCurrent = this.current;
|
||||
this.leftMenuStatus(this.innerCurrent);
|
||||
onMounted() {
|
||||
this.getMenuItemTop()
|
||||
// 设置默认index
|
||||
sleep(50).then(() => {
|
||||
this.swichMenu(this.innerCurrent)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
addUnit,
|
||||
// 点击左边的栏目切换
|
||||
async swichMenu(index) {
|
||||
if (this.mode == 'follow') {
|
||||
if(this.arr.length == 0) {
|
||||
await this.getMenuItemTop();
|
||||
}
|
||||
if (this.scrollIntoView != 'item' + index) {
|
||||
this.scrollIntoView = 'item' + index;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == this.innerCurrent) return;
|
||||
if (index == this.current) return;
|
||||
this.scrollRightTop = this.oldScrollTop;
|
||||
this.$nextTick(function(){
|
||||
this.innerCurrent = index;
|
||||
this.$emit('update:current', index);
|
||||
this.scrollRightTop = this.arr[index];
|
||||
this.current = index;
|
||||
this.leftMenuStatus(index);
|
||||
})
|
||||
},
|
||||
// 获取一个目标元素的高度
|
||||
getElRect(elClass, dataVal) {
|
||||
return new Promise((resolve, reject) => {
|
||||
new Promise((resolve, reject) => {
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select('.' + elClass).fields({
|
||||
size: true
|
||||
@@ -169,73 +117,54 @@
|
||||
},
|
||||
// 观测元素相交状态
|
||||
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);
|
||||
this._observerList.push(observer);
|
||||
// 检测相交状态
|
||||
// 检测右边scroll-view的id为itemxx的元素与u-cate-tab__right-box的相交状态
|
||||
// 如果跟.u-cate-tab__right-box底部相交,就动态设置左边栏目的活动状态
|
||||
observer.relativeTo('.u-cate-tab__right-box', {
|
||||
top: 10
|
||||
}).observe('#item' + index, (res) => {
|
||||
top: 0
|
||||
}).observe('#item' + index, res => {
|
||||
if (res.intersectionRatio > 0) {
|
||||
console.log('res', res);
|
||||
// 修复:确保正确获取索引
|
||||
let id = res.id ? res.id.substring(4) : index;
|
||||
this.leftMenuStatus(parseInt(id));
|
||||
let id = res.id.substring(4);
|
||||
this.leftMenuStatus(id);
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// 设置左边菜单的滚动状态
|
||||
async leftMenuStatus(index) {
|
||||
this.innerCurrent = index;
|
||||
this.$emit('update:current', index);
|
||||
this.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到顶部的距离
|
||||
async getMenuItemTop() {
|
||||
// await this.$nextTick();
|
||||
// console.log('getMenuItemTop')
|
||||
return new Promise(resolve => {
|
||||
getMenuItemTop() {
|
||||
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();
|
||||
}, 100);
|
||||
}, 10);
|
||||
return ;
|
||||
}
|
||||
// console.log(rects)
|
||||
this.rects = rects;
|
||||
this.arr = [];
|
||||
rects.forEach((rect) => {
|
||||
// 这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
|
||||
this.arr.push(rect.top - rects[0].top);
|
||||
})
|
||||
// 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))
|
||||
@@ -266,7 +195,7 @@
|
||||
return ;
|
||||
}
|
||||
}
|
||||
}, 100)
|
||||
}, 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -281,12 +210,11 @@
|
||||
.u-cate-tab__wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.u-search-inner {
|
||||
background-color: var(--up-bg-color, rgb(234, 234, 234));
|
||||
background-color: rgb(234, 234, 234);
|
||||
border-radius: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -306,23 +234,23 @@
|
||||
|
||||
.u-cate-tab__item {
|
||||
height: 110rpx;
|
||||
background: var(--up-bg-color, #f6f6f6);
|
||||
background: #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 26rpx;
|
||||
color: var(--up-content-color, #444);
|
||||
color: #444;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.u-cate-tab__item-active {
|
||||
position: relative;
|
||||
color: var(--up-main-color, #000);
|
||||
color: #000;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
background: var(--up-card-bg-color, #fff);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.u-cate-tab__item-active::before {
|
||||
@@ -340,7 +268,7 @@
|
||||
|
||||
.u-cate-tab__right-box {
|
||||
flex: 1;
|
||||
background-color: var(--up-page-bg-color, var(--up-bg-color, rgb(250, 250, 250)));
|
||||
background-color: rgb(250, 250, 250);
|
||||
}
|
||||
|
||||
.u-cate-tab__page-view {
|
||||
@@ -349,8 +277,7 @@
|
||||
|
||||
.u-cate-tab__page-item {
|
||||
margin-bottom: 30rpx;
|
||||
background-color: var(--up-card-bg-color, #fff);
|
||||
border: 1px solid var(--up-border-color, #e5e7eb);
|
||||
background-color: #fff;
|
||||
padding: 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
@@ -361,14 +288,14 @@
|
||||
|
||||
.item-title {
|
||||
font-size: 26rpx;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
color: $u-main-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.item-menu-name {
|
||||
font-weight: normal;
|
||||
font-size: 24rpx;
|
||||
color: var(--up-main-color, $u-main-color);
|
||||
color: $u-main-color;
|
||||
}
|
||||
|
||||
.item-container {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/cellGroup.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:54:16
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/cellGroup.js
|
||||
*/
|
||||
export default {
|
||||
// cell-group组件的props
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CellGroupDefaultProps from './cellGroup'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CellGroupDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 分组标题
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<view :style="[groupStyle, addStyle(customStyle)]" :class="[customClass]" class="u-cell-group">
|
||||
<view :style="[addStyle(customStyle)]" :class="[customClass]" class="u-cell-group">
|
||||
<view v-if="title" class="u-cell-group__title">
|
||||
<slot name="title">
|
||||
<text class="u-cell-group__title__text" :style="{ color: upThemeVar('--up-main-color', '#303133') }">{{ title }}</text>
|
||||
<text class="u-cell-group__title__text">{{ title }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
<view class="u-cell-group__wrapper">
|
||||
@@ -32,14 +32,6 @@
|
||||
export default {
|
||||
name: 'u-cell-group',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
computed: {
|
||||
groupStyle() {
|
||||
const fallbackBg = this.upThemeIsDark ? '#1c1c1e' : '#ffffff'
|
||||
return {
|
||||
backgroundColor: this.upThemeVar('--up-card-bg-color', fallbackBg)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addStyle
|
||||
}
|
||||
@@ -47,6 +39,7 @@
|
||||
</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;
|
||||
@@ -71,3 +64,4 @@
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/cell.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-23 20:53:09
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/cell.js
|
||||
*/
|
||||
export default {
|
||||
// cell组件的props
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CellDefaultProps from './cell'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CellDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 标题
|
||||
|
||||
@@ -7,34 +7,32 @@
|
||||
<view class="u-cell__left-icon-wrap" v-if="$slots.icon || icon">
|
||||
<slot name="icon" v-if="$slots.icon">
|
||||
</slot>
|
||||
<up-icon v-else :name="icon"
|
||||
<u-icon v-else :name="icon"
|
||||
:custom-style="iconStyle"
|
||||
:size="size === 'large' ? 22 : 18"></up-icon>
|
||||
:size="size === 'large' ? 22 : 18"></u-icon>
|
||||
</view>
|
||||
<view class="u-cell__title">
|
||||
<!-- 将slot与默认内容用if/else分开主要是因为微信小程序不支持slot嵌套传递,这样才能解决collapse组件的slot不失效问题,label暂时未用到。 -->
|
||||
<slot name="title" v-if="$slots.title || !title">
|
||||
</slot>
|
||||
<text v-else class="u-cell__title-text" :style="[cellTitleDynamicStyle, titleTextStyle]"
|
||||
<text v-else class="u-cell__title-text" :style="[titleTextStyle]"
|
||||
:class="[required && 'u-cell--required', disabled && 'u-cell--disabled', size === 'large' && 'u-cell__title-text--large']">{{ title }}</text>
|
||||
<slot name="label">
|
||||
<text class="u-cell__label" v-if="label"
|
||||
:style="[cellLabelDynamicStyle]"
|
||||
:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__label--large']">{{ label }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
<slot name="value">
|
||||
<text class="u-cell__value"
|
||||
:style="[cellValueDynamicStyle]"
|
||||
:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__value--large']"
|
||||
v-if="!testEmpty(value)">{{ value }}</text>
|
||||
</slot>
|
||||
<view class="u-cell__right-icon-wrap" v-if="$slots['right-icon'] || isLink"
|
||||
:class="[`u-cell__right-icon-wrap--${arrowDirection}`]">
|
||||
<up-icon v-if="rightIcon && !$slots['right-icon']" :name="rightIcon"
|
||||
:custom-style="rightIconStyle" :color="disabled ? cellDisabledColor : 'info'"
|
||||
:size="size === 'large' ? 18 : 16"></up-icon>
|
||||
<u-icon v-if="rightIcon && !$slots['right-icon']" :name="rightIcon"
|
||||
:custom-style="rightIconStyle" :color="disabled ? '#c8c9cc' : 'info'"
|
||||
:size="size === 'large' ? 18 : 16"></u-icon>
|
||||
<slot v-else name="right-icon">
|
||||
</slot>
|
||||
</view>
|
||||
@@ -93,24 +91,6 @@
|
||||
computed: {
|
||||
titleTextStyle() {
|
||||
return addStyle(this.titleStyle)
|
||||
},
|
||||
cellDisabledColor() {
|
||||
return this.upThemeVar('--up-disabled-color', '#c8c9cc')
|
||||
},
|
||||
cellTitleDynamicStyle() {
|
||||
return {
|
||||
color: this.upThemeVar('--up-main-color', '#303133')
|
||||
}
|
||||
},
|
||||
cellLabelDynamicStyle() {
|
||||
return {
|
||||
color: this.upThemeVar('--up-tips-color', '#909399')
|
||||
}
|
||||
},
|
||||
cellValueDynamicStyle() {
|
||||
return {
|
||||
color: this.upThemeVar('--up-content-color', '#606266')
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ['click'],
|
||||
@@ -133,6 +113,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
$u-cell-padding: 13px 15px !default;
|
||||
$u-cell-font-size: 15px !default;
|
||||
@@ -147,8 +128,8 @@
|
||||
$u-cell-label-line-height: 18px !default;
|
||||
$u-cell-value-font-size: 14px !default;
|
||||
$u-cell-value-color: $u-content-color !default;
|
||||
$u-cell-clickable-color: var(--up-hover-bg-color, #f1f1f1) !default;
|
||||
$u-cell-disabled-color: $u-disabled-color !default;
|
||||
$u-cell-clickable-color: $u-bg-color !default;
|
||||
$u-cell-disabled-color: #c8c9cc !default;
|
||||
$u-cell-padding-top-large: 13px !default;
|
||||
$u-cell-padding-bottom-large: 13px !default;
|
||||
$u-cell-value-font-size-large: 15px !default;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/checkboxGroup.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:54:47
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/checkboxGroup.js
|
||||
*/
|
||||
export default {
|
||||
// checkbox-group组件
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CheckboxGroupDefaultProps from './checkboxGroup'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
import defProps from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CheckboxGroupDefaultProps)
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 标识符
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
/**
|
||||
* checkboxGroup 复选框组
|
||||
* @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/checkbox.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/checkbox.html
|
||||
* @property {String} name 标识符
|
||||
* @property {Array} value 绑定的值
|
||||
* @property {String} shape 形状,circle-圆形,square-方形 (默认 'square' )
|
||||
@@ -109,16 +109,14 @@
|
||||
this.$emit("input", values);
|
||||
// #endif
|
||||
// 放在最后更新,否则change事件传出去的values不会更新
|
||||
this.$emit('change', values, {
|
||||
isChecked: childInstance.$data['isChecked'],
|
||||
name: childInstance.$props['name'] // 当前变动的checkbox的name值
|
||||
})
|
||||
this.$emit('change', values)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-checkbox-group {
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/checkbox.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-23 21:06:59
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/checkbox.js
|
||||
*/
|
||||
export default {
|
||||
// checkbox组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CheckboxDefaultProps from './checkbox'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CheckboxDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// checkbox的名称
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
:class="iconClasses"
|
||||
:style="[iconWrapStyle]"
|
||||
>
|
||||
<slot name="icon" :elIconSize="elIconSize" :elIconColor="elIconColor" :checked="isChecked" :elDisabled="elDisabled">
|
||||
<up-icon
|
||||
<slot name="icon" :elIconSize="elIconSize" :elIconColor="elIconColor">
|
||||
<u-icon
|
||||
class="u-checkbox__icon-wrap__icon"
|
||||
name="checkbox-mark"
|
||||
:size="elIconSize"
|
||||
@@ -111,16 +111,16 @@
|
||||
},
|
||||
// 组件选中激活时的颜色
|
||||
elActiveColor() {
|
||||
return this.activeColor ? this.activeColor : (this.parentData.activeColor ? this.parentData.activeColor : this.upThemeVar('--up-primary', '#2979ff'));
|
||||
return this.activeColor ? this.activeColor : (this.parentData.activeColor ? this.parentData.activeColor : '#2979ff');
|
||||
},
|
||||
// 组件选未中激活时的颜色
|
||||
elInactiveColor() {
|
||||
return this.inactiveColor ? this.inactiveColor : (this.parentData.inactiveColor ? this.parentData.inactiveColor :
|
||||
this.upThemeVar('--up-border-color', '#c8c9cc'));
|
||||
'#c8c9cc');
|
||||
},
|
||||
// label的颜色
|
||||
elLabelColor() {
|
||||
return this.labelColor ? this.labelColor : (this.parentData.labelColor ? this.parentData.labelColor : this.upThemeVar('--up-content-color', '#606266'))
|
||||
return this.labelColor ? this.labelColor : (this.parentData.labelColor ? this.parentData.labelColor : '#606266')
|
||||
},
|
||||
// 组件的形状
|
||||
elShape() {
|
||||
@@ -133,7 +133,7 @@
|
||||
},
|
||||
elIconColor() {
|
||||
const iconColor = this.iconColor ? this.iconColor : (this.parentData.iconColor ? this.parentData.iconColor :
|
||||
this.upThemeVar('--up-card-bg-color', '#ffffff'));
|
||||
'#ffffff');
|
||||
// 图标的颜色
|
||||
if (this.elDisabled) {
|
||||
// disabled状态下,已勾选的checkbox图标改为elInactiveColor
|
||||
@@ -149,12 +149,8 @@
|
||||
if (this.elDisabled) {
|
||||
classes.push('u-checkbox__icon-wrap--disabled')
|
||||
}
|
||||
if (this.isChecked) {
|
||||
if (this.elDisabled) {
|
||||
if (this.isChecked && this.elDisabled) {
|
||||
classes.push('u-checkbox__icon-wrap--disabled--checked')
|
||||
} else {
|
||||
classes.push('u-checkbox__icon-wrap--checked')
|
||||
}
|
||||
}
|
||||
// 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
|
||||
// #ifdef MP-ALIPAY || MP-TOUTIAO
|
||||
@@ -165,7 +161,7 @@
|
||||
iconWrapStyle() {
|
||||
// checkbox的整体样式
|
||||
const style = {}
|
||||
style.backgroundColor = this.isChecked && !this.elDisabled ? this.elActiveColor : this.upThemeVar('--up-card-bg-color', '#ffffff')
|
||||
style.backgroundColor = this.isChecked && !this.elDisabled ? this.elActiveColor : '#ffffff'
|
||||
style.borderColor = this.isChecked && !this.elDisabled ? this.elActiveColor : this.elInactiveColor
|
||||
style.width = addUnit(this.elSize)
|
||||
style.height = addUnit(this.elSize)
|
||||
@@ -181,7 +177,7 @@
|
||||
const style = {}
|
||||
if (!this.usedAlone) {
|
||||
if (this.parentData.borderBottom && this.parentData.placement === 'row') {
|
||||
error('检测到您将borderBottom设置为true,需要同时将up-checkbox-group的placement设置为column才有效')
|
||||
error('检测到您将borderBottom设置为true,需要同时将u-checkbox-group的placement设置为column才有效')
|
||||
}
|
||||
// 当父组件设置了显示下边框并且排列形式为纵向时,给内容和边框之间加上一定间隔
|
||||
if (this.parentData.borderBottom && this.parentData.placement === 'column') {
|
||||
@@ -201,14 +197,13 @@
|
||||
// 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
|
||||
this.updateParentData()
|
||||
if (!this.parent) {
|
||||
error('up-checkbox必须搭配up-checkbox-group组件使用')
|
||||
error('u-checkbox必须搭配u-checkbox-group组件使用')
|
||||
}
|
||||
let value = '';
|
||||
// #ifdef VUE2
|
||||
value = this.parentData.value
|
||||
const value = this.parentData.value
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
value = this.parentData.modelValue
|
||||
const value = this.parentData.modelValue
|
||||
// #endif
|
||||
// 设置初始化时,是否默认选中的状态,父组件u-checkbox-group的value可能是array,所以额外判断
|
||||
if (this.checked) {
|
||||
@@ -253,9 +248,7 @@
|
||||
}
|
||||
},
|
||||
emitEvent() {
|
||||
this.$emit('change', this.isChecked, {
|
||||
name: this.name
|
||||
})
|
||||
this.$emit('change', this.isChecked)
|
||||
// 双向绑定
|
||||
if (this.usedAlone) {
|
||||
this.$emit('update:checked', this.isChecked)
|
||||
@@ -288,6 +281,7 @@
|
||||
</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;
|
||||
@@ -298,7 +292,7 @@
|
||||
$u-checkbox-icon-wrap-checked-color:#fff !default;
|
||||
$u-checkbox-icon-wrap-checked-background-color:red !default;
|
||||
$u-checkbox-icon-wrap-checked-border-color:#2979ff !default;
|
||||
$u-checkbox-icon-wrap-disabled-background-color:var(--up-bg-color, #ebedf0) !default;
|
||||
$u-checkbox-icon-wrap-disabled-background-color:#ebedf0 !default;
|
||||
$u-checkbox-icon-wrap-disabled-checked-color:#c8c9cc !default;
|
||||
$u-checkbox-label-margin-left:5px !default;
|
||||
$u-checkbox-label-margin-right:12px !default;
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
<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: itemWidth, 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: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
this.currentIndex = newVal
|
||||
}
|
||||
}
|
||||
},
|
||||
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>
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/circleProgress.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:55:02
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/circleProgress.js
|
||||
*/
|
||||
export default {
|
||||
// circleProgress 组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CircleProgressDefaultProps from './circleProgress'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CircleProgressDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
percentage: {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
/**
|
||||
* CircleProgress 圆形进度条 TODO: 待完善
|
||||
* @description 展示操作或任务的当前进度,比如上传文件,是一个圆形的进度环。
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/circleProgress.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/circleProgress.html
|
||||
* @property {String | Number} percentage 圆环进度百分比值,为数值类型,0-100 (默认 30 )
|
||||
* @example
|
||||
*/
|
||||
@@ -112,6 +112,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-circle-progress {
|
||||
@include flex(row);
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
<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: var(--up-tips-color, #909399);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.u-location-city,
|
||||
.list__item__city-name {
|
||||
color: var(--up-main-color, #303133);
|
||||
}
|
||||
.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;
|
||||
color: var(--up-main-color, #303133);
|
||||
border: 1px solid var(--up-border-color, #ededed);
|
||||
background-color: var(--up-card-bg-color, #ffffff);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/codeInput.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:55:58
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/codeInput.js
|
||||
*/
|
||||
export default {
|
||||
// codeInput 组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CodeInputDefaultProps from './codeInput'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CodeInputDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 键盘弹起时,是否自动上推页面
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
/**
|
||||
* CodeInput 验证码输入
|
||||
* @description 该组件一般用于验证用户短信验证码的场景,也可以结合uview-plus的键盘组件使用
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/codeInput.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/codeInput.html
|
||||
* @property {String | Number} maxlength 最大输入长度 (默认 6 )
|
||||
* @property {Boolean} dot 是否用圆点填充 (默认 false )
|
||||
* @property {String} mode 显示模式,box-盒子模式,line-底部横线模式 (默认 'box' )
|
||||
@@ -220,6 +220,7 @@
|
||||
</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;
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/code.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:55:27
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/code.js
|
||||
*/
|
||||
import { t } from '../../libs/i18n'
|
||||
|
||||
export default {
|
||||
// code 组件
|
||||
code: {
|
||||
seconds: 60,
|
||||
startText: t("up.code.send"),
|
||||
changeText: t("up.code.resendAfter"),
|
||||
endText: t("up.code.resend"),
|
||||
startText: '获取验证码',
|
||||
changeText: 'X秒重新获取',
|
||||
endText: '重新获取',
|
||||
keepRunning: false,
|
||||
uniqueKey: ''
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CodeDefaultProps from './code'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CodeDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 倒计时总秒数
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/**
|
||||
* Code 验证码输入框
|
||||
* @description 考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件 不提供界面显示,只提供提示语,由用户将提示语嵌入到具体的场景
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/code.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/code.html
|
||||
* @property {String | Number} seconds 倒计时所需的秒数(默认 60 )
|
||||
* @property {String} startText 开始前的提示语,见官网说明(默认 '获取验证码' )
|
||||
* @property {String} changeText 倒计时期间的提示语,必须带有字母"x",见官网说明(默认 'X秒重新获取' )
|
||||
@@ -128,4 +128,5 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
</style>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/col.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:56:12
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/col.js
|
||||
*/
|
||||
export default {
|
||||
// col 组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import ColDefaultProps from './col'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(ColDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 占父容器宽度的多少等分,总分为12份
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* CodeInput 栅格系统的列
|
||||
* @description 该组件一般用于Layout 布局 通过基础的 12 分栏,迅速简便地创建布局
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/Layout.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/Layout.html
|
||||
* @property {String | Number} span 栅格占据的列数,总12等份 (默认 12 )
|
||||
* @property {String | Number} offset 分栏左边偏移,计算方式与span相同 (默认 0 )
|
||||
* @property {String} justify 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`) (默认 'start' )
|
||||
@@ -100,6 +100,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-col {
|
||||
padding: 0;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/collapseItem.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:56:42
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/collapseItem.js
|
||||
*/
|
||||
export default {
|
||||
// collapseItem 组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CollapseItemDefaultProps from './collapseItem'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CollapseItemDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 标题
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</template>
|
||||
<template #icon>
|
||||
<slot name="icon">
|
||||
<up-icon v-if="!$slots.icon && icon" :size="22" :name="icon"></up-icon>
|
||||
<u-icon v-if="!$slots.icon && icon" :size="22" :name="icon"></u-icon>
|
||||
</slot>
|
||||
</template>
|
||||
<template #value>
|
||||
@@ -36,7 +36,7 @@
|
||||
</template>
|
||||
<template #right-icon>
|
||||
<template v-if="showRight">
|
||||
<up-icon v-if="!$slots['right-icon']" :size="16" name="arrow-right"></up-icon>
|
||||
<u-icon v-if="!$slots['right-icon']" :size="16" name="arrow-right"></u-icon>
|
||||
<slot name="right-icon">
|
||||
</slot>
|
||||
</template>
|
||||
@@ -71,7 +71,7 @@
|
||||
/**
|
||||
* collapseItem 折叠面板Item
|
||||
* @description 通过折叠面板收纳内容区域(搭配u-collapse使用)
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/collapse.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/collapse.html
|
||||
* @property {String} title 标题
|
||||
* @property {String} value 标题右侧内容
|
||||
* @property {String} label 标题下方的描述信息
|
||||
@@ -201,7 +201,7 @@
|
||||
// 查询内容高度
|
||||
queryRect() {
|
||||
// #ifndef APP-NVUE
|
||||
// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://uview-plus.jiangruyi.com/js/getRect.html
|
||||
// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://ijry.github.io/uview-plus/js/getRect.html
|
||||
// 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同
|
||||
return new Promise(resolve => {
|
||||
this.$uGetRect(`#${this.elId}`).then(size => {
|
||||
@@ -225,6 +225,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-collapse-item {
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/collapse.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:56:30
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/collapse.js
|
||||
*/
|
||||
export default {
|
||||
// collapse 组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CollapseDefaultProps from './collapse'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CollapseDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 当前展开面板的name,非手风琴模式:[<string | number>],手风琴模式:string | number
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/**
|
||||
* collapse 折叠面板
|
||||
* @description 通过折叠面板收纳内容区域
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/collapse.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/collapse.html
|
||||
* @property {String | Number | Array} value 当前展开面板的name,非手风琴模式:[<string | number>],手风琴模式:string | number
|
||||
* @property {Boolean} accordion 是否手风琴模式( 默认 false )
|
||||
* @property {Boolean} border 是否显示外边框 ( 默认 true )
|
||||
@@ -87,4 +87,5 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/columnNotice.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:57:16
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/columnNotice.js
|
||||
*/
|
||||
export default {
|
||||
// columnNotice 组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import ColumnNoticeDefaultProps from './columnNotice'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(ColumnNoticeDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 显示的内容,字符串
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
class="u-notice__left-icon"
|
||||
v-if="icon"
|
||||
>
|
||||
<up-icon
|
||||
<u-icon
|
||||
:name="icon"
|
||||
:color="color"
|
||||
size="19"
|
||||
></up-icon>
|
||||
></u-icon>
|
||||
</view>
|
||||
</slot>
|
||||
<swiper
|
||||
@@ -40,19 +40,19 @@
|
||||
class="u-notice__right-icon"
|
||||
v-if="['link', 'closable'].includes(mode)"
|
||||
>
|
||||
<up-icon
|
||||
<u-icon
|
||||
v-if="mode === 'link'"
|
||||
name="arrow-right"
|
||||
:size="17"
|
||||
:color="color"
|
||||
></up-icon>
|
||||
<up-icon
|
||||
></u-icon>
|
||||
<u-icon
|
||||
v-if="mode === 'closable'"
|
||||
name="close"
|
||||
:size="16"
|
||||
:color="color"
|
||||
@click="close"
|
||||
></up-icon>
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -66,7 +66,7 @@
|
||||
/**
|
||||
* ColumnNotice 滚动通知中的垂直滚动 内部组件
|
||||
* @description 该组件用于滚动通告场景,是其中的垂直滚动方式
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/noticeBar.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/noticeBar.html
|
||||
* @property {Array} text 显示的内容,字符串
|
||||
* @property {String} icon 是否显示左侧的音量图标 ( 默认 'volume' )
|
||||
* @property {String} mode 通告模式,link-显示右箭头,closable-显示右侧关闭图标
|
||||
@@ -128,6 +128,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-notice {
|
||||
@include flex;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<template>
|
||||
<view @click="handleClick">
|
||||
<slot>{{ t("up.common.copy") }}</slot>
|
||||
<slot>复制</slot>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { t } from '../../libs/i18n'
|
||||
export default {
|
||||
name: "up-copy",
|
||||
props: {
|
||||
@@ -18,17 +17,16 @@ export default {
|
||||
},
|
||||
notice: {
|
||||
type: String,
|
||||
default: t("up.common.copy") + t("up.common.success")
|
||||
default: '复制成功'
|
||||
}
|
||||
},
|
||||
emits: ['success'],
|
||||
methods: {
|
||||
t,
|
||||
handleClick() {
|
||||
let content = this.content;
|
||||
if (!content) {
|
||||
uni.showToast({
|
||||
title: t("up.common.none"),
|
||||
title: '暂无',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
@@ -36,7 +34,7 @@ export default {
|
||||
}
|
||||
content = typeof content === 'string' ? content : content.toString() // 复制内容,必须字符串,数字需要转换为字符串
|
||||
/**
|
||||
* 复制逻辑
|
||||
* 小程序端 和 app端的复制逻辑
|
||||
*/
|
||||
let that = this;
|
||||
uni.setClipboardData({
|
||||
@@ -44,7 +42,7 @@ export default {
|
||||
success: function() {
|
||||
if (that.alertStyle == 'modal') {
|
||||
uni.showModal({
|
||||
title: "up.common.tip",
|
||||
title: '提示',
|
||||
content: that.notice
|
||||
});
|
||||
} else {
|
||||
@@ -57,7 +55,7 @@ export default {
|
||||
},
|
||||
fail:function(){
|
||||
uni.showToast({
|
||||
title: t("up.common.copy") + t("up.common.fail"),
|
||||
title: '复制失败',
|
||||
icon: 'none',
|
||||
duration:3000,
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/countDown.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 17:11:29
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/countDown.js
|
||||
*/
|
||||
export default {
|
||||
// u-count-down 计时器组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CountDownDefaultProps from './countDown'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CountDownDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 倒计时时长,单位ms
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<view class="u-count-down">
|
||||
<slot :days="timeData.days" :hours="timeData.hours"
|
||||
:minutes="timeData.minutes" :seconds="timeData.seconds">
|
||||
<slot>
|
||||
<text class="u-count-down__text">{{ formattedTime }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
@@ -113,7 +112,6 @@
|
||||
this.remainTime = remain
|
||||
// 根据剩余的毫秒时间,得出该有天,小时,分钟等的值,返回一个对象
|
||||
const timeData = parseTimeData(remain)
|
||||
this.timeData = timeData;
|
||||
this.$emit('change', timeData)
|
||||
// 得出格式化后的时间
|
||||
this.formattedTime = parseFormat(this.format, timeData)
|
||||
@@ -153,6 +151,7 @@
|
||||
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;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/countTo.js
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-20 16:57:32
|
||||
* @FilePath : /u-view2.0/uview-ui/libs/config/props/countTo.js
|
||||
*/
|
||||
export default {
|
||||
// countTo 组件
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import CountToDefaultProps from './countTo'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(CountToDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 开始的数值,默认从0增长到某一个数
|
||||
|
||||
@@ -17,7 +17,7 @@ import { addUnit } from '../../libs/function/index';
|
||||
/**
|
||||
* countTo 数字滚动
|
||||
* @description 该组件一般用于需要滚动数字到某一个值的场景,目标要求是一个递增的值。
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/countTo.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/countTo.html
|
||||
* @property {String | Number} startVal 开始的数值,默认从0增长到某一个数(默认 0 )
|
||||
* @property {String | Number} endVal 要滚动的目标数值,必须 (默认 0 )
|
||||
* @property {String | Number} duration 滚动到目标数值的动画持续时间,单位为毫秒(ms) (默认 2000 )
|
||||
@@ -178,6 +178,8 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-count-num {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-flex;
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
<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;
|
||||
width: 100%;
|
||||
|
||||
&--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;
|
||||
}
|
||||
|
||||
&--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>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,12 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 3.0
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-20 16:44:21
|
||||
* @LastAuthor : jry
|
||||
* @lastTime : 2025-12-19 08:55:21
|
||||
* @FilePath : /uview-plus/libs/config/props/datetimePicker.js
|
||||
* @LastAuthor : LQ
|
||||
* @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: {
|
||||
@@ -23,24 +22,16 @@ export default {
|
||||
maxHour: 23,
|
||||
minMinute: 0,
|
||||
maxMinute: 59,
|
||||
minSecond: 0,
|
||||
maxSecond: 59,
|
||||
filter: null,
|
||||
formatter: null,
|
||||
loading: false,
|
||||
itemHeight: 44,
|
||||
cancelText: t("up.common.cancel"),
|
||||
confirmText: t("up.common.confirm"),
|
||||
cancelText: '取消',
|
||||
confirmText: '确认',
|
||||
cancelColor: '#909193',
|
||||
confirmColor: '#3c9cff',
|
||||
visibleItemCount: 5,
|
||||
closeOnClickOverlay: false,
|
||||
defaultIndex: [],
|
||||
inputBorder: 'surround',
|
||||
disabled: false,
|
||||
disabledColor: '',
|
||||
placeholder: t("up.common.pleaseChoose"),
|
||||
inputProps: {},
|
||||
pageInline: false
|
||||
defaultIndex: []
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,28 +1,15 @@
|
||||
import { defineMixin } from '../../libs/vue'
|
||||
import DatetimePickerDefaultProps from './datetimePicker'
|
||||
import { registerComponentProps } from '../../libs/config/props.js'
|
||||
|
||||
const defProps = registerComponentProps(DatetimePickerDefaultProps)
|
||||
import defProps from '../../libs/config/props.js'
|
||||
export const props = defineMixin({
|
||||
props: {
|
||||
// 是否显示input
|
||||
hasInput: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
inputProps: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
inputBorder: {
|
||||
type: String,
|
||||
default: () => defProps.input.inputBorder
|
||||
default: () => false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: () => defProps.input.disabled
|
||||
default: () => false
|
||||
},
|
||||
disabledColor:{
|
||||
type: String,
|
||||
@@ -30,7 +17,7 @@ export const props = defineMixin({
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: () => defProps.input.placeholder
|
||||
default: () => '请选择'
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
@@ -75,7 +62,7 @@ export const props = defineMixin({
|
||||
type: String,
|
||||
default: () => defProps.datetimePicker.title
|
||||
},
|
||||
// 展示格式,支持date、time、year-month、datetime、datehour、timesecond、datetimesecond
|
||||
// 展示格式,mode=date为日期选择,mode=time为时间选择,mode=year-month为年月选择,mode=datetime为日期时间选择
|
||||
mode: {
|
||||
type: String,
|
||||
default: () => defProps.datetimePicker.mode
|
||||
@@ -92,36 +79,26 @@ export const props = defineMixin({
|
||||
// 最小默认值为前10年
|
||||
default: () => defProps.datetimePicker.minDate
|
||||
},
|
||||
// 可选的最小小时,仅mode=time/timesecond有效
|
||||
// 可选的最小小时,仅mode=time有效
|
||||
minHour: {
|
||||
type: Number,
|
||||
default: () => defProps.datetimePicker.minHour
|
||||
},
|
||||
// 可选的最大小时,仅mode=time/timesecond有效
|
||||
// 可选的最大小时,仅mode=time有效
|
||||
maxHour: {
|
||||
type: Number,
|
||||
default: () => defProps.datetimePicker.maxHour
|
||||
},
|
||||
// 可选的最小分钟,仅mode=time/timesecond有效
|
||||
// 可选的最小分钟,仅mode=time有效
|
||||
minMinute: {
|
||||
type: Number,
|
||||
default: () => defProps.datetimePicker.minMinute
|
||||
},
|
||||
// 可选的最大分钟,仅mode=time/timesecond有效
|
||||
// 可选的最大分钟,仅mode=time有效
|
||||
maxMinute: {
|
||||
type: Number,
|
||||
default: () => defProps.datetimePicker.maxMinute
|
||||
},
|
||||
// 可选的最小秒,仅mode=timesecond有效
|
||||
minSecond: {
|
||||
type: Number,
|
||||
default: () => defProps.datetimePicker.minSecond
|
||||
},
|
||||
// 可选的最大秒,仅mode=timesecond有效
|
||||
maxSecond: {
|
||||
type: Number,
|
||||
default: () => defProps.datetimePicker.maxSecond
|
||||
},
|
||||
// 选项过滤函数
|
||||
filter: {
|
||||
type: [Function, null],
|
||||
@@ -176,21 +153,6 @@ export const props = defineMixin({
|
||||
defaultIndex: {
|
||||
type: Array,
|
||||
default: () => defProps.datetimePicker.defaultIndex
|
||||
},
|
||||
// 是否页面内展示
|
||||
pageInline:{
|
||||
type: Boolean,
|
||||
default: () => defProps.datetimePicker.pageInline
|
||||
},
|
||||
// 蒙层样式样式
|
||||
maskClass: {
|
||||
type: String,
|
||||
defualt: ''
|
||||
},
|
||||
// 蒙层样式样式
|
||||
maskStyle: {
|
||||
type: String,
|
||||
defualt: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,17 +5,20 @@
|
||||
>
|
||||
<slot name="trigger" :value="inputValue">
|
||||
<up-input
|
||||
:placeholder="placeholder"
|
||||
:readonly="!!showByClickInput"
|
||||
border="surround"
|
||||
v-model="inputValue"
|
||||
v-bind="inputPropsInner"
|
||||
:disabled="disabled"
|
||||
:disabledColor="disabledColor"
|
||||
></up-input>
|
||||
<cover-view class="input-cover">
|
||||
</cover-view>
|
||||
<div class="input-cover">
|
||||
</div>
|
||||
</slot>
|
||||
</view>
|
||||
<u-picker
|
||||
ref="picker"
|
||||
:show="pageInline || show || (hasInput && showByClickInput)"
|
||||
:show="show || (hasInput && showByClickInput)"
|
||||
:popupMode="popupMode"
|
||||
:closeOnClickOverlay="closeOnClickOverlay"
|
||||
:columns="columns"
|
||||
@@ -29,9 +32,6 @@
|
||||
:cancelColor="cancelColor"
|
||||
:confirmColor="confirmColor"
|
||||
:toolbarRightSlot="toolbarRightSlot"
|
||||
:pageInline="pageInline"
|
||||
:maskClass="maskClass"
|
||||
:maskStyle="resolvedMaskStyle"
|
||||
@close="close"
|
||||
@cancel="cancel"
|
||||
@confirm="confirm"
|
||||
@@ -61,26 +61,24 @@
|
||||
import { props } from './props';
|
||||
import { mpMixin } from '../../libs/mixin/mpMixin';
|
||||
import { mixin } from '../../libs/mixin/mixin';
|
||||
import dayjs from './dayjs.esm.min.js';
|
||||
import dayjs from 'dayjs/esm/index';
|
||||
import { range, error, padZero } from '../../libs/function/index';
|
||||
import test from '../../libs/function/test';
|
||||
/**
|
||||
* DatetimePicker 时间日期选择器
|
||||
* @description 此选择器用于时间日期
|
||||
* @tutorial https://uview-plus.jiangruyi.com/components/datetimePicker.html
|
||||
* @tutorial https://ijry.github.io/uview-plus/components/datetimePicker.html
|
||||
* @property {Boolean} show 用于控制选择器的弹出与收起 ( 默认 false )
|
||||
* @property {Boolean} showToolbar 是否显示顶部的操作栏 ( 默认 true )
|
||||
* @property {String | Number} modelValue 绑定值
|
||||
* @property {String} title 顶部标题
|
||||
* @property {String} mode 展示格式 mode=date为日期选择,mode=time为时间选择,mode=year-month为年月选择,mode=datetime为日期时间选择,mode=datehour为日期小时选择,mode=timesecond为时分秒选择,mode=datetimesecond为日期时分秒选择 ( 默认 ‘datetime )
|
||||
* @property {String} mode 展示格式 mode=date为日期选择,mode=time为时间选择,mode=year-month为年月选择,mode=datetime为日期时间选择 ( 默认 ‘datetime )
|
||||
* @property {Number} maxDate 可选的最大时间 默认值为后10年
|
||||
* @property {Number} minDate 可选的最小时间 默认值为前10年
|
||||
* @property {Number} minHour 可选的最小小时,仅mode=time/timesecond有效 ( 默认 0 )
|
||||
* @property {Number} maxHour 可选的最大小时,仅mode=time/timesecond有效 ( 默认 23 )
|
||||
* @property {Number} minMinute 可选的最小分钟,仅mode=time/timesecond有效 ( 默认 0 )
|
||||
* @property {Number} maxMinute 可选的最大分钟,仅mode=time/timesecond有效 ( 默认 59 )
|
||||
* @property {Number} minSecond 可选的最小秒,仅mode=timesecond有效 ( 默认 0 )
|
||||
* @property {Number} maxSecond 可选的最大秒,仅mode=timesecond有效 ( 默认 59 )
|
||||
* @property {Number} minHour 可选的最小小时,仅mode=time有效 ( 默认 0 )
|
||||
* @property {Number} maxHour 可选的最大小时,仅mode=time有效 ( 默认 23 )
|
||||
* @property {Number} minMinute 可选的最小分钟,仅mode=time有效 ( 默认 0 )
|
||||
* @property {Number} maxMinute 可选的最大分钟,仅mode=time有效 ( 默认 59 )
|
||||
* @property {Function} filter 选项过滤函数
|
||||
* @property {Function} formatter 选项格式化函数
|
||||
* @property {Boolean} loading 是否显示加载中状态 ( 默认 false )
|
||||
@@ -113,16 +111,7 @@
|
||||
},
|
||||
watch: {
|
||||
show(newValue, oldValue) {
|
||||
if (!newValue && this.hasInput) {
|
||||
this.showByClickInput = false
|
||||
}
|
||||
if (newValue) {
|
||||
// #ifdef VUE3
|
||||
this.innerValue = this.correctValue(this.modelValue)
|
||||
// #endif
|
||||
// #ifdef VUE2
|
||||
this.innerValue = this.correctValue(this.value)
|
||||
// #endif
|
||||
this.updateColumnValue(this.innerValue)
|
||||
}
|
||||
},
|
||||
@@ -145,64 +134,22 @@
|
||||
computed: {
|
||||
// 如果以下这些变量发生了变化,意味着需要重新初始化各列的值
|
||||
propsChange() {
|
||||
return [this.mode, this.maxDate, this.minDate, this.minHour, this.maxHour, this.minMinute, this.maxMinute, this.minSecond, this.maxSecond, this.filter, this.modelValue]
|
||||
},
|
||||
resolvedMaskStyle() {
|
||||
if (this.upHasProp('maskStyle')) {
|
||||
return this.maskStyle || ''
|
||||
}
|
||||
if (this.upThemeIsDark) {
|
||||
const topBottomStrong = 'rgba(28, 28, 30, 0.95)'
|
||||
const topBottomWeak = 'rgba(28, 28, 30, 0.6)'
|
||||
return `background-image: linear-gradient(180deg, ${topBottomStrong}, ${topBottomWeak}), linear-gradient(0deg, ${topBottomStrong}, ${topBottomWeak});`
|
||||
}
|
||||
return ''
|
||||
},
|
||||
// input的props
|
||||
inputPropsInner() {
|
||||
return {
|
||||
border: this.inputBorder,
|
||||
placeholder: this.placeholder,
|
||||
disabled: this.disabled,
|
||||
disabledColor: this.disabledColor,
|
||||
...this.inputProps
|
||||
}
|
||||
return [this.mode, this.maxDate, this.minDate, this.minHour, this.maxHour, this.minMinute, this.maxMinute, this.filter, ]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
// pageInline模式下picker-view常驻显示,在原生端(Android/HarmonyOS)需要
|
||||
// 等待原生picker-view组件完成初始化后再重新设置选中值,否则滚动位置可能不生效
|
||||
if (this.pageInline) {
|
||||
setTimeout(() => {
|
||||
this.updateIndexs(this.innerValue)
|
||||
}, 200)
|
||||
}
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ['close', 'cancel', 'confirm', 'change', 'update:modelValue'],
|
||||
// #endif
|
||||
methods: {
|
||||
toInt(value, fallback = 0) {
|
||||
const num = parseInt(value, 10)
|
||||
return Number.isFinite(num) ? num : fallback
|
||||
},
|
||||
// 按列安全读取 picker 值,避免快速滚动时出现越界/空值
|
||||
safeColumnValue(values, columnIndex, optionIndex, fallback = '') {
|
||||
const column = Array.isArray(values[columnIndex]) ? values[columnIndex] : []
|
||||
if (!column.length) return fallback
|
||||
let index = Number(optionIndex)
|
||||
if (!Number.isFinite(index)) index = 0
|
||||
index = range(0, column.length - 1, index)
|
||||
const value = column[index]
|
||||
return value == null ? fallback : value
|
||||
},
|
||||
getInputValue(newValue) {
|
||||
if (newValue == '' || !newValue || newValue == undefined) {
|
||||
this.inputValue = ''
|
||||
return
|
||||
}
|
||||
if (this.mode === 'time' || this.mode === 'timesecond') {
|
||||
if (this.mode == 'time') {
|
||||
this.inputValue = newValue
|
||||
} else {
|
||||
if (this.format) {
|
||||
@@ -216,21 +163,12 @@
|
||||
case 'year-month':
|
||||
format = 'YYYY-MM'
|
||||
break;
|
||||
case 'datehour':
|
||||
format = 'YYYY-MM-DD HH'
|
||||
break;
|
||||
case 'datetime':
|
||||
format = 'YYYY-MM-DD HH:mm'
|
||||
break;
|
||||
case 'datetimesecond':
|
||||
format = 'YYYY-MM-DD HH:mm:ss'
|
||||
break;
|
||||
case 'time':
|
||||
format = 'HH:mm'
|
||||
break;
|
||||
case 'timesecond':
|
||||
format = 'HH:mm:ss'
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -256,13 +194,7 @@
|
||||
},
|
||||
// 关闭选择器
|
||||
close() {
|
||||
if (this.hasInput) {
|
||||
this.showByClickInput = false
|
||||
}
|
||||
if (this.closeOnClickOverlay) {
|
||||
if (this.hasInput) {
|
||||
this.showByClickInput = false
|
||||
}
|
||||
this.$emit('close')
|
||||
}
|
||||
},
|
||||
@@ -292,13 +224,7 @@
|
||||
},
|
||||
//用正则截取输出值,当出现多组数字时,抛出错误
|
||||
intercept(e,type){
|
||||
if (e === undefined || e === null) {
|
||||
return type ? '0000' : '00'
|
||||
}
|
||||
let judge = String(e).match(/\d+/g)
|
||||
if (!judge || judge.length === 0) {
|
||||
return type ? '0000' : '00'
|
||||
}
|
||||
let judge = e.match(/\d+/g)
|
||||
//判断是否掺杂数字
|
||||
if(judge.length>1){
|
||||
error("请勿在过滤或格式化函数时添加数字")
|
||||
@@ -315,66 +241,30 @@
|
||||
// 列发生变化时触发
|
||||
change(e) {
|
||||
const { indexs, values } = e
|
||||
const safeValues = Array.isArray(values) ? values : []
|
||||
let selectValue = ''
|
||||
if (this.mode === 'time' || this.mode === 'timesecond') {
|
||||
if(this.mode === 'time') {
|
||||
// 根据value各列索引,从各列数组中,取出当前时间的选中值
|
||||
const hourText = this.safeColumnValue(safeValues, 0, indexs[0], padZero(this.minHour))
|
||||
const minuteText = this.safeColumnValue(safeValues, 1, indexs[1], padZero(this.minMinute))
|
||||
let hour = this.toInt(this.intercept(hourText), this.minHour)
|
||||
let minute = this.toInt(this.intercept(minuteText), this.minMinute)
|
||||
let second = this.minSecond
|
||||
hour = range(this.minHour, this.maxHour, hour)
|
||||
minute = range(this.minMinute, this.maxMinute, minute)
|
||||
selectValue = `${padZero(hour)}:${padZero(minute)}`
|
||||
if (this.mode === 'timesecond') {
|
||||
const secondText = this.safeColumnValue(safeValues, 2, indexs[2], padZero(this.minSecond))
|
||||
second = range(this.minSecond, this.maxSecond, this.toInt(this.intercept(secondText), this.minSecond))
|
||||
selectValue = `${selectValue}:${padZero(second)}`
|
||||
}
|
||||
selectValue = `${this.intercept(values[0][indexs[0]])}:${this.intercept(values[1][indexs[1]])}`
|
||||
} else {
|
||||
const validCurrent = dayjs(this.innerValue).isValid() ? dayjs(this.innerValue) : dayjs(this.minDate)
|
||||
const currentYear = validCurrent.year()
|
||||
const currentMonth = validCurrent.month() + 1
|
||||
const currentDate = validCurrent.date()
|
||||
const currentHour = validCurrent.hour()
|
||||
const currentMinute = validCurrent.minute()
|
||||
const currentSecond = validCurrent.second()
|
||||
|
||||
const yearText = this.safeColumnValue(safeValues, 0, indexs[0], `${currentYear}`)
|
||||
const monthText = this.safeColumnValue(safeValues, 1, indexs[1], padZero(currentMonth))
|
||||
// 将选择的值转为数值,比如'03'转为数值的3,'2019'转为数值的2019
|
||||
let year = this.toInt(this.intercept(yearText, 'year'), currentYear)
|
||||
let month = this.toInt(this.intercept(monthText), currentMonth)
|
||||
let hour = 0, minute = 0, second = 0
|
||||
month = range(1, 12, month)
|
||||
const year = parseInt(this.intercept(values[0][indexs[0]],'year'))
|
||||
const month = parseInt(this.intercept(values[1][indexs[1]]))
|
||||
let date = parseInt(values[2] ? this.intercept(values[2][indexs[2]]) : 1)
|
||||
let hour = 0, minute = 0
|
||||
// 此月份的最大天数
|
||||
const maxDate = dayjs(`${year}-${month}`).daysInMonth()
|
||||
const dayText = this.safeColumnValue(safeValues, 2, indexs[2], padZero(Math.min(currentDate, maxDate)))
|
||||
let date = this.toInt(this.intercept(dayText), Math.min(currentDate, maxDate))
|
||||
// year-month模式下,date不会出现在列中,设置为1,为了符合后边需要减1的需求
|
||||
if (this.mode === 'year-month') {
|
||||
date = 1
|
||||
}
|
||||
// 不允许超过maxDate值
|
||||
date = range(1, maxDate, date)
|
||||
if (this.mode === 'datehour' || this.mode === 'datetime' || this.mode === 'datetimesecond') {
|
||||
const hourText = this.safeColumnValue(safeValues, 3, indexs[3], padZero(currentHour))
|
||||
hour = range(0, 23, this.toInt(this.intercept(hourText), currentHour))
|
||||
}
|
||||
if (this.mode === 'datetime' || this.mode === 'datetimesecond') {
|
||||
const minuteText = this.safeColumnValue(safeValues, 4, indexs[4], padZero(currentMinute))
|
||||
minute = range(0, 59, this.toInt(this.intercept(minuteText), currentMinute))
|
||||
}
|
||||
if (this.mode === 'datetimesecond') {
|
||||
const secondText = this.safeColumnValue(safeValues, 5, indexs[5], padZero(currentSecond))
|
||||
second = range(0, 59, this.toInt(this.intercept(secondText), currentSecond))
|
||||
date = Math.min(maxDate, date)
|
||||
if (this.mode === 'datetime') {
|
||||
hour = parseInt(this.intercept(values[3][indexs[3]]))
|
||||
minute = parseInt(this.intercept(values[4][indexs[4]]))
|
||||
}
|
||||
// 转为时间模式
|
||||
selectValue = Number(new Date(year, month - 1, date, hour, minute, second))
|
||||
if (!Number.isFinite(selectValue)) {
|
||||
selectValue = this.correctValue(this.innerValue)
|
||||
}
|
||||
selectValue = Number(new Date(year, month - 1, date, hour, minute))
|
||||
}
|
||||
// 取出准确的合法值,防止超越边界的情况
|
||||
selectValue = this.correctValue(selectValue)
|
||||
@@ -395,44 +285,33 @@
|
||||
this.innerValue = value
|
||||
this.updateColumns()
|
||||
// 延迟执行,等待u-picker组件列数据更新完后再设置选中值索引
|
||||
// pageInline模式下picker-view常驻显示,需额外等待原生组件滚动到位
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.updateIndexs(value)
|
||||
}, this.pageInline ? 100 : 0)
|
||||
})
|
||||
}, 0);
|
||||
},
|
||||
// 更新索引
|
||||
updateIndexs(value) {
|
||||
let values = []
|
||||
const formatter = this.formatter || this.innerFormatter
|
||||
if (this.mode === 'time' || this.mode === 'timesecond') {
|
||||
if (this.mode === 'time') {
|
||||
// 将time模式的时间用:分隔成数组
|
||||
const timeArr = value.split(':')
|
||||
// 使用formatter格式化方法进行管道处理
|
||||
values = [formatter('hour', timeArr[0]), formatter('minute', timeArr[1])]
|
||||
if (this.mode === 'timesecond') {
|
||||
values.push(formatter('second', timeArr[2]))
|
||||
}
|
||||
} else {
|
||||
const date = new Date(value)
|
||||
values = [
|
||||
formatter('year', `${dayjs(value).year()}`),
|
||||
// 月份补0
|
||||
formatter('month', padZero(dayjs(value).month() + 1))
|
||||
]
|
||||
if (this.mode === 'date' || this.mode === 'datehour' || this.mode === 'datetime' || this.mode === 'datetimesecond') {
|
||||
if (this.mode === 'date') {
|
||||
// date模式,需要添加天列
|
||||
values.push(formatter('day', padZero(dayjs(value).date())))
|
||||
}
|
||||
if (this.mode === 'datehour' || this.mode === 'datetime' || this.mode === 'datetimesecond') {
|
||||
values.push(formatter('hour', padZero(dayjs(value).hour())))
|
||||
}
|
||||
if (this.mode === 'datetime' || this.mode === 'datetimesecond') {
|
||||
if (this.mode === 'datetime') {
|
||||
// 数组的push方法,可以写入多个参数
|
||||
values.push(formatter('minute', padZero(dayjs(value).minute())))
|
||||
}
|
||||
if (this.mode === 'datetimesecond') {
|
||||
values.push(formatter('second', padZero(dayjs(value).second())))
|
||||
values.push(formatter('day', padZero(dayjs(value).date())), formatter('hour', padZero(dayjs(value).hour())), formatter('minute', padZero(dayjs(value).minute())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,34 +359,22 @@
|
||||
},
|
||||
// 得出合法的时间
|
||||
correctValue(value) {
|
||||
const isDateMode = !['time', 'timesecond'].includes(this.mode)
|
||||
const isDateMode = this.mode !== 'time'
|
||||
// if (isDateMode && !test.date(value)) {
|
||||
if (isDateMode && !dayjs.unix(value).isValid()) {
|
||||
// 如果是日期类型,但是又没有设置合法的当前时间的话,使用最小时间为当前时间
|
||||
value = this.minDate
|
||||
} else if (!isDateMode && !value) {
|
||||
// 如果是时间类型,而又没有默认值的话,就用最小时间
|
||||
value = this.mode === 'timesecond'
|
||||
? `${padZero(this.minHour)}:${padZero(this.minMinute)}:${padZero(this.minSecond)}`
|
||||
: `${padZero(this.minHour)}:${padZero(this.minMinute)}`
|
||||
value = `${padZero(this.minHour)}:${padZero(this.minMinute)}`
|
||||
}
|
||||
// 时间类型
|
||||
if (!isDateMode) {
|
||||
if (String(value).indexOf(':') === -1) return error('时间错误,请传递如12:24的格式')
|
||||
const timeArr = String(value).split(':')
|
||||
let hour = timeArr[0]
|
||||
let minute = timeArr[1]
|
||||
let second = timeArr[2]
|
||||
let [hour, minute] = value.split(':')
|
||||
// 对时间补零,同时控制在最小值和最大值之间
|
||||
const hourNum = Number(hour)
|
||||
const minuteNum = Number(minute)
|
||||
hour = padZero(range(this.minHour, this.maxHour, Number.isNaN(hourNum) ? this.minHour : hourNum))
|
||||
minute = padZero(range(this.minMinute, this.maxMinute, Number.isNaN(minuteNum) ? this.minMinute : minuteNum))
|
||||
if (this.mode === 'timesecond') {
|
||||
const secondNum = Number(second)
|
||||
second = padZero(range(this.minSecond, this.maxSecond, Number.isNaN(secondNum) ? this.minSecond : secondNum))
|
||||
return `${hour}:${minute}:${second}`
|
||||
}
|
||||
hour = padZero(range(this.minHour, this.maxHour, Number(hour)))
|
||||
minute = padZero(range(this.minMinute, this.maxMinute, Number(minute)))
|
||||
return `${ hour }:${ minute }`
|
||||
} else {
|
||||
// 如果是日期格式,控制在最小日期和最大日期之间
|
||||
@@ -518,8 +385,8 @@
|
||||
},
|
||||
// 获取每列的最大和最小值
|
||||
getRanges() {
|
||||
if (this.mode === 'time' || this.mode === 'timesecond') {
|
||||
const timeColumns = [
|
||||
if (this.mode === 'time') {
|
||||
return [
|
||||
{
|
||||
type: 'hour',
|
||||
range: [this.minHour, this.maxHour],
|
||||
@@ -527,18 +394,11 @@
|
||||
{
|
||||
type: 'minute',
|
||||
range: [this.minMinute, this.maxMinute],
|
||||
},
|
||||
];
|
||||
}
|
||||
]
|
||||
if (this.mode === 'timesecond') {
|
||||
timeColumns.push({
|
||||
type: 'second',
|
||||
range: [this.minSecond, this.maxSecond],
|
||||
})
|
||||
}
|
||||
return timeColumns
|
||||
}
|
||||
const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSecond } = this.getBoundary('max', this.innerValue);
|
||||
const { minYear, minDate, minMonth, minHour, minMinute, minSecond } = this.getBoundary('min', this.innerValue);
|
||||
const { maxYear, maxDate, maxMonth, maxHour, maxMinute, } = this.getBoundary('max', this.innerValue);
|
||||
const { minYear, minDate, minMonth, minHour, minMinute, } = this.getBoundary('min', this.innerValue);
|
||||
const result = [
|
||||
{
|
||||
type: 'year',
|
||||
@@ -560,50 +420,28 @@
|
||||
type: 'minute',
|
||||
range: [minMinute, maxMinute],
|
||||
},
|
||||
{
|
||||
type: 'second',
|
||||
range: [minSecond, maxSecond],
|
||||
},
|
||||
];
|
||||
// 兜底:防止边界计算异常导致日列出现空范围(快速滚动场景)
|
||||
if (result[2] && result[2].type === 'day') {
|
||||
const start = Number(result[2].range[0])
|
||||
const end = Number(result[2].range[1])
|
||||
if (!Number.isFinite(start) || !Number.isFinite(end) || start > end) {
|
||||
const fallbackDays = dayjs(this.innerValue).isValid() ? dayjs(this.innerValue).daysInMonth() : 31
|
||||
result[2].range = [1, fallbackDays]
|
||||
}
|
||||
}
|
||||
if (this.mode === 'date')
|
||||
result.splice(3, 3);
|
||||
if (this.mode === 'datehour')
|
||||
result.splice(4, 2);
|
||||
if (this.mode === 'datetime')
|
||||
result.splice(5, 1);
|
||||
result.splice(3, 2);
|
||||
if (this.mode === 'year-month')
|
||||
result.splice(2, 4);
|
||||
result.splice(2, 3);
|
||||
return result;
|
||||
},
|
||||
// 根据minDate、maxDate、minHour、maxHour等边界值,判断各列的开始和结束边界值
|
||||
getBoundary(type, innerValue) {
|
||||
let value = new Date(innerValue)
|
||||
if(isNaN(value.getTime())){
|
||||
value = new Date()
|
||||
}
|
||||
const value = new Date(innerValue)
|
||||
const boundary = new Date(this[`${type}Date`])
|
||||
const year = dayjs(boundary).year()
|
||||
let month = 1
|
||||
let date = 1
|
||||
let hour = 0
|
||||
let minute = 0
|
||||
let second = 0
|
||||
if (type === 'max') {
|
||||
month = 12
|
||||
// 月份的天数
|
||||
date = dayjs(value).daysInMonth()
|
||||
hour = 23
|
||||
minute = 59
|
||||
second = 59
|
||||
}
|
||||
// 获取边界值,逻辑是:当年达到了边界值(最大或最小年),就检查月允许的最大和最小值,以此类推
|
||||
if (dayjs(value).year() === year) {
|
||||
@@ -614,9 +452,6 @@
|
||||
hour = dayjs(boundary).hour()
|
||||
if (dayjs(value).hour() === hour) {
|
||||
minute = dayjs(boundary).minute()
|
||||
if (dayjs(value).minute() === minute) {
|
||||
second = dayjs(boundary).second()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -626,8 +461,7 @@
|
||||
[`${type}Month`]: month,
|
||||
[`${type}Date`]: date,
|
||||
[`${type}Hour`]: hour,
|
||||
[`${type}Minute`]: minute,
|
||||
[`${type}Second`]: second
|
||||
[`${type}Minute`]: minute
|
||||
}
|
||||
},
|
||||
onShowByClickInput(){
|
||||
@@ -641,6 +475,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
.u-datetime-picker {
|
||||
flex: 1;
|
||||
&__has-input {
|
||||
@@ -662,7 +497,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--up-border-color, #eee);
|
||||
border: 1px solid #eee;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user