Browse Source
Merge branch 'devlop' of http://git.qniao.cn/dengxiongfei/paper-shopkeeper-app into devlop
devlop
Merge branch 'devlop' of http://git.qniao.cn/dengxiongfei/paper-shopkeeper-app into devlop
devlop
47 changed files with 1241 additions and 5600 deletions
Split View
Diff Options
-
10apis/clientCreditApi.js
-
8apis/commonApi.js
-
14apis/mineApi.js
-
1components/qn-footer/qn-footer.vue
-
87components/qn-form-item/qn-form-item.vue
-
25enums/index.js
-
3env/index.js
-
9main.js
-
95pages.json
-
118pages/client-credit/index.vue
-
296pages/enterprise-info/index.vue
-
0pages/fs-credit/index.vue
-
203pages/guarantee-agreement/index.vue
-
2pages/login/index.vue
-
259pages/mine/index.vue
-
181pages/month-credit/index.vue
-
59pages/page-view/index.vue
-
2pages/search/index.vue
-
9pages/toggle-supplier/index.vue
-
BINstatic/imgs/mine/contract-icon.png
-
BINstatic/imgs/mine/credit-icon.png
-
BINstatic/imgs/mine/default-avatar.png
-
BINstatic/imgs/mine/finance-icon.png
-
BINstatic/imgs/mine/money-icon.png
-
BINstatic/imgs/mine/order-icon.png
-
BINstatic/imgs/mine/user-avatar.png
-
52uni_modules/uni-data-picker/changelog.md
-
45uni_modules/uni-data-picker/components/uni-data-picker/keypress.js
-
537uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue
-
563uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js
-
333uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue
-
92uni_modules/uni-data-picker/package.json
-
22uni_modules/uni-data-picker/readme.md
-
85uni_modules/uni-datetime-picker/changelog.md
-
185uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
-
898uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
-
19uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json
-
8uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js
-
19uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json
-
19uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json
-
45uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js
-
927uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
-
981uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
-
410uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
-
90uni_modules/uni-datetime-picker/package.json
-
21uni_modules/uni-datetime-picker/readme.md
-
109utils/index.js
@ -0,0 +1,14 @@ |
|||
import http from '../utils/http/index.js' |
|||
|
|||
/** |
|||
* 纸掌柜获取纸盘商订单统计 |
|||
* @param {object} data |
|||
* @returns 订单统计 |
|||
* swagger:http://api-ops-uec-test.qniao.cn/uec/swagger-ui/index.html?urls.primaryName=CustomerApi#/%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81/authorizeByCaptchaUsingPOST
|
|||
*/ |
|||
export const getOrderStatistics = (data) => { |
|||
return http.get({ |
|||
url: '/base-paper-trading/get/supplier/order-volume-statistics', |
|||
data |
|||
}) |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
<template> |
|||
<view> |
|||
<view v-if="type === 'title'" class="qn-form-item qn-form-item__title"> |
|||
<text class="title">{{ label }}</text> |
|||
</view> |
|||
<view v-if="type === 'item'" class="qn-form-item qn-form-item" :style="{ flexWrap: size === 'large' ? 'wrap' : 'nowrap' }"> |
|||
<view class="label" :style="{ marginTop: size === 'large' ? '18rpx' : '' }"> |
|||
<uni-icons v-if="required" custom-prefix="iconfont" type="icon-required" size="14" color="#F5222D"></uni-icons> |
|||
<text class="label__text">{{ label }}</text> |
|||
</view> |
|||
<view class="value" :style="{ marginTop: size === 'large' ? '10rpx' : '' }"> |
|||
<slot></slot> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
/** |
|||
* @value {string} type 表单类型 title:标题 item:表单项 |
|||
* @value label 表单项名称 |
|||
* @value size 表单项大小 默认normal ,可选值:normal,large |
|||
* @value required 是否必填 默认false |
|||
* |
|||
*/ |
|||
export default { |
|||
props: { |
|||
type: { |
|||
type: String, |
|||
default: 'item' |
|||
}, |
|||
label: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
size: { |
|||
type: String, |
|||
default: 'normal' |
|||
}, |
|||
required: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.qn-form-item { |
|||
width: 750rpx; |
|||
padding: 0rpx 32rpx; |
|||
background-color: #fff; |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
border-bottom: 2rpx solid #d8d8d8; |
|||
min-height: 80rpx; |
|||
.label { |
|||
flex-grow: 0; |
|||
flex-shrink: 0; |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: flex-start; |
|||
margin-right: 20rpx; |
|||
.label__text { |
|||
font-size: 28rpx; |
|||
color: #000000; |
|||
} |
|||
} |
|||
.value { |
|||
flex-grow: 1; |
|||
flex-shrink: 1; |
|||
text-align: right; |
|||
} |
|||
} |
|||
.qn-form-item__title { |
|||
background-color: #f7f8fa; |
|||
padding: 20rpx 32rpx; |
|||
border: none; |
|||
.title { |
|||
font-size: 30rpx; |
|||
color: #888888; |
|||
} |
|||
} |
|||
</style> |
|||
@ -1,11 +1,12 @@ |
|||
import App from './App' |
|||
import pubFn from './common/js/publicFn.js' |
|||
import store from "./store"; |
|||
import store from './store' |
|||
import Vue from 'vue' |
|||
|
|||
Vue.config.productionTip = false |
|||
Vue.prototype.$store = store; |
|||
// Vue.prototype.$store = store
|
|||
App.mpType = 'app' |
|||
const app = new Vue({ |
|||
...App |
|||
...App, |
|||
store |
|||
}) |
|||
app.$mount() |
|||
@ -0,0 +1,203 @@ |
|||
<template> |
|||
<view> |
|||
<uni-nav-bar left-icon="back" @clickLeft="back" statusBar fixed title="担保协议"></uni-nav-bar> |
|||
<text v-html="content" style="white-space: pre-wrap; line-height: 1.5"></text> |
|||
<qn-footer height="120rpx"> |
|||
<view class="button-area"> |
|||
<view class="button button__cancel" @click="back"> |
|||
<text class="text">以后再说</text> |
|||
</view> |
|||
<view class="button button__submit" @click="makeCredit"> |
|||
<text class="text" style="color: white">签约</text> |
|||
</view> |
|||
</view> |
|||
</qn-footer> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
const content = ` |
|||
千鸟互联纸掌柜保证协议 |
|||
|
|||
甲方: 广州千鸟云纸信息科技有限公司 |
|||
注册地址: 广州市白云区钟落潭镇广从七路28号101 |
|||
法定代表人/负责人:邹依倩 |
|||
联系电话: |
|||
|
|||
乙方: |
|||
注册地址: |
|||
法定代表人/负责人: |
|||
联系电话: |
|||
|
|||
鉴于: |
|||
1、甲方广州千鸟云纸信息科技有限公司(以下简称“千鸟云纸”)隶属千鸟互联,千鸟互联旗下产品“纸掌柜”(以下简称“平台”),为原纸贸易商及印刷包装企业提供原纸采销、供应链金融等服务。 |
|||
2、乙方是原纸贸易商,通过平台进行原纸销售,推荐合作的原纸采购商(以下简称“交易客户”)使用平台供应链金融服务。 |
|||
鉴于乙方通过平台进行原纸销售,并将与原纸交易客户账期结算的交易订单,通过乙方的推荐和担保,交易客户通过使用甲方平台的供应链金融产品,将应付货款提前支付给乙方,形成交易客户与平台资金方的债务关系,为保证交易客户对平台资金方债务履行,保障平台资金方对交易客户债权的实现,乙方自愿为交易客户对平台资金方的债务承担连带担保责任。经甲乙双方共同协商,在平等自愿的基础上达成如下协议。 |
|||
|
|||
一、具体合作 |
|||
1、乙方通过平台于 年 月 日与原纸采购商 (以下简称“交易客户”)签订了编号为 的《原纸购销协议》,(以下简称“购销协议”),在交易客户通过使用甲方平台供应链金融产品将应付货款提前支付给乙方时,乙方为交易客户针对购销协议所欠平台资金方的债务承担的连带担保责任同时生效(以下简称“乙方担保”)。 |
|||
2、乙方对交易客户针对购销协议所欠平台资金方的债务承担经济上、法律上的连带责任。担保范围包括债务本金、利息、服务费(如有)、违约金、实现债权的费用(包括但不限于诉讼费、仲裁费、执行费、律师费、公告费、差旅费等)。 |
|||
3、乙方对交易客户针对购销协议所欠平台资金方债务的担保责任期限:为担保责任生效之日起至交易客户全部履行完毕对平台资金方项下全部债务、义务之日止。 |
|||
4、乙方对交易客户针对购销协议所欠平台资金方债务的担保是独立、持续有效、不可撤销和无条件的,不受任何相关方确定并生效的相关文件效力影响。在交易客户同时另有抵押、质押担保或其他保证人的情况下,即使放弃、变更或解除抵押、质押担保或变更、解除其他保证人保证责任,乙方依然按本协议对交易客户承担连带保证责任。 |
|||
|
|||
二、其他约定 |
|||
1、乙方对担保是绝无异议的,当交易客户未按期偿还平台资金方欠款时,平台资金方如果从甲方保证金账户扣除交易客户欠款,乙方依然承担担保责任。乙方同意在收到经甲方或平台资金方发出的书面索偿通知后五个工作日内,如数偿还书面索偿通知中的债务,而无须平台或资金方出具任何证明及其他文件。除非发生明显及重大错误,乙方接受甲方或平台资金方索偿要求的款项金额为准确数据。甲方及平台有权采取认为适当的方式,包括但不限于传真、邮寄、专人送达、在公众媒体上公告等方式对乙方进行催收。 |
|||
2、乙方保证是依法成立的具有保证人资格的法人,愿意用保证人所有或依法有权处分的资产作担保,保证履行本保证担保书规定的义务;同时乙方保证此担保是乙方的真实意思表示,不存在任何欺诈或胁迫的因素;担保决定符合企业章程规定,并已获得充分授权并经上级部门/董事/股东会等有权机构批准。 |
|||
3、如乙方未如期履行担保义务,甲方有权冻结乙方通过平台销售货款的待回款金额,及冻结乙方的原纸货物销售,待还清担保金额后。 |
|||
4、乙方保证不管本企业的工商登记事项、组织结构、股权结构、经营方式或财务状况发生任何变化或发生债务重组、重大关联交易等事项,均不影响在此协议项下对乙方的法律约束力,如发生上述变化可能影响乙方履行本保证担保书的能力时,乙方有义务立即通知甲方或平台资金方。 |
|||
5、乙方保证一旦发生涉及或可能涉及重大经济纠纷、诉讼、仲裁、执行,或财产被依法查封、扣押、冻结或监管,或发生影响偿债能力的其他重大不利事项时,乙方应及时通知甲方,并按要求落实债务的清偿及担保。 |
|||
6、非经甲方事先书面同意,乙方不得转让本协议项下的保证义务。经甲方事先同意而转让的,乙方在本协议项下的权利义务继受人或受让人均应受本协议担保书全部条款的约束。 |
|||
7、如针对本协议项下的担保人系两人或两人以上,则每一担保人对本协议主债权承担无限连带责任,各担保人均不得以其他人未承担担保责任为由提出偿还顺序先后、偿还金额份额分配等抗辩。 |
|||
8、在本协议担保书有效期间,甲方或平台资金方对借款人及/或保证人的任何违约或延误行为施以任何宽容、宽限或延缓执行相关借款合同和本协议内应享有的权益或权利,均不能损害、影响或限制甲方或平台资金方有关法律规定和本协议作为债权人应享有的一切权益和权利,也不能视为甲方或平台资金方放弃对现有或将来违约行为采取行动的权利。 |
|||
|
|||
三、违约责任 |
|||
乙方违反本协议的任何保证或条款的的,同意甲方有权选择采取下列部分或全部措施: |
|||
1、视情节轻重,暂停、减少或取消乙方在平台展示或销售的合作规模; |
|||
2、要求乙方赔偿由此给甲方或平台造成的全部损失,损失包括但不限于直接经济损失,以及为此支出的诉讼费(或仲裁费)、保全费、评估费、拍卖费、执行费、律师代理费、调查取证费等费用。 |
|||
采取法律、法规规定的其他救济措施。 |
|||
|
|||
四、争议及纠纷解决方式 |
|||
本协议适用中华人民共和国法律,因本协议所产生的争议及纠纷,乙方同意采取平台或甲乙双方其它协议约定的纠纷解决方式解决。 |
|||
|
|||
五、术语 |
|||
本协议所使用术语,除另有明确说明外,均具有与平台规定相同的含义。 |
|||
|
|||
六、通知 |
|||
1、乙方与甲方或平台资金方之间关于本协议或担保所提的通知、要求等应以书面形式、或平台消息发送。信息发出则视为送达(收件方拒收的,于拒收日视为送达);以邮政信函方式递交的,寄出后满3日视为送达;以传真方式递交的,收件方传真系统收到传真后视为送达;或在以在公众媒体上公告的方式向乙方催收的,自公告之日视为送达。 |
|||
2、乙方的联系地址为: ,乙方变更联系地址,应当及时通知甲方或平台资金方,否则自行承担因此可能产生的损失。 |
|||
|
|||
七、其他 |
|||
1、本协议于双方盖章之日起生效。本协议部分条款被认定无效的,不影响其他条款的效力,其他部分仍然有效。 |
|||
2、本协议属商业机密,乙方不得向第三方透露,对于泄密行为,甲方有权通过法律手段追究其违约责任。 |
|||
3、本保证担保书一式肆份,贵公司、保证人各执贰份,具同等法律效力。 |
|||
以下无正文,为《千鸟互联纸掌柜保证协议书》之签章页 |
|||
|
|||
甲 方(盖章): |
|||
法定代表人/委托代理人(签名): |
|||
日 期: 年 月 日 |
|||
|
|||
乙 方(盖章): |
|||
法定代表人/委托代理人(签名): |
|||
日 期: 年 月 日 |
|||
|
|||
签订地点:广州天河区 |
|||
` |
|||
import { getBaseInfo, getVerifyUrl } from '@/apis/commonApi.js' |
|||
import { go2, back, exit } from '@/utils/hook.js' |
|||
export default { |
|||
data() { |
|||
return { |
|||
/** |
|||
* @value {String} enterpriseId |
|||
* @value {String} enterpriseName |
|||
* @value {String} legalPersonName |
|||
* @value {String} mallSupplierId |
|||
*/ |
|||
params: null, |
|||
content |
|||
} |
|||
}, |
|||
onLoad(option) { |
|||
if (option) { |
|||
this.params = option |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none', |
|||
complete: () => { |
|||
setTimeout(() => { |
|||
back() |
|||
}, 1000) |
|||
} |
|||
}) |
|||
} |
|||
}, |
|||
methods: { |
|||
go2, |
|||
back, |
|||
// 签约 |
|||
async makeCredit() { |
|||
const res = await getBaseInfo({}, true) |
|||
if (!res || res.enterpriseList.length == 0) { |
|||
uni.showToast({ |
|||
title: '账号信息已变更,请重新登录', |
|||
icon: 'warn', |
|||
complete: () => { |
|||
setTimeout(() => { |
|||
exit() |
|||
}, 1000) |
|||
} |
|||
}) |
|||
return |
|||
} |
|||
let target = res.enterpriseList.find((item) => item.supplier.id === this.params.mallSupplierId) |
|||
if (!target) { |
|||
uni.showToast({ |
|||
title: '账号信息已变更,请重新登录', |
|||
icon: 'warn', |
|||
complete: () => { |
|||
setTimeout(() => { |
|||
exit() |
|||
}, 1000) |
|||
} |
|||
}) |
|||
return |
|||
} |
|||
// 校验是否实名认证 |
|||
if (target.fddEnterpriseStatus === 3) { |
|||
} else { |
|||
// 未实名认证 |
|||
uni.showModal({ |
|||
title: '提示', |
|||
content: '您还未实名认证,请先实名认证', |
|||
success: (res) => { |
|||
if (res.confirm) { |
|||
getVerifyUrl({ enterpriseId: target.id }).then((res) => { |
|||
if (res) { |
|||
go2('page-view', { title: '实名认证', url: encodeURIComponent(res) }) |
|||
} |
|||
}) |
|||
} else if (res.cancel) { |
|||
return |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.button-area { |
|||
width: 750rpx; |
|||
padding: 0 32rpx; |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
.button { |
|||
flex-grow: 0; |
|||
flex-shrink: 0; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
border-radius: 10rpx; |
|||
.text { |
|||
font-size: 30rpx; |
|||
font-weight: 500; |
|||
text-align: center; |
|||
} |
|||
} |
|||
.button__cancel { |
|||
width: 270rpx; |
|||
height: 88rpx; |
|||
border: 2rpx solid #979797; |
|||
} |
|||
.button__submit { |
|||
width: 400rpx; |
|||
height: 88rpx; |
|||
background: #007aff; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,181 @@ |
|||
<template> |
|||
<view> |
|||
<uni-nav-bar left-icon="back" @clickLeft="back" statusBar fixed title="月结授信"></uni-nav-bar> |
|||
<qn-form :columns="columns"> |
|||
<text slot="enterpriseName" class="item_text">{{ form.enterpriseName }}</text> |
|||
<text slot="legalPersonName" class="item_text">{{ form.legalPersonName }}</text> |
|||
<view slot="creditLine"> |
|||
<qn-easyinput :maxlength="9" v-model="form.creditLine" :inputBorder="false" text="right" placeholder="请设置授信额度"></qn-easyinput> |
|||
</view> |
|||
<view slot="settlementPeriod"> |
|||
<qn-data-picker |
|||
v-model="form.settlementPeriod" |
|||
text="right" |
|||
:border="false" |
|||
placeholder="请选择结算周期" |
|||
popup-title="请选择结算周期" |
|||
:map="{ text: 'label', value: 'value' }" |
|||
:clear-icon="false" |
|||
:localdata="settlementPeriodEnum" |
|||
></qn-data-picker> |
|||
</view> |
|||
</qn-form> |
|||
<qn-footer fixed height="120rpx"> |
|||
<view class="button_area"> |
|||
<view :class="{ button__submit: true, 'button__submit--disabled': !canSubmit }" @click="canSubmit && makeCredit()"> |
|||
<text class="text">立即授信</text> |
|||
</view> |
|||
</view> |
|||
</qn-footer> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { back, go2 } from '@/utils/hook.js' |
|||
import qnDataPicker from '@/components/qn-data-picker/qn-data-picker.vue' |
|||
import qnEasyinput from '@/components/qn-easyinput/qn-easyinput.vue' |
|||
import { settlementPeriodEnum } from '@/enums/index.js' |
|||
import qnForm from '@/components/qn-form/qn-form.vue' |
|||
import { makeMonthlyCreditInfo } from '@/apis/clientCreditApi.js' |
|||
const column = [ |
|||
{ |
|||
key: 1, |
|||
type: 'title', |
|||
label: '授信客户信息' |
|||
}, |
|||
{ |
|||
key: 'enterpriseName', |
|||
type: 'item', |
|||
label: '企业名称', |
|||
required: true |
|||
}, |
|||
{ |
|||
key: 'legalPersonName', |
|||
type: 'item', |
|||
label: '法人姓名', |
|||
required: true |
|||
}, |
|||
{ |
|||
key: 'creditLine', |
|||
type: 'item', |
|||
label: '授信额度(元)', |
|||
required: true |
|||
}, |
|||
{ |
|||
key: 'settlementPeriod', |
|||
type: 'item', |
|||
label: '结算周期', |
|||
required: true |
|||
} |
|||
] |
|||
export default { |
|||
components: { |
|||
qnForm, |
|||
qnDataPicker, |
|||
qnEasyinput |
|||
}, |
|||
data() { |
|||
return { |
|||
form: { |
|||
enterpriseId: null, |
|||
legalPersonName: null, |
|||
enterpriseName: null, |
|||
creditLine: null, |
|||
settlementPeriod: 1, |
|||
mallSupplierId: null |
|||
}, |
|||
settlementPeriodEnum: Object.freeze(settlementPeriodEnum), |
|||
columns: Object.freeze(column), |
|||
canSubmit: false |
|||
} |
|||
}, |
|||
methods: { |
|||
back, |
|||
makeCredit() { |
|||
console.log('makeCredit') |
|||
makeMonthlyCreditInfo(this.form).then((res) => { |
|||
if (res) { |
|||
uni.showToast({ |
|||
title: '授信成功', |
|||
icon: 'success', |
|||
duration: 2000, |
|||
success: () => { |
|||
setTimeout(() => { |
|||
go2('client-credit', {}, true) |
|||
}, 2000) |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
}, |
|||
onLoad(option) { |
|||
if (option) { |
|||
this.form.enterpriseId = option.enterpriseId |
|||
this.form.mallSupplierId = option.mallSupplierId |
|||
this.form.legalPersonName = option.legalPersonName |
|||
this.form.enterpriseName = option.enterpriseName |
|||
} |
|||
}, |
|||
created() { |
|||
if (!this.form.enterpriseId) { |
|||
uni.showToast({ |
|||
title: '进入页面错误', |
|||
icon: 'error', |
|||
duration: 2000, |
|||
complete: () => { |
|||
back() |
|||
} |
|||
}) |
|||
} |
|||
}, |
|||
watch: { |
|||
form: { |
|||
handler(val) { |
|||
let keys = Object.keys(val) |
|||
let flag = true |
|||
for (let key of keys) { |
|||
if (!val[key]) { |
|||
flag = false |
|||
break |
|||
} |
|||
} |
|||
this.canSubmit = flag |
|||
}, |
|||
deep: true |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.item_text { |
|||
font-size: 28rpx; |
|||
color: #333333; |
|||
} |
|||
.button_area { |
|||
width: 750rpx; |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 0 32rpx; |
|||
.button__submit { |
|||
flex-grow: 1; |
|||
height: 88rpx; |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: center; |
|||
background: #007aff; |
|||
border-radius: 10rpx; |
|||
.text { |
|||
font-size: 30rpx; |
|||
color: #ffffff; |
|||
} |
|||
} |
|||
.button__submit--disabled { |
|||
opacity: 0.5; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,59 @@ |
|||
<template> |
|||
<view> |
|||
<web-view :src="url"></web-view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { go2, back, exit } from '@/utils/hook.js' |
|||
import { makeSocket } from '@/utils/index.js' |
|||
export default { |
|||
data() { |
|||
return { |
|||
url: '', |
|||
socket: null |
|||
} |
|||
}, |
|||
onLoad(option) { |
|||
if (option) { |
|||
this.url = decodeURIComponent(option.url) |
|||
console.log(this.url, option) |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none', |
|||
complete: () => { |
|||
setTimeout(() => { |
|||
back() |
|||
}, 1000) |
|||
} |
|||
}) |
|||
} |
|||
}, |
|||
created() { |
|||
makeSocket({ pageInfo: 'page-view', retry: true }).then((res) => { |
|||
this.socket = res |
|||
this.socket.onMessage(this.getMessage) |
|||
}) |
|||
}, |
|||
methods: { |
|||
getMessage(data) { |
|||
console.log('getMessage:', data) |
|||
}, |
|||
destroySocket() { |
|||
if (this.socket) { |
|||
this.socket.close() |
|||
this.socket = null |
|||
} |
|||
} |
|||
}, |
|||
destroyed() { |
|||
this.destroySocket() |
|||
}, |
|||
onHide() { |
|||
this.destroySocket() |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style></style> |
|||
@ -0,0 +1,9 @@ |
|||
<template> |
|||
<view>切换供应商</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default {} |
|||
</script> |
|||
|
|||
<style></style> |
|||
@ -1,52 +0,0 @@ |
|||
## 1.0.1(2021-11-23) |
|||
- 修复 由上个版本引发的map、v-model等属性不生效的bug |
|||
## 1.0.0(2021-11-19) |
|||
- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
|||
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker) |
|||
## 0.4.9(2021-10-28) |
|||
- 修复 VUE2 v-model 概率无效的 bug |
|||
## 0.4.8(2021-10-27) |
|||
- 修复 v-model 概率无效的 bug |
|||
## 0.4.7(2021-10-25) |
|||
- 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+ |
|||
- 修复 树型 uniCloud 数据类型为 int 时报错的 bug |
|||
## 0.4.6(2021-10-19) |
|||
- 修复 非 VUE3 v-model 为 0 时无法选中的 bug |
|||
## 0.4.5(2021-09-26) |
|||
- 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效 |
|||
- 修复 readonly 为 true 时报错的 bug |
|||
## 0.4.4(2021-09-26) |
|||
- 修复 上一版本造成的 map 属性失效的 bug |
|||
- 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略 |
|||
## 0.4.3(2021-09-24) |
|||
- 修复 某些情况下级联未触发的 bug |
|||
## 0.4.2(2021-09-23) |
|||
- 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用 |
|||
- 新增 选项内容过长自动添加省略号 |
|||
## 0.4.1(2021-09-15) |
|||
- 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段 |
|||
## 0.4.0(2021-07-13) |
|||
- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
|||
## 0.3.5(2021-06-04) |
|||
- 修复 无法加载云端数据的问题 |
|||
## 0.3.4(2021-05-28) |
|||
- 修复 v-model 无效问题 |
|||
- 修复 loaddata 为空数据组时加载时间过长问题 |
|||
- 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点 |
|||
## 0.3.3(2021-05-12) |
|||
- 新增 组件示例地址 |
|||
## 0.3.2(2021-04-22) |
|||
- 修复 非树形数据有 where 属性查询报错的问题 |
|||
## 0.3.1(2021-04-15) |
|||
- 修复 本地数据概率无法回显时问题 |
|||
## 0.3.0(2021-04-07) |
|||
- 新增 支持云端非树形表结构数据 |
|||
- 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题 |
|||
## 0.2.0(2021-03-15) |
|||
- 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题 |
|||
## 0.1.9(2021-03-09) |
|||
- 修复 微信小程序某些情况下无法选择的问题 |
|||
## 0.1.8(2021-02-05) |
|||
- 优化 部分样式在 nvue 上的兼容表现 |
|||
## 0.1.7(2021-02-05) |
|||
- 调整为 uni_modules 目录规范 |
|||
@ -1,45 +0,0 @@ |
|||
// #ifdef H5
|
|||
export default { |
|||
name: 'Keypress', |
|||
props: { |
|||
disable: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
}, |
|||
mounted () { |
|||
const keyNames = { |
|||
esc: ['Esc', 'Escape'], |
|||
tab: 'Tab', |
|||
enter: 'Enter', |
|||
space: [' ', 'Spacebar'], |
|||
up: ['Up', 'ArrowUp'], |
|||
left: ['Left', 'ArrowLeft'], |
|||
right: ['Right', 'ArrowRight'], |
|||
down: ['Down', 'ArrowDown'], |
|||
delete: ['Backspace', 'Delete', 'Del'] |
|||
} |
|||
const listener = ($event) => { |
|||
if (this.disable) { |
|||
return |
|||
} |
|||
const keyName = Object.keys(keyNames).find(key => { |
|||
const keyName = $event.key |
|||
const value = keyNames[key] |
|||
return value === keyName || (Array.isArray(value) && value.includes(keyName)) |
|||
}) |
|||
if (keyName) { |
|||
// 避免和其他按键事件冲突
|
|||
setTimeout(() => { |
|||
this.$emit(keyName, {}) |
|||
}, 0) |
|||
} |
|||
} |
|||
document.addEventListener('keyup', listener) |
|||
this.$once('hook:beforeDestroy', () => { |
|||
document.removeEventListener('keyup', listener) |
|||
}) |
|||
}, |
|||
render: () => {} |
|||
} |
|||
// #endif
|
|||
@ -1,537 +0,0 @@ |
|||
<template> |
|||
<view class="uni-data-tree"> |
|||
<view class="uni-data-tree-input" @click="handleInput"> |
|||
<slot :options="options" :data="inputSelected" :error="errorMessage"> |
|||
<view class="input-value" :class="{'input-value-border': border}"> |
|||
<text v-if="errorMessage" class="selected-area error-text">{{errorMessage}}</text> |
|||
<view v-else-if="loading && !isOpened" class="selected-area"> |
|||
<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more> |
|||
</view> |
|||
<scroll-view v-else-if="inputSelected.length" class="selected-area" scroll-x="true"> |
|||
<view class="selected-list"> |
|||
<view class="selected-item" v-for="(item,index) in inputSelected" :key="index"> |
|||
<text>{{item.text}}</text><text v-if="index<inputSelected.length-1" |
|||
class="input-split-line">{{split}}</text> |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
<text v-else class="selected-area placeholder">{{placeholder}}</text> |
|||
<view v-show="clearIcon && !readonly && inputSelected.length" class="icon-clear" |
|||
@click.stop="clear"> |
|||
<uni-icons type="clear" color="#e1e1e1" size="14"></uni-icons> |
|||
</view> |
|||
<view class="arrow-area" v-if="(!clearIcon || !inputSelected.length) && !readonly "> |
|||
<view class="input-arrow"></view> |
|||
</view> |
|||
</view> |
|||
</slot> |
|||
</view> |
|||
<view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view> |
|||
<view class="uni-data-tree-dialog" v-if="isOpened"> |
|||
<view class="uni-popper__arrow"></view> |
|||
<view class="dialog-caption"> |
|||
<view class="title-area"> |
|||
<text class="dialog-title">{{popupTitle}}</text> |
|||
</view> |
|||
<view class="dialog-close" @click="handleClose"> |
|||
<view class="dialog-close-plus" data-id="close"></view> |
|||
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view> |
|||
</view> |
|||
</view> |
|||
<data-picker-view class="picker-view" ref="pickerView" v-model="dataValue" :localdata="localdata" |
|||
:preload="preload" :collection="collection" :field="field" :orderby="orderby" :where="where" |
|||
:step-searh="stepSearh" :self-field="selfField" :parent-field="parentField" :managed-mode="true" |
|||
:map="map" :ellipsis="ellipsis" @change="onchange" @datachange="ondatachange" @nodeclick="onnodeclick"> |
|||
</data-picker-view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import dataPicker from "../uni-data-pickerview/uni-data-picker.js" |
|||
import DataPickerView from "../uni-data-pickerview/uni-data-pickerview.vue" |
|||
|
|||
/** |
|||
* DataPicker 级联选择 |
|||
* @description 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 |
|||
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796 |
|||
* @property {String} popup-title 弹出窗口标题 |
|||
* @property {Array} localdata 本地数据,参考 |
|||
* @property {Boolean} border = [true|false] 是否有边框 |
|||
* @property {Boolean} readonly = [true|false] 是否仅读 |
|||
* @property {Boolean} preload = [true|false] 是否预加载数据 |
|||
* @value true 开启预加载数据,点击弹出窗口后显示已加载数据 |
|||
* @value false 关闭预加载数据,点击弹出窗口后开始加载数据 |
|||
* @property {Boolean} step-searh = [true|false] 是否分布查询 |
|||
* @value true 启用分布查询,仅查询当前选中节点 |
|||
* @value false 关闭分布查询,一次查询出所有数据 |
|||
* @property {String|DBFieldString} self-field 分布查询当前字段名称 |
|||
* @property {String|DBFieldString} parent-field 分布查询父字段名称 |
|||
* @property {String|DBCollectionString} collection 表名 |
|||
* @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 |
|||
* @property {String} orderby 排序字段及正序倒叙设置 |
|||
* @property {String|JQLString} where 查询条件 |
|||
* @event {Function} popupshow 弹出的选择窗口打开时触发此事件 |
|||
* @event {Function} popuphide 弹出的选择窗口关闭时触发此事件 |
|||
*/ |
|||
export default { |
|||
name: 'UniDataPicker', |
|||
emits: ['popupopened', 'popupclosed', 'nodeclick', 'input', 'change', 'update:modelValue'], |
|||
mixins: [dataPicker], |
|||
components: { |
|||
DataPickerView |
|||
}, |
|||
props: { |
|||
options: { |
|||
type: [Object, Array], |
|||
default () { |
|||
return {} |
|||
} |
|||
}, |
|||
popupTitle: { |
|||
type: String, |
|||
default: '请选择' |
|||
}, |
|||
placeholder: { |
|||
type: String, |
|||
default: '请选择' |
|||
}, |
|||
heightMobile: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
readonly: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
clearIcon: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
border: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
split: { |
|||
type: String, |
|||
default: '/' |
|||
}, |
|||
ellipsis: { |
|||
type: Boolean, |
|||
default: true |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
isOpened: false, |
|||
inputSelected: [] |
|||
} |
|||
}, |
|||
created() { |
|||
this.form = this.getForm('uniForms') |
|||
this.formItem = this.getForm('uniFormsItem') |
|||
if (this.formItem) { |
|||
if (this.formItem.name) { |
|||
this.rename = this.formItem.name |
|||
this.form.inputChildrens.push(this) |
|||
} |
|||
} |
|||
|
|||
this.$nextTick(() => { |
|||
this.load() |
|||
}) |
|||
}, |
|||
methods: { |
|||
clear() { |
|||
this.inputSelected.splice(0) |
|||
this._dispatchEvent([]) |
|||
}, |
|||
onPropsChange() { |
|||
this._treeData = [] |
|||
this.selectedIndex = 0 |
|||
this.load() |
|||
}, |
|||
load() { |
|||
if (this.readonly) { |
|||
this._processReadonly(this.localdata, this.dataValue) |
|||
return |
|||
} |
|||
|
|||
if (this.isLocaldata) { |
|||
this.loadData() |
|||
this.inputSelected = this.selected.slice(0) |
|||
} else if (!this.parentField && !this.selfField && this.hasValue) { |
|||
this.getNodeData(() => { |
|||
this.inputSelected = this.selected.slice(0) |
|||
}) |
|||
} else if (this.hasValue) { |
|||
this.getTreePath(() => { |
|||
this.inputSelected = this.selected.slice(0) |
|||
}) |
|||
} |
|||
}, |
|||
getForm(name = 'uniForms') { |
|||
let parent = this.$parent; |
|||
let parentName = parent.$options.name; |
|||
while (parentName !== name) { |
|||
parent = parent.$parent; |
|||
if (!parent) return false; |
|||
parentName = parent.$options.name; |
|||
} |
|||
return parent; |
|||
}, |
|||
show() { |
|||
this.isOpened = true |
|||
this.$nextTick(() => { |
|||
this.$refs.pickerView.updateData({ |
|||
treeData: this._treeData, |
|||
selected: this.selected, |
|||
selectedIndex: this.selectedIndex |
|||
}) |
|||
}) |
|||
this.$emit('popupopened') |
|||
}, |
|||
hide() { |
|||
this.isOpened = false |
|||
this.$emit('popupclosed') |
|||
}, |
|||
handleInput() { |
|||
if (this.readonly) { |
|||
return |
|||
} |
|||
this.show() |
|||
}, |
|||
handleClose(e) { |
|||
this.hide() |
|||
}, |
|||
onnodeclick(e) { |
|||
this.$emit('nodeclick', e) |
|||
}, |
|||
ondatachange(e) { |
|||
this._treeData = this.$refs.pickerView._treeData |
|||
}, |
|||
onchange(e) { |
|||
this.hide() |
|||
this.inputSelected = e |
|||
this._dispatchEvent(e) |
|||
}, |
|||
_processReadonly(dataList, value) { |
|||
var isTree = dataList.findIndex((item) => { |
|||
return item.children |
|||
}) |
|||
if (isTree > -1) { |
|||
let inputValue |
|||
if (Array.isArray(value)) { |
|||
inputValue = value[value.length - 1] |
|||
if (typeof inputValue === 'object' && inputValue.value) { |
|||
inputValue = inputValue.value |
|||
} |
|||
} else { |
|||
inputValue = value |
|||
} |
|||
this.inputSelected = this._findNodePath(inputValue, this.localdata) |
|||
return |
|||
} |
|||
|
|||
if (!this.hasValue) { |
|||
this.inputSelected = [] |
|||
return |
|||
} |
|||
|
|||
let result = [] |
|||
for (let i = 0; i < value.length; i++) { |
|||
var val = value[i] |
|||
var item = dataList.find((v) => { |
|||
return v.value == val |
|||
}) |
|||
if (item) { |
|||
result.push(item) |
|||
} |
|||
} |
|||
if (result.length) { |
|||
this.inputSelected = result |
|||
} |
|||
}, |
|||
_filterForArray(data, valueArray) { |
|||
var result = [] |
|||
for (let i = 0; i < valueArray.length; i++) { |
|||
var value = valueArray[i] |
|||
var found = data.find((item) => { |
|||
return item.value == value |
|||
}) |
|||
if (found) { |
|||
result.push(found) |
|||
} |
|||
} |
|||
return result |
|||
}, |
|||
_dispatchEvent(selected) { |
|||
let item = {} |
|||
if (selected.length) { |
|||
var value = new Array(selected.length) |
|||
for (var i = 0; i < selected.length; i++) { |
|||
value[i] = selected[i].value |
|||
} |
|||
item = selected[selected.length - 1] |
|||
} else { |
|||
item.value = '' |
|||
} |
|||
if (this.formItem) { |
|||
this.formItem.setValue(item.value) |
|||
} |
|||
|
|||
this.$emit('input', item.value) |
|||
this.$emit('update:modelValue', item.value) |
|||
this.$emit('change', { |
|||
detail: { |
|||
value: selected |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.uni-data-tree { |
|||
position: relative; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.error-text { |
|||
color: #DD524D; |
|||
} |
|||
|
|||
.input-value { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
align-items: center; |
|||
flex-wrap: nowrap; |
|||
font-size: 14px; |
|||
line-height: 38px; |
|||
padding: 0 5px; |
|||
overflow: hidden; |
|||
/* #ifdef APP-NVUE */ |
|||
height: 40px; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.input-value-border { |
|||
border: 1px solid #e5e5e5; |
|||
border-radius: 5px; |
|||
} |
|||
|
|||
.selected-area { |
|||
flex: 1; |
|||
overflow: hidden; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
} |
|||
|
|||
.load-more { |
|||
/* #ifndef APP-NVUE */ |
|||
margin-right: auto; |
|||
/* #endif */ |
|||
/* #ifdef APP-NVUE */ |
|||
width: 40px; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.selected-list { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
flex-wrap: nowrap; |
|||
padding: 0 5px; |
|||
} |
|||
|
|||
.selected-item { |
|||
flex-direction: row; |
|||
padding: 0 1px; |
|||
/* #ifndef APP-NVUE */ |
|||
white-space: nowrap; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.placeholder { |
|||
color: grey; |
|||
} |
|||
|
|||
.input-split-line { |
|||
opacity: .5; |
|||
} |
|||
|
|||
.arrow-area { |
|||
position: relative; |
|||
width: 20px; |
|||
/* #ifndef APP-NVUE */ |
|||
margin-bottom: 5px; |
|||
margin-left: auto; |
|||
display: flex; |
|||
/* #endif */ |
|||
justify-content: center; |
|||
transform: rotate(-45deg); |
|||
transform-origin: center; |
|||
} |
|||
|
|||
.input-arrow { |
|||
width: 7px; |
|||
height: 7px; |
|||
border-left: 1px solid #999; |
|||
border-bottom: 1px solid #999; |
|||
} |
|||
|
|||
.uni-data-tree-cover { |
|||
position: fixed; |
|||
left: 0; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: rgba(0, 0, 0, .4); |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: column; |
|||
z-index: 100; |
|||
} |
|||
|
|||
.uni-data-tree-dialog { |
|||
position: fixed; |
|||
left: 0; |
|||
top: 20%; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: #FFFFFF; |
|||
border-top-left-radius: 10px; |
|||
border-top-right-radius: 10px; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: column; |
|||
z-index: 102; |
|||
overflow: hidden; |
|||
/* #ifdef APP-NVUE */ |
|||
width: 750rpx; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.dialog-caption { |
|||
position: relative; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
/* border-bottom: 1px solid #f0f0f0; */ |
|||
} |
|||
|
|||
.title-area { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
align-items: center; |
|||
/* #ifndef APP-NVUE */ |
|||
margin: auto; |
|||
/* #endif */ |
|||
padding: 0 10px; |
|||
} |
|||
|
|||
.dialog-title { |
|||
/* font-weight: bold; */ |
|||
line-height: 44px; |
|||
} |
|||
|
|||
.dialog-close { |
|||
position: absolute; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
align-items: center; |
|||
padding: 0 15px; |
|||
} |
|||
|
|||
.dialog-close-plus { |
|||
width: 16px; |
|||
height: 2px; |
|||
background-color: #666; |
|||
border-radius: 2px; |
|||
transform: rotate(45deg); |
|||
} |
|||
|
|||
.dialog-close-rotate { |
|||
position: absolute; |
|||
transform: rotate(-45deg); |
|||
} |
|||
|
|||
.picker-view { |
|||
flex: 1; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
/* #ifdef H5 */ |
|||
@media all and (min-width: 768px) { |
|||
.uni-data-tree-cover { |
|||
background-color: transparent; |
|||
} |
|||
|
|||
.uni-data-tree-dialog { |
|||
position: absolute; |
|||
top: 55px; |
|||
height: auto; |
|||
min-height: 400px; |
|||
max-height: 50vh; |
|||
background-color: #fff; |
|||
border: 1px solid #EBEEF5; |
|||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
|||
border-radius: 4px; |
|||
overflow: unset; |
|||
} |
|||
|
|||
.dialog-caption { |
|||
display: none; |
|||
} |
|||
|
|||
.icon-clear { |
|||
margin-right: 5px; |
|||
} |
|||
} |
|||
|
|||
/* #endif */ |
|||
|
|||
/* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */ |
|||
.uni-popper__arrow, |
|||
.uni-popper__arrow::after { |
|||
position: absolute; |
|||
display: block; |
|||
width: 0; |
|||
height: 0; |
|||
border-color: transparent; |
|||
border-style: solid; |
|||
border-width: 6px; |
|||
} |
|||
|
|||
.uni-popper__arrow { |
|||
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
|||
top: -6px; |
|||
left: 10%; |
|||
margin-right: 3px; |
|||
border-top-width: 0; |
|||
border-bottom-color: #EBEEF5; |
|||
} |
|||
|
|||
.uni-popper__arrow::after { |
|||
content: " "; |
|||
top: 1px; |
|||
margin-left: -6px; |
|||
border-top-width: 0; |
|||
border-bottom-color: #fff; |
|||
} |
|||
</style> |
|||
@ -1,563 +0,0 @@ |
|||
export default { |
|||
props: { |
|||
localdata: { |
|||
type: [Array, Object], |
|||
default () { |
|||
return [] |
|||
} |
|||
}, |
|||
spaceInfo: { |
|||
type: Object, |
|||
default () { |
|||
return {} |
|||
} |
|||
}, |
|||
collection: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
action: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
field: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
orderby: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
where: { |
|||
type: [String, Object], |
|||
default: '' |
|||
}, |
|||
pageData: { |
|||
type: String, |
|||
default: 'add' |
|||
}, |
|||
pageCurrent: { |
|||
type: Number, |
|||
default: 1 |
|||
}, |
|||
pageSize: { |
|||
type: Number, |
|||
default: 20 |
|||
}, |
|||
getcount: { |
|||
type: [Boolean, String], |
|||
default: false |
|||
}, |
|||
getone: { |
|||
type: [Boolean, String], |
|||
default: false |
|||
}, |
|||
gettree: { |
|||
type: [Boolean, String], |
|||
default: false |
|||
}, |
|||
manual: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
value: { |
|||
type: [Array, String, Number], |
|||
default () { |
|||
return [] |
|||
} |
|||
}, |
|||
modelValue: { |
|||
type: [Array, String, Number], |
|||
default () { |
|||
return [] |
|||
} |
|||
}, |
|||
preload: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
stepSearh: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
selfField: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
parentField: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
multiple: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
map: { |
|||
type: Object, |
|||
default() { |
|||
return { |
|||
text: "text", |
|||
value: "value" |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
loading: false, |
|||
errorMessage: '', |
|||
loadMore: { |
|||
contentdown: '', |
|||
contentrefresh: '', |
|||
contentnomore: '' |
|||
}, |
|||
dataList: [], |
|||
selected: [], |
|||
selectedIndex: 0, |
|||
page: { |
|||
current: this.pageCurrent, |
|||
size: this.pageSize, |
|||
count: 0 |
|||
} |
|||
} |
|||
}, |
|||
computed: { |
|||
isLocaldata() { |
|||
return !this.collection.length |
|||
}, |
|||
postField() { |
|||
let fields = [this.field]; |
|||
if (this.parentField) { |
|||
fields.push(`${this.parentField} as parent_value`); |
|||
} |
|||
return fields.join(','); |
|||
}, |
|||
dataValue() { |
|||
let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null || this.modelValue !== undefined) |
|||
return isModelValue ? this.modelValue : this.value |
|||
}, |
|||
hasValue() { |
|||
if (typeof this.dataValue === 'number') { |
|||
return true |
|||
} |
|||
return (this.dataValue != null) && (this.dataValue.length > 0) |
|||
} |
|||
}, |
|||
created() { |
|||
this.$watch(() => { |
|||
var al = []; |
|||
['pageCurrent', |
|||
'pageSize', |
|||
'spaceInfo', |
|||
'value', |
|||
'modelValue', |
|||
'localdata', |
|||
'collection', |
|||
'action', |
|||
'field', |
|||
'orderby', |
|||
'where', |
|||
'getont', |
|||
'getcount', |
|||
'gettree' |
|||
].forEach(key => { |
|||
al.push(this[key]) |
|||
}); |
|||
return al |
|||
}, (newValue, oldValue) => { |
|||
let needReset = false |
|||
for (let i = 2; i < newValue.length; i++) { |
|||
if (newValue[i] != oldValue[i]) { |
|||
needReset = true |
|||
break |
|||
} |
|||
} |
|||
if (newValue[0] != oldValue[0]) { |
|||
this.page.current = this.pageCurrent |
|||
} |
|||
this.page.size = this.pageSize |
|||
|
|||
this.onPropsChange() |
|||
}) |
|||
this._treeData = [] |
|||
}, |
|||
methods: { |
|||
onPropsChange() { |
|||
this._treeData = [] |
|||
}, |
|||
getCommand(options = {}) { |
|||
/* eslint-disable no-undef */ |
|||
let db = uniCloud.database(this.spaceInfo) |
|||
|
|||
const action = options.action || this.action |
|||
if (action) { |
|||
db = db.action(action) |
|||
} |
|||
|
|||
const collection = options.collection || this.collection |
|||
db = db.collection(collection) |
|||
|
|||
const where = options.where || this.where |
|||
if (!(!where || !Object.keys(where).length)) { |
|||
db = db.where(where) |
|||
} |
|||
|
|||
const field = options.field || this.field |
|||
if (field) { |
|||
db = db.field(field) |
|||
} |
|||
|
|||
const orderby = options.orderby || this.orderby |
|||
if (orderby) { |
|||
db = db.orderBy(orderby) |
|||
} |
|||
|
|||
const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current |
|||
const size = options.pageSize !== undefined ? options.pageSize : this.page.size |
|||
const getCount = options.getcount !== undefined ? options.getcount : this.getcount |
|||
const getTree = options.gettree !== undefined ? options.gettree : this.gettree |
|||
|
|||
const getOptions = { |
|||
getCount, |
|||
getTree |
|||
} |
|||
if (options.getTreePath) { |
|||
getOptions.getTreePath = options.getTreePath |
|||
} |
|||
|
|||
db = db.skip(size * (current - 1)).limit(size).get(getOptions) |
|||
|
|||
return db |
|||
}, |
|||
getNodeData(callback) { |
|||
if (this.loading) { |
|||
return |
|||
} |
|||
this.loading = true |
|||
this.getCommand({ |
|||
field: this.postField, |
|||
where: this._pathWhere() |
|||
}).then((res) => { |
|||
this.loading = false |
|||
this.selected = res.result.data |
|||
callback && callback() |
|||
}).catch((err) => { |
|||
this.loading = false |
|||
this.errorMessage = err |
|||
}) |
|||
}, |
|||
getTreePath(callback) { |
|||
if (this.loading) { |
|||
return |
|||
} |
|||
this.loading = true |
|||
|
|||
this.getCommand({ |
|||
field: this.postField, |
|||
getTreePath: { |
|||
startWith: `${this.selfField}=='${this.dataValue}'` |
|||
} |
|||
}).then((res) => { |
|||
this.loading = false |
|||
let treePath = [] |
|||
this._extractTreePath(res.result.data, treePath) |
|||
this.selected = treePath |
|||
callback && callback() |
|||
}).catch((err) => { |
|||
this.loading = false |
|||
this.errorMessage = err |
|||
}) |
|||
}, |
|||
loadData() { |
|||
if (this.isLocaldata) { |
|||
this._processLocalData() |
|||
return |
|||
} |
|||
|
|||
if (this.dataValue != null) { |
|||
this._loadNodeData((data) => { |
|||
this._treeData = data |
|||
this._updateBindData() |
|||
this._updateSelected() |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if (this.stepSearh) { |
|||
this._loadNodeData((data) => { |
|||
this._treeData = data |
|||
this._updateBindData() |
|||
}) |
|||
} else { |
|||
this._loadAllData((data) => { |
|||
this._treeData = [] |
|||
this._extractTree(data, this._treeData, null) |
|||
this._updateBindData() |
|||
}) |
|||
} |
|||
}, |
|||
_loadAllData(callback) { |
|||
if (this.loading) { |
|||
return |
|||
} |
|||
this.loading = true |
|||
|
|||
this.getCommand({ |
|||
field: this.postField, |
|||
gettree: true, |
|||
startwith: `${this.selfField}=='${this.dataValue}'` |
|||
}).then((res) => { |
|||
this.loading = false |
|||
callback(res.result.data) |
|||
this.onDataChange() |
|||
}).catch((err) => { |
|||
this.loading = false |
|||
this.errorMessage = err |
|||
}) |
|||
}, |
|||
_loadNodeData(callback, pw) { |
|||
if (this.loading) { |
|||
return |
|||
} |
|||
this.loading = true |
|||
|
|||
this.getCommand({ |
|||
field: this.postField, |
|||
where: pw || this._postWhere(), |
|||
pageSize: 500 |
|||
}).then((res) => { |
|||
this.loading = false |
|||
callback(res.result.data) |
|||
this.onDataChange() |
|||
}).catch((err) => { |
|||
this.loading = false |
|||
this.errorMessage = err |
|||
}) |
|||
}, |
|||
_pathWhere() { |
|||
let result = [] |
|||
let where_field = this._getParentNameByField(); |
|||
if (where_field) { |
|||
result.push(`${where_field} == '${this.dataValue}'`) |
|||
} |
|||
|
|||
if (this.where) { |
|||
return `(${this.where}) && (${result.join(' || ')})` |
|||
} |
|||
|
|||
return result.join(' || ') |
|||
}, |
|||
_postWhere() { |
|||
let result = [] |
|||
let selected = this.selected |
|||
let parentField = this.parentField |
|||
if (parentField) { |
|||
result.push(`${parentField} == null || ${parentField} == ""`) |
|||
} |
|||
if (selected.length) { |
|||
for (var i = 0; i < selected.length - 1; i++) { |
|||
result.push(`${parentField} == '${selected[i].value}'`) |
|||
} |
|||
} |
|||
|
|||
let where = [] |
|||
if (this.where) { |
|||
where.push(`(${this.where})`) |
|||
} |
|||
if (result.length) { |
|||
where.push(`(${result.join(' || ')})`) |
|||
} |
|||
|
|||
return where.join(' && ') |
|||
}, |
|||
_nodeWhere() { |
|||
let result = [] |
|||
let selected = this.selected |
|||
if (selected.length) { |
|||
result.push(`${this.parentField} == '${selected[selected.length - 1].value}'`) |
|||
} |
|||
|
|||
if (this.where) { |
|||
return `(${this.where}) && (${result.join(' || ')})` |
|||
} |
|||
|
|||
return result.join(' || ') |
|||
}, |
|||
_getParentNameByField() { |
|||
const fields = this.field.split(','); |
|||
let where_field = null; |
|||
for (let i = 0; i < fields.length; i++) { |
|||
const items = fields[i].split('as'); |
|||
if (items.length < 2) { |
|||
continue; |
|||
} |
|||
if (items[1].trim() === 'value') { |
|||
where_field = items[0].trim(); |
|||
break; |
|||
} |
|||
} |
|||
return where_field |
|||
}, |
|||
_isTreeView() { |
|||
return (this.parentField && this.selfField) |
|||
}, |
|||
_updateSelected() { |
|||
var dl = this.dataList |
|||
var sl = this.selected |
|||
let textField = this.map.text |
|||
let valueField = this.map.value |
|||
for (var i = 0; i < sl.length; i++) { |
|||
var value = sl[i].value |
|||
var dl2 = dl[i] |
|||
for (var j = 0; j < dl2.length; j++) { |
|||
var item2 = dl2[j] |
|||
if (item2[valueField] === value) { |
|||
sl[i].text = item2[textField] |
|||
break |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
_updateBindData(node) { |
|||
const { |
|||
dataList, |
|||
hasNodes |
|||
} = this._filterData(this._treeData, this.selected) |
|||
|
|||
let isleaf = this._stepSearh === false && !hasNodes |
|||
|
|||
if (node) { |
|||
node.isleaf = isleaf |
|||
} |
|||
|
|||
this.dataList = dataList |
|||
this.selectedIndex = dataList.length - 1 |
|||
|
|||
if (!isleaf && this.selected.length < dataList.length) { |
|||
this.selected.push({ |
|||
value: null, |
|||
text: "请选择" |
|||
}) |
|||
} |
|||
|
|||
return { |
|||
isleaf, |
|||
hasNodes |
|||
} |
|||
}, |
|||
_filterData(data, paths) { |
|||
let dataList = [] |
|||
let hasNodes = true |
|||
|
|||
dataList.push(data.filter((item) => { |
|||
return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '') |
|||
})) |
|||
for (let i = 0; i < paths.length; i++) { |
|||
var value = paths[i].value |
|||
var nodes = data.filter((item) => { |
|||
return item.parent_value === value |
|||
}) |
|||
|
|||
if (nodes.length) { |
|||
dataList.push(nodes) |
|||
} else { |
|||
hasNodes = false |
|||
} |
|||
} |
|||
|
|||
return { |
|||
dataList, |
|||
hasNodes |
|||
} |
|||
}, |
|||
_extractTree(nodes, result, parent_value) { |
|||
let list = result || [] |
|||
let valueField = this.map.value |
|||
for (let i = 0; i < nodes.length; i++) { |
|||
let node = nodes[i] |
|||
|
|||
let child = {} |
|||
for (let key in node) { |
|||
if (key !== 'children') { |
|||
child[key] = node[key] |
|||
} |
|||
} |
|||
if (parent_value !== null && parent_value !== undefined && parent_value !== '') { |
|||
child.parent_value = parent_value |
|||
} |
|||
result.push(child) |
|||
|
|||
let children = node.children |
|||
if (children) { |
|||
this._extractTree(children, result, node[valueField]) |
|||
} |
|||
} |
|||
}, |
|||
_extractTreePath(nodes, result) { |
|||
let list = result || [] |
|||
for (let i = 0; i < nodes.length; i++) { |
|||
let node = nodes[i] |
|||
|
|||
let child = {} |
|||
for (let key in node) { |
|||
if (key !== 'children') { |
|||
child[key] = node[key] |
|||
} |
|||
} |
|||
result.push(child) |
|||
|
|||
let children = node.children |
|||
if (children) { |
|||
this._extractTreePath(children, result) |
|||
} |
|||
} |
|||
}, |
|||
_findNodePath(key, nodes, path = []) { |
|||
let textField = this.map.text |
|||
let valueField = this.map.value |
|||
for (let i = 0; i < nodes.length; i++) { |
|||
let node = nodes[i] |
|||
let children = node.children |
|||
let text = node[textField] |
|||
let value = node[valueField] |
|||
|
|||
path.push({ |
|||
value, |
|||
text |
|||
}) |
|||
|
|||
if (value === key) { |
|||
return path |
|||
} |
|||
|
|||
if (children) { |
|||
const p = this._findNodePath(key, children, path) |
|||
if (p.length) { |
|||
return p |
|||
} |
|||
} |
|||
|
|||
path.pop() |
|||
} |
|||
return [] |
|||
}, |
|||
_processLocalData() { |
|||
this._treeData = [] |
|||
this._extractTree(this.localdata, this._treeData) |
|||
|
|||
var inputValue = this.dataValue |
|||
if (inputValue === undefined) { |
|||
return |
|||
} |
|||
|
|||
if (Array.isArray(inputValue)) { |
|||
inputValue = inputValue[inputValue.length - 1] |
|||
if (typeof inputValue === 'object' && inputValue[this.map.value]) { |
|||
inputValue = inputValue[this.map.value] |
|||
} |
|||
} |
|||
|
|||
this.selected = this._findNodePath(inputValue, this.localdata) |
|||
} |
|||
} |
|||
} |
|||
@ -1,333 +0,0 @@ |
|||
<template> |
|||
<view class="uni-data-pickerview"> |
|||
<scroll-view class="selected-area" scroll-x="true" scroll-y="false" :show-scrollbar="false"> |
|||
<view class="selected-list"> |
|||
<template v-for="(item,index) in selected"> |
|||
<view class="selected-item" |
|||
:class="{'selected-item-active':index==selectedIndex, 'selected-item-text-overflow': ellipsis}" |
|||
:key="index" v-if="item.text" @click="handleSelect(index)"> |
|||
<text class="">{{item.text}}</text> |
|||
</view> |
|||
</template> |
|||
</view> |
|||
</scroll-view> |
|||
<view class="tab-c"> |
|||
<template v-for="(child, i) in dataList"> |
|||
<scroll-view class="list" :key="i" v-if="i==selectedIndex" :scroll-y="true"> |
|||
<view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in child" :key="j" |
|||
@click="handleNodeClick(item, i, j)"> |
|||
<text class="item-text item-text-overflow">{{item[map.text]}}</text> |
|||
<view class="check" v-if="selected.length > i && item[map.value] == selected[i].value"></view> |
|||
</view> |
|||
</scroll-view> |
|||
</template> |
|||
|
|||
<view class="loading-cover" v-if="loading"> |
|||
<uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more> |
|||
</view> |
|||
<view class="error-message" v-if="errorMessage"> |
|||
<text class="error-text">{{errorMessage}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import dataPicker from "./uni-data-picker.js" |
|||
|
|||
/** |
|||
* DataPickerview |
|||
* @description uni-data-pickerview |
|||
* @tutorial https://ext.dcloud.net.cn/plugin?id=3796 |
|||
* @property {Array} localdata 本地数据,参考 |
|||
* @property {Boolean} step-searh = [true|false] 是否分布查询 |
|||
* @value true 启用分布查询,仅查询当前选中节点 |
|||
* @value false 关闭分布查询,一次查询出所有数据 |
|||
* @property {String|DBFieldString} self-field 分布查询当前字段名称 |
|||
* @property {String|DBFieldString} parent-field 分布查询父字段名称 |
|||
* @property {String|DBCollectionString} collection 表名 |
|||
* @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 |
|||
* @property {String} orderby 排序字段及正序倒叙设置 |
|||
* @property {String|JQLString} where 查询条件 |
|||
*/ |
|||
export default { |
|||
name: 'UniDataPickerView', |
|||
emits: ['nodeclick', 'change', 'datachange', 'update:modelValue'], |
|||
mixins: [dataPicker], |
|||
props: { |
|||
managedMode: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
ellipsis: { |
|||
type: Boolean, |
|||
default: true |
|||
} |
|||
}, |
|||
data() { |
|||
return {} |
|||
}, |
|||
created() { |
|||
if (this.managedMode) { |
|||
return |
|||
} |
|||
|
|||
this.$nextTick(() => { |
|||
this.load() |
|||
}) |
|||
}, |
|||
methods: { |
|||
onPropsChange() { |
|||
this._treeData = [] |
|||
this.selectedIndex = 0 |
|||
this.load() |
|||
}, |
|||
load() { |
|||
if (this.isLocaldata) { |
|||
this.loadData() |
|||
} else if (this.dataValue.length) { |
|||
this.getTreePath((res) => { |
|||
this.loadData() |
|||
}) |
|||
} |
|||
}, |
|||
handleSelect(index) { |
|||
this.selectedIndex = index |
|||
}, |
|||
handleNodeClick(item, i, j) { |
|||
if (item.disable) { |
|||
return |
|||
} |
|||
const node = this.dataList[i][j] |
|||
const text = node[this.map.text] |
|||
const value = node[this.map.value] |
|||
if (i < this.selected.length - 1) { |
|||
this.selected.splice(i, this.selected.length - i) |
|||
this.selected.push({ |
|||
text, |
|||
value |
|||
}) |
|||
} else if (i === this.selected.length - 1) { |
|||
this.selected.splice(i, 1, { |
|||
text, |
|||
value |
|||
}) |
|||
} |
|||
|
|||
if (node.isleaf) { |
|||
this.onSelectedChange(node, node.isleaf) |
|||
return |
|||
} |
|||
|
|||
const { |
|||
isleaf, |
|||
hasNodes |
|||
} = this._updateBindData() |
|||
|
|||
if (!this._isTreeView() && !hasNodes) { |
|||
this.onSelectedChange(node, true) |
|||
return |
|||
} |
|||
|
|||
if (this.isLocaldata && (!hasNodes || isleaf)) { |
|||
this.onSelectedChange(node, true) |
|||
return |
|||
} |
|||
|
|||
if (!isleaf && !hasNodes) { |
|||
this._loadNodeData((data) => { |
|||
if (!data.length) { |
|||
node.isleaf = true |
|||
} else { |
|||
this._treeData.push(...data) |
|||
this._updateBindData(node) |
|||
} |
|||
this.onSelectedChange(node, node.isleaf) |
|||
}, this._nodeWhere()) |
|||
return |
|||
} |
|||
|
|||
this.onSelectedChange(node, false) |
|||
}, |
|||
updateData(data) { |
|||
this._treeData = data.treeData |
|||
this.selected = data.selected |
|||
if (!this._treeData.length) { |
|||
this.loadData() |
|||
} else { |
|||
//this.selected = data.selected |
|||
this._updateBindData() |
|||
} |
|||
}, |
|||
onDataChange() { |
|||
this.$emit('datachange') |
|||
}, |
|||
onSelectedChange(node, isleaf) { |
|||
if (isleaf) { |
|||
this._dispatchEvent() |
|||
} |
|||
|
|||
if (node) { |
|||
this.$emit('nodeclick', node) |
|||
} |
|||
}, |
|||
_dispatchEvent() { |
|||
this.$emit('change', this.selected.slice(0)) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style scoped> |
|||
.uni-data-pickerview { |
|||
flex: 1; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: column; |
|||
overflow: hidden; |
|||
height: 100%; |
|||
} |
|||
|
|||
.error-text { |
|||
color: #DD524D; |
|||
} |
|||
|
|||
.loading-cover { |
|||
position: absolute; |
|||
left: 0; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: rgba(255, 255, 255, .5); |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: column; |
|||
align-items: center; |
|||
z-index: 1001; |
|||
} |
|||
|
|||
.load-more { |
|||
/* #ifndef APP-NVUE */ |
|||
margin: auto; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.error-message { |
|||
background-color: #fff; |
|||
position: absolute; |
|||
left: 0; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
padding: 15px; |
|||
opacity: .9; |
|||
z-index: 102; |
|||
} |
|||
|
|||
/* #ifdef APP-NVUE */ |
|||
.selected-area { |
|||
width: 750rpx; |
|||
} |
|||
|
|||
/* #endif */ |
|||
|
|||
.selected-list { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
flex-wrap: nowrap; |
|||
padding: 0 5px; |
|||
border-bottom: 1px solid #f8f8f8; |
|||
} |
|||
|
|||
.selected-item { |
|||
margin-left: 10px; |
|||
margin-right: 10px; |
|||
padding: 12px 0; |
|||
text-align: center; |
|||
/* #ifndef APP-NVUE */ |
|||
white-space: nowrap; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.selected-item-text-overflow { |
|||
width: 168px; |
|||
/* fix nvue */ |
|||
overflow: hidden; |
|||
/* #ifndef APP-NVUE */ |
|||
width: 6em; |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
-o-text-overflow: ellipsis; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.selected-item-active { |
|||
border-bottom: 2px solid #007aff; |
|||
} |
|||
|
|||
.selected-item-text { |
|||
color: #007aff; |
|||
} |
|||
|
|||
.tab-c { |
|||
position: relative; |
|||
flex: 1; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.list { |
|||
flex: 1; |
|||
} |
|||
|
|||
.item { |
|||
padding: 12px 15px; |
|||
/* border-bottom: 1px solid #f0f0f0; */ |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
.is-disabled { |
|||
opacity: .5; |
|||
} |
|||
|
|||
.item-text { |
|||
/* flex: 1; */ |
|||
color: #333333; |
|||
} |
|||
|
|||
.item-text-overflow { |
|||
width: 280px; |
|||
/* fix nvue */ |
|||
overflow: hidden; |
|||
/* #ifndef APP-NVUE */ |
|||
width: 20em; |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
-o-text-overflow: ellipsis; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.check { |
|||
margin-right: 5px; |
|||
border: 2px solid #007aff; |
|||
border-left: 0; |
|||
border-top: 0; |
|||
height: 12px; |
|||
width: 6px; |
|||
transform-origin: center; |
|||
/* #ifndef APP-NVUE */ |
|||
transition: all 0.3s; |
|||
/* #endif */ |
|||
transform: rotate(45deg); |
|||
} |
|||
</style> |
|||
@ -1,92 +0,0 @@ |
|||
{ |
|||
"id": "uni-data-picker", |
|||
"displayName": "uni-data-picker 数据驱动的picker选择器", |
|||
"version": "1.0.1", |
|||
"description": "单列、多列级联选择器,常用于省市区城市选择、公司部门选择、多级分类等场景", |
|||
"keywords": [ |
|||
"uni-ui", |
|||
"uniui", |
|||
"picker", |
|||
"级联", |
|||
"省市区", |
|||
"" |
|||
], |
|||
"repository": "https://github.com/dcloudio/uni-ui", |
|||
"engines": { |
|||
"HBuilderX": "" |
|||
}, |
|||
"directories": { |
|||
"example": "../../temps/example_temps" |
|||
}, |
|||
"dcloudext": { |
|||
"category": [ |
|||
"前端组件", |
|||
"通用组件" |
|||
], |
|||
"sale": { |
|||
"regular": { |
|||
"price": "0.00" |
|||
}, |
|||
"sourcecode": { |
|||
"price": "0.00" |
|||
} |
|||
}, |
|||
"contact": { |
|||
"qq": "" |
|||
}, |
|||
"declaration": { |
|||
"ads": "无", |
|||
"data": "无", |
|||
"permissions": "无" |
|||
}, |
|||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
|||
}, |
|||
"uni_modules": { |
|||
"dependencies": [ |
|||
"uni-load-more", |
|||
"uni-icons", |
|||
"uni-scss" |
|||
], |
|||
"encrypt": [], |
|||
"platforms": { |
|||
"cloud": { |
|||
"tcb": "y", |
|||
"aliyun": "y" |
|||
}, |
|||
"client": { |
|||
"App": { |
|||
"app-vue": "y", |
|||
"app-nvue": "y" |
|||
}, |
|||
"H5-mobile": { |
|||
"Safari": "y", |
|||
"Android Browser": "y", |
|||
"微信浏览器(Android)": "y", |
|||
"QQ浏览器(Android)": "y" |
|||
}, |
|||
"H5-pc": { |
|||
"Chrome": "y", |
|||
"IE": "y", |
|||
"Edge": "y", |
|||
"Firefox": "y", |
|||
"Safari": "y" |
|||
}, |
|||
"小程序": { |
|||
"微信": "y", |
|||
"阿里": "y", |
|||
"百度": "y", |
|||
"字节跳动": "y", |
|||
"QQ": "y" |
|||
}, |
|||
"快应用": { |
|||
"华为": "u", |
|||
"联盟": "u" |
|||
}, |
|||
"Vue": { |
|||
"vue2": "y", |
|||
"vue3": "y" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
## DataPicker 级联选择 |
|||
> **组件名:uni-data-picker** |
|||
> 代码块: `uDataPicker` |
|||
> 关联组件:`uni-data-pickerview`、`uni-load-more`。 |
|||
|
|||
|
|||
`<uni-data-picker>` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。 |
|||
|
|||
支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 |
|||
|
|||
候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。 |
|||
|
|||
`<uni-data-picker>` 组件尤其适用于地址选择、分类选择等选择类。 |
|||
|
|||
`<uni-data-picker>` 支持本地数据、云端静态数据(json),uniCloud云数据库数据。 |
|||
|
|||
`<uni-data-picker>` 可以通过JQL直连uniCloud云数据库,配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema),可在schema2code中自动生成前端页面,还支持服务器端校验。 |
|||
|
|||
在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”,这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面,会自动生成地址管理的维护页面,自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。 |
|||
|
|||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-picker) |
|||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
|||
@ -1,85 +0,0 @@ |
|||
## 2.2.2(2021-12-10) |
|||
- 修复 clear-icon 属性在小程序平台不生效的 bug |
|||
## 2.2.1(2021-12-10) |
|||
- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的 bug |
|||
## 2.2.0(2021-11-19) |
|||
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
|||
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker) |
|||
## 2.1.5(2021-11-09) |
|||
- 新增 提供组件设计资源,组件样式调整 |
|||
## 2.1.4(2021-09-10) |
|||
- 修复 hide-second 在移动端的 bug |
|||
- 修复 单选赋默认值时,赋值日期未高亮的 bug |
|||
- 修复 赋默认值时,移动端未正确显示时间的 bug |
|||
## 2.1.3(2021-09-09) |
|||
- 新增 hide-second 属性,支持只使用时分,隐藏秒 |
|||
## 2.1.2(2021-09-03) |
|||
- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次 |
|||
- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法 |
|||
- 优化 调整字号大小,美化日历界面 |
|||
- 修复 因国际化导致的 placeholder 失效的 bug |
|||
## 2.1.1(2021-08-24) |
|||
- 新增 支持国际化 |
|||
- 优化 范围选择器在 pc 端过宽的问题 |
|||
## 2.1.0(2021-08-09) |
|||
- 新增 适配 vue3 |
|||
## 2.0.19(2021-08-09) |
|||
- 新增 支持作为 uni-forms 子组件相关功能 |
|||
- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug |
|||
## 2.0.18(2021-08-05) |
|||
- 修复 type 属性动态赋值无效的 bug |
|||
- 修复 ‘确认’按钮被 tabbar 遮盖 bug |
|||
- 修复 组件未赋值时范围选左、右日历相同的 bug |
|||
## 2.0.17(2021-08-04) |
|||
- 修复 范围选未正确显示当前值的 bug |
|||
- 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug |
|||
## 2.0.16(2021-07-21) |
|||
- 新增 return-type 属性支持返回 date 日期对象 |
|||
## 2.0.15(2021-07-14) |
|||
- 修复 单选日期类型,初始赋值后不在当前日历的 bug |
|||
- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效) |
|||
- 优化 移动端移除显示框的清空按钮,无实际用途 |
|||
## 2.0.14(2021-07-14) |
|||
- 修复 组件赋值为空,界面未更新的 bug |
|||
- 修复 start 和 end 不能动态赋值的 bug |
|||
- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的 bug |
|||
## 2.0.13(2021-07-08) |
|||
- 修复 范围选择不能动态赋值的 bug |
|||
## 2.0.12(2021-07-08) |
|||
- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug |
|||
## 2.0.11(2021-07-08) |
|||
- 优化 弹出层在超出视窗边缘定位不准确的问题 |
|||
## 2.0.10(2021-07-08) |
|||
- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的 bug |
|||
- 优化 弹出层在超出视窗边缘被遮盖的问题 |
|||
## 2.0.9(2021-07-07) |
|||
- 新增 maskClick 事件 |
|||
- 修复 特殊情况日历 rpx 布局错误的 bug,rpx -> px |
|||
- 修复 范围选择时清空返回值不合理的bug,['', ''] -> [] |
|||
## 2.0.8(2021-07-07) |
|||
- 新增 日期时间显示框支持插槽 |
|||
## 2.0.7(2021-07-01) |
|||
- 优化 添加 uni-icons 依赖 |
|||
## 2.0.6(2021-05-22) |
|||
- 修复 图标在小程序上不显示的 bug |
|||
- 优化 重命名引用组件,避免潜在组件命名冲突 |
|||
## 2.0.5(2021-05-20) |
|||
- 优化 代码目录扁平化 |
|||
## 2.0.4(2021-05-12) |
|||
- 新增 组件示例地址 |
|||
## 2.0.3(2021-05-10) |
|||
- 修复 ios 下不识别 '-' 日期格式的 bug |
|||
- 优化 pc 下弹出层添加边框和阴影 |
|||
## 2.0.2(2021-05-08) |
|||
- 修复 在 admin 中获取弹出层定位错误的bug |
|||
## 2.0.1(2021-05-08) |
|||
- 修复 type 属性向下兼容,默认值从 date 变更为 datetime |
|||
## 2.0.0(2021-04-30) |
|||
- 支持日历形式的日期+时间的范围选择 |
|||
> 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker) |
|||
## 1.0.6(2021-03-18) |
|||
- 新增 hide-second 属性,时间支持仅选择时、分 |
|||
- 修复 选择跟显示的日期不一样的 bug |
|||
- 修复 chang事件触发2次的 bug |
|||
- 修复 分、秒 end 范围错误的 bug |
|||
- 优化 更好的 nvue 适配 |
|||
@ -1,185 +0,0 @@ |
|||
<template> |
|||
<view class="uni-calendar-item__weeks-box" :class="{ |
|||
'uni-calendar-item--disable':weeks.disable, |
|||
'uni-calendar-item--before-checked-x':weeks.beforeMultiple, |
|||
'uni-calendar-item--multiple': weeks.multiple, |
|||
'uni-calendar-item--after-checked-x':weeks.afterMultiple, |
|||
}" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)"> |
|||
<view class="uni-calendar-item__weeks-box-item" :class="{ |
|||
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover), |
|||
'uni-calendar-item--checked-range-text': checkHover, |
|||
'uni-calendar-item--before-checked':weeks.beforeMultiple, |
|||
'uni-calendar-item--multiple': weeks.multiple, |
|||
'uni-calendar-item--after-checked':weeks.afterMultiple, |
|||
'uni-calendar-item--disable':weeks.disable, |
|||
}"> |
|||
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text> |
|||
<text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text> |
|||
</view> |
|||
<view :class="{'uni-calendar-item--isDay': weeks.isDay}"></view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { |
|||
weeks: { |
|||
type: Object, |
|||
default () { |
|||
return {} |
|||
} |
|||
}, |
|||
calendar: { |
|||
type: Object, |
|||
default: () => { |
|||
return {} |
|||
} |
|||
}, |
|||
selected: { |
|||
type: Array, |
|||
default: () => { |
|||
return [] |
|||
} |
|||
}, |
|||
lunar: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
checkHover: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
}, |
|||
methods: { |
|||
choiceDate(weeks) { |
|||
this.$emit('change', weeks) |
|||
}, |
|||
handleMousemove(weeks) { |
|||
this.$emit('handleMouse', weeks) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.uni-calendar-item__weeks-box { |
|||
flex: 1; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
margin: 1px 0; |
|||
position: relative; |
|||
} |
|||
|
|||
.uni-calendar-item__weeks-box-text { |
|||
font-size: 14px; |
|||
// font-family: Lato-Bold, Lato; |
|||
font-weight: bold; |
|||
color: #455997; |
|||
} |
|||
|
|||
.uni-calendar-item__weeks-lunar-text { |
|||
font-size: 12px; |
|||
color: #333; |
|||
} |
|||
|
|||
.uni-calendar-item__weeks-box-item { |
|||
position: relative; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
width: 40px; |
|||
height: 40px; |
|||
/* #ifdef H5 */ |
|||
cursor: pointer; |
|||
/* #endif */ |
|||
} |
|||
|
|||
|
|||
.uni-calendar-item__weeks-box-circle { |
|||
position: absolute; |
|||
top: 5px; |
|||
right: 5px; |
|||
width: 8px; |
|||
height: 8px; |
|||
border-radius: 8px; |
|||
background-color: #dd524d; |
|||
|
|||
} |
|||
|
|||
.uni-calendar-item__weeks-box .uni-calendar-item--disable { |
|||
// background-color: rgba(249, 249, 249, $uni-opacity-disabled); |
|||
cursor: default; |
|||
} |
|||
|
|||
.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable { |
|||
color: #D1D1D1; |
|||
} |
|||
|
|||
.uni-calendar-item--isDay { |
|||
position: absolute; |
|||
top: 10px; |
|||
right: 17%; |
|||
background-color: #dd524d; |
|||
width:6px; |
|||
height: 6px; |
|||
border-radius: 50%; |
|||
} |
|||
|
|||
.uni-calendar-item--extra { |
|||
color: #dd524d; |
|||
opacity: 0.8; |
|||
} |
|||
|
|||
.uni-calendar-item__weeks-box .uni-calendar-item--checked { |
|||
background-color: #007aff; |
|||
border-radius: 50%; |
|||
box-sizing: border-box; |
|||
border: 3px solid #fff; |
|||
} |
|||
|
|||
.uni-calendar-item--checked .uni-calendar-item--checked-text { |
|||
color: #fff; |
|||
} |
|||
|
|||
.uni-calendar-item--multiple .uni-calendar-item--checked-range-text { |
|||
color: #333; |
|||
} |
|||
|
|||
.uni-calendar-item--multiple { |
|||
background-color: #F6F7FC; |
|||
// color: #fff; |
|||
} |
|||
|
|||
.uni-calendar-item--multiple .uni-calendar-item--before-checked, |
|||
.uni-calendar-item--multiple .uni-calendar-item--after-checked { |
|||
background-color: #409eff; |
|||
border-radius: 50%; |
|||
box-sizing: border-box; |
|||
border: 3px solid #F6F7FC; |
|||
} |
|||
|
|||
.uni-calendar-item--before-checked .uni-calendar-item--checked-text, |
|||
.uni-calendar-item--after-checked .uni-calendar-item--checked-text { |
|||
color: #fff; |
|||
} |
|||
|
|||
.uni-calendar-item--before-checked-x { |
|||
border-top-left-radius: 50px; |
|||
border-bottom-left-radius: 50px; |
|||
box-sizing: border-box; |
|||
background-color: #F6F7FC; |
|||
} |
|||
|
|||
.uni-calendar-item--after-checked-x { |
|||
border-top-right-radius: 50px; |
|||
border-bottom-right-radius: 50px; |
|||
background-color: #F6F7FC; |
|||
} |
|||
</style> |
|||
@ -1,898 +0,0 @@ |
|||
<template> |
|||
<view class="uni-calendar" @mouseleave="leaveCale"> |
|||
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" |
|||
@click="clean"></view> |
|||
<view v-if="insert || show" class="uni-calendar__content" |
|||
:class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}"> |
|||
<view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}"> |
|||
<view v-if="left" class="uni-calendar__header-btn-box" @click.stop="pre"> |
|||
<view class="uni-calendar__header-btn uni-calendar--left"></view> |
|||
</view> |
|||
<picker mode="date" :value="date" fields="month" @change="bindDateChange"> |
|||
<text |
|||
class="uni-calendar__header-text">{{ (nowDate.year||'') + ' 年 ' + ( nowDate.month||'') +' 月'}}</text> |
|||
</picker> |
|||
<view v-if="right" class="uni-calendar__header-btn-box" @click.stop="next"> |
|||
<view class="uni-calendar__header-btn uni-calendar--right"></view> |
|||
</view> |
|||
<view v-if="!insert" class="dialog-close" @click="clean"> |
|||
<view class="dialog-close-plus" data-id="close"></view> |
|||
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view> |
|||
</view> |
|||
|
|||
<!-- <text class="uni-calendar__backtoday" @click="backtoday">回到今天</text> --> |
|||
</view> |
|||
<view class="uni-calendar__box"> |
|||
<view v-if="showMonth" class="uni-calendar__box-bg"> |
|||
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text> |
|||
</view> |
|||
<view class="uni-calendar__weeks" style="padding-bottom: 7px;"> |
|||
<view class="uni-calendar__weeks-day"> |
|||
<text class="uni-calendar__weeks-day-text">{{SUNText}}</text> |
|||
</view> |
|||
<view class="uni-calendar__weeks-day"> |
|||
<text class="uni-calendar__weeks-day-text">{{monText}}</text> |
|||
</view> |
|||
<view class="uni-calendar__weeks-day"> |
|||
<text class="uni-calendar__weeks-day-text">{{TUEText}}</text> |
|||
</view> |
|||
<view class="uni-calendar__weeks-day"> |
|||
<text class="uni-calendar__weeks-day-text">{{WEDText}}</text> |
|||
</view> |
|||
<view class="uni-calendar__weeks-day"> |
|||
<text class="uni-calendar__weeks-day-text">{{THUText}}</text> |
|||
</view> |
|||
<view class="uni-calendar__weeks-day"> |
|||
<text class="uni-calendar__weeks-day-text">{{FRIText}}</text> |
|||
</view> |
|||
<view class="uni-calendar__weeks-day"> |
|||
<text class="uni-calendar__weeks-day-text">{{SATText}}</text> |
|||
</view> |
|||
</view> |
|||
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex"> |
|||
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex"> |
|||
<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" |
|||
:selected="selected" :lunar="lunar" :checkHover="range" @change="choiceDate" |
|||
@handleMouse="handleMouse"> |
|||
</calendar-item> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view v-if="!insert && !range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top" |
|||
style="padding: 0 80px;"> |
|||
<view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view> |
|||
<time-picker type="time" :start="reactStartTime" :end="reactEndTime" v-model="time" |
|||
:disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style"> |
|||
</time-picker> |
|||
</view> |
|||
|
|||
<view v-if="!insert && range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top"> |
|||
<view class="uni-date-changed--time-start"> |
|||
<view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}} |
|||
</view> |
|||
<time-picker type="time" :start="reactStartTime" v-model="timeRange.startTime" :border="false" |
|||
:hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style"> |
|||
</time-picker> |
|||
</view> |
|||
<uni-icons type="arrowthinright" color="#999" style="line-height: 50px;"></uni-icons> |
|||
<view class="uni-date-changed--time-end"> |
|||
<view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view> |
|||
<time-picker type="time" :end="reactEndTime" v-model="timeRange.endTime" :border="false" |
|||
:hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style"> |
|||
</time-picker> |
|||
</view> |
|||
</view> |
|||
<view v-if="!insert" class="uni-date-changed uni-date-btn--ok"> |
|||
<!-- <view class="uni-calendar__header-btn-box"> |
|||
<text class="uni-calendar__button-text uni-calendar--fixed-width">{{okText}}</text> |
|||
</view> --> |
|||
<view class="uni-datetime-picker--btn" @click="confirm">确认</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import Calendar from './util.js'; |
|||
import calendarItem from './calendar-item.vue' |
|||
import timePicker from './time-picker.vue' |
|||
import { |
|||
initVueI18n |
|||
} from '@dcloudio/uni-i18n' |
|||
import messages from './i18n/index.js' |
|||
const { |
|||
t |
|||
} = initVueI18n(messages) |
|||
/** |
|||
* Calendar 日历 |
|||
* @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等 |
|||
* @tutorial https://ext.dcloud.net.cn/plugin?id=56 |
|||
* @property {String} date 自定义当前时间,默认为今天 |
|||
* @property {Boolean} lunar 显示农历 |
|||
* @property {String} startDate 日期选择范围-开始日期 |
|||
* @property {String} endDate 日期选择范围-结束日期 |
|||
* @property {Boolean} range 范围选择 |
|||
* @property {Boolean} insert = [true|false] 插入模式,默认为false |
|||
* @value true 弹窗模式 |
|||
* @value false 插入模式 |
|||
* @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容 |
|||
* @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
|||
* @property {Boolean} showMonth 是否选择月份为背景 |
|||
* @event {Function} change 日期改变,`insert :ture` 时生效 |
|||
* @event {Function} confirm 确认选择`insert :false` 时生效 |
|||
* @event {Function} monthSwitch 切换月份时触发 |
|||
* @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" /> |
|||
*/ |
|||
export default { |
|||
components: { |
|||
calendarItem, |
|||
timePicker |
|||
}, |
|||
props: { |
|||
date: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
defTime: { |
|||
type: [String, Object], |
|||
default: '' |
|||
}, |
|||
selectableTimes: { |
|||
type: [Object], |
|||
default () { |
|||
return {} |
|||
} |
|||
}, |
|||
selected: { |
|||
type: Array, |
|||
default () { |
|||
return [] |
|||
} |
|||
}, |
|||
lunar: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
startDate: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
endDate: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
range: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
typeHasTime: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
insert: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
showMonth: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
clearDate: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
left: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
right: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
checkHover: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
hideSecond: { |
|||
type: [Boolean], |
|||
default: false |
|||
}, |
|||
pleStatus: { |
|||
type: Object, |
|||
default () { |
|||
return { |
|||
before: '', |
|||
after: '', |
|||
data: [], |
|||
fulldate: '' |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
show: false, |
|||
weeks: [], |
|||
calendar: {}, |
|||
nowDate: '', |
|||
aniMaskShow: false, |
|||
firstEnter: true, |
|||
time: '', |
|||
timeRange: { |
|||
startTime: '', |
|||
endTime: '' |
|||
}, |
|||
tempSingleDate: '', |
|||
tempRange: { |
|||
before: '', |
|||
after: '' |
|||
} |
|||
} |
|||
}, |
|||
watch: { |
|||
date: { |
|||
immediate: true, |
|||
handler(newVal, oldVal) { |
|||
if (!this.range) { |
|||
this.tempSingleDate = newVal |
|||
setTimeout(() => { |
|||
this.init(newVal) |
|||
}, 100) |
|||
} |
|||
} |
|||
}, |
|||
defTime: { |
|||
immediate: true, |
|||
handler(newVal, oldVal) { |
|||
if (!this.range) { |
|||
this.time = newVal |
|||
} else { |
|||
// console.log('-----', newVal); |
|||
this.timeRange.startTime = newVal.start |
|||
this.timeRange.endTime = newVal.end |
|||
} |
|||
} |
|||
}, |
|||
startDate(val) { |
|||
this.cale.resetSatrtDate(val) |
|||
this.cale.setDate(this.nowDate.fullDate) |
|||
this.weeks = this.cale.weeks |
|||
}, |
|||
endDate(val) { |
|||
this.cale.resetEndDate(val) |
|||
this.cale.setDate(this.nowDate.fullDate) |
|||
this.weeks = this.cale.weeks |
|||
}, |
|||
selected(newVal) { |
|||
this.cale.setSelectInfo(this.nowDate.fullDate, newVal) |
|||
this.weeks = this.cale.weeks |
|||
}, |
|||
pleStatus: { |
|||
immediate: true, |
|||
handler(newVal, oldVal) { |
|||
const { |
|||
before, |
|||
after, |
|||
fulldate, |
|||
which |
|||
} = newVal |
|||
this.tempRange.before = before |
|||
this.tempRange.after = after |
|||
setTimeout(() => { |
|||
if (fulldate) { |
|||
this.cale.setHoverMultiple(fulldate) |
|||
if (before && after) { |
|||
this.cale.lastHover = true |
|||
if (this.rangeWithinMonth(after, before)) return |
|||
this.setDate(before) |
|||
} else { |
|||
this.cale.setMultiple(fulldate) |
|||
this.setDate(this.nowDate.fullDate) |
|||
this.calendar.fullDate = '' |
|||
this.cale.lastHover = false |
|||
} |
|||
} else { |
|||
this.cale.setDefaultMultiple(before, after) |
|||
if (which === 'left') { |
|||
this.setDate(before) |
|||
this.weeks = this.cale.weeks |
|||
} else { |
|||
this.setDate(after) |
|||
this.weeks = this.cale.weeks |
|||
} |
|||
this.cale.lastHover = true |
|||
} |
|||
}, 16) |
|||
} |
|||
} |
|||
}, |
|||
computed: { |
|||
reactStartTime() { |
|||
const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate |
|||
const res = activeDate === this.startDate ? this.selectableTimes.start : '' |
|||
return res |
|||
}, |
|||
reactEndTime() { |
|||
const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate |
|||
const res = activeDate === this.endDate ? this.selectableTimes.end : '' |
|||
return res |
|||
}, |
|||
/** |
|||
* for i18n |
|||
*/ |
|||
selectDateText() { |
|||
return t("uni-datetime-picker.selectDate") |
|||
}, |
|||
startDateText() { |
|||
return this.startPlaceholder || t("uni-datetime-picker.startDate") |
|||
}, |
|||
endDateText() { |
|||
return this.endPlaceholder || t("uni-datetime-picker.endDate") |
|||
}, |
|||
okText() { |
|||
return t("uni-datetime-picker.ok") |
|||
}, |
|||
monText() { |
|||
return t("uni-calender.MON") |
|||
}, |
|||
TUEText() { |
|||
return t("uni-calender.TUE") |
|||
}, |
|||
WEDText() { |
|||
return t("uni-calender.WED") |
|||
}, |
|||
THUText() { |
|||
return t("uni-calender.THU") |
|||
}, |
|||
FRIText() { |
|||
return t("uni-calender.FRI") |
|||
}, |
|||
SATText() { |
|||
return t("uni-calender.SAT") |
|||
}, |
|||
SUNText() { |
|||
return t("uni-calender.SUN") |
|||
}, |
|||
}, |
|||
created() { |
|||
// 获取日历方法实例 |
|||
this.cale = new Calendar({ |
|||
// date: new Date(), |
|||
selected: this.selected, |
|||
startDate: this.startDate, |
|||
endDate: this.endDate, |
|||
range: this.range, |
|||
// multipleStatus: this.pleStatus |
|||
}) |
|||
// 选中某一天 |
|||
// this.cale.setDate(this.date) |
|||
this.init(this.date) |
|||
// this.setDay |
|||
}, |
|||
methods: { |
|||
leaveCale() { |
|||
this.firstEnter = true |
|||
}, |
|||
handleMouse(weeks) { |
|||
if (weeks.disable) return |
|||
if (this.cale.lastHover) return |
|||
let { |
|||
before, |
|||
after |
|||
} = this.cale.multipleStatus |
|||
if (!before) return |
|||
this.calendar = weeks |
|||
// 设置范围选 |
|||
this.cale.setHoverMultiple(this.calendar.fullDate) |
|||
this.weeks = this.cale.weeks |
|||
// hover时,进入一个日历,更新另一个 |
|||
if (this.firstEnter) { |
|||
this.$emit('firstEnterCale', this.cale.multipleStatus) |
|||
this.firstEnter = false |
|||
} |
|||
}, |
|||
rangeWithinMonth(A, B) { |
|||
const [yearA, monthA] = A.split('-') |
|||
const [yearB, monthB] = B.split('-') |
|||
return yearA === yearB && monthA === monthB |
|||
}, |
|||
|
|||
// 取消穿透 |
|||
clean() { |
|||
this.close() |
|||
}, |
|||
|
|||
clearCalender() { |
|||
if (this.range) { |
|||
this.timeRange.startTime = '' |
|||
this.timeRange.endTime = '' |
|||
this.tempRange.before = '' |
|||
this.tempRange.after = '' |
|||
this.cale.multipleStatus.before = '' |
|||
this.cale.multipleStatus.after = '' |
|||
this.cale.multipleStatus.data = [] |
|||
this.cale.lastHover = false |
|||
} else { |
|||
this.time = '' |
|||
this.tempSingleDate = '' |
|||
} |
|||
this.calendar.fullDate = '' |
|||
this.setDate() |
|||
}, |
|||
|
|||
bindDateChange(e) { |
|||
const value = e.detail.value + '-1' |
|||
this.init(value) |
|||
}, |
|||
/** |
|||
* 初始化日期显示 |
|||
* @param {Object} date |
|||
*/ |
|||
init(date) { |
|||
this.cale.setDate(date) |
|||
this.weeks = this.cale.weeks |
|||
this.nowDate = this.calendar = this.cale.getInfo(date) |
|||
}, |
|||
// choiceDate(weeks) { |
|||
// if (weeks.disable) return |
|||
// this.calendar = weeks |
|||
// // 设置多选 |
|||
// this.cale.setMultiple(this.calendar.fullDate, true) |
|||
// this.weeks = this.cale.weeks |
|||
// this.tempSingleDate = this.calendar.fullDate |
|||
// this.tempRange.before = this.cale.multipleStatus.before |
|||
// this.tempRange.after = this.cale.multipleStatus.after |
|||
// this.change() |
|||
// }, |
|||
/** |
|||
* 打开日历弹窗 |
|||
*/ |
|||
open() { |
|||
// 弹窗模式并且清理数据 |
|||
if (this.clearDate && !this.insert) { |
|||
this.cale.cleanMultipleStatus() |
|||
// this.cale.setDate(this.date) |
|||
this.init(this.date) |
|||
} |
|||
this.show = true |
|||
this.$nextTick(() => { |
|||
setTimeout(() => { |
|||
this.aniMaskShow = true |
|||
}, 50) |
|||
}) |
|||
}, |
|||
/** |
|||
* 关闭日历弹窗 |
|||
*/ |
|||
close() { |
|||
this.aniMaskShow = false |
|||
this.$nextTick(() => { |
|||
setTimeout(() => { |
|||
this.show = false |
|||
this.$emit('close') |
|||
}, 300) |
|||
}) |
|||
}, |
|||
/** |
|||
* 确认按钮 |
|||
*/ |
|||
confirm() { |
|||
this.setEmit('confirm') |
|||
this.close() |
|||
}, |
|||
/** |
|||
* 变化触发 |
|||
*/ |
|||
change() { |
|||
if (!this.insert) return |
|||
this.setEmit('change') |
|||
}, |
|||
/** |
|||
* 选择月份触发 |
|||
*/ |
|||
monthSwitch() { |
|||
let { |
|||
year, |
|||
month |
|||
} = this.nowDate |
|||
this.$emit('monthSwitch', { |
|||
year, |
|||
month: Number(month) |
|||
}) |
|||
}, |
|||
/** |
|||
* 派发事件 |
|||
* @param {Object} name |
|||
*/ |
|||
setEmit(name) { |
|||
let { |
|||
year, |
|||
month, |
|||
date, |
|||
fullDate, |
|||
lunar, |
|||
extraInfo |
|||
} = this.calendar |
|||
this.$emit(name, { |
|||
range: this.cale.multipleStatus, |
|||
year, |
|||
month, |
|||
date, |
|||
time: this.time, |
|||
timeRange: this.timeRange, |
|||
fulldate: fullDate, |
|||
lunar, |
|||
extraInfo: extraInfo || {} |
|||
}) |
|||
}, |
|||
/** |
|||
* 选择天触发 |
|||
* @param {Object} weeks |
|||
*/ |
|||
choiceDate(weeks) { |
|||
if (weeks.disable) return |
|||
this.calendar = weeks |
|||
this.calendar.userChecked = true |
|||
// 设置多选 |
|||
this.cale.setMultiple(this.calendar.fullDate, true) |
|||
this.weeks = this.cale.weeks |
|||
this.tempSingleDate = this.calendar.fullDate |
|||
this.tempRange.before = this.cale.multipleStatus.before |
|||
this.tempRange.after = this.cale.multipleStatus.after |
|||
this.change() |
|||
}, |
|||
/** |
|||
* 回到今天 |
|||
*/ |
|||
backtoday() { |
|||
let date = this.cale.getDate(new Date()).fullDate |
|||
// this.cale.setDate(date) |
|||
this.init(date) |
|||
this.change() |
|||
}, |
|||
/** |
|||
* 比较时间大小 |
|||
*/ |
|||
dateCompare(startDate, endDate) { |
|||
// 计算截止时间 |
|||
startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
|||
// 计算详细项的截止时间 |
|||
endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
|||
if (startDate <= endDate) { |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
}, |
|||
/** |
|||
* 上个月 |
|||
*/ |
|||
pre() { |
|||
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate |
|||
this.setDate(preDate) |
|||
this.monthSwitch() |
|||
|
|||
}, |
|||
/** |
|||
* 下个月 |
|||
*/ |
|||
next() { |
|||
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate |
|||
this.setDate(nextDate) |
|||
this.monthSwitch() |
|||
}, |
|||
/** |
|||
* 设置日期 |
|||
* @param {Object} date |
|||
*/ |
|||
setDate(date) { |
|||
this.cale.setDate(date) |
|||
this.weeks = this.cale.weeks |
|||
this.nowDate = this.cale.getInfo(date) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.uni-calendar { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.uni-calendar__mask { |
|||
position: fixed; |
|||
bottom: 0; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
background-color: rgba(0, 0, 0, 0.4); |
|||
transition-property: opacity; |
|||
transition-duration: 0.3s; |
|||
opacity: 0; |
|||
/* #ifndef APP-NVUE */ |
|||
z-index: 99; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-calendar--mask-show { |
|||
opacity: 1 |
|||
} |
|||
|
|||
.uni-calendar--fixed { |
|||
position: fixed; |
|||
bottom: calc(var(--window-bottom)); |
|||
left: 0; |
|||
right: 0; |
|||
transition-property: transform; |
|||
transition-duration: 0.3s; |
|||
transform: translateY(460px); |
|||
/* #ifndef APP-NVUE */ |
|||
z-index: 99; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-calendar--ani-show { |
|||
transform: translateY(0); |
|||
} |
|||
|
|||
.uni-calendar__content { |
|||
background-color: #fff; |
|||
} |
|||
|
|||
.uni-calendar__content-mobile { |
|||
border-top-left-radius: 10px; |
|||
border-top-right-radius: 10px; |
|||
box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1); |
|||
} |
|||
|
|||
.uni-calendar__header { |
|||
position: relative; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
justify-content: center; |
|||
align-items: center; |
|||
height: 50px; |
|||
} |
|||
|
|||
.uni-calendar__header-mobile { |
|||
padding: 10px; |
|||
padding-bottom: 0; |
|||
} |
|||
|
|||
.uni-calendar--fixed-top { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
border-top-color: rgba(0, 0, 0, 0.4); |
|||
border-top-style: solid; |
|||
border-top-width: 1px; |
|||
} |
|||
|
|||
.uni-calendar--fixed-width { |
|||
width: 50px; |
|||
} |
|||
|
|||
.uni-calendar__backtoday { |
|||
position: absolute; |
|||
right: 0; |
|||
top: 25rpx; |
|||
padding: 0 5px; |
|||
padding-left: 10px; |
|||
height: 25px; |
|||
line-height: 25px; |
|||
font-size: 12px; |
|||
border-top-left-radius: 25px; |
|||
border-bottom-left-radius: 25px; |
|||
color: #fff; |
|||
background-color: #f1f1f1; |
|||
} |
|||
|
|||
.uni-calendar__header-text { |
|||
text-align: center; |
|||
width: 100px; |
|||
font-size: 15px; |
|||
color: #666; |
|||
} |
|||
|
|||
.uni-calendar__button-text { |
|||
text-align: center; |
|||
width: 100px; |
|||
font-size: 14px; |
|||
color: #007aff; |
|||
/* #ifndef APP-NVUE */ |
|||
letter-spacing: 3px; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-calendar__header-btn-box { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: center; |
|||
width: 50px; |
|||
height: 50px; |
|||
} |
|||
|
|||
.uni-calendar__header-btn { |
|||
width: 9px; |
|||
height: 9px; |
|||
border-left-color: #808080; |
|||
border-left-style: solid; |
|||
border-left-width: 1px; |
|||
border-top-color: #555555; |
|||
border-top-style: solid; |
|||
border-top-width: 1px; |
|||
} |
|||
|
|||
.uni-calendar--left { |
|||
transform: rotate(-45deg); |
|||
} |
|||
|
|||
.uni-calendar--right { |
|||
transform: rotate(135deg); |
|||
} |
|||
|
|||
|
|||
.uni-calendar__weeks { |
|||
position: relative; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
} |
|||
|
|||
.uni-calendar__weeks-item { |
|||
flex: 1; |
|||
} |
|||
|
|||
.uni-calendar__weeks-day { |
|||
flex: 1; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
height: 40px; |
|||
border-bottom-color: #F5F5F5; |
|||
border-bottom-style: solid; |
|||
border-bottom-width: 1px; |
|||
} |
|||
|
|||
.uni-calendar__weeks-day-text { |
|||
font-size: 12px; |
|||
color: #B2B2B2; |
|||
} |
|||
|
|||
.uni-calendar__box { |
|||
position: relative; |
|||
// padding: 0 10px; |
|||
padding-bottom: 7px; |
|||
} |
|||
|
|||
.uni-calendar__box-bg { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
justify-content: center; |
|||
align-items: center; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
} |
|||
|
|||
.uni-calendar__box-bg-text { |
|||
font-size: 200px; |
|||
font-weight: bold; |
|||
color: #999; |
|||
opacity: 0.1; |
|||
text-align: center; |
|||
/* #ifndef APP-NVUE */ |
|||
line-height: 1; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-date-changed { |
|||
padding: 0 10px; |
|||
// line-height: 50px; |
|||
text-align: center; |
|||
color: #333; |
|||
border-top-color: #DCDCDC; |
|||
; |
|||
border-top-style: solid; |
|||
border-top-width: 1px; |
|||
flex: 1; |
|||
} |
|||
|
|||
.uni-date-btn--ok { |
|||
padding: 20px 15px; |
|||
} |
|||
|
|||
.uni-date-changed--time-start { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
align-items: center; |
|||
} |
|||
|
|||
.uni-date-changed--time-end { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
align-items: center; |
|||
} |
|||
|
|||
.uni-date-changed--time-date { |
|||
color: #999; |
|||
line-height: 50px; |
|||
margin-right: 5px; |
|||
// opacity: 0.6; |
|||
} |
|||
|
|||
.time-picker-style { |
|||
// width: 62px; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
justify-content: center; |
|||
align-items: center |
|||
} |
|||
|
|||
.mr-10 { |
|||
margin-right: 10px; |
|||
} |
|||
|
|||
.dialog-close { |
|||
position: absolute; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
align-items: center; |
|||
padding: 0 25px; |
|||
margin-top: 10px; |
|||
} |
|||
|
|||
.dialog-close-plus { |
|||
width: 16px; |
|||
height: 2px; |
|||
background-color: #737987; |
|||
border-radius: 2px; |
|||
transform: rotate(45deg); |
|||
} |
|||
|
|||
.dialog-close-rotate { |
|||
position: absolute; |
|||
transform: rotate(-45deg); |
|||
} |
|||
|
|||
.uni-datetime-picker--btn { |
|||
border-radius: 100px; |
|||
height: 40px; |
|||
line-height: 40px; |
|||
background-color: #007aff; |
|||
color: #fff; |
|||
font-size: 16px; |
|||
letter-spacing: 5px; |
|||
} |
|||
|
|||
/* #ifndef APP-NVUE */ |
|||
.uni-datetime-picker--btn:active { |
|||
opacity: 0.7; |
|||
} |
|||
/* #endif */ |
|||
</style> |
|||
@ -1,19 +0,0 @@ |
|||
{ |
|||
"uni-datetime-picker.selectDate": "select date", |
|||
"uni-datetime-picker.selectTime": "select time", |
|||
"uni-datetime-picker.selectDateTime": "select datetime", |
|||
"uni-datetime-picker.startDate": "start date", |
|||
"uni-datetime-picker.endDate": "end date", |
|||
"uni-datetime-picker.startTime": "start time", |
|||
"uni-datetime-picker.endTime": "end time", |
|||
"uni-datetime-picker.ok": "ok", |
|||
"uni-datetime-picker.clear": "clear", |
|||
"uni-datetime-picker.cancel": "cancel", |
|||
"uni-calender.MON": "MON", |
|||
"uni-calender.TUE": "TUE", |
|||
"uni-calender.WED": "WED", |
|||
"uni-calender.THU": "THU", |
|||
"uni-calender.FRI": "FRI", |
|||
"uni-calender.SAT": "SAT", |
|||
"uni-calender.SUN": "SUN" |
|||
} |
|||
@ -1,8 +0,0 @@ |
|||
import en from './en.json' |
|||
import zhHans from './zh-Hans.json' |
|||
import zhHant from './zh-Hant.json' |
|||
export default { |
|||
en, |
|||
'zh-Hans': zhHans, |
|||
'zh-Hant': zhHant |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
{ |
|||
"uni-datetime-picker.selectDate": "选择日期", |
|||
"uni-datetime-picker.selectTime": "选择时间", |
|||
"uni-datetime-picker.selectDateTime": "选择日期时间", |
|||
"uni-datetime-picker.startDate": "开始日期", |
|||
"uni-datetime-picker.endDate": "结束日期", |
|||
"uni-datetime-picker.startTime": "开始时间", |
|||
"uni-datetime-picker.endTime": "结束时间", |
|||
"uni-datetime-picker.ok": "确定", |
|||
"uni-datetime-picker.clear": "清除", |
|||
"uni-datetime-picker.cancel": "取消", |
|||
"uni-calender.SUN": "日", |
|||
"uni-calender.MON": "一", |
|||
"uni-calender.TUE": "二", |
|||
"uni-calender.WED": "三", |
|||
"uni-calender.THU": "四", |
|||
"uni-calender.FRI": "五", |
|||
"uni-calender.SAT": "六" |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
{ |
|||
"uni-datetime-picker.selectDate": "選擇日期", |
|||
"uni-datetime-picker.selectTime": "選擇時間", |
|||
"uni-datetime-picker.selectDateTime": "選擇日期時間", |
|||
"uni-datetime-picker.startDate": "開始日期", |
|||
"uni-datetime-picker.endDate": "結束日期", |
|||
"uni-datetime-picker.startTime": "開始时间", |
|||
"uni-datetime-picker.endTime": "結束时间", |
|||
"uni-datetime-picker.ok": "確定", |
|||
"uni-datetime-picker.clear": "清除", |
|||
"uni-datetime-picker.cancel": "取消", |
|||
"uni-calender.SUN": "日", |
|||
"uni-calender.MON": "一", |
|||
"uni-calender.TUE": "二", |
|||
"uni-calender.WED": "三", |
|||
"uni-calender.THU": "四", |
|||
"uni-calender.FRI": "五", |
|||
"uni-calender.SAT": "六" |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
// #ifdef H5
|
|||
export default { |
|||
name: 'Keypress', |
|||
props: { |
|||
disable: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
}, |
|||
mounted () { |
|||
const keyNames = { |
|||
esc: ['Esc', 'Escape'], |
|||
tab: 'Tab', |
|||
enter: 'Enter', |
|||
space: [' ', 'Spacebar'], |
|||
up: ['Up', 'ArrowUp'], |
|||
left: ['Left', 'ArrowLeft'], |
|||
right: ['Right', 'ArrowRight'], |
|||
down: ['Down', 'ArrowDown'], |
|||
delete: ['Backspace', 'Delete', 'Del'] |
|||
} |
|||
const listener = ($event) => { |
|||
if (this.disable) { |
|||
return |
|||
} |
|||
const keyName = Object.keys(keyNames).find(key => { |
|||
const keyName = $event.key |
|||
const value = keyNames[key] |
|||
return value === keyName || (Array.isArray(value) && value.includes(keyName)) |
|||
}) |
|||
if (keyName) { |
|||
// 避免和其他按键事件冲突
|
|||
setTimeout(() => { |
|||
this.$emit(keyName, {}) |
|||
}, 0) |
|||
} |
|||
} |
|||
document.addEventListener('keyup', listener) |
|||
this.$once('hook:beforeDestroy', () => { |
|||
document.removeEventListener('keyup', listener) |
|||
}) |
|||
}, |
|||
render: () => {} |
|||
} |
|||
// #endif
|
|||
@ -1,927 +0,0 @@ |
|||
<template> |
|||
<view class="uni-datetime-picker"> |
|||
<view @click="initTimePicker"> |
|||
<slot> |
|||
<view class="uni-datetime-picker-timebox-pointer" |
|||
:class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}"> |
|||
<text class="uni-datetime-picker-text">{{time}}</text> |
|||
<view v-if="!time" class="uni-datetime-picker-time"> |
|||
<text class="uni-datetime-picker-text">{{selectTimeText}}</text> |
|||
</view> |
|||
</view> |
|||
</slot> |
|||
</view> |
|||
<view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view> |
|||
<view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']" |
|||
:style="fixNvueBug"> |
|||
<view class="uni-title"> |
|||
<text class="uni-datetime-picker-text">{{selectTimeText}}</text> |
|||
</view> |
|||
<view v-if="dateShow" class="uni-datetime-picker__container-box"> |
|||
<picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd" |
|||
@change="bindDateChange"> |
|||
<picker-view-column> |
|||
<view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index"> |
|||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
|||
</view> |
|||
</picker-view-column> |
|||
<picker-view-column> |
|||
<view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index"> |
|||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
|||
</view> |
|||
</picker-view-column> |
|||
<picker-view-column> |
|||
<view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index"> |
|||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
|||
</view> |
|||
</picker-view-column> |
|||
</picker-view> |
|||
<!-- 兼容 nvue 不支持伪类 --> |
|||
<text class="uni-datetime-picker-sign sign-left">-</text> |
|||
<text class="uni-datetime-picker-sign sign-right">-</text> |
|||
</view> |
|||
<view v-if="timeShow" class="uni-datetime-picker__container-box"> |
|||
<picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']" |
|||
:indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange"> |
|||
<picker-view-column> |
|||
<view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index"> |
|||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
|||
</view> |
|||
</picker-view-column> |
|||
<picker-view-column> |
|||
<view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index"> |
|||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
|||
</view> |
|||
</picker-view-column> |
|||
<picker-view-column v-if="!hideSecond"> |
|||
<view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index"> |
|||
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
|||
</view> |
|||
</picker-view-column> |
|||
</picker-view> |
|||
<!-- 兼容 nvue 不支持伪类 --> |
|||
<text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text> |
|||
<text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text> |
|||
</view> |
|||
<view class="uni-datetime-picker-btn"> |
|||
<view @click="clearTime"> |
|||
<text class="uni-datetime-picker-btn-text">{{clearText}}</text> |
|||
</view> |
|||
<view class="uni-datetime-picker-btn-group"> |
|||
<view class="uni-datetime-picker-cancel" @click="tiggerTimePicker"> |
|||
<text class="uni-datetime-picker-btn-text">{{cancelText}}</text> |
|||
</view> |
|||
<view @click="setTime"> |
|||
<text class="uni-datetime-picker-btn-text">{{okText}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<!-- #ifdef H5 --> |
|||
<!-- <keypress v-if="visible" @esc="tiggerTimePicker" @enter="setTime" /> --> |
|||
<!-- #endif --> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
// #ifdef H5 |
|||
import keypress from './keypress' |
|||
// #endif |
|||
import { |
|||
initVueI18n |
|||
} from '@dcloudio/uni-i18n' |
|||
import messages from './i18n/index.js' |
|||
const { t } = initVueI18n(messages) |
|||
|
|||
/** |
|||
* DatetimePicker 时间选择器 |
|||
* @description 可以同时选择日期和时间的选择器 |
|||
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx |
|||
* @property {String} type = [datetime | date | time] 显示模式 |
|||
* @property {Boolean} multiple = [true|false] 是否多选 |
|||
* @property {String|Number} value 默认值 |
|||
* @property {String|Number} start 起始日期或时间 |
|||
* @property {String|Number} end 起始日期或时间 |
|||
* @property {String} return-type = [timestamp | string] |
|||
* @event {Function} change 选中发生变化触发 |
|||
*/ |
|||
|
|||
export default { |
|||
name: 'UniDatetimePicker', |
|||
components: { |
|||
// #ifdef H5 |
|||
keypress |
|||
// #endif |
|||
}, |
|||
data() { |
|||
return { |
|||
indicatorStyle: `height: 50px;`, |
|||
visible: false, |
|||
fixNvueBug: {}, |
|||
dateShow: true, |
|||
timeShow: true, |
|||
title: '日期和时间', |
|||
// 输入框当前时间 |
|||
time: '', |
|||
// 当前的年月日时分秒 |
|||
year: 1920, |
|||
month: 0, |
|||
day: 0, |
|||
hour: 0, |
|||
minute: 0, |
|||
second: 0, |
|||
// 起始时间 |
|||
startYear: 1920, |
|||
startMonth: 1, |
|||
startDay: 1, |
|||
startHour: 0, |
|||
startMinute: 0, |
|||
startSecond: 0, |
|||
// 结束时间 |
|||
endYear: 2120, |
|||
endMonth: 12, |
|||
endDay: 31, |
|||
endHour: 23, |
|||
endMinute: 59, |
|||
endSecond: 59, |
|||
} |
|||
}, |
|||
props: { |
|||
type: { |
|||
type: String, |
|||
default: 'datetime' |
|||
}, |
|||
value: { |
|||
type: [String, Number], |
|||
default: '' |
|||
}, |
|||
modelValue: { |
|||
type: [String, Number], |
|||
default: '' |
|||
}, |
|||
start: { |
|||
type: [Number, String], |
|||
default: '' |
|||
}, |
|||
end: { |
|||
type: [Number, String], |
|||
default: '' |
|||
}, |
|||
returnType: { |
|||
type: String, |
|||
default: 'string' |
|||
}, |
|||
disabled: { |
|||
type: [Boolean, String], |
|||
default: false |
|||
}, |
|||
border: { |
|||
type: [Boolean, String], |
|||
default: true |
|||
}, |
|||
hideSecond: { |
|||
type: [Boolean, String], |
|||
default: false |
|||
} |
|||
}, |
|||
watch: { |
|||
value: { |
|||
handler(newVal, oldVal) { |
|||
if (newVal) { |
|||
this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari 日期格式 |
|||
this.initTime(false) |
|||
} else { |
|||
this.time = '' |
|||
this.parseValue(Date.now()) |
|||
} |
|||
}, |
|||
immediate: true |
|||
}, |
|||
type: { |
|||
handler(newValue) { |
|||
if (newValue === 'date') { |
|||
this.dateShow = true |
|||
this.timeShow = false |
|||
this.title = '日期' |
|||
} else if (newValue === 'time') { |
|||
this.dateShow = false |
|||
this.timeShow = true |
|||
this.title = '时间' |
|||
} else { |
|||
this.dateShow = true |
|||
this.timeShow = true |
|||
this.title = '日期和时间' |
|||
} |
|||
}, |
|||
immediate: true |
|||
}, |
|||
start: { |
|||
handler(newVal) { |
|||
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'start') //兼容 iOS、safari 日期格式 |
|||
}, |
|||
immediate: true |
|||
}, |
|||
end: { |
|||
handler(newVal) { |
|||
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') //兼容 iOS、safari 日期格式 |
|||
}, |
|||
immediate: true |
|||
}, |
|||
|
|||
// 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项 |
|||
months(newVal) { |
|||
this.checkValue('month', this.month, newVal) |
|||
}, |
|||
days(newVal) { |
|||
this.checkValue('day', this.day, newVal) |
|||
}, |
|||
hours(newVal) { |
|||
this.checkValue('hour', this.hour, newVal) |
|||
}, |
|||
minutes(newVal) { |
|||
this.checkValue('minute', this.minute, newVal) |
|||
}, |
|||
seconds(newVal) { |
|||
this.checkValue('second', this.second, newVal) |
|||
} |
|||
}, |
|||
computed: { |
|||
// 当前年、月、日、时、分、秒选择范围 |
|||
years() { |
|||
return this.getCurrentRange('year') |
|||
}, |
|||
|
|||
months() { |
|||
return this.getCurrentRange('month') |
|||
}, |
|||
|
|||
days() { |
|||
return this.getCurrentRange('day') |
|||
}, |
|||
|
|||
hours() { |
|||
return this.getCurrentRange('hour') |
|||
}, |
|||
|
|||
minutes() { |
|||
return this.getCurrentRange('minute') |
|||
}, |
|||
|
|||
seconds() { |
|||
return this.getCurrentRange('second') |
|||
}, |
|||
|
|||
// picker 当前值数组 |
|||
ymd() { |
|||
return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay] |
|||
}, |
|||
hms() { |
|||
return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond] |
|||
}, |
|||
|
|||
// 当前 date 是 start |
|||
currentDateIsStart() { |
|||
return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay |
|||
}, |
|||
|
|||
// 当前 date 是 end |
|||
currentDateIsEnd() { |
|||
return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay |
|||
}, |
|||
|
|||
// 当前年、月、日、时、分、秒的最小值和最大值 |
|||
minYear() { |
|||
return this.startYear |
|||
}, |
|||
maxYear() { |
|||
return this.endYear |
|||
}, |
|||
minMonth() { |
|||
if (this.year === this.startYear) { |
|||
return this.startMonth |
|||
} else { |
|||
return 1 |
|||
} |
|||
}, |
|||
maxMonth() { |
|||
if (this.year === this.endYear) { |
|||
return this.endMonth |
|||
} else { |
|||
return 12 |
|||
} |
|||
}, |
|||
minDay() { |
|||
if (this.year === this.startYear && this.month === this.startMonth) { |
|||
return this.startDay |
|||
} else { |
|||
return 1 |
|||
} |
|||
}, |
|||
maxDay() { |
|||
if (this.year === this.endYear && this.month === this.endMonth) { |
|||
return this.endDay |
|||
} else { |
|||
return this.daysInMonth(this.year, this.month) |
|||
} |
|||
}, |
|||
minHour() { |
|||
if (this.type === 'datetime') { |
|||
if (this.currentDateIsStart) { |
|||
return this.startHour |
|||
} else { |
|||
return 0 |
|||
} |
|||
} |
|||
if (this.type === 'time') { |
|||
return this.startHour |
|||
} |
|||
}, |
|||
maxHour() { |
|||
if (this.type === 'datetime') { |
|||
if (this.currentDateIsEnd) { |
|||
return this.endHour |
|||
} else { |
|||
return 23 |
|||
} |
|||
} |
|||
if (this.type === 'time') { |
|||
return this.endHour |
|||
} |
|||
}, |
|||
minMinute() { |
|||
if (this.type === 'datetime') { |
|||
if (this.currentDateIsStart && this.hour === this.startHour) { |
|||
return this.startMinute |
|||
} else { |
|||
return 0 |
|||
} |
|||
} |
|||
if (this.type === 'time') { |
|||
if (this.hour === this.startHour) { |
|||
return this.startMinute |
|||
} else { |
|||
return 0 |
|||
} |
|||
} |
|||
}, |
|||
maxMinute() { |
|||
if (this.type === 'datetime') { |
|||
if (this.currentDateIsEnd && this.hour === this.endHour) { |
|||
return this.endMinute |
|||
} else { |
|||
return 59 |
|||
} |
|||
} |
|||
if (this.type === 'time') { |
|||
if (this.hour === this.endHour) { |
|||
return this.endMinute |
|||
} else { |
|||
return 59 |
|||
} |
|||
} |
|||
}, |
|||
minSecond() { |
|||
if (this.type === 'datetime') { |
|||
if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) { |
|||
return this.startSecond |
|||
} else { |
|||
return 0 |
|||
} |
|||
} |
|||
if (this.type === 'time') { |
|||
if (this.hour === this.startHour && this.minute === this.startMinute) { |
|||
return this.startSecond |
|||
} else { |
|||
return 0 |
|||
} |
|||
} |
|||
}, |
|||
maxSecond() { |
|||
if (this.type === 'datetime') { |
|||
if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) { |
|||
return this.endSecond |
|||
} else { |
|||
return 59 |
|||
} |
|||
} |
|||
if (this.type === 'time') { |
|||
if (this.hour === this.endHour && this.minute === this.endMinute) { |
|||
return this.endSecond |
|||
} else { |
|||
return 59 |
|||
} |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* for i18n |
|||
*/ |
|||
selectTimeText() { |
|||
return t("uni-datetime-picker.selectTime") |
|||
}, |
|||
okText() { |
|||
return t("uni-datetime-picker.ok") |
|||
}, |
|||
clearText() { |
|||
return t("uni-datetime-picker.clear") |
|||
}, |
|||
cancelText() { |
|||
return t("uni-datetime-picker.cancel") |
|||
} |
|||
}, |
|||
|
|||
mounted() { |
|||
// #ifdef APP-NVUE |
|||
const res = uni.getSystemInfoSync(); |
|||
this.fixNvueBug = { |
|||
top: res.windowHeight / 2, |
|||
left: res.windowWidth / 2 |
|||
} |
|||
// #endif |
|||
}, |
|||
|
|||
methods: { |
|||
/** |
|||
* @param {Object} item |
|||
* 小于 10 在前面加个 0 |
|||
*/ |
|||
|
|||
lessThanTen(item) { |
|||
return item < 10 ? '0' + item : item |
|||
}, |
|||
|
|||
/** |
|||
* 解析时分秒字符串,例如:00:00:00 |
|||
* @param {String} timeString |
|||
*/ |
|||
parseTimeType(timeString) { |
|||
if (timeString) { |
|||
let timeArr = timeString.split(':') |
|||
this.hour = Number(timeArr[0]) |
|||
this.minute = Number(timeArr[1]) |
|||
this.second = Number(timeArr[2]) |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000 |
|||
* @param {String | Number} datetime |
|||
*/ |
|||
initPickerValue(datetime) { |
|||
let defaultValue = null |
|||
if (datetime) { |
|||
defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end) |
|||
} else { |
|||
defaultValue = Date.now() |
|||
defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end) |
|||
} |
|||
this.parseValue(defaultValue) |
|||
}, |
|||
|
|||
/** |
|||
* 初始值规则: |
|||
* - 用户设置初始值 value |
|||
* - 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start |
|||
* - 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start |
|||
* - 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end |
|||
* - 无起始终止时间,则初始值为 value |
|||
* - 无初始值 value,则初始值为当前本地时间 Date.now() |
|||
* @param {Object} value |
|||
* @param {Object} dateBase |
|||
*/ |
|||
compareValueWithStartAndEnd(value, start, end) { |
|||
let winner = null |
|||
value = this.superTimeStamp(value) |
|||
start = this.superTimeStamp(start) |
|||
end = this.superTimeStamp(end) |
|||
|
|||
if (start && end) { |
|||
if (value < start) { |
|||
winner = new Date(start) |
|||
} else if (value > end) { |
|||
winner = new Date(end) |
|||
} else { |
|||
winner = new Date(value) |
|||
} |
|||
} else if (start && !end) { |
|||
winner = start <= value ? new Date(value) : new Date(start) |
|||
} else if (!start && end) { |
|||
winner = value <= end ? new Date(value) : new Date(end) |
|||
} else { |
|||
winner = new Date(value) |
|||
} |
|||
|
|||
return winner |
|||
}, |
|||
|
|||
/** |
|||
* 转换为可比较的时间戳,接受日期、时分秒、时间戳 |
|||
* @param {Object} value |
|||
*/ |
|||
superTimeStamp(value) { |
|||
let dateBase = '' |
|||
if (this.type === 'time' && value && typeof value === 'string') { |
|||
const now = new Date() |
|||
const year = now.getFullYear() |
|||
const month = now.getMonth() + 1 |
|||
const day = now.getDate() |
|||
dateBase = year + '/' + month + '/' + day + ' ' |
|||
} |
|||
if (Number(value) && typeof value !== NaN) { |
|||
value = parseInt(value) |
|||
dateBase = 0 |
|||
} |
|||
return this.createTimeStamp(dateBase + value) |
|||
}, |
|||
|
|||
/** |
|||
* 解析默认值 value,字符串、时间戳 |
|||
* @param {Object} defaultTime |
|||
*/ |
|||
parseValue(value) { |
|||
if (!value) { |
|||
return |
|||
} |
|||
if (this.type === 'time' && typeof value === "string") { |
|||
this.parseTimeType(value) |
|||
} else { |
|||
let defaultDate = null |
|||
defaultDate = new Date(value) |
|||
if (this.type !== 'time') { |
|||
this.year = defaultDate.getFullYear() |
|||
this.month = defaultDate.getMonth() + 1 |
|||
this.day = defaultDate.getDate() |
|||
} |
|||
if (this.type !== 'date') { |
|||
this.hour = defaultDate.getHours() |
|||
this.minute = defaultDate.getMinutes() |
|||
this.second = defaultDate.getSeconds() |
|||
} |
|||
} |
|||
if (this.hideSecond) { |
|||
this.second = 0 |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 解析可选择时间范围 start、end,年月日字符串、时间戳 |
|||
* @param {Object} defaultTime |
|||
*/ |
|||
parseDatetimeRange(point, pointType) { |
|||
// 时间为空,则重置为初始值 |
|||
if (!point) { |
|||
if (pointType === 'start') { |
|||
this.startYear = 1920 |
|||
this.startMonth = 1 |
|||
this.startDay = 1 |
|||
this.startHour = 0 |
|||
this.startMinute = 0 |
|||
this.startSecond = 0 |
|||
} |
|||
if (pointType === 'end') { |
|||
this.endYear = 2120 |
|||
this.endMonth = 12 |
|||
this.endDay = 31 |
|||
this.endHour = 23 |
|||
this.endMinute = 59 |
|||
this.endSecond = 59 |
|||
} |
|||
return |
|||
} |
|||
if (this.type === 'time') { |
|||
const pointArr = point.split(':') |
|||
this[pointType + 'Hour'] = Number(pointArr[0]) |
|||
this[pointType + 'Minute'] = Number(pointArr[1]) |
|||
this[pointType + 'Second'] = Number(pointArr[2]) |
|||
} else { |
|||
if (!point) { |
|||
pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60 |
|||
return |
|||
} |
|||
if (Number(point) && Number(point) !== NaN) { |
|||
point = parseInt(point) |
|||
} |
|||
// datetime 的 end 没有时分秒, 则不限制 |
|||
const hasTime = /[0-9]:[0-9]/ |
|||
if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test( |
|||
point)) { |
|||
point = point + ' 23:59:59' |
|||
} |
|||
const pointDate = new Date(point) |
|||
this[pointType + 'Year'] = pointDate.getFullYear() |
|||
this[pointType + 'Month'] = pointDate.getMonth() + 1 |
|||
this[pointType + 'Day'] = pointDate.getDate() |
|||
if (this.type === 'datetime') { |
|||
this[pointType + 'Hour'] = pointDate.getHours() |
|||
this[pointType + 'Minute'] = pointDate.getMinutes() |
|||
this[pointType + 'Second'] = pointDate.getSeconds() |
|||
} |
|||
} |
|||
}, |
|||
|
|||
// 获取 年、月、日、时、分、秒 当前可选范围 |
|||
getCurrentRange(value) { |
|||
const range = [] |
|||
for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) { |
|||
range.push(i) |
|||
} |
|||
return range |
|||
}, |
|||
|
|||
// 字符串首字母大写 |
|||
capitalize(str) { |
|||
return str.charAt(0).toUpperCase() + str.slice(1) |
|||
}, |
|||
|
|||
// 检查当前值是否在范围内,不在则当前值重置为可选范围第一项 |
|||
checkValue(name, value, values) { |
|||
if (values.indexOf(value) === -1) { |
|||
this[name] = values[0] |
|||
} |
|||
}, |
|||
|
|||
// 每个月的实际天数 |
|||
daysInMonth(year, month) { // Use 1 for January, 2 for February, etc. |
|||
return new Date(year, month, 0).getDate(); |
|||
}, |
|||
|
|||
//兼容 iOS、safari 日期格式 |
|||
fixIosDateFormat(value) { |
|||
if (typeof value === 'string') { |
|||
value = value.replace(/-/g, '/') |
|||
} |
|||
return value |
|||
}, |
|||
|
|||
/** |
|||
* 生成时间戳 |
|||
* @param {Object} time |
|||
*/ |
|||
createTimeStamp(time) { |
|||
if (!time) return |
|||
if (typeof time === "number") { |
|||
return time |
|||
} else { |
|||
time = time.replace(/-/g, '/') |
|||
if (this.type === 'date') { |
|||
time = time + ' ' + '00:00:00' |
|||
} |
|||
return Date.parse(time) |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 生成日期或时间的字符串 |
|||
*/ |
|||
createDomSting() { |
|||
const yymmdd = this.year + |
|||
'-' + |
|||
this.lessThanTen(this.month) + |
|||
'-' + |
|||
this.lessThanTen(this.day) |
|||
|
|||
let hhmmss = this.lessThanTen(this.hour) + |
|||
':' + |
|||
this.lessThanTen(this.minute) |
|||
|
|||
if (!this.hideSecond) { |
|||
hhmmss = hhmmss + ':' + this.lessThanTen(this.second) |
|||
} |
|||
|
|||
if (this.type === 'date') { |
|||
return yymmdd |
|||
} else if (this.type === 'time') { |
|||
return hhmmss |
|||
} else { |
|||
return yymmdd + ' ' + hhmmss |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 初始化返回值,并抛出 change 事件 |
|||
*/ |
|||
initTime(emit = true) { |
|||
this.time = this.createDomSting() |
|||
if (!emit) return |
|||
if (this.returnType === 'timestamp' && this.type !== 'time') { |
|||
this.$emit('change', this.createTimeStamp(this.time)) |
|||
this.$emit('input', this.createTimeStamp(this.time)) |
|||
this.$emit('update:modelValue', this.createTimeStamp(this.time)) |
|||
} else { |
|||
this.$emit('change', this.time) |
|||
this.$emit('input', this.time) |
|||
this.$emit('update:modelValue', this.time) |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 用户选择日期或时间更新 data |
|||
* @param {Object} e |
|||
*/ |
|||
bindDateChange(e) { |
|||
const val = e.detail.value |
|||
this.year = this.years[val[0]] |
|||
this.month = this.months[val[1]] |
|||
this.day = this.days[val[2]] |
|||
}, |
|||
bindTimeChange(e) { |
|||
const val = e.detail.value |
|||
this.hour = this.hours[val[0]] |
|||
this.minute = this.minutes[val[1]] |
|||
this.second = this.seconds[val[2]] |
|||
}, |
|||
|
|||
/** |
|||
* 初始化弹出层 |
|||
*/ |
|||
initTimePicker() { |
|||
if (this.disabled) return |
|||
const value = this.fixIosDateFormat(this.value) |
|||
this.initPickerValue(value) |
|||
this.visible = !this.visible |
|||
}, |
|||
|
|||
/** |
|||
* 触发或关闭弹框 |
|||
*/ |
|||
tiggerTimePicker(e) { |
|||
this.visible = !this.visible |
|||
}, |
|||
|
|||
/** |
|||
* 用户点击“清空”按钮,清空当前值 |
|||
*/ |
|||
clearTime() { |
|||
this.time = '' |
|||
this.$emit('change', this.time) |
|||
this.$emit('input', this.time) |
|||
this.$emit('update:modelValue', this.time) |
|||
this.tiggerTimePicker() |
|||
}, |
|||
|
|||
/** |
|||
* 用户点击“确定”按钮 |
|||
*/ |
|||
setTime() { |
|||
this.initTime() |
|||
this.tiggerTimePicker() |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.uni-datetime-picker { |
|||
/* #ifndef APP-NVUE */ |
|||
/* width: 100%; */ |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-datetime-picker-view { |
|||
height: 130px; |
|||
width: 270px; |
|||
/* #ifndef APP-NVUE */ |
|||
cursor: pointer; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-datetime-picker-item { |
|||
height: 50px; |
|||
line-height: 50px; |
|||
text-align: center; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.uni-datetime-picker-btn { |
|||
margin-top: 60px; |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
cursor: pointer; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
.uni-datetime-picker-btn-text { |
|||
font-size: 14px; |
|||
color: #007AFF; |
|||
} |
|||
|
|||
.uni-datetime-picker-btn-group { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
} |
|||
|
|||
.uni-datetime-picker-cancel { |
|||
margin-right: 30px; |
|||
} |
|||
|
|||
.uni-datetime-picker-mask { |
|||
position: fixed; |
|||
bottom: 0px; |
|||
top: 0px; |
|||
left: 0px; |
|||
right: 0px; |
|||
background-color: rgba(0, 0, 0, 0.4); |
|||
transition-duration: 0.3s; |
|||
z-index: 998; |
|||
} |
|||
|
|||
.uni-datetime-picker-popup { |
|||
border-radius: 8px; |
|||
padding: 30px; |
|||
width: 270px; |
|||
/* #ifdef APP-NVUE */ |
|||
height: 500px; |
|||
/* #endif */ |
|||
/* #ifdef APP-NVUE */ |
|||
width: 330px; |
|||
/* #endif */ |
|||
background-color: #fff; |
|||
position: fixed; |
|||
top: 50%; |
|||
left: 50%; |
|||
transform: translate(-50%, -50%); |
|||
transition-duration: 0.3s; |
|||
z-index: 999; |
|||
} |
|||
|
|||
.fix-nvue-height { |
|||
/* #ifdef APP-NVUE */ |
|||
height: 330px; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-datetime-picker-time { |
|||
color: grey; |
|||
} |
|||
|
|||
.uni-datetime-picker-column { |
|||
height: 50px; |
|||
} |
|||
|
|||
.uni-datetime-picker-timebox { |
|||
|
|||
border: 1px solid #E5E5E5; |
|||
border-radius: 5px; |
|||
padding: 7px 10px; |
|||
/* #ifndef APP-NVUE */ |
|||
box-sizing: border-box; |
|||
cursor: pointer; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-datetime-picker-timebox-pointer { |
|||
/* #ifndef APP-NVUE */ |
|||
cursor: pointer; |
|||
/* #endif */ |
|||
} |
|||
|
|||
|
|||
.uni-datetime-picker-disabled { |
|||
opacity: 0.4; |
|||
/* #ifdef H5 */ |
|||
cursor: not-allowed !important; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-datetime-picker-text { |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.uni-datetime-picker-sign { |
|||
position: absolute; |
|||
top: 53px; |
|||
/* 减掉 10px 的元素高度,兼容nvue */ |
|||
color: #999; |
|||
/* #ifdef APP-NVUE */ |
|||
font-size: 16px; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.sign-left { |
|||
left: 86px; |
|||
} |
|||
|
|||
.sign-right { |
|||
right: 86px; |
|||
} |
|||
|
|||
.sign-center { |
|||
left: 135px; |
|||
} |
|||
|
|||
.uni-datetime-picker__container-box { |
|||
position: relative; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
margin-top: 40px; |
|||
} |
|||
|
|||
.time-hide-second { |
|||
width: 180px; |
|||
} |
|||
</style> |
|||
@ -1,981 +0,0 @@ |
|||
<template> |
|||
<view class="uni-date"> |
|||
<view class="uni-date-editor" @click="show"> |
|||
<slot> |
|||
<view class="uni-date-editor--x" :class="{'uni-date-editor--x__disabled': disabled, |
|||
'uni-date-x--border': border}"> |
|||
<view v-if="!isRange" class="uni-date-x uni-date-single"> |
|||
<uni-icons type="calendar" color="#e1e1e1" size="22"></uni-icons> |
|||
<input class="uni-date__x-input" type="text" v-model="singleVal" |
|||
:placeholder="singlePlaceholderText" :disabled="true" /> |
|||
</view> |
|||
<view v-else class="uni-date-x uni-date-range"> |
|||
<uni-icons type="calendar" color="#e1e1e1" size="22"></uni-icons> |
|||
<input class="uni-date__x-input t-c" type="text" v-model="range.startDate" |
|||
:placeholder="startPlaceholderText" :disabled="true" /> |
|||
<slot> |
|||
<view class="">{{rangeSeparator}}</view> |
|||
</slot> |
|||
<input class="uni-date__x-input t-c" type="text" v-model="range.endDate" |
|||
:placeholder="endPlaceholderText" :disabled="true" /> |
|||
</view> |
|||
<view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear"> |
|||
<uni-icons type="clear" color="#e1e1e1" size="18"></uni-icons> |
|||
</view> |
|||
</view> |
|||
</slot> |
|||
</view> |
|||
|
|||
<view v-show="popup" class="uni-date-mask" @click="close"></view> |
|||
<view v-if="!isPhone" ref="datePicker" v-show="popup" class="uni-date-picker__container"> |
|||
<view v-if="!isRange" class="uni-date-single--x" :style="popover"> |
|||
<view class="uni-popper__arrow"></view> |
|||
<view v-if="hasTime" class="uni-date-changed popup-x-header"> |
|||
<input class="uni-date__input t-c" type="text" v-model="tempSingleDate" |
|||
:placeholder="selectDateText" /> |
|||
<time-picker type="time" v-model="time" :border="false" :disabled="!tempSingleDate" |
|||
:start="reactStartTime" :end="reactEndTime" :hideSecond="hideSecond" style="width: 100%;"> |
|||
<input class="uni-date__input t-c" type="text" v-model="time" :placeholder="selectTimeText" |
|||
:disabled="!tempSingleDate" /> |
|||
</time-picker> |
|||
</view> |
|||
<calendar ref="pcSingle" :showMonth="false" |
|||
:start-date="caleRange.startDate" :end-date="caleRange.endDate" :date="defSingleDate" |
|||
@change="singleChange" style="padding: 0 8px;" /> |
|||
<view v-if="hasTime" class="popup-x-footer"> |
|||
<!-- <text class="">此刻</text> --> |
|||
<text class="confirm" @click="confirmSingleChange">{{okText}}</text> |
|||
</view> |
|||
<view class="uni-date-popper__arrow"></view> |
|||
</view> |
|||
|
|||
<view v-else class="uni-date-range--x" :style="popover"> |
|||
<view class="uni-popper__arrow"></view> |
|||
<view v-if="hasTime" class="popup-x-header uni-date-changed"> |
|||
<view class="popup-x-header--datetime"> |
|||
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate" |
|||
:placeholder="startDateText" /> |
|||
<time-picker type="time" v-model="tempRange.startTime" :start="reactStartTime" :border="false" |
|||
:disabled="!tempRange.startDate" :hideSecond="hideSecond"> |
|||
<input class="uni-date__input uni-date-range__input" type="text" |
|||
v-model="tempRange.startTime" :placeholder="startTimeText" |
|||
:disabled="!tempRange.startDate" /> |
|||
</time-picker> |
|||
</view> |
|||
<uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons> |
|||
<view class="popup-x-header--datetime"> |
|||
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate" |
|||
:placeholder="endDateText" /> |
|||
<time-picker type="time" v-model="tempRange.endTime" :end="reactEndTime" :border="false" |
|||
:disabled="!tempRange.endDate" :hideSecond="hideSecond"> |
|||
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime" |
|||
:placeholder="endTimeText" :disabled="!tempRange.endDate" /> |
|||
</time-picker> |
|||
</view> |
|||
</view> |
|||
<view class="popup-x-body"> |
|||
<calendar ref="left" :showMonth="false" |
|||
:start-date="caleRange.startDate" :end-date="caleRange.endDate" :range="true" |
|||
@change="leftChange" :pleStatus="endMultipleStatus" @firstEnterCale="updateRightCale" |
|||
@monthSwitch="leftMonthSwitch" style="padding: 0 8px;" /> |
|||
<calendar ref="right" :showMonth="false" |
|||
:start-date="caleRange.startDate" :end-date="caleRange.endDate" :range="true" |
|||
@change="rightChange" :pleStatus="startMultipleStatus" @firstEnterCale="updateLeftCale" |
|||
@monthSwitch="rightMonthSwitch" style="padding: 0 8px;border-left: 1px solid #F1F1F1;" /> |
|||
</view> |
|||
<view v-if="hasTime" class="popup-x-footer"> |
|||
<text class="" @click="clear">{{clearText}}</text> |
|||
<text class="confirm" @click="confirmRangeChange">{{okText}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<calendar v-show="isPhone" ref="mobile" :clearDate="false" :date="defSingleDate" :defTime="reactMobDefTime" |
|||
:start-date="caleRange.startDate" :end-date="caleRange.endDate" :selectableTimes="mobSelectableTime" |
|||
:pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :typeHasTime="hasTime" :insert="false" |
|||
:hideSecond="hideSecond" @confirm="mobileChange" /> |
|||
</view> |
|||
</template> |
|||
<script> |
|||
/** |
|||
* DatetimePicker 时间选择器 |
|||
* @description 同时支持 PC 和移动端使用日历选择日期和日期范围 |
|||
* @tutorial https://ext.dcloud.net.cn/plugin?id=3962 |
|||
* @property {String} type 选择器类型 |
|||
* @property {String|Number|Array|Date} value 绑定值 |
|||
* @property {String} placeholder 单选择时的占位内容 |
|||
* @property {String} start 起始时间 |
|||
* @property {String} end 终止时间 |
|||
* @property {String} start-placeholder 范围选择时开始日期的占位内容 |
|||
* @property {String} end-placeholder 范围选择时结束日期的占位内容 |
|||
* @property {String} range-separator 选择范围时的分隔符 |
|||
* @property {Boolean} border = [true|false] 是否有边框 |
|||
* @property {Boolean} disabled = [true|false] 是否禁用 |
|||
* @property {Boolean} clearIcon = [true|false] 是否显示清除按钮(仅PC端适用) |
|||
* @event {Function} change 确定日期时触发的事件 |
|||
* @event {Function} show 打开弹出层 |
|||
* @event {Function} close 关闭弹出层 |
|||
* @event {Function} clear 清除上次选中的状态和值 |
|||
**/ |
|||
import calendar from './calendar.vue' |
|||
import timePicker from './time-picker.vue' |
|||
import { |
|||
initVueI18n |
|||
} from '@dcloudio/uni-i18n' |
|||
import messages from './i18n/index.js' |
|||
const { |
|||
t |
|||
} = initVueI18n(messages) |
|||
|
|||
export default { |
|||
name: 'UniDatetimePicker', |
|||
components: { |
|||
calendar, |
|||
timePicker |
|||
}, |
|||
data() { |
|||
return { |
|||
isRange: false, |
|||
hasTime: false, |
|||
mobileRange: false, |
|||
// 单选 |
|||
singleVal: '', |
|||
tempSingleDate: '', |
|||
defSingleDate: '', |
|||
time: '', |
|||
// 范围选 |
|||
caleRange: { |
|||
startDate: '', |
|||
startTime: '', |
|||
endDate: '', |
|||
endTime: '' |
|||
}, |
|||
range: { |
|||
startDate: '', |
|||
// startTime: '', |
|||
endDate: '', |
|||
// endTime: '' |
|||
}, |
|||
tempRange: { |
|||
startDate: '', |
|||
startTime: '', |
|||
endDate: '', |
|||
endTime: '' |
|||
}, |
|||
// 左右日历同步数据 |
|||
startMultipleStatus: { |
|||
before: '', |
|||
after: '', |
|||
data: [], |
|||
fulldate: '' |
|||
}, |
|||
endMultipleStatus: { |
|||
before: '', |
|||
after: '', |
|||
data: [], |
|||
fulldate: '' |
|||
}, |
|||
visible: false, |
|||
popup: false, |
|||
popover: null, |
|||
isEmitValue: false, |
|||
isPhone: false, |
|||
isFirstShow: true, |
|||
} |
|||
}, |
|||
props: { |
|||
type: { |
|||
type: String, |
|||
default: 'datetime' |
|||
}, |
|||
value: { |
|||
type: [String, Number, Array, Date], |
|||
default: '' |
|||
}, |
|||
modelValue: { |
|||
type: [String, Number, Array, Date], |
|||
default: '' |
|||
}, |
|||
start: { |
|||
type: [Number, String], |
|||
default: '' |
|||
}, |
|||
end: { |
|||
type: [Number, String], |
|||
default: '' |
|||
}, |
|||
returnType: { |
|||
type: String, |
|||
default: 'string' |
|||
}, |
|||
placeholder: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
startPlaceholder: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
endPlaceholder: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
rangeSeparator: { |
|||
type: String, |
|||
default: '-' |
|||
}, |
|||
border: { |
|||
type: [Boolean], |
|||
default: true |
|||
}, |
|||
disabled: { |
|||
type: [Boolean], |
|||
default: false |
|||
}, |
|||
clearIcon: { |
|||
type: [Boolean], |
|||
default: true |
|||
}, |
|||
hideSecond: { |
|||
type: [Boolean], |
|||
default: false |
|||
} |
|||
}, |
|||
watch: { |
|||
type: { |
|||
immediate: true, |
|||
handler(newVal, oldVal) { |
|||
if (newVal.indexOf('time') !== -1) { |
|||
this.hasTime = true |
|||
} else { |
|||
this.hasTime = false |
|||
} |
|||
if (newVal.indexOf('range') !== -1) { |
|||
this.isRange = true |
|||
} else { |
|||
this.isRange = false |
|||
} |
|||
} |
|||
}, |
|||
value: { |
|||
immediate: true, |
|||
handler(newVal, oldVal) { |
|||
if (this.isEmitValue) { |
|||
this.isEmitValue = false |
|||
return |
|||
} |
|||
this.initPicker(newVal) |
|||
} |
|||
}, |
|||
|
|||
start: { |
|||
immediate: true, |
|||
handler(newVal, oldVal) { |
|||
if (!newVal) return |
|||
const { |
|||
defDate, |
|||
defTime |
|||
} = this.parseDate(newVal) |
|||
this.caleRange.startDate = defDate |
|||
if (this.hasTime) { |
|||
this.caleRange.startTime = defTime |
|||
} |
|||
} |
|||
}, |
|||
|
|||
end: { |
|||
immediate: true, |
|||
handler(newVal, oldVal) { |
|||
if (!newVal) return |
|||
const { |
|||
defDate, |
|||
defTime |
|||
} = this.parseDate(newVal) |
|||
this.caleRange.endDate = defDate |
|||
if (this.hasTime) { |
|||
this.caleRange.endTime = defTime |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
computed: { |
|||
reactStartTime() { |
|||
const activeDate = this.isRange ? this.tempRange.startDate : this.tempSingleDate |
|||
const res = activeDate === this.caleRange.startDate ? this.caleRange.startTime : '' |
|||
return res |
|||
}, |
|||
reactEndTime() { |
|||
const activeDate = this.isRange ? this.tempRange.endDate : this.tempSingleDate |
|||
const res = activeDate === this.caleRange.endDate ? this.caleRange.endTime : '' |
|||
return res |
|||
}, |
|||
reactMobDefTime() { |
|||
const times = { |
|||
start: this.tempRange.startTime, |
|||
end: this.tempRange.endTime |
|||
} |
|||
return this.isRange ? times : this.time |
|||
}, |
|||
mobSelectableTime() { |
|||
return { |
|||
start: this.caleRange.startTime, |
|||
end: this.caleRange.endTime |
|||
} |
|||
}, |
|||
datePopupWidth() { |
|||
// todo |
|||
return this.isRange ? 653 : 301 |
|||
}, |
|||
|
|||
/** |
|||
* for i18n |
|||
*/ |
|||
singlePlaceholderText() { |
|||
return this.placeholder || (this.type === 'date' ? this.selectDateText : t( |
|||
"uni-datetime-picker.selectDateTime")) |
|||
}, |
|||
startPlaceholderText() { |
|||
return this.startPlaceholder || this.startDateText |
|||
}, |
|||
endPlaceholderText() { |
|||
return this.endPlaceholder || this.endDateText |
|||
}, |
|||
selectDateText() { |
|||
return t("uni-datetime-picker.selectDate") |
|||
}, |
|||
selectTimeText() { |
|||
return t("uni-datetime-picker.selectTime") |
|||
}, |
|||
startDateText() { |
|||
return this.startPlaceholder || t("uni-datetime-picker.startDate") |
|||
}, |
|||
startTimeText() { |
|||
return t("uni-datetime-picker.startTime") |
|||
}, |
|||
endDateText() { |
|||
return this.endPlaceholder || t("uni-datetime-picker.endDate") |
|||
}, |
|||
endTimeText() { |
|||
return t("uni-datetime-picker.endTime") |
|||
}, |
|||
okText() { |
|||
return t("uni-datetime-picker.ok") |
|||
}, |
|||
clearText() { |
|||
return t("uni-datetime-picker.clear") |
|||
}, |
|||
showClearIcon() { |
|||
const { clearIcon, disabled, singleVal, range } = this |
|||
const bool = clearIcon && !disabled && (singleVal || (range.startDate && range.endDate)) |
|||
return bool |
|||
} |
|||
}, |
|||
created() { |
|||
this.form = this.getForm('uniForms') |
|||
this.formItem = this.getForm('uniFormsItem') |
|||
|
|||
// if (this.formItem) { |
|||
// if (this.formItem.name) { |
|||
// this.rename = this.formItem.name |
|||
// this.form.inputChildrens.push(this) |
|||
// } |
|||
// } |
|||
}, |
|||
mounted() { |
|||
this.platform() |
|||
}, |
|||
methods: { |
|||
/** |
|||
* 获取父元素实例 |
|||
*/ |
|||
getForm(name = 'uniForms') { |
|||
let parent = this.$parent; |
|||
let parentName = parent.$options.name; |
|||
while (parentName !== name) { |
|||
parent = parent.$parent; |
|||
if (!parent) return false |
|||
parentName = parent.$options.name; |
|||
} |
|||
return parent; |
|||
}, |
|||
initPicker(newVal) { |
|||
if (!newVal || Array.isArray(newVal) && !newVal.length) { |
|||
this.$nextTick(() => { |
|||
this.clear(false) |
|||
}) |
|||
return |
|||
} |
|||
if (!Array.isArray(newVal) && !this.isRange) { |
|||
const { |
|||
defDate, |
|||
defTime |
|||
} = this.parseDate(newVal) |
|||
this.singleVal = defDate |
|||
this.tempSingleDate = defDate |
|||
this.defSingleDate = defDate |
|||
if (this.hasTime) { |
|||
this.singleVal = defDate + ' ' + defTime |
|||
this.time = defTime |
|||
} |
|||
} else { |
|||
const [before, after] = newVal |
|||
if (!before && !after) return |
|||
const defBefore = this.parseDate(before) |
|||
const defAfter = this.parseDate(after) |
|||
const startDate = defBefore.defDate |
|||
const endDate = defAfter.defDate |
|||
this.range.startDate = this.tempRange.startDate = startDate |
|||
this.range.endDate = this.tempRange.endDate = endDate |
|||
|
|||
if (this.hasTime) { |
|||
this.range.startDate = defBefore.defDate + ' ' + defBefore.defTime |
|||
this.range.endDate = defAfter.defDate + ' ' + defAfter.defTime |
|||
this.tempRange.startTime = defBefore.defTime |
|||
this.tempRange.endTime = defAfter.defTime |
|||
} |
|||
const defaultRange = { |
|||
before: defBefore.defDate, |
|||
after: defAfter.defDate |
|||
} |
|||
this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, { |
|||
which: 'right' |
|||
}) |
|||
this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, { |
|||
which: 'left' |
|||
}) |
|||
} |
|||
}, |
|||
updateLeftCale(e) { |
|||
const left = this.$refs.left |
|||
// 设置范围选 |
|||
left.cale.setHoverMultiple(e.after) |
|||
left.setDate(this.$refs.left.nowDate.fullDate) |
|||
}, |
|||
updateRightCale(e) { |
|||
const right = this.$refs.right |
|||
// 设置范围选 |
|||
right.cale.setHoverMultiple(e.after) |
|||
right.setDate(this.$refs.right.nowDate.fullDate) |
|||
}, |
|||
platform() { |
|||
const systemInfo = uni.getSystemInfoSync() |
|||
this.isPhone = systemInfo.windowWidth <= 500 |
|||
this.windowWidth = systemInfo.windowWidth |
|||
}, |
|||
show(event) { |
|||
if (this.disabled) { |
|||
return |
|||
} |
|||
this.platform() |
|||
if (this.isPhone) { |
|||
this.$refs.mobile.open() |
|||
return |
|||
} |
|||
this.popover = { |
|||
top: '10px' |
|||
} |
|||
const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor") |
|||
dateEditor.boundingClientRect(rect => { |
|||
if (this.windowWidth - rect.left < this.datePopupWidth) { |
|||
this.popover.right = 0 |
|||
} |
|||
}).exec() |
|||
setTimeout(() => { |
|||
this.popup = !this.popup |
|||
if (!this.isPhone && this.isRange && this.isFirstShow) { |
|||
this.isFirstShow = false |
|||
const { |
|||
startDate, |
|||
endDate |
|||
} = this.range |
|||
if (startDate && endDate) { |
|||
if (this.diffDate(startDate, endDate) < 30) { |
|||
this.$refs.right.next() |
|||
} |
|||
} else { |
|||
this.$refs.right.next() |
|||
this.$refs.right.cale.lastHover = false |
|||
} |
|||
} |
|||
|
|||
}, 50) |
|||
}, |
|||
|
|||
close() { |
|||
setTimeout(() => { |
|||
this.popup = false |
|||
this.$emit('maskClick', this.value) |
|||
}, 20) |
|||
}, |
|||
setEmit(value) { |
|||
if (this.returnType === "timestamp" || this.returnType === "date") { |
|||
if (!Array.isArray(value)) { |
|||
if (!this.hasTime) { |
|||
value = value + ' ' + '00:00:00' |
|||
} |
|||
value = this.createTimestamp(value) |
|||
if (this.returnType === "date") { |
|||
value = new Date(value) |
|||
} |
|||
} else { |
|||
if (!this.hasTime) { |
|||
value[0] = value[0] + ' ' + '00:00:00' |
|||
value[1] = value[1] + ' ' + '00:00:00' |
|||
} |
|||
value[0] = this.createTimestamp(value[0]) |
|||
value[1] = this.createTimestamp(value[1]) |
|||
if (this.returnType === "date") { |
|||
value[0] = new Date(value[0]) |
|||
value[1] = new Date(value[1]) |
|||
} |
|||
} |
|||
} |
|||
this.formItem && this.formItem.setValue(value) |
|||
this.$emit('change', value) |
|||
this.$emit('input', value) |
|||
this.$emit('update:modelValue', value) |
|||
this.isEmitValue = true |
|||
}, |
|||
createTimestamp(date) { |
|||
date = this.fixIosDateFormat(date) |
|||
return Date.parse(new Date(date)) |
|||
}, |
|||
singleChange(e) { |
|||
this.tempSingleDate = e.fulldate |
|||
if (this.hasTime) return |
|||
this.confirmSingleChange() |
|||
}, |
|||
|
|||
confirmSingleChange() { |
|||
if (!this.tempSingleDate) { |
|||
this.popup = false |
|||
return |
|||
} |
|||
if (this.hasTime) { |
|||
this.singleVal = this.tempSingleDate + ' ' + (this.time ? this.time : '00:00:00') |
|||
} else { |
|||
this.singleVal = this.tempSingleDate |
|||
} |
|||
this.setEmit(this.singleVal) |
|||
this.popup = false |
|||
}, |
|||
|
|||
leftChange(e) { |
|||
const { |
|||
before, |
|||
after |
|||
} = e.range |
|||
this.rangeChange(before, after) |
|||
const obj = { |
|||
before: e.range.before, |
|||
after: e.range.after, |
|||
data: e.range.data, |
|||
fulldate: e.fulldate |
|||
} |
|||
this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj) |
|||
}, |
|||
|
|||
rightChange(e) { |
|||
const { |
|||
before, |
|||
after |
|||
} = e.range |
|||
this.rangeChange(before, after) |
|||
const obj = { |
|||
before: e.range.before, |
|||
after: e.range.after, |
|||
data: e.range.data, |
|||
fulldate: e.fulldate |
|||
} |
|||
this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj) |
|||
}, |
|||
|
|||
mobileChange(e) { |
|||
if (this.isRange) { |
|||
const { |
|||
before, |
|||
after |
|||
} = e.range |
|||
this.handleStartAndEnd(before, after, true) |
|||
if (this.hasTime) { |
|||
const { |
|||
startTime, |
|||
endTime |
|||
} = e.timeRange |
|||
this.tempRange.startTime = startTime |
|||
this.tempRange.endTime = endTime |
|||
} |
|||
this.confirmRangeChange() |
|||
|
|||
} else { |
|||
if (this.hasTime) { |
|||
this.singleVal = e.fulldate + ' ' + e.time |
|||
} else { |
|||
this.singleVal = e.fulldate |
|||
} |
|||
this.setEmit(this.singleVal) |
|||
} |
|||
this.$refs.mobile.close() |
|||
}, |
|||
|
|||
rangeChange(before, after) { |
|||
if (!(before && after)) return |
|||
this.handleStartAndEnd(before, after, true) |
|||
if (this.hasTime) return |
|||
this.confirmRangeChange() |
|||
}, |
|||
|
|||
confirmRangeChange() { |
|||
if (!this.tempRange.startDate && !this.tempRange.endDate) { |
|||
this.popup = false |
|||
return |
|||
} |
|||
let start, end |
|||
if (!this.hasTime) { |
|||
start = this.range.startDate = this.tempRange.startDate |
|||
end = this.range.endDate = this.tempRange.endDate |
|||
} else { |
|||
start = this.range.startDate = this.tempRange.startDate + ' ' + |
|||
(this.tempRange.startTime ? this.tempRange.startTime : '00:00:00') |
|||
end = this.range.endDate = this.tempRange.endDate + ' ' + |
|||
(this.tempRange.endTime ? this.tempRange.endTime : '00:00:00') |
|||
} |
|||
const displayRange = [start, end] |
|||
this.setEmit(displayRange) |
|||
this.popup = false |
|||
}, |
|||
|
|||
handleStartAndEnd(before, after, temp = false) { |
|||
if (!(before && after)) return |
|||
const type = temp ? 'tempRange' : 'range' |
|||
if (this.dateCompare(before, after)) { |
|||
this[type].startDate = before |
|||
this[type].endDate = after |
|||
} else { |
|||
this[type].startDate = after |
|||
this[type].endDate = before |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 比较时间大小 |
|||
*/ |
|||
dateCompare(startDate, endDate) { |
|||
// 计算截止时间 |
|||
startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
|||
// 计算详细项的截止时间 |
|||
endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
|||
if (startDate <= endDate) { |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 比较时间差 |
|||
*/ |
|||
diffDate(startDate, endDate) { |
|||
// 计算截止时间 |
|||
startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
|||
// 计算详细项的截止时间 |
|||
endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
|||
const diff = (endDate - startDate) / (24 * 60 * 60 * 1000) |
|||
return Math.abs(diff) |
|||
}, |
|||
|
|||
clear(needEmit = true) { |
|||
if (!this.isRange) { |
|||
this.singleVal = '' |
|||
this.tempSingleDate = '' |
|||
this.time = '' |
|||
if (this.isPhone) { |
|||
this.$refs.mobile && this.$refs.mobile.clearCalender() |
|||
} else { |
|||
this.$refs.pcSingle && this.$refs.pcSingle.clearCalender() |
|||
} |
|||
if (needEmit) { |
|||
this.formItem && this.formItem.setValue('') |
|||
this.$emit('change', '') |
|||
this.$emit('input', '') |
|||
this.$emit('update:modelValue', '') |
|||
} |
|||
} else { |
|||
this.range.startDate = '' |
|||
this.range.endDate = '' |
|||
this.tempRange.startDate = '' |
|||
this.tempRange.startTime = '' |
|||
this.tempRange.endDate = '' |
|||
this.tempRange.endTime = '' |
|||
if (this.isPhone) { |
|||
this.$refs.mobile && this.$refs.mobile.clearCalender() |
|||
} else { |
|||
this.$refs.left && this.$refs.left.clearCalender() |
|||
this.$refs.right && this.$refs.right.clearCalender() |
|||
this.$refs.right && this.$refs.right.next() |
|||
} |
|||
if (needEmit) { |
|||
this.formItem && this.formItem.setValue([]) |
|||
this.$emit('change', []) |
|||
this.$emit('input', []) |
|||
this.$emit('update:modelValue', []) |
|||
} |
|||
} |
|||
}, |
|||
|
|||
parseDate(date) { |
|||
date = this.fixIosDateFormat(date) |
|||
const defVal = new Date(date) |
|||
const year = defVal.getFullYear() |
|||
const month = defVal.getMonth() + 1 |
|||
const day = defVal.getDate() |
|||
const hour = defVal.getHours() |
|||
const minute = defVal.getMinutes() |
|||
const second = defVal.getSeconds() |
|||
const defDate = year + '-' + this.lessTen(month) + '-' + this.lessTen(day) |
|||
const defTime = this.lessTen(hour) + ':' + this.lessTen(minute) + (this.hideSecond ? '' : (':' + this |
|||
.lessTen(second))) |
|||
return { |
|||
defDate, |
|||
defTime |
|||
} |
|||
}, |
|||
|
|||
lessTen(item) { |
|||
return item < 10 ? '0' + item : item |
|||
}, |
|||
|
|||
//兼容 iOS、safari 日期格式 |
|||
fixIosDateFormat(value) { |
|||
if (typeof value === 'string') { |
|||
value = value.replace(/-/g, '/') |
|||
} |
|||
return value |
|||
}, |
|||
|
|||
leftMonthSwitch(e) { |
|||
// console.log('leftMonthSwitch 返回:', e) |
|||
}, |
|||
rightMonthSwitch(e) { |
|||
// console.log('rightMonthSwitch 返回:', e) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.uni-date-x { |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 0 10px; |
|||
border-radius: 4px; |
|||
background-color: #fff; |
|||
color: #666; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.uni-date-x--border { |
|||
box-sizing: border-box; |
|||
border-radius: 4px; |
|||
border: 1px solid #dcdfe6; |
|||
} |
|||
|
|||
.uni-date-editor--x { |
|||
position: relative; |
|||
} |
|||
|
|||
.uni-date-editor--x .uni-date__icon-clear { |
|||
position: absolute; |
|||
top: 0; |
|||
right: 0; |
|||
display: inline-block; |
|||
box-sizing: border-box; |
|||
border: 9px solid transparent; |
|||
/* #ifdef H5 */ |
|||
cursor: pointer; |
|||
/* #endif */ |
|||
} |
|||
|
|||
.uni-date__x-input { |
|||
padding: 0 8px; |
|||
height: 40px; |
|||
width: 100%; |
|||
line-height: 40px; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.t-c { |
|||
text-align: center; |
|||
} |
|||
|
|||
.uni-date__input { |
|||
height: 40px; |
|||
width: 100%; |
|||
line-height: 40px; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.uni-date-range__input { |
|||
text-align: center; |
|||
max-width: 142px; |
|||
} |
|||
|
|||
.uni-date-picker__container { |
|||
position: relative; |
|||
/* position: fixed; |
|||
left: 0; |
|||
right: 0; |
|||
top: 0; |
|||
bottom: 0; |
|||
box-sizing: border-box; |
|||
z-index: 996; |
|||
font-size: 14px; */ |
|||
} |
|||
|
|||
.uni-date-mask { |
|||
position: fixed; |
|||
bottom: 0px; |
|||
top: 0px; |
|||
left: 0px; |
|||
right: 0px; |
|||
background-color: rgba(0, 0, 0, 0); |
|||
transition-duration: 0.3s; |
|||
z-index: 996; |
|||
} |
|||
|
|||
.uni-date-single--x { |
|||
/* padding: 0 8px; */ |
|||
background-color: #fff; |
|||
position: absolute; |
|||
top: 0; |
|||
z-index: 999; |
|||
border: 1px solid #EBEEF5; |
|||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
|||
border-radius: 4px; |
|||
} |
|||
|
|||
.uni-date-range--x { |
|||
/* padding: 0 8px; */ |
|||
background-color: #fff; |
|||
position: absolute; |
|||
top: 0; |
|||
z-index: 999; |
|||
border: 1px solid #EBEEF5; |
|||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
|||
border-radius: 4px; |
|||
} |
|||
|
|||
.uni-date-editor--x__disabled { |
|||
opacity: 0.4; |
|||
cursor: default; |
|||
} |
|||
|
|||
.uni-date-editor--logo { |
|||
width: 16px; |
|||
height: 16px; |
|||
vertical-align: middle; |
|||
} |
|||
|
|||
/* 添加时间 */ |
|||
.popup-x-header { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
/* justify-content: space-between; */ |
|||
} |
|||
|
|||
.popup-x-header--datetime { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
flex: 1; |
|||
} |
|||
|
|||
.popup-x-body { |
|||
display: flex; |
|||
} |
|||
|
|||
.popup-x-footer { |
|||
padding: 0 15px; |
|||
border-top-color: #F1F1F1; |
|||
border-top-style: solid; |
|||
border-top-width: 1px; |
|||
/* background-color: #fff; */ |
|||
line-height: 40px; |
|||
text-align: right; |
|||
color: #666; |
|||
} |
|||
|
|||
.popup-x-footer text:hover { |
|||
color: #007aff; |
|||
cursor: pointer; |
|||
opacity: 0.8; |
|||
} |
|||
|
|||
.popup-x-footer .confirm { |
|||
margin-left: 20px; |
|||
color: #007aff; |
|||
} |
|||
|
|||
.uni-date-changed { |
|||
/* background-color: #fff; */ |
|||
text-align: center; |
|||
color: #333; |
|||
border-bottom-color: #F1F1F1; |
|||
border-bottom-style: solid; |
|||
border-bottom-width: 1px; |
|||
/* padding: 0 50px; */ |
|||
} |
|||
|
|||
.uni-date-changed--time text { |
|||
/* padding: 0 20px; */ |
|||
height: 50px; |
|||
line-height: 50px; |
|||
} |
|||
|
|||
.uni-date-changed .uni-date-changed--time { |
|||
/* display: flex; */ |
|||
flex: 1; |
|||
} |
|||
|
|||
.uni-date-changed--time-date { |
|||
color: #333; |
|||
opacity: 0.6; |
|||
} |
|||
|
|||
.mr-50 { |
|||
margin-right: 50px; |
|||
} |
|||
|
|||
/* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */ |
|||
.uni-popper__arrow, |
|||
.uni-popper__arrow::after { |
|||
position: absolute; |
|||
display: block; |
|||
width: 0; |
|||
height: 0; |
|||
border-color: transparent; |
|||
border-style: solid; |
|||
border-width: 6px; |
|||
} |
|||
|
|||
.uni-popper__arrow { |
|||
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
|||
top: -6px; |
|||
left: 10%; |
|||
margin-right: 3px; |
|||
border-top-width: 0; |
|||
border-bottom-color: #EBEEF5; |
|||
} |
|||
|
|||
.uni-popper__arrow::after { |
|||
content: " "; |
|||
top: 1px; |
|||
margin-left: -6px; |
|||
border-top-width: 0; |
|||
border-bottom-color: #fff; |
|||
} |
|||
</style> |
|||
@ -1,410 +0,0 @@ |
|||
class Calendar { |
|||
constructor({ |
|||
date, |
|||
selected, |
|||
startDate, |
|||
endDate, |
|||
range, |
|||
// multipleStatus
|
|||
} = {}) { |
|||
// 当前日期
|
|||
this.date = this.getDate(new Date()) // 当前初入日期
|
|||
// 打点信息
|
|||
this.selected = selected || []; |
|||
// 范围开始
|
|||
this.startDate = startDate |
|||
// 范围结束
|
|||
this.endDate = endDate |
|||
this.range = range |
|||
// 多选状态
|
|||
this.cleanMultipleStatus() |
|||
// 每周日期
|
|||
this.weeks = {} |
|||
// this._getWeek(this.date.fullDate)
|
|||
// this.multipleStatus = multipleStatus
|
|||
this.lastHover = false |
|||
} |
|||
/** |
|||
* 设置日期 |
|||
* @param {Object} date |
|||
*/ |
|||
setDate(date) { |
|||
this.selectDate = this.getDate(date) |
|||
this._getWeek(this.selectDate.fullDate) |
|||
} |
|||
|
|||
/** |
|||
* 清理多选状态 |
|||
*/ |
|||
cleanMultipleStatus() { |
|||
this.multipleStatus = { |
|||
before: '', |
|||
after: '', |
|||
data: [] |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 重置开始日期 |
|||
*/ |
|||
resetSatrtDate(startDate) { |
|||
// 范围开始
|
|||
this.startDate = startDate |
|||
|
|||
} |
|||
|
|||
/** |
|||
* 重置结束日期 |
|||
*/ |
|||
resetEndDate(endDate) { |
|||
// 范围结束
|
|||
this.endDate = endDate |
|||
} |
|||
|
|||
/** |
|||
* 获取任意时间 |
|||
*/ |
|||
getDate(date, AddDayCount = 0, str = 'day') { |
|||
if (!date) { |
|||
date = new Date() |
|||
} |
|||
if (typeof date !== 'object') { |
|||
date = date.replace(/-/g, '/') |
|||
} |
|||
const dd = new Date(date) |
|||
switch (str) { |
|||
case 'day': |
|||
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
|
|||
break |
|||
case 'month': |
|||
if (dd.getDate() === 31) { |
|||
dd.setDate(dd.getDate() + AddDayCount) |
|||
} else { |
|||
dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
|
|||
} |
|||
break |
|||
case 'year': |
|||
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
|
|||
break |
|||
} |
|||
const y = dd.getFullYear() |
|||
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
|
|||
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
|
|||
return { |
|||
fullDate: y + '-' + m + '-' + d, |
|||
year: y, |
|||
month: m, |
|||
date: d, |
|||
day: dd.getDay() |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 获取上月剩余天数 |
|||
*/ |
|||
_getLastMonthDays(firstDay, full) { |
|||
let dateArr = [] |
|||
for (let i = firstDay; i > 0; i--) { |
|||
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate() |
|||
dateArr.push({ |
|||
date: beforeDate, |
|||
month: full.month - 1, |
|||
disable: true |
|||
}) |
|||
} |
|||
return dateArr |
|||
} |
|||
/** |
|||
* 获取本月天数 |
|||
*/ |
|||
_currentMonthDys(dateData, full) { |
|||
let dateArr = [] |
|||
let fullDate = this.date.fullDate |
|||
for (let i = 1; i <= dateData; i++) { |
|||
let isinfo = false |
|||
let nowDate = full.year + '-' + (full.month < 10 ? |
|||
full.month : full.month) + '-' + (i < 10 ? |
|||
'0' + i : i) |
|||
// 是否今天
|
|||
let isDay = fullDate === nowDate |
|||
// 获取打点信息
|
|||
let info = this.selected && this.selected.find((item) => { |
|||
if (this.dateEqual(nowDate, item.date)) { |
|||
return item |
|||
} |
|||
}) |
|||
|
|||
// 日期禁用
|
|||
let disableBefore = true |
|||
let disableAfter = true |
|||
if (this.startDate) { |
|||
// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
|
|||
// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
|
|||
disableBefore = this.dateCompare(this.startDate, nowDate) |
|||
} |
|||
|
|||
if (this.endDate) { |
|||
// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
|
|||
// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
|
|||
disableAfter = this.dateCompare(nowDate, this.endDate) |
|||
} |
|||
let multiples = this.multipleStatus.data |
|||
let checked = false |
|||
let multiplesStatus = -1 |
|||
if (this.range) { |
|||
if (multiples) { |
|||
multiplesStatus = multiples.findIndex((item) => { |
|||
return this.dateEqual(item, nowDate) |
|||
}) |
|||
} |
|||
if (multiplesStatus !== -1) { |
|||
checked = true |
|||
} |
|||
} |
|||
let data = { |
|||
fullDate: nowDate, |
|||
year: full.year, |
|||
date: i, |
|||
multiple: this.range ? checked : false, |
|||
beforeMultiple: this.isLogicBefore(nowDate, this.multipleStatus.before, this.multipleStatus.after), |
|||
afterMultiple: this.isLogicAfter(nowDate, this.multipleStatus.before, this.multipleStatus.after), |
|||
month: full.month, |
|||
disable: !(disableBefore && disableAfter), |
|||
isDay, |
|||
userChecked: false |
|||
} |
|||
if (info) { |
|||
data.extraInfo = info |
|||
} |
|||
|
|||
dateArr.push(data) |
|||
} |
|||
return dateArr |
|||
} |
|||
/** |
|||
* 获取下月天数 |
|||
*/ |
|||
_getNextMonthDays(surplus, full) { |
|||
let dateArr = [] |
|||
for (let i = 1; i < surplus + 1; i++) { |
|||
dateArr.push({ |
|||
date: i, |
|||
month: Number(full.month) + 1, |
|||
disable: true |
|||
}) |
|||
} |
|||
return dateArr |
|||
} |
|||
|
|||
/** |
|||
* 获取当前日期详情 |
|||
* @param {Object} date |
|||
*/ |
|||
getInfo(date) { |
|||
if (!date) { |
|||
date = new Date() |
|||
} |
|||
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate) |
|||
return dateInfo |
|||
} |
|||
|
|||
/** |
|||
* 比较时间大小 |
|||
*/ |
|||
dateCompare(startDate, endDate) { |
|||
// 计算截止时间
|
|||
startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
|||
// 计算详细项的截止时间
|
|||
endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
|||
if (startDate <= endDate) { |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 比较时间是否相等 |
|||
*/ |
|||
dateEqual(before, after) { |
|||
// 计算截止时间
|
|||
before = new Date(before.replace('-', '/').replace('-', '/')) |
|||
// 计算详细项的截止时间
|
|||
after = new Date(after.replace('-', '/').replace('-', '/')) |
|||
if (before.getTime() - after.getTime() === 0) { |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 比较真实起始日期 |
|||
*/ |
|||
|
|||
isLogicBefore(currentDay, before, after) { |
|||
let logicBefore = before |
|||
if (before && after) { |
|||
logicBefore = this.dateCompare(before, after) ? before : after |
|||
} |
|||
return this.dateEqual(logicBefore, currentDay) |
|||
} |
|||
|
|||
isLogicAfter(currentDay, before, after) { |
|||
let logicAfter = after |
|||
if (before && after) { |
|||
logicAfter = this.dateCompare(before, after) ? after : before |
|||
} |
|||
return this.dateEqual(logicAfter, currentDay) |
|||
} |
|||
|
|||
/** |
|||
* 获取日期范围内所有日期 |
|||
* @param {Object} begin |
|||
* @param {Object} end |
|||
*/ |
|||
geDateAll(begin, end) { |
|||
var arr = [] |
|||
var ab = begin.split('-') |
|||
var ae = end.split('-') |
|||
var db = new Date() |
|||
db.setFullYear(ab[0], ab[1] - 1, ab[2]) |
|||
var de = new Date() |
|||
de.setFullYear(ae[0], ae[1] - 1, ae[2]) |
|||
var unixDb = db.getTime() - 24 * 60 * 60 * 1000 |
|||
var unixDe = de.getTime() - 24 * 60 * 60 * 1000 |
|||
for (var k = unixDb; k <= unixDe;) { |
|||
k = k + 24 * 60 * 60 * 1000 |
|||
arr.push(this.getDate(new Date(parseInt(k))).fullDate) |
|||
} |
|||
return arr |
|||
} |
|||
|
|||
/** |
|||
* 获取多选状态 |
|||
*/ |
|||
setMultiple(fullDate) { |
|||
let { |
|||
before, |
|||
after |
|||
} = this.multipleStatus |
|||
if (!this.range) return |
|||
if (before && after) { |
|||
if (!this.lastHover) { |
|||
this.lastHover = true |
|||
return |
|||
} |
|||
this.multipleStatus.before = fullDate |
|||
this.multipleStatus.after = '' |
|||
this.multipleStatus.data = [] |
|||
this.multipleStatus.fulldate = '' |
|||
this.lastHover = false |
|||
} else { |
|||
if (!before) { |
|||
this.multipleStatus.before = fullDate |
|||
this.lastHover = false |
|||
} else { |
|||
this.multipleStatus.after = fullDate |
|||
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { |
|||
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus |
|||
.after); |
|||
} else { |
|||
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus |
|||
.before); |
|||
} |
|||
this.lastHover = true |
|||
} |
|||
} |
|||
this._getWeek(fullDate) |
|||
} |
|||
|
|||
/** |
|||
* 鼠标 hover 更新多选状态 |
|||
*/ |
|||
setHoverMultiple(fullDate) { |
|||
let { |
|||
before, |
|||
after |
|||
} = this.multipleStatus |
|||
|
|||
if (!this.range) return |
|||
if (this.lastHover) return |
|||
|
|||
if (!before) { |
|||
this.multipleStatus.before = fullDate |
|||
} else { |
|||
this.multipleStatus.after = fullDate |
|||
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { |
|||
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after); |
|||
} else { |
|||
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before); |
|||
} |
|||
} |
|||
this._getWeek(fullDate) |
|||
} |
|||
|
|||
/** |
|||
* 更新默认值多选状态 |
|||
*/ |
|||
setDefaultMultiple(before, after) { |
|||
this.multipleStatus.before = before |
|||
this.multipleStatus.after = after |
|||
if (before && after) { |
|||
if (this.dateCompare(before, after)) { |
|||
this.multipleStatus.data = this.geDateAll(before, after); |
|||
this._getWeek(after) |
|||
} else { |
|||
this.multipleStatus.data = this.geDateAll(after, before); |
|||
this._getWeek(before) |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取每周数据 |
|||
* @param {Object} dateData |
|||
*/ |
|||
_getWeek(dateData) { |
|||
const { |
|||
fullDate, |
|||
year, |
|||
month, |
|||
date, |
|||
day |
|||
} = this.getDate(dateData) |
|||
let firstDay = new Date(year, month - 1, 1).getDay() |
|||
let currentDay = new Date(year, month, 0).getDate() |
|||
let dates = { |
|||
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
|
|||
currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
|
|||
nextMonthDays: [], // 下个月开始几天
|
|||
weeks: [] |
|||
} |
|||
let canlender = [] |
|||
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length) |
|||
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData)) |
|||
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays) |
|||
let weeks = {} |
|||
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
|
|||
for (let i = 0; i < canlender.length; i++) { |
|||
if (i % 7 === 0) { |
|||
weeks[parseInt(i / 7)] = new Array(7) |
|||
} |
|||
weeks[parseInt(i / 7)][i % 7] = canlender[i] |
|||
} |
|||
this.canlender = canlender |
|||
this.weeks = weeks |
|||
} |
|||
|
|||
//静态方法
|
|||
// static init(date) {
|
|||
// if (!this.instance) {
|
|||
// this.instance = new Calendar(date);
|
|||
// }
|
|||
// return this.instance;
|
|||
// }
|
|||
} |
|||
|
|||
|
|||
export default Calendar |
|||
@ -1,90 +0,0 @@ |
|||
{ |
|||
"id": "uni-datetime-picker", |
|||
"displayName": "uni-datetime-picker 日期选择器", |
|||
"version": "2.2.2", |
|||
"description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择", |
|||
"keywords": [ |
|||
"uni-datetime-picker", |
|||
"uni-ui", |
|||
"uniui", |
|||
"日期时间选择器", |
|||
"日期时间" |
|||
], |
|||
"repository": "https://github.com/dcloudio/uni-ui", |
|||
"engines": { |
|||
"HBuilderX": "" |
|||
}, |
|||
"directories": { |
|||
"example": "../../temps/example_temps" |
|||
}, |
|||
"dcloudext": { |
|||
"category": [ |
|||
"前端组件", |
|||
"通用组件" |
|||
], |
|||
"sale": { |
|||
"regular": { |
|||
"price": "0.00" |
|||
}, |
|||
"sourcecode": { |
|||
"price": "0.00" |
|||
} |
|||
}, |
|||
"contact": { |
|||
"qq": "" |
|||
}, |
|||
"declaration": { |
|||
"ads": "无", |
|||
"data": "无", |
|||
"permissions": "无" |
|||
}, |
|||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
|||
}, |
|||
"uni_modules": { |
|||
"dependencies": [ |
|||
"uni-scss", |
|||
"uni-icons" |
|||
], |
|||
"encrypt": [], |
|||
"platforms": { |
|||
"cloud": { |
|||
"tcb": "y", |
|||
"aliyun": "y" |
|||
}, |
|||
"client": { |
|||
"App": { |
|||
"app-vue": "y", |
|||
"app-nvue": "n" |
|||
}, |
|||
"H5-mobile": { |
|||
"Safari": "y", |
|||
"Android Browser": "y", |
|||
"微信浏览器(Android)": "y", |
|||
"QQ浏览器(Android)": "y" |
|||
}, |
|||
"H5-pc": { |
|||
"Chrome": "y", |
|||
"IE": "y", |
|||
"Edge": "y", |
|||
"Firefox": "y", |
|||
"Safari": "y" |
|||
}, |
|||
"小程序": { |
|||
"微信": "y", |
|||
"阿里": "y", |
|||
"百度": "y", |
|||
"字节跳动": "y", |
|||
"QQ": "y" |
|||
}, |
|||
"快应用": { |
|||
"华为": "u", |
|||
"联盟": "u" |
|||
}, |
|||
"Vue": { |
|||
"vue2": "y", |
|||
"vue3": "y" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
|
|||
|
|||
> `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护` |
|||
|
|||
## DatetimePicker 时间选择器 |
|||
|
|||
> **组件名:uni-datetime-picker** |
|||
> 代码块: `uDatetimePicker` |
|||
|
|||
|
|||
该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。 |
|||
|
|||
若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。 |
|||
|
|||
**_点击 picker 默认值规则:_** |
|||
|
|||
- 若设置初始值 value, 会显示在 picker 显示框中 |
|||
- 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中 |
|||
|
|||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker) |
|||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
|||
@ -0,0 +1,109 @@ |
|||
import env from '@/env/index.js' |
|||
import store from '@/store/index.js' |
|||
/** |
|||
* 日期格式化,样例 yyyy-mm-dd hh:MM:ss |
|||
* @param date Date 需要转换的日期 |
|||
* @param fmt string 转化的格式 |
|||
*/ |
|||
export const dateTimeFormat = (date, fmt) => { |
|||
if (!date) { |
|||
throw new Error('日期不正确') |
|||
} |
|||
let ret |
|||
const opt = { |
|||
'y+': date.getFullYear().toString(), // 年
|
|||
'm+': (date.getMonth() + 1).toString(), // 月
|
|||
'd+': date.getDate().toString(), // 日
|
|||
'h+': date.getHours().toString(), // 时
|
|||
'M+': date.getMinutes().toString(), // 分
|
|||
's+': date.getSeconds().toString() // 秒
|
|||
} |
|||
for (let k in opt) { |
|||
ret = new RegExp('(' + k + ')').exec(fmt) |
|||
if (ret) { |
|||
fmt = fmt.replace(ret[1], ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, '0')) |
|||
} |
|||
} |
|||
return fmt |
|||
} |
|||
|
|||
/** |
|||
* 创建websocket |
|||
* @param {*} data |
|||
* @value {string} pageInfo 页面信息 |
|||
* @value {true} retry 是否重连,默认false |
|||
* @returns 一个websocket实例 |
|||
*/ |
|||
export const makeSocket = async ({ pageInfo = '', retry = false }) => { |
|||
const socket = { |
|||
sockTask: null, |
|||
close: function () { |
|||
this.sockTask.close({ code: 1000 }) |
|||
closeFlag = true |
|||
}, |
|||
onMessage: function () {} |
|||
} |
|||
|
|||
let limitedNum = 0 |
|||
let closeFlag = false |
|||
let timer = null |
|||
|
|||
async function createSocket() { |
|||
let url = '' |
|||
if (env == 'production') { |
|||
url = 'wss://api-client-yyt.qniao.cn/qn-websocket-service/wechatwebsock?token=' |
|||
} else if (env == 'test') { |
|||
url = 'wss://api-client-yyt-test.qniao.cn/qn-websocket-service/wechatwebsock?token=' |
|||
} |
|||
const token = store.state.qnToken |
|||
const socketTask = await uni.connectSocket({ |
|||
url: `${url}${token}`, |
|||
header: { |
|||
'content-type': 'application/json' |
|||
}, |
|||
success: () => { |
|||
console.log('websocket连接成功') |
|||
}, |
|||
fail: () => { |
|||
console.log('websocket连接失败') |
|||
} |
|||
}) |
|||
socketTask.onOpen(() => { |
|||
console.log(pageInfo + ' onOpen') |
|||
timer = setInterval(() => { |
|||
socketTask.send({ data: 'ping' }) |
|||
}, 10000) |
|||
}) |
|||
socketTask.onClose(() => { |
|||
console.log(pageInfo + ' onClose') |
|||
clearInterval(timer) |
|||
timer = null |
|||
if (!closeFlag && retry && limitedNum < 20) { |
|||
limitedNum++ |
|||
console.log('重连次数:' + limitedNum) |
|||
createSocket() |
|||
} |
|||
}) |
|||
|
|||
socketTask.onError(() => { |
|||
console.log(pageInfo + ' error') |
|||
}) |
|||
|
|||
socket.sockTask = socketTask |
|||
} |
|||
|
|||
await createSocket() |
|||
if (socket) { |
|||
socket.onMessage = (fn) => { |
|||
socket.sockTask.onMessage((res) => { |
|||
let data = JSON.parse(res.data) |
|||
console.log(pageInfo + '接收到消息:', data) |
|||
if (data.type != 'heartbeat') { |
|||
fn(data) |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
|
|||
return socket |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save