合立app移动端项目-任务详情页静态功能开发、表单提交节流处理

ccongli-dev-app-hl-0920
LI-CCONG\李聪聪 1 year ago
parent 96e9a4e16a
commit a7a348ac11

@ -3,7 +3,7 @@ spring:
name: yunxi-server
profiles:
active: local
active: dev
main:
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。

@ -5,7 +5,7 @@ ENV = 'development'
VUE_APP_TITLE = 云息供应链管理平台
# 云息供应链管理平台/开发环境
VUE_APP_BASE_API = 'http://localhost:8091'
VUE_APP_BASE_API = 'http://222.71.165.187:8091'
# 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true

@ -23,6 +23,7 @@ export default {
</script>
<style lang="scss">
/* 自定义图标 */
@import "@/static/iconfont.css";
/** 引入全局基本样式 */
@ -31,6 +32,11 @@ export default {
/* 引入 uView 基础样式 */
@import '@/uni_modules/uview-ui/index.scss';
/* 引入 uniapp 辅助样式 */
@import '@/uni_modules/uni-scss/index.scss';
/*每个页面公共scss */
@import 'app.scss';
</style>

@ -1,5 +1,5 @@
/* 页面公共scss */
.container {
// font-family: "Alibaba PuHuiTi 2.0 55 Regular";
background-color: $custom-bg-color;
margin: 0;
padding: 0;

@ -1,3 +1,4 @@
/* uView组件全局属性 */
uni.$u.props.gap.bgColor = '#ffffff'
uni.$u.props.gap.height = '10'
uni.$u.props.button.throttleTime = 500 // 节流: 0.5s内只触发一次

File diff suppressed because one or more lines are too long

@ -1,742 +0,0 @@
<template>
<view class="w-picker-view">
<picker-view v-if="fields=='year'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='month'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='day'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='hour'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='minute'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='second'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.seconds" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{
years:[],
months:[],
days:[],
hours:[],
minutes:[],
seconds:[]
},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
startYear:{
type:[String,Number],
default:""
},
endYear:{
type:[String,Number],
default:""
},
value:{
type:[String,Array,Number],
default:""
},
current:{//
type:Boolean,
default:false
},
disabledAfter:{//
type:Boolean,
default:false
},
fields:{
type:String,
default:"day"
}
},
watch:{
fields(val){
this.initData();
},
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg,example
switch(this.fields){
case "year":
strReg=/^\d{4}$/;
example="2019";
break;
case "month":
strReg=/^\d{4}-\d{2}$/;
example="2019-02";
break;
case "day":
strReg=/^\d{4}-\d{2}-\d{2}$/;
example="2019-02-01";
break;
case "hour":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}(:\d{2}){1,2}?$/;
example="2019-02-01 18:00:00或2019-02-01 18";
break;
case "minute":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2}){0,1}?$/;
example="2019-02-01 18:06:00或2019-02-01 18:06";
break;
case "second":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
example="2019-02-01 18:06:01";
break;
}
if(!strReg.test(value)){
console.log(new Error("请传入与mode、fields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day,hour,minute){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let months=[],days=[],hours=[],minutes=[],seconds=[];
let disabledAfter=this.disabledAfter;
let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
let totalDays=new Date(year,month,0).getDate();//;
let daysLen=disabledAfter?((year*1<curYear||month*1<curMonth)?totalDays:curDay):totalDays;
let hoursLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)?24:curHour+1):24;
let minutesLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay||hour*1<curHour)?60:curMinute+1):60;
let secondsLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay||hour*1<curHour||minute*1<curMinute)?60:curSecond+1):60;
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
};
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
for(let hour=0;hour<hoursLen;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<minutesLen;minute++){
minutes.push(this.formatNum(minute));
}
for(let second=0;second<secondsLen;second++){
seconds.push(this.formatNum(second));
}
return{
months,
days,
hours,
minutes,
seconds
}
},
isLeapYear (Year) {
if (((Year % 4)==0) && ((Year % 100)!=0) || ((Year % 400)==0)) {
return true;
} else {
return false;
}
},
getData(dVal){
//
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let fields=this.fields;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonthdays=curDate.curMonthdays;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let defaultDate=this.getDefaultDate();
let startYear=this.getStartDate().getFullYear();
let endYear=this.getEndDate().getFullYear();
//year,month,day,hour;,
let years=[],months=[],days=[],hours=[],minutes=[],seconds=[];
let year=dVal[0]*1;
let month=dVal[1]*1;
let day=dVal[2]*1;
let hour=dVal[3]*1;
let minute=dVal[4]*1;
let monthsLen=disabledAfter?(year<curYear?12:curDate.curMonth):12;
let daysLen=disabledAfter?((year<curYear||month<curMonth)?defaultDate.defaultDays:curDay):(curFlag?curMonthdays:defaultDate.defaultDays);
let hoursLen=disabledAfter?((year<curYear||month<curMonth||day<curDay)?24:curHour+1):24;
let minutesLen=disabledAfter?((year<curYear||month<curMonth||day<curDay||hour<curHour)?60:curMinute+1):60;
let secondsLen=disabledAfter?((year<curYear||month<curMonth||day<curDay||hour<curHour||minute<curMinute)?60:curSecond+1):60;
for(let year=startYear;year<=(disabledAfter?curYear:endYear);year++){
years.push(year.toString())
}
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
}
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
for(let hour=0;hour<hoursLen;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<minutesLen;minute++){
minutes.push(this.formatNum(minute));
}
// for(let second=0;second<(disabledAfter?curDate.curSecond+1:60);second++){
// seconds.push(this.formatNum(second));
// }
for(let second=0;second<60;second++){
seconds.push(this.formatNum(second));
}
return {
years,
months,
days,
hours,
minutes,
seconds
}
},
getCurrenDate(){
let curDate=new Date();
let curYear=curDate.getFullYear();
let curMonth=curDate.getMonth()+1;
let curMonthdays=new Date(curYear,curMonth,0).getDate();
let curDay=curDate.getDate();
let curHour=curDate.getHours();
let curMinute=curDate.getMinutes();
let curSecond=curDate.getSeconds();
return{
curDate,
curYear,
curMonth,
curMonthdays,
curDay,
curHour,
curMinute,
curSecond
}
},
getDefaultDate(){
let value=this.value;
let reg=/-/g;
let defaultDate=value?new Date(value.replace(reg,"/")):new Date();
let defaultYear=defaultDate.getFullYear();
let defaultMonth=defaultDate.getMonth()+1;
let defaultDay=defaultDate.getDate();
let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
return{
defaultDate,
defaultYear,
defaultMonth,
defaultDay,
defaultDays
}
},
getStartDate(){
let start=this.startYear;
let startDate="";
let reg=/-/g;
if(start){
startDate=new Date(start+"/01/01");
}else{
startDate=new Date("1970/01/01");
}
return startDate;
},
getEndDate(){
let end=this.endYear;
let reg=/-/g;
let endDate="";
if(end){
endDate=new Date(end+"/12/01");
}else{
endDate=new Date();
}
return endDate;
},
getDval(){
let value=this.value;
let fields=this.fields;
let dVal=null;
let aDate=new Date();
let year=this.formatNum(aDate.getFullYear());
let month=this.formatNum(aDate.getMonth()+1);
let day=this.formatNum(aDate.getDate());
let hour=this.formatNum(aDate.getHours());
let minute=this.formatNum(aDate.getMinutes());
let second=this.formatNum(aDate.getSeconds());
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[year,month,day,hour,minute,second]
}else{
switch(this.fields){
case "year":
dVal=value?[value]:[];
break;
case "month":
dVal=value?value.split("-"):[];
break;
case "day":
dVal=value?value.split("-"):[];
break;
case "hour":
dVal=[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")];
break;
case "minute":
dVal=value?[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")]:[];
break;
case "second":
dVal=[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")];
break;
}
}
}else{
dVal=[year,month,day,hour,minute,second]
}
return dVal;
},
initData(){
let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
let years=[],months=[],days=[],hours=[],minutes=[],seconds=[];
let dVal=[],pickVal=[];
let value=this.value;
let reg=/-/g;
let range={};
let result="",full="",year,month,day,hour,minute,second,obj={};
let defaultDate=this.getDefaultDate();
let defaultYear=defaultDate.defaultYear;
let defaultMonth=defaultDate.defaultMonth;
let defaultDay=defaultDate.defaultDay;
let defaultDays=defaultDate.defaultDays;
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curMonthdays=curDate.curMonthdays;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let dateData=[];
dVal=this.getDval();
startDate=this.getStartDate();
endDate=this.getEndDate();
startYear=startDate.getFullYear();
startMonth=startDate.getMonth();
startDay=startDate.getDate();
endYear=endDate.getFullYear();
endMonth=endDate.getMonth();
endDay=endDate.getDate();
dateData=this.getData(dVal);
years=dateData.years;
months=dateData.months;
days=dateData.days;
hours=dateData.hours;
minutes=dateData.minutes;
seconds=dateData.seconds;
switch(this.fields){
case "year":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0
]:(curFlag?[
years.indexOf(curYear+'')
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0
]);
range={years};
year=dVal[0]?dVal[0]:years[0];
result=full=`${year}`;
obj={
year
}
break;
case "month":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth))
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0
]);
range={years,months};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
result=full=`${year+'-'+month}`;
obj={
year,
month
}
break;
case "day":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0
]);
range={years,months,days};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
result=full=`${year+'-'+month+'-'+day}`;
obj={
year,
month,
day
}
break;
case "hour":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0
]);
range={years,months,days,hours};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
result=`${year+'-'+month+'-'+day+' '+hour}`;
full=`${year+'-'+month+'-'+day+' '+hour+':00:00'}`;
obj={
year,
month,
day,
hour
}
break;
case "minute":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
minutes.indexOf(this.formatNum(curMinute)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0
]);
range={years,months,days,hours,minutes};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
minute=dVal[4]?dVal[4]:minutes[0];
full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':00'}`;
result=`${year+'-'+month+'-'+day+' '+hour+':'+minute}`;
obj={
year,
month,
day,
hour,
minute
}
break;
case "second":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0,
dVal[5]&&seconds.indexOf(dVal[5])!=-1?seconds.indexOf(dVal[5]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
minutes.indexOf(this.formatNum(curMinute)),
seconds.indexOf(this.formatNum(curSecond)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0,
dVal[5]&&seconds.indexOf(dVal[5])!=-1?seconds.indexOf(dVal[5]):0
]);
range={years,months,days,hours,minutes,seconds};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
minute=dVal[4]?dVal[4]:minutes[0];
second=dVal[5]?dVal[5]:seconds[0];
result=full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':'+second}`;
obj={
year,
month,
day,
hour,
minute,
second
}
break;
default:
range={years,months,days};
break;
}
this.range=range;
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
});
this.$nextTick(()=>{
this.pickVal=pickVal;
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let year="",month="",day="",hour="",minute="",second="";
let result="",full="",obj={};
let months=null,days=null,hours=null,minutes=null,seconds=null;
let disabledAfter=this.disabledAfter;
let leapYear=false,resetData={};
year=(arr[0]||arr[0]==0)?data.years[arr[0]]||data.years[data.years.length-1]:"";
month=(arr[1]||arr[1]==0)?data.months[arr[1]]||data.months[data.months.length-1]:"";
day=(arr[2]||arr[2]==0)?data.days[arr[2]]||data.days[data.days.length-1]:"";
hour=(arr[3]||arr[3]==0)?data.hours[arr[3]]||data.hours[data.hours.length-1]:"";
minute=(arr[4]||arr[4]==0)?data.minutes[arr[4]]||data.minutes[data.minutes.length-1]:"";
second=(arr[5]||arr[5]==0)?data.seconds[arr[5]]||data.seconds[data.seconds.length-1]:"";
resetData=this.resetData(year,month,day,hour,minute);//;
leapYear=this.isLeapYear(year);//;
switch(this.fields){
case "year":
result=full=`${year}`;
obj={
year
};
break;
case "month":
result=full=`${year+'-'+month}`;
if(this.disabledAfter)months=resetData.months;
if(months)this.range.months=months;
obj={
year,
month
}
break;
case "day":
result=full=`${year+'-'+month+'-'+day}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
obj={
year,
month,
day
}
break;
case "hour":
result=`${year+'-'+month+'-'+day+' '+hour}`;
full=`${year+'-'+month+'-'+day+' '+hour+':00:00'}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
obj={
year,
month,
day,
hour
}
break;
case "minute":
full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':00'}`;
result=`${year+'-'+month+'-'+day+' '+hour+':'+minute}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
minutes=resetData.minutes;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
if(minutes)this.range.minutes=minutes;
obj={
year,
month,
day,
hour,
minute
};
break;
case "second":
result=full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':'+second}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
minutes=resetData.minutes;
//seconds=resetData.seconds;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
if(minutes)this.range.minutes=minutes;
//if(seconds)this.range.seconds=seconds;
obj={
year,
month,
day,
hour,
minute,
second
}
break;
}
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

@ -1,345 +0,0 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.sections" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
startYear:{
type:String,
default:""
},
endYear:{
type:String,
default:""
},
value:{
type:[String,Array,Number],
default:""
},
current:{//
type:Boolean,
default:false
},
disabledAfter:{//
type:Boolean,
default:false
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{4}-\d{2}-\d{2} [\u4e00-\u9fa5]{2}$/,example;
if(!strReg.test(value)){
console.log(new Error("请传入与mode、fields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let months=[],days=[],sections=[];
let disabledAfter=this.disabledAfter;
let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
let totalDays=new Date(year,month,0).getDate();//;
let daysLen=disabledAfter?((year*1<curYear||month*1<curMonth)?totalDays:curDay):totalDays;
let sectionFlag=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)==true?false:true):(curHour>12==true?true:false);
sections=["上午","下午"];
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
};
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
if(sectionFlag){
sections=["上午"];
}
return{
months,
days,
sections
}
},
getData(dVal){
//
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonthdays=curDate.curMonthdays;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let defaultDate=this.getDefaultDate();
let startYear=this.getStartDate().getFullYear();
let endYear=this.getEndDate().getFullYear();
let years=[],months=[],days=[],sections=[];
let year=dVal[0]*1;
let month=dVal[1]*1;
let day=dVal[2]*1;
let monthsLen=disabledAfter?(year<curYear?12:curDate.curMonth):12;
let daysLen=disabledAfter?((year<curYear||month<curMonth)?defaultDate.defaultDays:curDay):(curFlag?curMonthdays:defaultDate.defaultDays);
let sectionFlag=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)==true?false:true):(curHour>12==true?true:false);
for(let year=startYear;year<=(disabledAfter?curYear:endYear);year++){
years.push(year.toString())
}
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
}
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
if(sectionFlag){
sections=["下午"];
}else{
sections=["上午","下午"];
}
return {
years,
months,
days,
sections
}
},
getCurrenDate(){
let curDate=new Date();
let curYear=curDate.getFullYear();
let curMonth=curDate.getMonth()+1;
let curMonthdays=new Date(curYear,curMonth,0).getDate();
let curDay=curDate.getDate();
let curHour=curDate.getHours();
let curSection="上午";
if(curHour>=12){
curSection="下午";
}
return{
curDate,
curYear,
curMonth,
curMonthdays,
curDay,
curHour,
curSection
}
},
getDefaultDate(){
let value=this.value;
let reg=/-/g;
let defaultDate=value?new Date(value.split(" ")[0].replace(reg,"/")):new Date();
let defaultYear=defaultDate.getFullYear();
let defaultMonth=defaultDate.getMonth()+1;
let defaultDay=defaultDate.getDate();
let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
return{
defaultDate,
defaultYear,
defaultMonth,
defaultDay,
defaultDays
}
},
getStartDate(){
let start=this.startYear;
let startDate="";
let reg=/-/g;
if(start){
startDate=new Date(start+"/01/01");
}else{
startDate=new Date("1970/01/01");
}
return startDate;
},
getEndDate(){
let end=this.endYear;
let reg=/-/g;
let endDate="";
if(end){
endDate=new Date(end+"/12/31");
}else{
endDate=new Date();
}
return endDate;
},
getDval(){
let value=this.value;
let dVal=null;
let aDate=new Date();
let year=this.formatNum(aDate.getFullYear());
let month=this.formatNum(aDate.getMonth()+1);
let day=this.formatNum(aDate.getDate());
let hour=aDate.getHours();
let section="上午";
if(hour>=12)section="下午";
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[year,month,day,section]
}else{
let v=value.split(" ");
dVal=[...v[0].split("-"),v[1]];
}
}else{
dVal=[year,month,day,section]
}
return dVal;
},
initData(){
let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
let years=[],months=[],days=[],sections=[];
let dVal=[],pickVal=[];
let value=this.value;
let reg=/-/g;
let range={};
let result="",full="",year,month,day,section,obj={};
let defaultDate=this.getDefaultDate();
let defaultYear=defaultDate.defaultYear;
let defaultMonth=defaultDate.defaultMonth;
let defaultDay=defaultDate.defaultDay;
let defaultDays=defaultDate.defaultDays;
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curMonthdays=curDate.curMonthdays;
let curDay=curDate.curDay;
let curSection=curDate.curSection;
let dateData=[];
dVal=this.getDval();
startDate=this.getStartDate();
endDate=this.getEndDate();
startYear=startDate.getFullYear();
startMonth=startDate.getMonth();
startDay=startDate.getDate();
endYear=endDate.getFullYear();
endMonth=endDate.getMonth();
endDay=endDate.getDate();
dateData=this.getData(dVal);
years=dateData.years;
months=dateData.months;
days=dateData.days;
sections=dateData.sections;
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&sections.indexOf(dVal[3])!=-1?sections.indexOf(dVal[3]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
sections.indexOf(curSection),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&sections.indexOf(dVal[3])!=-1?sections.indexOf(dVal[3]):0
]);
range={years,months,days,sections};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
section=dVal[3]?dVal[3]:sections[0];
result=full=`${year+'-'+month+'-'+day+' '+section}`;
obj={
year,
month,
day,
section
}
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:full,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let year="",month="",day="",section="";
let result="",full="",obj={};
let months=null,days=null,sections=null;
let disabledAfter=this.disabledAfter;
year=(arr[0]||arr[0]==0)?data.years[arr[0]]||data.years[data.years.length-1]:"";
month=(arr[1]||arr[1]==0)?data.months[arr[1]]||data.months[data.months.length-1]:"";
day=(arr[2]||arr[2]==0)?data.days[arr[2]]||data.days[data.days.length-1]:"";
section=(arr[3]||arr[3]==0)?data.sections[arr[3]]||data.sections[data.sections.length-1]:"";
result=full=`${year+'-'+month+'-'+day+' '+section}`;
let resetData=this.resetData(year,month,day);
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
sections=resetData.sections;
}else{
if(year%4==0||(month!=this.checkObj.month)){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(sections)this.range.sections=sections;
obj={
year,
month,
day,
section
}
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

@ -1,274 +0,0 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column v-for="(group,gIndex) in range" :key="gIndex">
<view class="w-picker-item" v-for="(item,index) in group" :key="index">{{item[nodeKey]}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:[],
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[Array,String],
default:""
},
defaultType:{
type:String,
default:"label"
},
options:{
type:Array,
default(){
return []
}
},
defaultProps:{
type:Object,
default(){
return{
lable:"label",
value:"value",
children:"children"
}
}
},
level:{
//
type:[Number,String],
default:2
}
},
computed:{
nodeKey(){
return this.defaultProps.label;
},
nodeVal(){
return this.defaultProps.value;
},
nodeChild(){
return this.defaultProps.children;
}
},
watch:{
value(val){
if(this.options.length!=0){
this.initData();
}
},
options(val){
this.initData();
}
},
created() {
if(this.options.length!=0){
this.initData();
}
},
methods:{
getData(){
//
let options=this.options;
let col1={},col2={},col3={},col4={};
let arr1=options,arr2=[],arr3=[],arr4=[];
let col1Index=0,col2Index=0,col3Index=0,col4Index=0;
let a1="",a2="",a3="",a4="";
let dVal=[],obj={};
let value=this.value;
let data=[];
a1=value[0];
a2=value[1];
if(this.level>2){
a3=value[2];
}
if(this.level>3){
a4=value[3];
};
/*第1列*/
col1Index=arr1.findIndex((v)=>{
return v[this.defaultType]==a1
});
col1Index=value?(col1Index!=-1?col1Index:0):0;
col1=arr1[col1Index];
/*第2列*/
arr2=arr1[col1Index][this.nodeChild];
col2Index=arr2.findIndex((v)=>{
return v[this.defaultType]==a2
});
col2Index=value?(col2Index!=-1?col2Index:0):0;
col2=arr2[col2Index];
/*第3列*/
if(this.level>2){
arr3=arr2[col2Index][this.nodeChild];
col3Index=arr3.findIndex((v)=>{
return v[this.defaultType]==a3;
});
col3Index=value?(col3Index!=-1?col3Index:0):0;
col3=arr3[col3Index];
};
/*第4列*/
if(this.level>3){
arr4=arr3[col4Index][this.nodeChild];
col4Index=arr4.findIndex((v)=>{
return v[this.defaultType]==a4;
});
col4Index=value?(col4Index!=-1?col4Index:0):0;
col4=arr4[col4Index];
};
switch(this.level*1){
case 2:
dVal=[col1Index,col2Index];
obj={
col1,
col2
}
data=[arr1,arr2];
break;
case 3:
dVal=[col1Index,col2Index,col3Index];
obj={
col1,
col2,
col3
}
data=[arr1,arr2,arr3];
break;
case 4:
dVal=[col1Index,col2Index,col3Index,col4Index];
obj={
col1,
col2,
col3,
col4
}
data=[arr1,arr2,arr3,arr4];
break
}
return {
data,
dVal,
obj
}
},
initData(){
let dataData=this.getData();
let data=dataData.data;
let arr1=data[0];
let arr2=data[1];
let arr3=data[2]||[];
let arr4=data[3]||[];
let obj=dataData.obj;
let col1=obj.col1,col2=obj.col2,col3=obj.col3||{},col4=obj.col4||{};
let result="",value=[];
let range=[];
switch(this.level){
case 2:
value=[col1[this.nodeVal],col2[this.nodeVal]];
result=`${col1[this.nodeKey]+col2[this.nodeKey]}`;
range=[arr1,arr2];
break;
case 3:
value=[col1[this.nodeVal],col2[this.nodeVal],col3[this.nodeVal]];
result=`${col1[this.nodeKey]+col2[this.nodeKey]+col3[this.nodeKey]}`;
range=[arr1,arr2,arr3];
break;
case 4:
value=[col1[this.nodeVal],col2[this.nodeVal],col3[this.nodeVal],col4[this.nodeVal]];
result=`${col1[this.nodeKey]+col2[this.nodeKey]+col3[this.nodeKey]+col4[this.nodeKey]}`;
range=[arr1,arr2,arr3,arr4];
break;
}
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=dataData.dVal;
});
this.$emit("change",{
result:result,
value:value,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let col1Index=arr[0],col2Index=arr[1],col3Index=arr[2]||0,col4Index=arr[3]||0;
let arr1=[],arr2=[],arr3=[],arr4=[];
let col1,col2,col3,col4,obj={};
let result="",value=[];
arr1=this.options;
arr2=(arr1[col1Index]&&arr1[col1Index][this.nodeChild])||arr1[arr1.length-1][this.nodeChild]||[];
col1=arr1[col1Index]||arr1[arr1.length-1]||{};
col2=arr2[col2Index]||arr2[arr2.length-1]||{};
if(this.level>2){
arr3=(arr2[col2Index]&&arr2[col2Index][this.nodeChild])||arr2[arr2.length-1][this.nodeChild];
col3=arr3[col3Index]||arr3[arr3.length-1]||{};
}
if(this.level>3){
arr4=(arr3[col3Index]&&arr3[col3Index][this.nodeChild])||arr3[arr3.length-1][this.nodeChild]||[];
col4=arr4[col4Index]||arr4[arr4.length-1]||{};
}
switch(this.level){
case 2:
obj={
col1,
col2
}
this.range=[arr1,arr2];
result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')}`;
value=[col1[this.nodeVal]||'',col2[this.nodeVal]||''];
break;
case 3:
obj={
col1,
col2,
col3
}
this.range=[arr1,arr2,arr3];
result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')+(col3[this.nodeKey]||'')}`;
value=[col1[this.nodeVal]||'',col2[this.nodeVal]||'',col3[this.nodeVal]||''];
break;
case 4:
obj={
col1,
col2,
col3,
col4
}
this.range=[arr1,arr2,arr3,arr4];
result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')+(col3[this.nodeKey]||'')+(col4[this.nodeKey]||'')}`;
value=[col1[this.nodeVal]||'',col2[this.nodeVal]||'',col3[this.nodeVal]||'',col4[this.nodeVal]||''];
break;
}
this.checkObj=obj;
this.pickVal=arr;
this.$emit("change",{
result:result,
value:value,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

@ -1,344 +0,0 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.fyears" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.fmonths" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.fdays" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex1">
<view class="w-picker-item">-</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.tyears" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.tmonths" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.tdays" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[String,Array],
default(){
return []
}
},
current:{//
type:Boolean,
default:false
},
startYear:{
type:[String,Number],
default:1970
},
endYear:{
type:[String,Number],
default:new Date().getFullYear()
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{4}-\d{2}-\d{2}$/,example="2020-04-03";
if(!strReg.test(value[0])||!strReg.test(value[1])){
console.log(new Error("请传入与mode匹配的value值例["+example+","+example+"]"))
}
return strReg.test(value[0])&&strReg.test(value[1]);
},
resetToData(fmonth,fday,tyear,tmonth){
let range=this.range;
let tmonths=[],tdays=[];
let yearFlag=tyear!=range.tyears[0];
let monthFlag=tyear!=range.tyears[0]||tmonth!=range.tmonths[0];
let ttotal=new Date(tyear,tmonth,0).getDate();
for(let i=yearFlag?1:fmonth*1;i<=12;i++){
tmonths.push(this.formatNum(i))
}
for(let i=monthFlag?1:fday*1;i<=ttotal;i++){
tdays.push(this.formatNum(i))
}
return{
tmonths,
tdays
}
},
resetData(fyear,fmonth,fday,tyear,tmonth){
let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[];
let startYear=this.startYear;
let endYear=this.endYear;
let ftotal=new Date(fyear,fmonth,0).getDate();
let ttotal=new Date(tyear,tmonth,0).getDate();
for(let i=startYear*1;i<=endYear;i++){
fyears.push(this.formatNum(i))
}
for(let i=1;i<=12;i++){
fmonths.push(this.formatNum(i))
}
for(let i=1;i<=ftotal;i++){
fdays.push(this.formatNum(i))
}
for(let i=fyear*1;i<=endYear;i++){
tyears.push(this.formatNum(i))
}
for(let i=fmonth*1;i<=12;i++){
tmonths.push(this.formatNum(i))
}
for(let i=fday*1;i<=ttotal;i++){
tdays.push(this.formatNum(i))
}
return {
fyears,
fmonths,
fdays,
tyears,
tmonths,
tdays
}
},
getData(dVal){
let start=this.startYear*1;
let end=this.endYear*1;
let value=dVal;
let flag=this.current;
let aToday=new Date();
let tYear,tMonth,tDay,tHours,tMinutes,tSeconds,pickVal=[];
let initstartDate=new Date(start.toString());
let endDate=new Date(end.toString());
if(start>end){
initstartDate=new Date(end.toString());
endDate=new Date(start.toString());
};
let startYear=initstartDate.getFullYear();
let startMonth=initstartDate.getMonth()+1;
let endYear=endDate.getFullYear();
let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[],returnArr=[],startDVal=[],endDVal=[];
let curMonth=flag?value[1]*1:(startDVal[1]*1+1);
let curMonth1=flag?value[5][1]*1:(value[5]*1+1);
let totalDays=new Date(value[0],value[1],0).getDate();
let totalDays1=new Date(value[4],value[5],0).getDate();
for(let s=startYear;s<=endYear;s++){
fyears.push(this.formatNum(s));
};
for(let m=1;m<=12;m++){
fmonths.push(this.formatNum(m));
};
for(let d=1;d<=totalDays;d++){
fdays.push(this.formatNum(d));
};
for(let s=value[0]*1;s<=endYear;s++){
tyears.push(this.formatNum(s));
};
if(value[4]*1>value[0]*1){
for(let m=1;m<=12;m++){
tmonths.push(this.formatNum(m));
};
for(let d=1;d<=totalDays1;d++){
tdays.push(this.formatNum(d));
};
}else{
for(let m=value[1]*1;m<=12;m++){
tmonths.push(this.formatNum(m));
};
for(let d=value[2]*1;d<=totalDays1;d++){
tdays.push(this.formatNum(d));
};
};
pickVal=[
fyears.indexOf(value[0])==-1?0:fyears.indexOf(value[0]),
fmonths.indexOf(value[1])==-1?0:fmonths.indexOf(value[1]),
fdays.indexOf(value[2])==-1?0:fdays.indexOf(value[2]),
0,
tyears.indexOf(value[4])==-1?0:tyears.indexOf(value[4]),
tmonths.indexOf(value[5])==-1?0:tmonths.indexOf(value[5]),
tdays.indexOf(value[6])==-1?0:tdays.indexOf(value[6])
];
return {
fyears,
fmonths,
fdays,
tyears,
tmonths,
tdays,
pickVal
}
},
getDval(){
let value=this.value;
let fields=this.fields;
let dVal=null;
let aDate=new Date();
let fyear=this.formatNum(aDate.getFullYear());
let fmonth=this.formatNum(aDate.getMonth()+1);
let fday=this.formatNum(aDate.getDate());
let tyear=this.formatNum(aDate.getFullYear());
let tmonth=this.formatNum(aDate.getMonth()+1);
let tday=this.formatNum(aDate.getDate());
if(value&&value.length>0){
let flag=this.checkValue(value);
if(!flag){
dVal=[fyear,fmonth,fday,"-",tyear,tmonth,tday]
}else{
dVal=[...value[0].split("-"),"-",...value[1].split("-")];
}
}else{
dVal=[fyear,fmonth,fday,"-",tyear,tmonth,tday]
}
return dVal;
},
initData(){
let range=[],pickVal=[];
let result="",full="",obj={};
let dVal=this.getDval();
let dateData=this.getData(dVal);
let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[];
let fyear,fmonth,fday,tyear,tmonth,tday;
pickVal=dateData.pickVal;
fyears=dateData.fyears;
fmonths=dateData.fmonths;
fdays=dateData.fdays;
tyears=dateData.tyears;
tmonths=dateData.tmonths;
tdays=dateData.tdays;
range={
fyears,
fmonths,
fdays,
tyears,
tmonths,
tdays,
}
fyear=range.fyears[pickVal[0]];
fmonth=range.fmonths[pickVal[1]];
fday=range.fdays[pickVal[2]];
tyear=range.tyears[pickVal[4]];
tmonth=range.tmonths[pickVal[5]];
tday=range.tdays[pickVal[6]];
obj={
fyear,
fmonth,
fday,
tyear,
tmonth,
tday
}
result=`${fyear+'-'+fmonth+'-'+fday+'至'+tyear+'-'+tmonth+'-'+tday}`;
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:result.split("至"),
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let result="",full="",obj={};
let year="",month="",day="",hour="",minute="",second="",note=[],province,city,area;
let checkObj=this.checkObj;
let days=[],months=[],endYears=[],endMonths=[],endDays=[],startDays=[];
let mode=this.mode;
let col1,col2,col3,d,a,h,m;
let xDate=new Date().getTime();
let range=this.range;
let fyear=range.fyears[arr[0]]||range.fyears[range.fyears.length-1];
let fmonth=range.fmonths[arr[1]]||range.fmonths[range.fmonths.length-1];
let fday=range.fdays[arr[2]]||range.fdays[range.fdays.length-1];
let tyear=range.tyears[arr[4]]||range.tyears[range.tyears.length-1];
let tmonth=range.tmonths[arr[5]]||range.tmonths[range.tmonths.length-1];
let tday=range.tdays[arr[6]]||range.tdays[range.tdays.length-1];
let resetData=this.resetData(fyear,fmonth,fday,tyear,tmonth);
if(fyear!=checkObj.fyear||fmonth!=checkObj.fmonth||fday!=checkObj.fday){
arr[4]=0;
arr[5]=0;
arr[6]=0;
range.tyears=resetData.tyears;
range.tmonths=resetData.tmonths;
range.tdays=resetData.tdays;
tyear=range.tyears[0];
checkObj.tyears=range.tyears[0];
tmonth=range.tmonths[0];
checkObj.tmonths=range.tmonths[0];
tday=range.tdays[0];
checkObj.tdays=range.tdays[0];
}
if(fyear!=checkObj.fyear||fmonth!=checkObj.fmonth){
range.fdays=resetData.fdays;
};
if(tyear!=checkObj.tyear){
arr[5]=0;
arr[6]=0;
let toData=this.resetToData(fmonth,fday,tyear,tmonth);
range.tmonths=toData.tmonths;
range.tdays=toData.tdays;
tmonth=range.tmonths[0];
checkObj.tmonths=range.tmonths[0];
tday=range.tdays[0];
checkObj.tdays=range.tdays[0];
};
if(tmonth!=checkObj.tmonth){
arr[6]=0;
let toData=this.resetToData(fmonth,fday,tyear,tmonth);
range.tdays=toData.tdays;
tday=range.tdays[0];
checkObj.tdays=range.tdays[0];
};
result=`${fyear+'-'+fmonth+'-'+fday+'至'+tyear+'-'+tmonth+'-'+tday}`;
obj={
fyear,fmonth,fday,tyear,tmonth,tday
}
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=arr;
})
this.$emit("change",{
result:result,
value:result.split("至"),
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

@ -1,183 +0,0 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.provinces" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.citys" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column v-if="!hideArea">
<view class="w-picker-item" v-for="(item,index) in range.areas" :key="index">{{item.label}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
import areaData from "./areadata/areadata.js"
export default {
data() {
return {
pickVal:[],
range:{
provinces:[],
citys:[],
areas:[]
},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[Array,String],
default:""
},
defaultType:{
type:String,
default:"label"
},
hideArea:{
type:Boolean,
default:false
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
getData(){
//
let provinces=areaData;
let dVal=[];
let value=this.value;
let a1=value[0];//
let a2=value[1];//
let a3=value[2];//
let province,city,area;
let provinceIndex=provinces.findIndex((v)=>{
return v[this.defaultType]==a1
});
provinceIndex=value?(provinceIndex!=-1?provinceIndex:0):0;
let citys=provinces[provinceIndex].children;
let cityIndex=citys.findIndex((v)=>{
return v[this.defaultType]==a2
});
cityIndex=value?(cityIndex!=-1?cityIndex:0):0;
let areas=citys[cityIndex].children;
let areaIndex=areas.findIndex((v)=>{
return v[this.defaultType]==a3;
});
areaIndex=value?(areaIndex!=-1?areaIndex:0):0;
dVal=this.hideArea?[provinceIndex,cityIndex]:[provinceIndex,cityIndex,areaIndex];
province=provinces[provinceIndex];
city=citys[cityIndex];
area=areas[areaIndex];
let obj=this.hideArea?{
province,
city
}:{
province,
city,
area
}
return this.hideArea?{
provinces,
citys,
dVal,
obj
}:{
provinces,
citys,
areas,
dVal,
obj
}
},
initData(){
let dataData=this.getData();
let provinces=dataData.provinces;
let citys=dataData.citys;
let areas=this.hideArea?[]:dataData.areas;
let obj=dataData.obj;
let province=obj.province,city=obj.city,area=this.hideArea?{}:obj.area;
let value=this.hideArea?[province.value,city.value]:[province.value,city.value,area.value];
let result=this.hideArea?`${province.label+city.label}`:`${province.label+city.label+area.label}`;
this.range=this.hideArea?{
provinces,
citys,
}:{
provinces,
citys,
areas
};
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=dataData.dVal;
});
this.$emit("change",{
result:result,
value:value,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let provinceIndex=arr[0],cityIndex=arr[1],areaIndex=this.hideArea?0:arr[2];
let provinces=areaData;
let citys=(provinces[provinceIndex]&&provinces[provinceIndex].children)||provinces[provinces.length-1].children||[];
let areas=this.hideArea?[]:((citys[cityIndex]&&citys[cityIndex].children)||citys[citys.length-1].children||[]);
let province=provinces[provinceIndex]||provinces[provinces.length-1],
city=citys[cityIndex]||[citys.length-1],
area=this.hideArea?{}:(areas[areaIndex]||[areas.length-1]);
let obj=this.hideArea?{
province,
city
}:{
province,
city,
area
}
if(this.checkObj.province.label!=province.label){
//;
this.range.citys=citys;
if(!this.hideArea){
this.range.areas=areas;
}
}
if(this.checkObj.city.label!=city.label){
//;
if(!this.hideArea){
this.range.areas=areas;
}
}
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=arr;
})
let result=this.hideArea?`${province.label+city.label}`:`${province.label+city.label+area.label}`;
let value=this.hideArea?[province.value,city.value]:[province.value,city.value,area.value];
this.$emit("change",{
result:result,
value:value,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

@ -1,129 +0,0 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range" :key="index">{{item[nodeKey]}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
props:{
itemHeight:{
type:String,
default:"44px"
},
options:{
type:[Array,Object],
default(){
return []
}
},
value:{
type:String,
default:""
},
defaultType:{
type:String,
default:"label"
},
defaultProps:{
type:Object,
default(){
return{
label:"label",
value:"value"
}
}
}
},
data() {
return {
pickVal:[]
};
},
computed:{
nodeKey(){
return this.defaultProps.label;
},
nodeValue(){
return this.defaultProps.value;
},
range(){
return this.options
}
},
watch:{
value(val){
if(this.options.length!=0){
this.initData();
}
},
options(val){
this.initData();
}
},
created() {
if(this.options.length!=0){
this.initData();
}
},
methods:{
initData(){
let dVal=this.value||"";
let data=this.range;
let pickVal=[0];
let cur=null;
let label="";
let value,idx;
if(this.defaultType==this.nodeValue){
value=data.find((v)=>v[this.nodeValue]==dVal);
idx=data.findIndex((v)=>v[this.nodeValue]==dVal);
}else{
value=data.find((v)=>v[this.nodeKey]==dVal);
idx=data.findIndex((v)=>v[this.nodeKey]==dVal);
}
pickVal=[idx!=-1?idx:0];
this.$nextTick(()=>{
this.pickVal=pickVal;
});
if(this.defaultType==this.nodeValue){
this.$emit("change",{
result:value?value[this.nodeKey]:data[0][this.nodeKey],
value:dVal||data[0][this.nodeKey],
obj:value?value:data[0]
})
}else{
this.$emit("change",{
result:dVal||data[0][this.nodeKey],
value:value?value[this.nodeValue]:data[0][this.nodeValue],
obj:value?value:data[0]
})
}
},
handlerChange(e){
let arr=[...e.detail.value];
let pickVal=[arr[0]||0];
let data=this.range;
let cur=data[arr[0]];
let label="";
let value="";
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:cur[this.nodeKey],
value:cur[this.nodeValue],
obj:cur
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

@ -1,250 +0,0 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.dates" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item.label}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[String,Array,Number],
default:""
},
current:{//
type:Boolean,
default:false
},
expand:{
type:[Number,String],
default:30
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2})?$/,example="2019-12-12 18:05:00或者2019-12-12 18:05";
if(!strReg.test(value)){
console.log(new Error("请传入与mode、fields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let months=[],days=[],sections=[];
let disabledAfter=this.disabledAfter;
let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
let totalDays=new Date(year,month,0).getDate();//;
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
};
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
return{
months,
days,
sections
}
},
getData(dVal){
//
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let dates=[],hours=[],minutes=[];
let curDate=new Date();
let curYear=curDate.getFullYear();
let curMonth=curDate.getMonth();
let curDay=curDate.getDate();
let aDate=new Date(curYear,curMonth,curDay);
for(let i=0;i<this.expand*1;i++){
aDate=new Date(curYear,curMonth,curDay+i);
let year=aDate.getFullYear();
let month=aDate.getMonth()+1;
let day=aDate.getDate();
let label=year+"-"+this.formatNum(month)+"-"+this.formatNum(day);
switch(i){
case 0:
label="今天";
break;
case 1:
label="明天";
break;
case 2:
label="后天";
break
}
dates.push({
label:label,
value:year+"-"+this.formatNum(month)+"-"+this.formatNum(day)
})
};
for(let i=0;i<24;i++){
hours.push({
label:this.formatNum(i),
value:this.formatNum(i)
})
}
for(let i=0;i<60;i++){
minutes.push({
label:this.formatNum(i),
value:this.formatNum(i)
})
}
return {
dates,
hours,
minutes
}
},
getDefaultDate(){
let value=this.value;
let reg=/-/g;
let defaultDate=value?new Date(value.replace(reg,"/")):new Date();
let defaultYear=defaultDate.getFullYear();
let defaultMonth=defaultDate.getMonth()+1;
let defaultDay=defaultDate.getDate();
let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
return{
defaultDate,
defaultYear,
defaultMonth,
defaultDay,
defaultDays
}
},
getDval(){
let value=this.value;
let dVal=null;
let aDate=new Date();
let year=this.formatNum(aDate.getFullYear());
let month=this.formatNum(aDate.getMonth()+1);
let day=this.formatNum(aDate.getDate());
let date=this.formatNum(year)+"-"+this.formatNum(month)+"-"+this.formatNum(day);
let hour=aDate.getHours();
let minute=aDate.getMinutes();
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[date,hour,minute]
}else{
let v=value.split(" ");
dVal=[v[0],...v[1].split(":")];
}
}else{
dVal=[date,hour,minute]
}
return dVal;
},
initData(){
let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
let dates=[],hours=[],minutes=[];
let dVal=[],pickVal=[];
let value=this.value;
let reg=/-/g;
let range={};
let result="",full="",date,hour,minute,obj={};
let defaultDate=this.getDefaultDate();
let defaultYear=defaultDate.defaultYear;
let defaultMonth=defaultDate.defaultMonth;
let defaultDay=defaultDate.defaultDay;
let defaultDays=defaultDate.defaultDays;
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let dateData=[];
dVal=this.getDval();
dateData=this.getData(dVal);
dates=dateData.dates;
hours=dateData.hours;
minutes=dateData.minutes;
pickVal=[
dates.findIndex(n => n.value == dVal[0])!=-1?dates.findIndex(n => n.value == dVal[0]):0,
hours.findIndex(n => n.value == dVal[1])!=-1?hours.findIndex(n => n.value == dVal[1]):0,
minutes.findIndex(n => n.value == dVal[2])!=-1?minutes.findIndex(n => n.value == dVal[2]):0,
];
range={dates,hours,minutes};
date=dVal[0]?dVal[0]:dates[0].label;
hour=dVal[1]?dVal[1]:hours[0].label;
minute=dVal[2]?dVal[2]:minutes[0].label;
result=full=`${date+' '+hour+':'+minute}`;
obj={
date,
hour,
minute
}
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:full,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let date="",hour="",minute="";
let result="",full="",obj={};
let disabledAfter=this.disabledAfter;
date=(arr[0]||arr[0]==0)?data.dates[arr[0]]||data.dates[data.dates.length-1]:"";
hour=(arr[1]||arr[1]==0)?data.hours[arr[1]]||data.hours[data.hours.length-1]:"";
minute=(arr[2]||arr[2]==0)?data.minutes[arr[2]]||data.minutes[data.minutes.length-1]:"";
result=full=`${date.label+' '+hour.label+':'+minute.label+':00'}`;
obj={
date,
hour,
minute
}
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

@ -1,218 +0,0 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column v-if="second">
<view class="w-picker-item" v-for="(item,index) in range.seconds" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[String,Array,Number],
default:""
},
current:{//
type:Boolean,
default:false
},
second:{
type:Boolean,
default:true
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{2}:\d{2}:\d{2}$/,example="18:00:05";
if(!strReg.test(value)){
console.log(new Error("请传入与mode、fields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day,hour,minute){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
for(let hour=0;hour<24;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<60;minute++){
minutes.push(this.formatNum(minute));
}
for(let second=0;second<60;second++){
seconds.push(this.formatNum(second));
}
return{
hours,
minutes,
seconds
}
},
getData(curDate){
//
let hours=[],minutes=[],seconds=[];
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let fields=this.fields;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
for(let hour=0;hour<24;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<60;minute++){
minutes.push(this.formatNum(minute));
}
for(let second=0;second<60;second++){
seconds.push(this.formatNum(second));
}
return this.second?{
hours,
minutes,
seconds
}:{
hours,
minutes
}
},
getCurrenDate(){
let curDate=new Date();
let curHour=curDate.getHours();
let curMinute=curDate.getMinutes();
let curSecond=curDate.getSeconds();
return this.second?{
curHour,
curMinute,
curSecond
}:{
curHour,
curMinute,
}
},
getDval(){
let value=this.value;
let fields=this.fields;
let dVal=null;
let aDate=new Date();
let hour=this.formatNum(aDate.getHours());
let minute=this.formatNum(aDate.getMinutes());
let second=this.formatNum(aDate.getSeconds());
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[hour,minute,second]
}else{
dVal=value?value.split(":"):[];
}
}else{
dVal=this.second?[hour,minute,second]:[hour,minute]
}
return dVal;
},
initData(){
let curDate=this.getCurrenDate();
let dateData=this.getData(curDate);
let pickVal=[],obj={},full="",result="",hour="",minute="",second="";
let dVal=this.getDval();
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let hours=dateData.hours;
let minutes=dateData.minutes;
let seconds=dateData.seconds;
let defaultArr=this.second?[
dVal[0]&&hours.indexOf(dVal[0])!=-1?hours.indexOf(dVal[0]):0,
dVal[1]&&minutes.indexOf(dVal[1])!=-1?minutes.indexOf(dVal[1]):0,
dVal[2]&&seconds.indexOf(dVal[2])!=-1?seconds.indexOf(dVal[2]):0
]:[
dVal[0]&&hours.indexOf(dVal[0])!=-1?hours.indexOf(dVal[0]):0,
dVal[1]&&minutes.indexOf(dVal[1])!=-1?minutes.indexOf(dVal[1]):0
];
pickVal=disabledAfter?defaultArr:(curFlag?(this.second?[
hours.indexOf(this.formatNum(curDate.curHour)),
minutes.indexOf(this.formatNum(curDate.curMinute)),
seconds.indexOf(this.formatNum(curDate.curSecond)),
]:[
hours.indexOf(this.formatNum(curDate.curHour)),
minutes.indexOf(this.formatNum(curDate.curMinute))
]):defaultArr);
this.range=dateData;
this.checkObj=obj;
hour=dVal[0]?dVal[0]:hours[0];
minute=dVal[1]?dVal[1]:minutes[0];
if(this.second)second=dVal[2]?dVal[2]:seconds[0];
result=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute}`;
full=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute+':00'}`;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:full,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let hour="",minute="",second="",result="",full="",obj={};
hour=(arr[0]||arr[0]==0)?data.hours[arr[0]]||data.hours[data.hours.length-1]:"";
minute=(arr[1]||arr[1]==0)?data.minutes[arr[1]]||data.minutes[data.minutes.length-1]:"";
if(this.second)second=(arr[2]||arr[2]==0)?data.seconds[arr[2]]||data.seconds[data.seconds.length-1]:"";
obj=this.second?{
hour,
minute,
second
}:{
hour,
minute
};
this.checkObj=obj;
result=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute}`;
full=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute+':00'}`;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

@ -1,26 +0,0 @@
.w-picker-flex2{
flex:2;
}
.w-picker-flex1{
flex:1;
}
.w-picker-view {
width: 100%;
height: 476upx;
overflow: hidden;
background-color: rgba(255, 255, 255, 1);
z-index: 666;
}
.d-picker-view{
height: 100%;
}
.w-picker-item {
text-align: center;
width: 100%;
height: 88upx;
line-height: 88upx;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30upx;
}

@ -1,340 +0,0 @@
<template name="w-picker">
<view class="w-picker" :key="createKey" :data-key="createKey">
<view class="mask" :class="{'visible':visible}" @tap="onCancel" @touchmove.stop.prevent catchtouchmove="true"></view>
<view class="w-picker-cnt" :class="{'visible':visible}">
<view class="w-picker-header" @touchmove.stop.prevent catchtouchmove="true">
<text @tap.stop.prevent="onCancel">取消</text>
<slot></slot>
<text :style="{'color':themeColor}" @tap.stop.prevent="pickerConfirm">确定</text>
</view>
<date-picker
v-if="mode=='date'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:fields="fields"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</date-picker>
<range-picker
v-if="mode=='range'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</range-picker>
<half-picker
v-if="mode=='half'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</half-picker>
<shortterm-picker
v-if="mode=='shortTerm'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
expand="60"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</shortterm-picker>
<time-picker
v-if="mode=='time'"
class="w-picker-wrapper"
:value="value"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
:second="second"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</time-picker>
<selector-picker
v-if="mode=='selector'"
class="w-picker-wrapper"
:value="value"
:item-height="itemHeight"
:options="options"
:default-type="defaultType"
:default-props="defaultProps"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</selector-picker>
<region-picker
v-if="mode=='region'"
class="w-picker-wrapper"
:value="value"
:hide-area="hideArea"
:default-type="defaultType"
:item-height="itemHeight"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</region-picker>
<linkage-picker
v-if="mode=='linkage'"
class="w-picker-wrapper"
:value="value"
:options="options"
:level="level"
:default-type="defaultType"
:default-props="defaultProps"
:item-height="itemHeight"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</linkage-picker>
</view>
</view>
</template>
<script>
import datePicker from "./date-picker.vue"
import rangePicker from "./range-picker.vue"
import halfPicker from "./half-picker.vue"
import shorttermPicker from "./shortterm-picker.vue"
import timePicker from "./time-picker.vue"
import selectorPicker from "./selector-picker.vue"
import regionPicker from "./region-picker.vue"
import linkagePicker from "./linkage-picker.vue"
export default {
name:"w-picker",
components:{
datePicker,
rangePicker,
halfPicker,
timePicker,
selectorPicker,
shorttermPicker,
regionPicker,
linkagePicker
},
props:{
mode:{
type:String,
default:"date"
},
value:{//
type:[String,Array,Number],
default:""
},
current:{//
type:Boolean,
default:false
},
themeColor:{//
type:String,
default:"#f5a200"
},
fields:{//:yearmonthdayhourminutesecond
type:String,
default:"date"
},
disabledAfter:{//
type:Boolean,
default:false
},
second:{//time-picker
type:Boolean,
default:true
},
options:{//selector,region
type:[Array,Object],
default(){
return []
}
},
defaultProps:{//selector,linkagle
type:Object,
default(){
return{
label:"label",
value:"value",
children:"children"
}
}
},
defaultType:{
type:String,
default:"label"
},
hideArea:{//mode=region
type:Boolean,
default:false
},
level:{
//,2-4;
type:[Number,String],
default:2
},
timeout:{//,
type:Boolean,
default:false
},
expand:{//mode=shortterm
type:[Number,String],
default:30
},
startYear:{
type:[String,Number],
default:1970
},
endYear:{
type:[String,Number],
default:new Date().getFullYear()
},
visible:{
type:Boolean,
default:false
}
},
created() {
this.createKey=Math.random()*1000;
},
data() {
return {
itemHeight:`height: ${uni.upx2px(88)}px;`,
result:{},
confirmFlag:true
};
},
methods:{
touchStart(){
if(this.timeout){
this.confirmFlag=false;
}
},
touchEnd(){
if(this.timeout){
setTimeout(()=>{
this.confirmFlag=true;
},500)
}
},
handlerChange(res){
let _this=this;
this.result={...res};
},
show(){
this.$emit("update:visible",true);
},
hide(){
this.$emit("update:visible",false);
},
onCancel(res){
this.$emit("update:visible",false);
this.$emit("cancel");
},
pickerConfirm(){
if(!this.confirmFlag){
return;
};
this.$emit("confirm",this.result);
this.$emit("update:visible",false);
}
}
}
</script>
<style lang="scss">
.w-picker-item {
text-align: center;
width: 100%;
height: 88upx;
line-height: 88upx;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30upx;
}
.w-picker{
z-index: 888;
.mask {
position: fixed;
z-index: 1000;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
visibility: hidden;
opacity: 0;
transition: all 0.3s ease;
}
.mask.visible{
visibility: visible;
opacity: 1;
}
.w-picker-cnt {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
transition: all 0.3s ease;
transform: translateY(100%);
z-index: 3000;
background-color: #fff;
}
.w-picker-cnt.visible {
transform: translateY(0);
}
.w-picker-header{
display: flex;
align-items: center;
padding: 0 30upx;
height: 88upx;
background-color: #fff;
position: relative;
text-align: center;
font-size: 32upx;
justify-content: space-between;
border-bottom: solid 1px #eee;
.w-picker-btn{
font-size: 30upx;
}
}
.w-picker-hd:after {
content: ' ';
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 1px;
border-bottom: 1px solid #e5e5e5;
color: #e5e5e5;
transform-origin: 0 100%;
transform: scaleY(0.5);
}
}
</style>

@ -1,106 +0,0 @@
<template>
<view class="address-box">
<view v-if="!address.area" class="address-empty">
<view class="address-tips">
<u--text :lines="1" size="14px" color="#333" bold="true" text="请选择收货地址"></u--text>
<u-icon class="icon-arrow-right" name="arrow-right"></u-icon>
</view>
</view>
<view v-else class="address-selected">
<view class="address-area">
<view class="default-tag">默认</view>
<u--text :lines="1" size="12px" color="#666" :text="address.area"></u--text>
</view>
<view class="address-detail">
<u--text :lines="2" size="14px" color="#333" :bold="true" :text="address.detail"></u--text>
<u-icon class="icon-arrow-right" name="arrow-right"></u-icon>
</view>
<view class="address-user">
<view class="user-text">{{ address.name }}</view>
<view class="user-text">{{ address.mobile }}</view>
</view>
</view>
<image class="address-line" src="/static/images/address-line.png"></image>
</view>
</template>
<script>
/**
* 地址选择
*/
export default {
name: 'yd-address-select',
props: {
address: {
type: Object,
default: () => []
}
},
data() {
return {}
},
methods: {}
}
</script>
<style lang="scss" scoped>
.address-box {
padding: 0 10rpx;
background-color: #fff;
border-radius: 0 0 30rpx 30rpx;
.address-empty {
padding: 20rpx 20rpx 0;
.address-tips {
@include flex-space-between;
padding: 10rpx 0;
.icon-arrow-right {
margin-left: 50rpx;
}
}
}
.address-selected {
padding: 20rpx 20rpx 0;
.address-area {
@include flex-left;
.default-tag {
font-size: 22rpx;
color: $u-primary;
border: 1rpx solid $u-primary;
padding: 0 6rpx;
margin-right: 10rpx;
border-radius: 5rpx;
}
}
.address-detail {
@include flex-space-between;
padding: 10rpx 0;
.icon-arrow-right {
margin-left: 50rpx;
}
}
.address-user {
@include flex-left;
.user-text {
color: #666;
font-size: 24rpx;
margin-right: 10rpx;
}
}
}
.address-line {
width: 100%;
margin: 0 auto;
height: 5rpx;
}
}
</style>

@ -1,60 +0,0 @@
<template>
<u-swiper :list="bannerList" :keyName="keyName" previousMargin="20" nextMargin="20" circular height="150"
@change="e => (current = e.current)" :autoplay="true" @click="handleSwiperClick">
<view slot="indicator" class="indicator">
<view class="indicator__dot" v-for="(item, index) in bannerList" :key="index"
:class="[index === current && 'indicator__dot--active']"></view>
</view>
</u-swiper>
</template>
<script>
/**
* 广告滚动图
*/
export default {
name: 'yd-banner',
components: {},
props: {
keyName: {
type: String,
default: 'url'
},
bannerList: {
type: Array,
default: () => []
}
},
data() {
return {
current: 0,
currentNum: 0
}
},
methods: {
handleSwiperClick(index) {
console.log('点击了图片索引值:', index)
}
}
}
</script>
<style lang="scss" scoped>
.indicator {
@include flex(row);
justify-content: center;
&__dot {
height: 15rpx;
width: 15rpx;
border-radius: 100rpx;
background-color: rgba(255, 255, 255, 0.35);
margin: 0 10rpx;
transition: background-color 0.3s;
&--active {
background-color: $custom-bg-color;
}
}
}
</style>

@ -1,116 +0,0 @@
<template>
<view>
<view class="product-item" v-for="(item, index) in productList" :key="item.productId" @click="handleProductItemClick(item.productId)">
<view class="product-check" @click.stop="handleCheckProduct(item.productId, item.checked)">
<u-icon v-if="item.checked" name="checkmark-circle-fill" color="#3c9cff" size="22"></u-icon>
<view v-else class="un-check-box"></view>
</view>
<image class="product-image" :src="item.coverUrl"></image>
<view class="item-info">
<view class="info-text">
<u--text :lines="1" size="15px" color="#333333" :text="item.productTitle"></u--text>
<u-gap height="2px"></u-gap>
<u--text class="info-tips" :lines="1" size="12px" color="#939393" :text="item.tips"></u--text>
</view>
<view class="price-number-box">
<view class="price-box">
<yd-text-price color="red" size="13" intSize="20" :price="item.sellPrice"></yd-text-price>
<yd-text-price v-if="item.strikePrice" style="margin-left: 5px" decoration="line-through" color="#999" size="12" :price="item.sellPrice"></yd-text-price>
</view>
<view class="number-box" @click.stop>
<u-number-box min="1" max="999" bgColor="#fff" integer :disableMinus="false" :disabledInput="true" :name="item.productId" :value="item.productCount" @change="handleProductCountChange"></u-number-box>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* 购物车商品列表
*/
export default {
name: 'yd-cart-product',
props: {
productList: {
type: Array,
default: () => []
}
},
data() {
return {
//status: 'nomore',
loadingText: '加载中...',
loadmoreText: '上拉加载更多',
nomoreText: '没有更多了'
}
},
methods: {
handleProductItemClick(productId) {
uni.$u.route('/pages/product/product', {
id: productId
})
},
handleItemCartClick(productId) {
this.$store.dispatch('CartProductCountChange', { productIds: [productId], productCount: 1, addition: true }).then(res => {
uni.$u.toast('已添加到购物车')
})
},
handleCheckProduct(productId, checked) {
this.$emit('productCheckedChange', productId, !checked)
},
handleProductCountChange({ name, value }) {
this.$emit('productCartProductCountChange', name, value)
}
}
}
</script>
<style lang="scss" scoped>
.product-item {
background: #ffffff;
@include flex-space-between;
border-bottom: $custom-border-style;
padding: 10rpx 0 0 5rpx;
.product-check {
padding: 20rpx;
.un-check-box {
width: 20px;
height: 20px;
border: 1px solid #939393;
border-radius: 50%;
}
}
.product-image {
width: 180rpx;
height: 180rpx;
border-radius: 10rpx;
}
.item-info {
flex: 1;
padding: 20rpx 20rpx 0;
.info-text {
height: 70rpx;
padding-bottom: 10rpx;
}
.price-number-box {
@include flex-space-between;
.price-box {
@include flex-left();
}
.number-box {
height: 100rpx;
@include flex-center;
}
}
}
}
</style>

@ -1,100 +0,0 @@
<template>
<view>
<view class="product-item" v-for="(item, index) in productList" :key="item.productId">
<image class="product-image" :src="item.coverUrl"></image>
<view class="item-info">
<view class="info-text">
<u--text :lines="1" size="15px" color="#333333" :text="item.productTitle"></u--text>
<u-gap height="10"></u-gap>
<yd-text-price class="product-price" size="13" intSize="16" :price="item.sellPrice"></yd-text-price>
</view>
<view class="price-number-box">
<view class="number-box">
<view class="product-number"> {{ item.productCount }} </view> 小计
</view>
<view class="number-box" @click.stop>
<yd-text-price size="13" intSize="18" :price="item.totalPrice"></yd-text-price>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* 订单商品列表
*/
export default {
name: 'yd-order-product',
props: {
productList: {
type: Array,
default: () => []
}
},
data() {
return {}
},
methods: {
handleProductItemClick(productId) {
uni.$u.route('/pages/product/product', {
id: productId
})
}
}
}
</script>
<style lang="scss" scoped>
.product-item {
background: #ffffff;
@include flex-space-between;
border-bottom: $custom-border-style;
padding: 10rpx 0 0 5rpx;
.product-check {
padding: 20rpx;
.un-check-box {
width: 20px;
height: 20px;
border: 1px solid #939393;
border-radius: 50%;
}
}
.product-image {
width: 180rpx;
height: 180rpx;
border-radius: 10rpx;
}
.item-info {
flex: 1;
padding: 0 20rpx;
.info-text {
padding-bottom: 10rpx;
.product-price {
margin-top: 15rpx;
}
}
.price-number-box {
@include flex-space-between;
.number-box {
font-size: 24rpx;
.product-number {
width: 200rpx;
}
}
.number-box {
height: 60rpx;
@include flex-center;
}
}
}
}
</style>

@ -1,173 +0,0 @@
<template>
<view>
<view v-if="showType === 'normal'">
<u-gap height="180" bgColor="#398ade"></u-gap>
<view class="prod-block">
<view class="bloc-header">
<text class="bloc-title">{{title}}</text>
<text class="see-more">查看更多</text>
</view>
<view class="prod-grid">
<view class="prod-item" v-for="(item, index) in productList" :key="item.id" @click="handleProdItemClick(item.id)">
<image class="prod-image" :src="item.image"></image>
<view class="item-info">
<view class="info-text">
<u--text :lines="2" size="14px" color="#333333" :text="item.title"></u--text>
</view>
<view class="price-and-cart">
<yd-text-price color="red" size="12" intSize="18" :price="item.price"></yd-text-price>
<u-icon name="shopping-cart" color="#2979ff" size="28"></u-icon>
</view>
</view>
</view>
</view>
</view>
</view>
<view v-if="showType === 'half'">
<view class="prod-block half">
<view class="bloc-header">
<text class="bloc-title">{{title}}</text>
<text class="more">更多 &gt;</text>
</view>
<view class="prod-grid half">
<view class="prod-item" v-for="(item, index) in productList" :key="item.id" @click="handleProdItemClick(item.id)">
<image class="prod-image" :src="item.image"></image>
<view class="item-info">
<view class="info-text">
<u--text :lines="1" size="14px" color="#333333" :text="item.title"></u--text>
<u--text :lines="1" size="12px" color="#939393" :text="item.desc"></u--text>
</view>
<view class="price-and-cart">
<yd-text-price color="red" size="12" intSize="18" :price="item.price"></yd-text-price>
<u-icon name="shopping-cart" color="#2979ff" size="28"></u-icon>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* 商品列表
*/
export default {
name: 'yd-product-box',
components: {},
props: {
showType: {
type: String,
default: 'normal'
},
title: {
type: String,
default: '商品推荐'
},
productList: {
type: Array,
default: () => []
}
},
computed: {},
methods: {
handleProdItemClick(productId) {
uni.$u.route('/pages/product/product', {
id: productId
})
}
}
}
</script>
<style lang="scss" scoped>
.prod-block {
margin-top: -160px;
.bloc-header {
@include flex-space-between;
padding: 10rpx 20rpx;
.bloc-title {
color: $custom-bg-color;
font-size: 34rpx;
}
.see-more {
color: $custom-bg-color;
background: $u-primary;
padding: 0 30rpx;
height: 50rpx;
line-height: 50rpx;
border-radius: 50rpx;
font-size: 24rpx;
}
}
&.half {
margin-top: 0;
.bloc-header {
margin-top: 50rpx;
margin-bottom: 20rpx;
.bloc-title {
color: #333333;
}
.more {
font-size: 24rpx;
}
}
}
.prod-grid {
width: 730rpx;
margin: 0 auto;
@include flex;
flex-wrap: wrap;
justify-content: left;
&.half {
.prod-item {
width: 345rpx;
margin: 10rpx;
.prod-image {
width: 345rpx;
height: 345rpx;
}
}
}
.prod-item {
width: 223rpx;
margin: 10rpx;
background: #ffffff;
border-radius: 10rpx;
box-shadow: 0rpx 6rpx 8rpx rgba(58, 134, 185, 0.2);
.prod-image {
width: 223rpx;
height: 223rpx;
border-radius: 10rpx 10rpx 0 0;
}
.item-info {
padding: 15rpx;
.info-text {
height: 70rpx;
padding-bottom: 10rpx;
}
.price-and-cart {
@include flex-space-between;
}
}
}
}
}
</style>

@ -1,114 +0,0 @@
<template>
<view>
<view class="prod-block list">
<view class="bloc-header">
<text class="bloc-title">更多宝贝</text>
<text></text>
</view>
<view class="prod-list" v-for="(item, index) in productList" :key="item.id">
<view class="prod-item" @click="handleProdItemClick(item.id)">
<image class="prod-image" :src="item.image"></image>
<view class="item-info">
<view class="info-text">
<u--text :lines="1" size="14px" color="#333333" :text="item.title"></u--text>
<u-gap height="2px"></u-gap>
<u--text class="info-desc" :lines="2" size="12px" color="#939393" :text="item.desc"></u--text>
</view>
<view class="price-and-cart">
<yd-text-price color="red" size="12" intSize="18" :price="item.price"></yd-text-price>
<u-icon name="shopping-cart" color="#2979ff" size="28"></u-icon>
</view>
</view>
</view>
</view>
</view>
<!--加载更多-->
<u-loadmore fontSize="28rpx" :line="true" :status="moreStatus" :loading-text="loadingText" :loadmore-text="loadmoreText" :nomore-text="nomoreText" />
</view>
</template>
<script>
/**
* 商品列表(加载更多)
*/
export default {
name: 'yd-product-more',
components: {},
props: {
title: {
type: String,
default: '商品推荐'
},
productList: {
type: Array,
default: () => []
},
moreStatus: {
type: String,
default: 'nomore'
}
},
data() {
return {
//status: 'nomore',
loadingText: '加载中...',
loadmoreText: '上拉加载更多',
nomoreText: '已经到底了'
}
},
methods: {
handleProdItemClick(productId) {
uni.$u.route('/pages/product/product', {
id: productId
})
}
}
}
</script>
<style lang="scss" scoped>
.prod-block {
margin-top: 0;
.bloc-header {
margin-top: 50rpx;
margin-bottom: 20rpx;
.bloc-title {
margin-left: 20rpx;
color: #333333;
}
}
.prod-list {
.prod-item {
background: #ffffff;
@include flex-space-between;
border-bottom: $custom-border-style;
padding: 20rpx;
.prod-image {
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
}
.item-info {
flex: 1;
padding: 20rpx 20rpx 0;
.info-text {
height: 100rpx;
padding-bottom: 10rpx;
}
.price-and-cart {
@include flex-space-between;
}
}
}
}
}
</style>

@ -1,73 +0,0 @@
<template>
<view>
<text v-for="(item, index) in textArray" :key="index" :style="{ fontSize: (index === 1 ? integerSize : size) + 'px', color, textDecoration }">
{{ item }}
</text>
</view>
</template>
<script>
/**
* 此组件简单的显示驼峰式的价格
*/
export default {
name: 'yd-text-price',
components: {},
props: {
//
symbol: {
type: String,
default: '¥'
},
price: {
type: [String, Number],
default: ''
},
color: {
type: String,
default: '#333333'
},
//
size: {
type: [String, Number],
default: 15
},
//
intSize: {
type: [String, Number],
default: ''
},
//线线
decoration: {
type: String,
default: 'none'
}
},
data() {
return {
textDecoration: this.decoration
}
},
computed: {
textArray() {
let array = []
if (this.price === '' || this.price === undefined) {
return []
}
array.push(this.symbol)
if (!/^\d+(\.\d+)?$/.test(this.price)) {
console.error('组件<yd-text-price :text="???" 此处参数应为金额数字')
} else {
let arr = parseFloat(this.price).toFixed(2).split('.')
array.push(arr[0])
array.push('.' + arr[1])
}
return array
},
integerSize() {
return this.intSize ? this.intSize : this.size
}
}
}
</script>
<style scoped></style>

@ -1,5 +1,5 @@
<template>
<view class="gdbanner">
<view class="container">
<u-swiper :list="list" keyName="imgurl" :autoplay="false" circular @change="changeBanner" @click="clickBanner" :height="150">
<!-- 自定义指示器 -->
<view slot="indicator" class="indicator">
@ -64,7 +64,7 @@
</script>
<style lang="scss" scoped>
.gdbanner {
.container {
padding: 0 10px;
}

@ -1,5 +1,5 @@
<template>
<view class="seencard">
<view class="container">
<uni-card spacing="0" margin="5px">
<image slot='cover' class="unimg" mode="aspectFill" :src="info.imgurl" @click="seenInfoClick"></image>
<text class="content">{{info.title}}</text>
@ -66,7 +66,7 @@
<style lang="scss" scoped>
// @import 'styles/base.scss';
.seencard {
.container {
width: 100%;
}

@ -1,5 +1,5 @@
<template>
<view class="taskcard">
<view class="container">
<uni-card padding="0" spacing="0" margin="10">
<view slot="title">
<view class="headerbox">
@ -12,7 +12,7 @@
</view>
<view class="content">
<view class="subject">计划消耗时间: {{info.hours}} 工时</view>
<view class="info">
<view class="info" @click="taskInfoClick(info.id)">
<view class="info_item">产品名称: {{info.productName}}</view>
<view class="info_item">
<view>任务编号: {{info.id}}</view>
@ -85,14 +85,22 @@
}
},
methods: {
//
removeClick() {
this.$refs.msg.show({
type: 'default',
message: "删除功能暂未开发~",
});
},
// /
actionsClick(type) {
console.log("操作类型:" + type);
},
//
taskInfoClick(taskId) {
uni.navigateTo({
url: `/pages/renwu/info?id=${taskId}`
});
}
},

@ -65,9 +65,6 @@
pn() {
console.log(Object.assign(this.default, this.params));
return Object.assign(this.default, this.params);
},
navHeight() {
}
}
}

@ -1,72 +1,76 @@
{
"name" : "合立机械",
"appid" : "__UNI__CA099E3",
"description" : "合力机械移动端",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
"name": "合立机械",
"appid": "__UNI__CA099E3",
"description": "合力机械移动端",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
/* 5+App */
"app-plus": {
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wxd4d71484fd08f94b",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
/* */
"modules": {},
/* */
"distribute": {
/* android */
"android": {
"permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios": {},
/* SDK */
"sdkConfigs": {}
}
},
/* */
"quickapp": {},
/* */
"mp-weixin": {
"appid": "wxd4d71484fd08f94b",
"setting": {
"urlCheck": false,
"minified": true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2"
}
"usingComponents": true,
"optimization": {
"subPackages": true
}
},
"mp-alipay": {
"usingComponents": true
},
"mp-baidu": {
"usingComponents": true
},
"mp-toutiao": {
"usingComponents": true
},
"uniStatistics": {
"enable": false
},
"vueVersion": "2"
}

@ -1,17 +1,21 @@
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/renwu/info",
"style": {
"navigationBarTitleText": "任务详情"
}
},
{
"path": "pages/renwu/index",
"style": {
"navigationBarTitleText": "任务中心",
"navigationStyle": "custom" //
"navigationBarTitleText": "任务中心"
}
},
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"navigationStyle": "custom" //
"navigationBarTitleText": "首页"
}
},
{
@ -20,7 +24,6 @@
"navigationBarTitleText": "搜索"
}
},
{
"path": "pages/xiaoxi/index",
"style": {
@ -96,10 +99,11 @@
]
},
"globalStyle": {
"navigationStyle": "custom", //
"navigationBarTextStyle": "black",
"navigationBarTitleText": "合立机械",
"navigationBarBackgroundColor": "#ffffff",
"backgroundColor": "#ffffff",
"onReachBottomDistance": 50
}
}
}

@ -1,148 +0,0 @@
<template>
<view class="container">
<view class="address-box">
<u--form labelPosition="left" :model="formData" :rules="rules" ref="form">
<u-form-item label="收件人名称" prop="name" labelWidth="90" borderBottom ref="item-name">
<u-input type="text" maxlength="11" v-model="formData.name" clearable placeholder="请填写收件人名称" border="none"></u-input>
</u-form-item>
<u-form-item label="手机号" prop="mobile" labelWidth="90" borderBottom ref="item-mobile">
<u-input type="number" maxlength="11" v-model="formData.mobile" clearable placeholder="请填写手机号" border="none"></u-input>
</u-form-item>
<u-form-item label="省市地区" prop="areaText" labelWidth="90" borderBottom @click=" regionVisible = true; hideKeyboard()" ref="item-areaText">
<u--input v-model="formData.areaText" disabled disabledColor="#ffffff" placeholder="请选择省市地区" border="none"></u--input>
<u-icon slot="right" name="arrow-right"></u-icon>
<w-picker :visible.sync="regionVisible" mode="region" :value="defaultRegion" default-type="value" :hide-area="false" @confirm="onConfirm($event, 'region')" @cancel="onCancel" ref="region"></w-picker>
</u-form-item>
<u-form-item label="详细地址" prop="detail" labelWidth="90" borderBottom ref="item-detail">
<u--textarea placeholder="请输入街道门牌号不低于6个字" v-model="formData.detail" count></u--textarea>
</u-form-item>
<u-form-item label="默认地址" prop="type" labelWidth="90" borderBottom ref="item-type">
<u-radio-group v-model="formData.type">
<u-radio :customStyle="{ marginRight: '16px' }" v-for="(item, index) in typeList" :key="index" :label="item.name" :name="item.value"></u-radio>
</u-radio-group>
</u-form-item>
<view class="btn-group">
<u-button type="primary" text="保存地址" customStyle="margin-top: 50px" @click="handleSubmit"></u-button>
</view>
</u--form>
</view>
</view>
</template>
<script>
import { createAddress } from '../../api/address'
export default {
data() {
return {
regionVisible: false,
defaultRegion: ['110000', '110100', '110101'],
typeList: [
{
name: '是',
value: 1
},
{
name: '否',
value: 2
}
],
formData: {
name: '',
mobile: '',
areaText: '',
areaCode: '',
detail: '',
detailAddress: '',
type: 1
},
rules: {
name: [
{
type: 'string',
min: 2,
max: 12,
required: true,
message: '请填写收件人名称',
trigger: ['blur', 'change']
},
{
message: '收件人名称不能为空',
// blurchange
trigger: ['change', 'blur']
}
],
mobile: [
{
type: 'integer',
required: true,
message: '请填写手机号',
trigger: ['blur', 'change']
},
{
//
validator: (rule, value, callback) => {
// truefalse
// uni.$u.test.mobile()truefalse
return uni.$u.test.mobile(value)
},
message: '手机号码不正确',
// blurchange
trigger: ['change', 'blur']
}
],
areaText: {
type: 'string',
required: true,
message: '请选择省市地区',
trigger: ['blur', 'change']
},
detailAddress: {
type: 'string',
min: 6,
max: 30,
required: true,
message: '请填写详细地址',
trigger: ['blur', 'change']
}
}
}
},
onLoad() {},
methods: {
onConfirm(res) {
this.formData.areaText = res.result
this.formData.areaCode = res.value[2]
},
onCancel() {},
hideKeyboard() {
uni.hideKeyboard()
},
handleSubmit() {
this.$refs.form.validate().then(res => {
this.formData.detailAddress = this.formData.areaText + this.formData.detail
console.log(this.formData)
createAddress(this.formData).then(res => {
uni.$u.toast('地址已保存')
setTimeout(() => {
uni.navigateBack()
}, 300)
})
})
}
}
}
</script>
<style lang="scss" scoped>
.address-box {
width: 690rpx;
margin: 0 auto;
padding-top: 30rpx;
}
</style>

@ -1,116 +0,0 @@
<template>
<view class="container">
<view class="address-list" v-for="(item, index) in addressList" :key="item.id">
<view class="address-item" @click="handleClick" @longpress="handleLongPress(item)">
<view class="left">
<u-avatar :text="item.name ? item.name.slice(0, 1) : 'U'" fontSize="18" randomBgColor></u-avatar>
</view>
<view class="middle">
<view class="info">
<view class="name">{{ item.name }}</view>
<view class="mobile">{{ item.mobile }}</view>
<u-tag class="type" v-if="item.type === 1" text="默认" plain size="mini" type="success"></u-tag>
</view>
<view class="detail">
<u--text :lines="2" size="14" color="#939393" :text="item.detailAddress"></u--text>
</view>
</view>
<navigator class="right" :url="`/pages/address/update?addressId=${item.id}`" open-type="navigate" hover-class="none">
<u-icon name="edit-pen" size="28"></u-icon>
</navigator>
</view>
</view>
<navigator class="fixed-btn-box" url="/pages/address/create" open-type="navigate" hover-class="none">
<u-button type="primary" size="large" text="新增地址"></u-button>
</navigator>
<u-safe-bottom customStyle="background: #ffffff"></u-safe-bottom>
</view>
</template>
<script>
import { getAddressList, deleteAddress } from '../../api/address'
export default {
data() {
return {
addressList: []
}
},
onLoad() {},
onShow() {
this.loadAddressListData()
},
methods: {
loadAddressListData() {
getAddressList().then(res => {
this.addressList = res.data
})
},
handleLongPress(item) {
uni.showModal({
title: '提示',
content: `删除收件人[${item.name}${item.mobile}]\n地址${item.detailAddress.slice(0, 5)}......${item.detailAddress.slice(-6)}`,
success: res => {
if (res.confirm) {
deleteAddress({ id: item.id }).then(res => {
uni.$u.toast('地址已删除')
this.loadAddressListData();
})
} else if (res.cancel) {
//console.log('')
}
}
})
},
handleClick(){
// TODO
}
}
}
</script>
<style lang="scss" scoped>
.address-list {
.address-item {
padding: 10rpx 0;
border-bottom: $custom-border-style;
@include flex-space-between;
.left {
margin: 20rpx;
}
.middle {
flex: 1;
margin: 20rpx;
@include flex(column);
.info {
@include flex-left;
.name {
font-size: 30rpx;
font-weight: 700;
}
.mobile {
font-size: 28rpx;
margin-left: 15rpx;
}
.type {
margin-left: 15rpx;
}
}
.detail {
margin-top: 10rpx;
}
}
.right {
margin: 20rpx;
}
}
}
.fixed-btn-box {
position: fixed;
bottom: 0;
left: 0;
width: 750rpx;
}
</style>

@ -1,178 +0,0 @@
<template>
<view class="container">
<view class="address-box">
<u--form labelPosition="left" :model="formData" :rules="rules" ref="form">
<u-form-item label="收件人名称" prop="name" labelWidth="90" borderBottom ref="item-name">
<u-input type="text" maxlength="11" v-model="formData.name" clearable placeholder="请填写收件人名称" border="none"></u-input>
</u-form-item>
<u-form-item label="手机号" prop="mobile" labelWidth="90" borderBottom ref="item-mobile">
<u-input type="number" maxlength="11" v-model="formData.mobile" clearable placeholder="请填写手机号" border="none"></u-input>
</u-form-item>
<u-form-item label="省市地区" prop="areaText" labelWidth="90" borderBottom @click=" regionVisible = true; hideKeyboard()" ref="item-areaText">
<u--input v-model="formData.areaText" disabled disabledColor="#ffffff" placeholder="请选择省市地区" border="none"></u--input>
<u-icon slot="right" name="arrow-right"></u-icon>
<w-picker :visible.sync="regionVisible" mode="region" :value="defaultRegion" default-type="value" :hide-area="false" @confirm="onConfirm($event, 'region')" @cancel="onCancel" ref="region"></w-picker>
</u-form-item>
<u-form-item label="详细地址" prop="detail" labelWidth="90" borderBottom ref="item-detail">
<u--textarea placeholder="请输入街道门牌号不低于6个字" v-model="formData.detail" count></u--textarea>
</u-form-item>
<u-form-item label="默认地址" prop="type" labelWidth="90" borderBottom ref="item-type">
<u-radio-group v-model="formData.type">
<u-radio :customStyle="{ marginRight: '16px' }" v-for="(item, index) in typeList" :key="index" :label="item.name" :name="item.value"></u-radio>
</u-radio-group>
</u-form-item>
<view class="btn-group">
<u-button type="primary" text="更新地址" customStyle="margin-top: 50px" @click="handleSubmit"></u-button>
</view>
</u--form>
</view>
</view>
</template>
<script>
import { getAddressById, updateAddress } from '../../api/address'
export default {
data() {
return {
id: '',
regionVisible: false,
defaultRegion: ['110000', '110100', '110101'],
typeList: [
{
name: '是',
value: 1
},
{
name: '否',
value: 2
}
],
formData: {
id: '',
name: '',
mobile: '',
areaText: '',
areaCode: '',
detail: '',
detailAddress: '',
type: 1
},
rules: {
name: [
{
type: 'string',
min: 2,
max: 12,
required: true,
message: '请填写收件人名称',
trigger: ['blur', 'change']
},
{
message: '收件人名称不能为空',
// blurchange
trigger: ['change', 'blur']
}
],
mobile: [
{
type: 'integer',
required: true,
message: '请填写手机号',
trigger: ['blur', 'change']
},
{
//
validator: (rule, value, callback) => {
// truefalse
// uni.$u.test.mobile()truefalse
return uni.$u.test.mobile(value)
},
message: '手机号码不正确',
// blurchange
trigger: ['change', 'blur']
}
],
areaText: {
type: 'string',
required: true,
message: '请选择省市地区',
trigger: ['blur', 'change']
},
detailAddress: {
type: 'string',
min: 6,
max: 30,
required: true,
message: '请填写详细地址',
trigger: ['blur', 'change']
}
}
}
},
onLoad(e) {
if (!e.addressId) {
uni.$u.toast('请求参数错误')
} else {
this.id = e.addressId
this.loadAddressData()
}
},
methods: {
loadAddressData() {
getAddressById({ id: this.id }).then(res => {
this.formData = res.data
this.initRegionData()
})
},
initRegionData(){
//
if (this.formData.areaCode) {
const areaCode = this.formData.areaCode + ''
//code--code
this.defaultRegion.splice(0, 3, areaCode.substring(0,2).padEnd(6, '0'), areaCode.substring(0,4).padEnd(6, '0'), areaCode)
this.$nextTick(res => {
let areaText = this.$refs.region._data.result.result
this.formData.areaText = areaText
//--
this.formData.detail = this.formData.detailAddress.replace(areaText, '')
this.$forceUpdate();
})
}
},
onConfirm(res) {
this.formData.areaText = res.result
this.formData.areaCode = res.value[2]
},
onCancel() {},
hideKeyboard() {
uni.hideKeyboard()
},
handleSubmit() {
this.$refs.form.validate().then(res => {
this.formData.detailAddress = this.formData.areaText + this.formData.detail
console.log(this.formData)
updateAddress(this.formData).then(res => {
uni.$u.toast('地址已更新')
setTimeout(() => {
uni.navigateBack()
}, 300)
})
})
}
}
}
</script>
<style lang="scss" scoped>
.address-box {
width: 690rpx;
margin: 0 auto;
padding-top: 30rpx;
}
</style>

@ -1,219 +0,0 @@
<template>
<view class="container">
<!-- 收货地址选择 -->
<yd-address-select :address="address"></yd-address-select>
<!-- 订单商品信息 -->
<view class="checkout-goods">
<yd-order-goods :goods-list="checkoutList"></yd-order-goods>
</view>
<view class="freight-coupon-box">
<vidw class="box-row">
<view class="title">运费</view>
<yd-text-price class="freight-fee" size="15" :price="freightAmount"></yd-text-price>
</vidw>
<vidw class="box-row">
<view class="coupon-wrap">
<view class="coupon-title-tag">
<view class="title">优惠券</view>
<scroll-view class="coupon-tag-list" scroll-x="true">
<view v-for="item in couponList" :key="item.couponId" class="coupon-tag-item">{{ item.couponTag }}</view>
</scroll-view>
</view>
<yd-text-price class="coupon-fee" color="red" size="15" symbol="-¥" :price="couponAmount"></yd-text-price>
</view>
<u-icon name="arrow-right"></u-icon>
</vidw>
</view>
<!-- 订单备注信息 -->
<view class="user-remark-box">
<view class="title">订单备注</view>
<u--input maxlength="50" border="none" fontSize="14" v-model="remark" placeholder="如您需要请备注"></u--input>
</view>
<view class="cart-btn-container">
<view class="order-total-wrap">
<view class="order-total-info">
<view class="info-text">合计</view>
<view>
<yd-text-price color="red" size="15" intSize="20" :price="totalAmount"></yd-text-price>
</view>
</view>
<view class="cart-btn-group">
<u-button style="margin-left: 10px" class="main-btn" type="primary" shape="circle" size="small" text="提交订单" @click="handleSubmitOrder"></u-button>
</view>
</view>
<u-safe-bottom customStyle="background: #ffffff"></u-safe-bottom>
</view>
</view>
</template>
<script>
import { checkoutCartProduct } from '../../api/order'
export default {
components: {},
data() {
return {
checkedProduct: [],
address: {
name: '客户',
mobile: '139****6563',
area: 'XXX市XXX区',
detail: 'XXX街道XXX小区XXX号楼XX-XXX'
},
checkoutList: [],
couponList: [
{
couponId: 3,
couponTag: '6元运费券'
}
],
totalAmount: 0,
freightAmount: 6,
couponAmount: 6,
remark: ''
}
},
onLoad(e) {
const checkedProduct = e.checkedProduct
if (checkedProduct) {
this.checkedProduct = JSON.parse(checkedProduct)
this.loadCheckoutProductData()
} else {
uni.$u.toast('请求参数错误')
}
},
methods: {
loadCheckoutProductData() {
checkoutCartProduct(this.checkedProduct)
.then(res => {
this.checkoutList = res.data.checkoutList || []
this.totalAmount = res.data.totalAmount || 0
})
.catch(err => {
console.log(err)
})
}
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: $custom-bg-color;
height: 100vh;
overflow-x: scroll;
}
.checkout-goods {
background-color: #fff;
margin-top: 20rpx;
padding: 20rpx;
border-radius: 20rpx;
}
.freight-coupon-box {
background-color: #fff;
margin-top: 20rpx;
padding: 20rpx 30rpx;
border-radius: 20rpx;
.box-row {
@include flex-space-between;
padding: 10rpx 0;
.coupon-wrap {
@include flex-space-between;
width: 670rpx;
.coupon-title-tag {
@include flex-left;
.coupon-tag-list {
@include flex-left;
overflow-x: scroll;
width: 360rpx;
.coupon-tag-item {
display: inline-block;
font-size: 22rpx;
color: red;
border: 1rpx solid red;
padding: 1px 6rpx;
margin-right: 10rpx;
border-radius: 5rpx;
}
}
}
}
.title {
font-weight: 700;
font-size: 30rpx;
color: #333;
margin-right: 30rpx;
}
.freight-fee {
margin-right: 50rpx;
}
.coupon-fee {
margin-right: 20rpx;
}
}
}
.user-remark-box {
@include flex-space-between;
background-color: #fff;
margin-top: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
.title {
font-weight: 700;
font-size: 30rpx;
color: #333;
margin-right: 30rpx;
}
}
.cart-btn-container {
position: fixed;
bottom: 0;
left: 0;
.order-total-wrap {
background: $custom-bg-color;
border-top: $custom-border-style;
width: 750rpx;
@include flex-space-between();
height: 100rpx;
.order-total-info {
@include flex-left;
.info-text {
margin-left: 20rpx;
font-size: 26rpx;
font-weight: bold;
color: #666666;
}
}
.cart-btn-group {
@include flex-right();
width: 360rpx;
padding-right: 10px;
.btn-gap {
width: 20rpx;
}
}
}
}
</style>

@ -1,63 +0,0 @@
<template>
<view class="container">
<view class="confirm-con">
<!-- TODO 收货地址 -->
<navigator url="/pages/address/list?type=1">
<yd-address-select :address="address"></yd-address-select>
</navigator>
<!-- TODO 商品信息 -->
<view class="goods contain">
<yd-order-product :productList="productList"></yd-order-product>
<view class="item row-between">
<view>买家留言</view>
<u-input v-model="remark" :clearable="false"
placeholder="请添加备注150 字以内)"></u-input>
</view>
</view>
</view>
<!-- TODO 优惠劵 -->
<!-- TODO 价格信息 -->
<!-- TODO 底部提交订单 -->
</view>
</template>
<script>
export default {
data() {
return {
address: { //
name: 'test',
mobile: '15601691300',
area: '奥特曼奥特曼'
},
productList: [{ //
coverUrl: '',
productTitle: '奥特曼',
sellPrice: 1024,
productCount: 2048,
totalPrice: 1024
}],
remark: '', //
x
}
}
}
</script>
<style>
.confirm-con {
overflow: hidden;
padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
}
.contain {
border-radius: 14rpx;
margin: 20rpx 20rpx 0;
background-color: #fff;
overflow: hidden;
}
</style>

@ -1,252 +0,0 @@
<template>
<view class="container">
<viwe class="detail-header">
<view class="order-status">{{ order.status | getStatusName }}</view>
</viwe>
<view class="order-product">
<view class="order-item" v-for="item in order.items" :key="item.id">
<image class="product-image" :src="item.picUrl"></image>
<view class="item-info">
<view class="info-text">
<u--text :lines="1" size="15px" color="#333333" :text="item.spuName"></u--text>
<u-gap height="10"></u-gap>
<yd-text-price class="product-price" size="13" intSize="14" :price="item.originalUnitPrice"></yd-text-price>
</view>
<view class="price-number-box">
<view class="number-box">
<view class="product-number"> {{ item.count }} </view>
小计
</view>
<view class="number-box" @click.stop>
<yd-text-price size="13" intSize="16" :price="item.originalPrice"></yd-text-price>
</view>
</view>
</view>
</view>
</view>
<view v-if="order.no" >
<view v-if="order.no" class="base-info">
<view class="info-item">
<view class="item-name">订单编号</view>
<view class="item-value">{{ order.no }}</view>
</view>
<view class="info-item">
<view class="item-name">下单时间</view>
<view class="item-value">{{ order.createTime }}</view>
</view>
<view v-if="order.payOrderId" class="info-item">
<view class="item-name">支付方式</view>
<view class="item-value">{{ order.payOrderId }}</view>
</view>
<view v-if="order.payTime" class="info-item">
<view class="item-name">支付时间</view>
<view class="item-value">{{ order.payTime }}</view>
</view>
</view>
<view class="delivery-info">
<view class="info-item">
<view class="item-name">收货地址</view>
<view class="item-value">{{ order.receiverAreaName + order.receiverDetailAddress + order.receiverDetailAddress + order.receiverDetailAddress }}</view>
</view>
<view v-if="order.receiverName" class="info-item">
<view class="item-name">收货人</view>
<view class="item-value">{{ order.receiverName }}</view>
</view>
<view v-if="order.receiverMobile" class="info-item">
<view class="item-name">联系电话</view>
<view class="item-value">{{ order.receiverMobile }}</view>
</view>
</view>
<view class="delivery-info">
<view class="info-item">
<view class="item-name">商品总额</view>
<yd-text-price class="product-price" size="13" intSize="16" :price="order.originalPrice"></yd-text-price>
</view>
<view class="info-item">
<view class="item-name">运费</view>
<yd-text-price class="product-price" size="13" intSize="16" :price="order.deliveryPrice"></yd-text-price>
</view>
<view class="info-item">
<view class="item-name">优惠</view>
<yd-text-price class="product-price" size="13" intSize="16" symbol="-¥" :price="order.discountPrice"></yd-text-price>
</view>
<view class="info-item">
<view class="item-name">订单金额</view>
<yd-text-price class="product-price" size="15" intSize="20" :price="order.orderPrice"></yd-text-price>
</view>
</view>
</view>
</view>
</template>
<script>
import { getOrderDetail } from '../../api/order'
import orderStatus from '@/common/orderStatus'
export default {
name: 'orderDetail',
filters: {
getStatusName(status) {
return orderStatus[status + ''].name
}
},
data() {
return {
orderId: undefined,
order: {}
}
},
onLoad(e) {
this.orderId = e.orderId
if (!this.orderId) {
uni.$u.toast('请求参数错误')
} else {
this.loadOrderDetailData()
}
},
methods: {
loadOrderDetailData() {
getOrderDetail({ id: this.orderId })
.then(res => {
this.order = res.data || {}
})
.catch(err => {
console.log(err)
})
}
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #f3f3f3;
height: 100vh;
}
.detail-header {
@include flex-center();
background-color: $custom-bg-color;
padding: 10rpx 0;
border-radius: 0 0 20rpx 20rpx;
}
.order-product {
background-color: $custom-bg-color;
border-radius: 20rpx;
margin: 20rpx;
padding: 10rpx 20rpx;
.order-item {
background: #ffffff;
@include flex-space-between;
padding: 10rpx 0 10rpx 5rpx;
.product-check {
padding: 20rpx;
.un-check-box {
width: 20px;
height: 20px;
border: 1px solid #939393;
border-radius: 50%;
}
}
.product-image {
width: 180rpx;
height: 180rpx;
border-radius: 10rpx;
}
.item-info {
flex: 1;
padding: 0 20rpx;
.info-text {
padding-bottom: 10rpx;
.product-price {
margin-top: 15rpx;
}
}
.price-number-box {
@include flex-space-between;
.number-box {
font-size: 24rpx;
.product-number {
width: 200rpx;
}
}
.number-box {
height: 60rpx;
@include flex-center;
}
}
}
}
}
.base-info {
background-color: $custom-bg-color;
border-radius: 20rpx;
margin: 20rpx;
padding: 20rpx;
.info-item {
@include flex-left();
padding: 10rpx 0;
.item-name {
color: #999;
font-size: 26rpx;
}
.item-value {
color: #333;
font-size: 26rpx;
}
}
}
.delivery-info {
background-color: $custom-bg-color;
border-radius: 20rpx;
margin: 20rpx;
padding: 20rpx;
.info-item {
@include flex-left();
padding: 10rpx 0;
.item-name {
color: #999;
font-size: 26rpx;
width: 260rpx;
}
.item-value {
color: #333;
font-size: 26rpx;
}
}
}
.delivery-info {
background-color: $custom-bg-color;
border-radius: 20rpx;
margin: 20rpx;
padding: 20rpx;
.info-item {
@include flex-space-between();
padding: 10rpx 0;
.item-name {
color: #999;
font-size: 26rpx;
}
}
}
</style>

@ -1,195 +0,0 @@
<template>
<view class="container">
<u-sticky style="top: 0" offset-top="0">
<u-tabs :list="tabArray" :current="tabIndex" itemStyle="padding-left: 18px; padding-right: 18px; height: 36px;" @change="handleStatusChange"></u-tabs>
</u-sticky>
<view class="order-list">
<view v-for="order in orderList" :key="order.no" class="order-item">
<view class="order-header">
<view class="order-no">订单编号{{ order.no }}</view>
<view class="order-status">{{ order.status | getStatusName }}</view>
</view>
<view v-if="order.items.length === 1" class="order-single-item" @click="handleOrderClick(order.id)">
<view class="item-wrap" v-for="item in order.items" :key="item.id">
<view class="item-info">
<image class="item-cover" :src="item.picUrl"></image>
<u--text :lines="2" size="15px" color="#333333" :text="item.spuName"></u--text>
</view>
<view class="item-count">{{ item.count }}</view>
</view>
</view>
<view v-else class="order-multi-item" @click="handleOrderClick(order.id)">
<u-scroll-list :indicator="false">
<view class="item-wrap" v-for="item in order.items" :key="item.id">
<image class="item-image" :src="item.picUrl"></image>
</view>
</u-scroll-list>
<view class="product-count">{{ order.productCount }}</view>
</view>
<view class="order-btn-group">
<view class="order-btn">再次购买</view>
<view class="order-btn">其他操作</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { getOrderPage } from '../../api/order'
import orderStatus from '@/common/orderStatus'
export default {
name: 'orderList',
filters: {
getStatusName(status) {
return orderStatus[status + ''].name
}
},
data() {
return {
pageNo: 1,
tabIndex: 0,
orderList: []
}
},
computed: {
tabArray() {
let tabArray = [{ name: '全部', status: 'all' }]
for (let status in orderStatus) {
if (status !== '40') {
tabArray.push({ name: orderStatus[status].name, status: status })
}
}
return tabArray
}
},
onLoad(e) {
const status = e.status
if (status !== undefined) {
this.tabArray.forEach((item, index) => {
if (item.status === status) {
this.tabIndex = index
}
})
}
this.loadOrderPageData()
},
methods: {
handleStatusChange({ index }) {
this.tabIndex = index
this.loadOrderPageData()
},
loadOrderPageData() {
let params = { pageNo: this.pageNo }
const status = this.tabArray[this.tabIndex].status
if (status !== 'all') {
params.orderStatus = status
}
getOrderPage(params)
.then(res => {
this.orderList = res.data.list || []
})
.catch(err => {
console.log(err)
})
},
handleOrderClick(orderId) {
uni.$u.route('/pages/order/detail', {
orderId: orderId
})
}
}
}
</script>
<style lang="scss" scoped>
.order-list {
background-color: #f3f3f3;
.order-item {
padding: 20rpx;
background-color: #ffffff;
border-bottom: $custom-border-style;
.order-header {
@include flex-space-between;
height: 80rpx;
.order-no {
font-size: 28rpx;
color: #333;
}
.order-status {
font-size: 24rpx;
color: #999;
}
}
.order-single-item {
.item-wrap {
@include flex-space-between();
.item-info {
@include flex-left();
.item-cover {
width: 100rpx;
height: 100rpx;
border-radius: 10rpx;
margin-right: 15rpx;
}
}
.item-count {
color: #999;
font-size: 24rpx;
width: 120rpx;
text-align: right;
}
}
}
.order-multi-item {
@include flex-space-between();
.item-wrap {
margin-right: 20rpx;
.item-image {
width: 100rpx;
height: 100rpx;
border-radius: 10rpx;
}
}
.product-count {
color: #999;
font-size: 24rpx;
width: 120rpx;
text-align: right;
}
}
.order-btn-group {
margin-top: 10rpx;
@include flex-right();
.order-btn {
width: 120rpx;
height: 36rpx;
line-height: 36rpx;
border-radius: 36rpx;
border: 1px solid #777;
color: #777;
font-size: 22rpx;
text-align: center;
margin-left: 15rpx;
}
}
}
}
</style>

@ -1,576 +0,0 @@
<template>
<view class="container">
<!-- 商品轮播图 -->
<u-swiper :list="spu.picUrls" @change="e => (currentNum = e.current)" :autoplay="false"
height="750rpx" radius="0" indicatorStyle="right: 20px">
<view slot="indicator" class="indicator-num">
<text class="indicator-num__text">{{ currentNum + 1 }}/{{ spu.picUrls.length }}</text>
</view>
</u-swiper>
<view class="product-box">
<!-- TODO @Sfmind样式讨论要不要改成类似 likeshop 的样子第一栏价格 + 分享第二栏商品名第三栏库存销量浏览量 -->
<view class="prod-info">
<view class="info-text">
<u--text :lines="2" size="14px" color="#333333" :text="spu.name"></u--text>
</view>
<view class="price-and-cart">
<!-- TODO @Sfmindcustom-text-price 会报错 -->
<custom-text-price color="red" size="16" intSize="26" :price="spu.minPrice"></custom-text-price>
</view>
</view>
<view class="prod-favor">
<u-icon name="star" color="#2979ff" size="28"></u-icon>
</view>
</view>
<u-gap height="8" bgColor="#f3f3f3"></u-gap>
<view class="row-box">
<view class="row-left">规格</view>
<view class="row-right" @click="skuPopup = true">
<view class="row-content">
<view class="sku-box">
<view v-if="spu.skus.length > 0" class="sku-item">
<view class="sku-desc">{{ spu.skus[currentSkuIndex].desc }}</view>
</view>
</view>
</view>
<view class="row-more">
<u-icon name="more-dot-fill" color="#939393" size="14"></u-icon>
</view>
</view>
</view>
<!-- 商品 SKU 选择弹窗 -->
<u-popup :show="skuPopup" :round="10" :closeable="true" :closeOnClickOverlay="false" @close="skuPopup = false">
<view class="sku-popup-slot">
<view class="current-sku-info">
<u--image class="current-sku-img" :showLoading="true" :src="spu.skus[currentSkuIndex].picUrl"
width="120rpx" height="120rpx"></u--image>
<view class="current-sku-desc">
<!-- TODO @Sfmind:name 这里的选择规格值的拼接 -->
<view class="name">{{ spu.skus[currentSkuIndex].desc }}</view>
<custom-text-price color="red" size="12" intSize="18" :price="spu.skus[currentSkuIndex].price"></custom-text-price>
<view class="current-sku-stock">库存 {{ 1 }}</view>
</view>
</view>
<view class="sku-selection">
<!-- TODO @Sfmind:name 这里是规格的具体选项 -->
<view class="sku-item" :class="{ active: currentSkuIndex === index }" v-for="(item, index) in spu.skus"
:key="item.id" @click="handleSkuItemClick(index)">{{ item.desc }}</view>
</view>
<view class="sku-num-box">
<view class="text">选择数量</view>
<u-number-box integer></u-number-box>
</view>
<view class="sku-btn-group">
<view class="btn-item-main">
<u-button type="warning" shape="circle" size="small" text="加入购物车"></u-button>
</view>
<view class="btn-item-main">
<u-button type="error" shape="circle" size="small" text="立即购买"></u-button>
</view>
</view>
</view>
</u-popup>
<u-gap height="8" bgColor="#f3f3f3"></u-gap>
<view class="row-box">
<view class="row-left">促销</view>
<view class="row-right" v-if="promotionList.length > 0" @click="promotionPopup = true">
<view class="row-content">
<view class="prom-box">
<view class="prom-item">
<view class="prom-title">{{ promotionList[0].title }}</view>
<text class="prom-desc">{{ promotionList[0].desc }}</text>
</view>
</view>
</view>
<view class="row-more">
<u-icon name="more-dot-fill" color="#939393" size="14"></u-icon>
</view>
</view>
</view>
<!-- 促销信息弹窗 -->
<u-popup :show="promotionPopup" :round="10" :closeable="true" :closeOnClickOverlay="false" @close="promotionPopup = false">
<view class="prom-popup-slot">
<view class="prom-info">促销信息</view>
<view class="prom-list">
<view v-for="(item, index) in promotionList" :key="index.id" class="prom-item">
<view class="prom-title">{{ item.title }}</view>
<text class="prom-desc">{{ item.desc }}</text>
</view>
</view>
</view>
</u-popup>
<u-gap height="8" bgColor="#f3f3f3"></u-gap>
<view class="row-box">
<view class="row-left">领券</view>
<view class="row-right" @click="handleCouponClick">
<view class="row-content">
<view class="coupon-box">
<view v-if="couponList.length > 0" class="coupon-list">
<view v-for="(item, index) in couponList" :key="item.id" class="coupon-item">
<view v-if="index < 2" class="coupon-desc">{{ item.desc }}</view>
</view>
</view>
<view class="coupon-total"> {{ couponList.length }} </view>
</view>
</view>
<view class="row-more">
<u-icon name="more-dot-fill" color="#939393" size="14"></u-icon>
</view>
</view>
</view>
<u-gap height="8" bgColor="#f3f3f3"></u-gap>
<view class="evaluation-box-wrap">
<view class="evaluation-box">
<view class="evaluation-title">评价</view>
<view class="evaluation-info">
<view class="evan-type-list">
<view class="evan-type-item" :class="{ active: currentEvanIndex === index }" v-for="(item, index) in evanTypeList" :key="item.id" @click="handleEvanTypeClick(index)"> {{ item.name }}({{ item.count }}) </view>
</view>
<view class="comment-empty" v-if="true">
<u-empty mode="comment" width="350rpx" height="350rpx" icon="/static/images/empty/comment.png"></u-empty>
</view>
<view v-else class="comment-list" style="min-height: 50px"> </view>
</view>
</view>
</view>
<!-- TODO @Sfmind:缺个商品详情 -->
<view class="fixed-btn-box">
<view class="btn-group">
<navigator class="btn-item" url="/pages/index/index" open-type="switchTab" hover-class="none">
<u-icon name="home" :size="24"></u-icon>
<view class="btn-text">首页</view>
</navigator>
<navigator class="btn-item" url="/pages/xxx/xxx" open-type="navigate" hover-class="none">
<u-icon name="server-man" :size="24"></u-icon>
<view class="btn-text">客服</view>
</navigator>
<!-- TODO @Sfmind:改成收藏 -->
<navigator class="btn-item" url="/pages/cart/cart" open-type="switchTab" hover-class="none">
<u-icon name="star" :size="24"></u-icon>
<view class="btn-text">收藏</view>
</navigator>
<view class="btn-item-main">
<u-button type="warning" shape="circle" size="small" text="加入购物车"></u-button>
</view>
<view class="btn-item-main">
<u-button type="error" color="#ea322b" shape="circle" size="small" text="立即购买"></u-button>
</view>
</view>
<u-safe-bottom customStyle="background: #ffffff"></u-safe-bottom>
</view>
</view>
</template>
<script>
import { getSpuDetail } from '../../api/product';
export default {
data() {
return {
current: 0,
currentNum: 0,
currentSkuIndex: 0,
skuPopup: false,
spu: {
id: '',
picUrls: [],
minPrice: '13.00',
sku: [
{
id: 0,
picUrl: 'https://cdn.uviewui.com/uview/album/1.jpg',
price: 13.0,
desc: '山不在高,有仙则名。'
},
{
id: 1,
picUrl: 'https://cdn.uviewui.com/uview/album/2.jpg',
price: 11.0,
desc: '水不在深,有龙则灵。'
},
{
id: 2,
picUrl: 'https://cdn.uviewui.com/uview/album/3.jpg',
price: 10.0,
desc: '斯是陋室,惟吾德馨。'
}
]
},
promotionPopup: false,
promotionList: [
{
id: 0,
title: '满额减',
desc: '全场满500减100'
},
{
id: 1,
title: '满额减',
desc: '全场满300减50'
},
{
id: 2,
title: '满额减',
desc: '全场满200减20'
},
{
id: 3,
title: '满额减',
desc: '全场满100减5'
}
],
couponList: [
{
id: 0,
title: '优惠券',
desc: '满50减10'
},
{
id: 1,
title: '优惠券',
desc: '满30减5'
},
{
id: 2,
title: '优惠券',
desc: '满20减2'
}
],
currentEvanIndex: 0,
evanTypeList: [
{
id: '0',
name: '全部',
count: 0
},
{
id: '1',
name: '好评',
count: 0
},
{
id: '2',
name: '中评',
count: 0
},
{
id: '3',
name: '差评',
count: 0
},
{
id: '4',
name: '有图',
count: 0
}
]
}
},
onLoad(e) {
if (!e.id) {
uni.$u.toast('请求参数错误')
return;
}
//
this.spu.id = e.id
this.loadProductData()
},
methods: {
loadProductData() {
getSpuDetail(this.spu.id).then(res => {
// this.spu.desc = res.data.description.replace(/<[^>]*>/g,'');
// console.log(res)
this.spu = res.data;
})
},
handleSkuItemClick(index) {
this.currentSkuIndex = index
},
handleCouponClick() {
// TODO
},
handleEvanTypeClick(index) {
this.currentEvanIndex = index
// TODO
}
},
computed: {
hasLogin() {
return this.$store.getters.hasLogin
}
}
}
</script>
<style lang="scss" scoped>
.indicator-num {
@include flex-center;
padding: 2px 0;
background-color: rgba(0, 0, 0, 0.35);
border-radius: 100px;
width: 35px;
&__text {
color: #ffffff;
font-size: 12px;
}
}
.product-box {
padding: 40rpx 40rpx 10rpx 40rpx;
@include flex;
border-bottom: $custom-border-style;
.prod-info {
padding-right: 30rpx;
.info-text {
padding-bottom: 10rpx;
}
.price-and-cart {
@include flex-space-between;
}
}
.prod-favor {
margin-top: 15rpx;
}
}
.row-box {
@include flex-left;
padding: 0 30rpx;
height: 70rpx;
.row-left {
width: 70rpx;
font-size: 24rpx;
color: #939393;
}
.row-right {
@include flex-space-between;
flex: 1;
.row-content {
flex: 1;
.prom-box {
@include flex-left;
.prom-item {
@include flex-left;
font-size: 22rpx;
.prom-title {
padding: 1rpx 10rpx;
border: 1rpx solid red;
border-radius: 5rpx;
color: red;
transform: scale(0.9);
}
.prom-desc {
margin-left: 15rpx;
}
}
}
.coupon-box {
@include flex-space-between;
.coupon-list {
@include flex-left;
.coupon-item {
@include flex-left;
font-size: 22rpx;
.coupon-desc {
padding: 2rpx 15rpx;
margin-right: 15rpx;
background: red;
color: #ffffff;
}
}
}
.coupon-total {
color: #939393;
font-size: 12rpx;
padding: 0 15rpx;
}
}
.sku-box {
@include flex-space-between;
.sku-item {
@include flex-left;
font-size: 22rpx;
.sku-desc {
margin-left: 15rpx;
font-weight: 700;
}
}
}
}
.row-more {
@include flex-right;
width: 30rpx;
}
}
}
.sku-popup-slot {
width: 750rpx;
.current-sku-info {
@include flex;
padding: 30rpx 100rpx 0 30rpx;
.current-sku-img {
border-radius: 10rpx;
/deep/ * {
border-radius: 10rpx;
}
}
.current-sku-desc {
padding: 0 30rpx;
font-size: 28rpx;
.current-sku-stock {
height: 40rpx;
line-height: 40rpx;
color: #666666;
font-size: 24rpx;
}
}
}
.sku-selection {
margin: 30rpx;
font-size: 26rpx;
color: #939393;
.sku-item {
margin-bottom: 20rpx;
border-radius: 6rpx;
padding: 5rpx 15rpx;
border: 1rpx solid #e3e3e3;
width: fit-content !important;
&.active {
color: #666666;
border: 1rpx solid #666666;
}
}
}
.sku-num-box {
@include flex-space-between padding: 30rpx;
.text {
font-size: 30rpx;
}
}
.sku-btn-group {
@include flex-space-around;
height: 100rpx;
.btn-item-main {
width: 350rpx;
}
}
}
.prom-popup-slot {
width: 750rpx;
min-height: 500rpx;
.prom-info {
background: #f3f3f3;
line-height: 90rpx;
padding-left: 30rpx;
font-size: 36rpx;
border-radius: 10px 10px 0 0;
}
.prom-list {
padding: 30rpx;
.prom-item {
@include flex-left;
font-size: 22rpx;
margin-bottom: 15rpx;
.prom-title {
padding: 1rpx 10rpx;
border: 1rpx solid red;
border-radius: 5rpx;
color: red;
transform: scale(0.9);
}
.prom-desc {
margin-left: 15rpx;
}
}
}
}
.evaluation-box-wrap {
background: #f3f3f3;
.evaluation-box {
border-radius: 20rpx 20rpx 0 0;
background: $custom-bg-color;
padding-bottom: 120rpx;
.evaluation-title {
border-radius: 20rpx 20rpx 0 0;
padding: 20rpx 30rpx;
border-bottom: $custom-border-style;
font-size: 30rpx;
}
.evaluation-info {
}
.evan-type-list {
padding: 20rpx;
@include flex-space-around;
.evan-type-item {
border-radius: 8rpx;
padding: 7rpx 12rpx;
background: #f3f3f3;
font-size: 22rpx;
text-align: center;
&.active {
background: #ffffff;
border: 1rpx solid red;
padding: 5rpx 10rpx;
color: red;
}
}
}
.comment-empty {
margin-bottom: 100rpx;
}
}
}
.fixed-btn-box {
position: fixed;
bottom: 0;
left: 0;
.btn-group {
background: $custom-bg-color;
border-top: $custom-border-style;
width: 750rpx;
@include flex-space-around;
height: 100rpx;
.btn-item {
width: 80rpx;
@include flex-center(column);
.btn-text {
font-size: 18rpx;
color: #666666;
}
}
.btn-item-main {
width: 200rpx;
}
}
}
</style>

@ -1,205 +0,0 @@
<template>
<view class="container">
<!-- 搜索框 -->
<view class="search-wrap">
<u-search placeholder="搜索" disabled height="32" bgColor="#f2f2f2" margin="0 20rpx" :show-action="false"
@click="handleSearchClick"></u-search>
</view>
<!-- 分类内容 -->
<view class="category-box">
<!-- 左侧导航栏 -->
<scroll-view scroll-y="true" class='box-left'>
<view class="category-item" v-for="(item, index) in categoryList" :key="item.id">
<view class="item-title" :class="{ active: currentIndex === index }" @click="handleCategoryClick(index)">
<text>{{ item.name }}</text>
</view>
</view>
</scroll-view>
<!-- 右侧分类内容 -->
<scroll-view scroll-y="true" class="box-right">
<view class="category-image">
<image :showLoading="true" :src="categoryList[currentIndex].picUrl" mode='widthFix' @click="click"></image>
</view>
<view class="sub-category-box" v-for="(item, index) in categoryList[currentIndex].children" :key="item.id">
<view class="sub-category-header">
<view class="title">{{ item.name }}</view>
<view class="more" @click="handleCategory(item, 0)">查看更多</view>
</view>
<view class="sub-category-grid">
<u-grid col="3">
<u-grid-item v-for="(subItem, subIndex) in item.children" :key="subItem.id">
<view class="sub-category-item" @click="handleCategory(item, subIndex)">
<u-icon name="photo" :size="80" v-if="subItem.picUrl === null"></u-icon>
<image :src="subItem.picUrl" v-if="subItem.picUrl != null" mode='widthFix' />
<text class="sub-category-title">{{ subItem.name }}</text>
</view>
</u-grid-item>
</u-grid>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import { categoryListData } from '../../api/category';
import { handleTree, convertTree } from '../../utils/tree.js';
export default {
data() {
return {
currentIndex: 0,
categoryList: []
}
},
onLoad() {
this.handleCategoryList();
},
methods: {
//
handleSearchClick(e) {
uni.$u.route('/pages/search/search')
},
//
handleCategoryClick(index) {
if (this.currentIndex !== index) {
this.currentIndex = index
}
},
//
handleCategoryList() {
categoryListData().then(res => {
this.categoryList = handleTree(res.data, "id", "parentId");
})
},
handleCategory(item, index){
// console.log(item)
// console.log(index)
uni.navigateTo({
url:"./product-list?item="+encodeURIComponent(JSON.stringify(item))+"&index="+index
})
}
}
}
</script>
<style lang="scss" scoped>
.search-wrap {
background: #ffffff;
position: fixed;
top: 0;
left: 0;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.07);
padding: 20rpx 0;
width: 100%;
z-index: 3;
}
.category-box {
position: fixed;
display: flex;
overflow: hidden;
margin-top: 100rpx;
height: calc(100% - 100rpx);
.box-left {
width: 200rpx;
padding-top: 5rpx;
overflow: scroll;
z-index: 2;
background-color: #f2f2f2;
.category-item {
line-height: 80rpx;
height: 80rpx;
text-align: center;
color: #777;
.item-title {
font-size: 28rpx;
&.active {
font-size: 28rpx;
font-weight: bold;
position: relative;
background: #fff;
color: $u-primary;
}
&.active::before {
position: absolute;
left: 0;
content: "";
width: 8rpx;
height: 32rpx;
top: 25rpx;
background: $u-primary;
}
}
}
}
.box-right {
width: 550rpx;
height: 100%;
box-sizing: border-box;
z-index: 1;
.category-image {
width: 510rpx;
box-sizing: border-box;
overflow: hidden;
position: relative;
margin: 30rpx 20rpx 0;
image {
width: 100%;
}
}
.sub-category-box {
.sub-category-header {
@include flex-space-between;
padding: 20rpx 20rpx;
.title {
font-size: 28rpx;
font-weight: bolder;
}
.more {
font-size: 22rpx;
color: #939393;
}
}
.sub-category-grid {
padding: 0 15rpx;
.sub-category-item {
@include flex-center(column);
background: #fff;
image {
text-align: center;
width: 150rpx;
height: 150rpx;
line-height: 150rpx;
font-size: 0;
}
.sub-category-title {
margin: 15rpx 0;
font-size: 22rpx;
}
}
}
}
}
}
</style>

@ -19,7 +19,6 @@
<!-- 消息提示 -->
<u-toast ref="msg"></u-toast>
</view>
</template>
@ -98,7 +97,7 @@
loadData() {
let status = this.statusList[this.curNow].value;
// console.log(this.statusMap);
this.mockData();
this.mockData();
},
mockData() {
//
@ -136,6 +135,7 @@
<style lang="scss" scoped>
.tasklist {
background-color: #f9f9f9;
// font-family: $custom-ttf;
.item {
padding: 24rpx 0;
@ -143,4 +143,4 @@
font-size: 28rpx;
}
}
</style>
</style>

@ -0,0 +1,285 @@
<template>
<view class="container">
<!-- 导航栏 -->
<u-navbar leftText="返回" title="任务开工" :placeholder="true" leftIconColor=" #409eff" @leftClick="backClick" />
<view class="info-item">
<view class="title">#30 任务名称 (任务编号: 90569010)</view>
<view class="target">
<view class="imgbox">
<image slot='cover' class="unimg" mode="aspectFill" :src="info.imgurl"></image>
<text class="name">xxx产品</text>
</view>
<view class="number">
<text>计划数量: </text>
<text style="color:#409eff;font-size: 16px;margin-left: 8px;">30</text>
</view>
</view>
<view class="finishtime">
<text>完工时间: </text>
<text style="color:#999;margin-left: 8px;">2023-10-11 19:58:51</text>
</view>
</view>
<u-gap height="5px"></u-gap>
<view class="info-item">
<view class="header">计划加工用时</view>
<!-- <view class="header">阿里巴巴</view> -->
<view class="time">
<u-icon name="clock" color="#f56c6c" size="18"></u-icon>
<text class="hour">80:00:00 (小时)</text>
</view>
</view>
<u-gap height="5px"></u-gap>
<view class="info-item">
<view class="header">任务操作</view>
<view class="options">
<u-button type="primary" :plain="true" :hairline="true" text="领料申请" @click="applyPickingClick"></u-button>
<u-button type="primary" :plain="true" :hairline="true" text="完工提报" @click="reportCompletionClick"></u-button>
<u-button type="primary" :plain="false" :hairline="true" text="质检申请" @click="checkQualityClick"></u-button>
</view>
</view>
<!-- 领料申请 -->
<u-popup :show="popupApplyShow" mode="center" @close="popupApplyShow = false" :customStyle="{'width': '350px'}"
closeable>
<view class="picklist">
<material-apply :picklist="picklist" :show.sync="popupApplyShow" :checkNumber="checkValid"></material-apply>
</view>
</u-popup>
<!-- 完工报告 -->
<u-popup :show="popupReviewShow" mode="center" @close="popupReviewShow = false" :customStyle="{'width': '350px'}"
closeable>
<view class="picklist">
<work-report :picklist="picklist" :info="info" :show.sync="popupReviewShow"></work-report>
</view>
</u-popup>
<!-- 返库物料选择 -->
<u-popup :show="restoreSelectShow" mode="center" @close="restoreSelectShow = false" closeable>
<view class="select-box">
<uni-section title="选择返库物料" type="line">
<uni-data-select :localdata="restoreRange" @change="changeRestore"></uni-data-select>
</uni-section>
</view>
</u-popup>
<!-- 消息提示 -->
<u-toast ref="msg"></u-toast>
</view>
</template>
<script>
import materialApply from './material-apply.vue'
import workReport from './work-report.vue'
export default {
components: {
materialApply,
workReport
},
provide() {
return {
checkData: this.checkValid,
}
},
data() {
return {
info: {
id: 90569010,
title: "#001任务名称",
productName: "xxx产品",
unit: "件",
imgurl: require('@/static/images/wuliao.png'),
hours: 80,
number: 30,
completed: 0,
status: 1,
creator: '管理员',
createtime: '2023-10-01 09:58:51',
pointer: '小王',
finishtime: '2023-10-11 19:58:51',
},
picklist: [{
"id": 10001,
"name": "实物物料202212050001",
"spec": "330ml/瓶",
"number": 1,
"unit": "瓶"
},
{
"id": 10002,
"name": "实物物料202212050002",
"spec": "330ml/瓶",
"number": 2,
"unit": "瓶"
},
{
"id": 10003,
"name": "实物物料202212050003",
"spec": "330ml/瓶",
"number": 3,
"unit": "瓶"
},
{
"id": 10004,
"name": "实物物料202212050004",
"spec": "100颗",
"number": 20,
"unit": "件"
},
{
"id": 10005,
"name": "实物物料202212050005",
"spec": "100颗",
"number": 10,
"unit": "件"
},
{
"id": 10006,
"name": "实物物料202212050006",
"spec": "100颗",
"number": 10,
"unit": "件"
}
],
popupApplyShow: false,
popupReviewShow: false,
}
},
onLoad(params) {
console.log(params); //
},
methods: {
backClick() {
uni.switchTab({
url: "/pages/renwu/index?status=1"
});
},
//
applyPickingClick() {
this.popupApplyShow = true;
},
//
reportCompletionClick() {
this.popupReviewShow = true;
},
//
checkQualityClick() {
this.$refs.msg.show({
type: 'default',
message: "质检申请暂未开发~",
});
},
//
checkValid(that, val) {
let invalid = false;
if (!/^\d{1,3}$/.test(val) || Number.parseInt(val) <= 0) {
invalid = true;
that.$nextTick(() => {
that.$refs.uToast.show({
type: 'error',
icon: false,
message: "必须为1-999之间的整数",
})
});
}
return invalid ? {
'border-color': '#f56c6c !important'
} : {};
}
}
}
</script>
<style lang="scss" scoped>
@import "../../styles/base.scss";
.container {
padding: 10px;
// font-family: "Alibaba PuHuiTi 2.0 55 Regular";
}
.info-item {
min-height: 60px;
padding: 10px;
border-radius: 5px;
border: 1px solid #f2f2f2;
background-color: $custom-bg-color;
.title {
color: #409eff;
font-size: 14px;
}
.target {
margin-top: 10px;
@include flex-space-between(row);
.imgbox {
@include imgbox(30, 30);
}
.number {
font-size: 14px;
}
}
.options {
margin-top: 10px;
@include flex-space-around(row);
::v-deep {
.u-button {
width: 100px;
height: 35px;
border-radius: 10px;
}
}
}
.finishtime {
margin-top: 10px;
font-size: 14px;
}
.header {
font-size: 13px;
font-weight: 500;
color: #666;
text-align: left;
}
.time {
margin-top: 10px;
@include flex-left(row);
.hour {
color: #f56c6c;
margin-left: 5px;
font-size: 28px;
font-weight: 700;
}
}
}
.picklist {
::v-deep {
.uni-table-scroll {
max-height: 360px;
}
.uni-table-th {
background-color: #f5f5f5;
}
.uni-table-td {
vertical-align: middle;
}
.u-alert__content__title {
margin: 0;
}
}
}
</style>

@ -0,0 +1,97 @@
<template>
<view class="container">
<uni-card title="加工领料量表" padding="5" spacing="0" margin="0">
<u-alert title="注意向下滑动查看,以防遗漏" type="warning" :show-icon="true"></u-alert>
<u-gap height="5px"></u-gap>
<uni-table ref="table" :loading="false" border stripe emptyText="暂无物料数据">
<uni-tr>
<uni-th width="120" align="center">物料名称</uni-th>
<uni-th width="80" align="center">规格</uni-th>
<uni-th width="100" align="center">数量</uni-th>
</uni-tr>
<uni-tr v-for="(item, index) in datalist" :key="index">
<uni-td width="120" align="center">{{ item.name }}</uni-td>
<uni-td width="80" align="center">{{ item.spec }}</uni-td>
<uni-td width="100" align="center">
<u-input placeholder="数量" v-model="item.number" type="number" maxlength="3" @blur="formatNumber(item)"
:custom-style="checkNumber(item.number)">
<u-text :text="item.unit" slot="suffix" margin="0 3px 0 0"></u-text>
</u-input>
</uni-td>
</uni-tr>
</uni-table>
<!-- 提交申请 -->
<view slot="actions" class="actions">
<u-button type="success" :hairline="true" text="提交物料" @click="submitPicking"></u-button>
<u-button type="error" :hairline="true" text="再看看" @click="closePicking"></u-button>
</view>
</uni-card>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
export default {
inject: ['checkData'],
props: {
picklist: {
type: Array,
require: false,
default: () => []
},
show: {
type: Boolean,
require: false,
default: false
}
},
//
options: {
styleIsolation: 'shared',
},
data() {
return {
datalist: []
}
},
created() {
//
this.datalist = uni.$u.deepClone(this.picklist);
},
methods: {
//
submitPicking() {
var ids = this.datalist.map(v => v.id);
console.log(ids); // todo
this.$emit("update:show", false);
},
//
closePicking() {
this.$emit("update:show", false);
},
//
formatNumber(item) {
item.number = this.$base.filterNonNumeric(item.number + '');
},
//
checkNumber(val) {
this.checkData(this,val);
}
}
}
</script>
<style lang="scss" scoped>
.actions {
margin-bottom: 20px;
@include flex-space-around(row);
::v-deep {
.u-button {
width: 100px;
height: 35px;
}
}
}
</style>

@ -1,164 +0,0 @@
<template>
<view class="container">
<u-navbar :title="title" :autoBack="true" placeholder="true" titleStyle="font-size: 28rpx">
</u-navbar>
<view class="context">
<!-- 分类列表 -->
<!-- TODO @Luowenfeng不应该展示商品分类应该是上面一个筛选之后是综合销量价格的排序 -->
<view class="tabs-top">
<u-tabs :list="categoryList" @click="changeTabs" :current="current" lineHeight="2" lineWidth="85rpx"
itemStyle="padding-left: 15px; padding-right: 15px; height: 85rpx;"></u-tabs>
</view>
<!-- 商品列表 -->
<scroll-view scroll-y="true" class="product-list" enable-flex="true">
<view class="flex-box">
<block v-for="(item, index) in productList[current]" :key="index">
<view class="product-item">
<view class="product-image">
<image :src="item.picUrls[0]" mode='widthFix' />
</view>
<view class="product-button">
<view class="product-text">{{ item.name }}</view>
<view class="product-price-button">
<text class="product-price">
<text class="price-size">{{ towNumber(item.minPrice) }}</text></text>
<text class="product-like-count">销量 {{ item.salesCount }}</text>
</view>
</view>
</view>
</block>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import {
productSpuPage
} from '../../api/product';
export default {
data() {
return {
title: "",
current: 0,
categoryList: [],
productList: {}
}
},
onLoad(option) {
const item = JSON.parse(decodeURIComponent(option.item))
this.title = item.name
this.categoryList = item.children
this.handleProductSpu(option.index);
},
methods: {
changeTabs(item) {
if (item.index !== this.current) {
this.handleProductSpu(item.index)
}
},
handleProductSpu(index) {
let param = {}
param.categoryId = this.categoryList[index].id
console.log(this.categoryList)
console.log(index)
productSpuPage(param).then(res => {
this.productList[index] = res.data.list
this.current = index
})
},
towNumber(val) {
return (val / 100).toFixed(2)
}
}
}
</script>
<style lang="scss" scoped>
.context {
width: 100vw;
position: fixed;
top: 160rpx;
left: 0;
}
.tabs-top {
position: relative;
top: 0;
width: 100%;
height: 85rpx;
}
.product-list {
position: relative;
background-color: #f2f2f2;
height: calc(100vh - 88rpx - 100rpx - var(--status-bar-height));
width: 100%;
.flex-box {
width: 730rpx;
margin: 0 auto;
@include flex;
flex-wrap: wrap;
justify-content: left;
.product-item {
width: 345rpx;
height: 450rpx;
background-color: #ffffff;
margin: 20rpx 10rpx 0;
border-radius: 20rpx;
.product-image {
width: 100%;
height: 300rpx;
overflow: hidden;
border-radius: 20rpx;
image {
width: 100%;
}
}
.product-button {
width: 330rpx;
margin: 15rpx auto 0;
.product-text {
font-size: 25rpx;
height: 70rpx;
overflow: hidden;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
}
.product-price-button {
font-size: 20rpx;
margin-top: 20rpx;
.product-price {
color: red;
.price-size {
font-size: 26rpx;
}
}
.product-like-count {
font-size: 16rpx;
margin-left: 10rpx;
}
}
}
}
}
// }
}
</style>

@ -0,0 +1,177 @@
<template>
<view class="container">
<view class="subject">
<view>{{title}}</view>
<view class="options">
<u-button type="success" icon="plus" size="mini" @click="addRow"></u-button>
<u-button type="primary" icon="minus" size="mini" @click="removeRows"
:disabled="selectedIndexs.length == 0"></u-button>
</view>
</view>
<view class="uni-mt-4">
<uni-table ref="table2" border stripe emptyText="暂未添加记录" type="selection" @selection-change="selectRowChange">
<uni-tr>
<uni-th width="120" align="center">物料名称</uni-th>
<uni-th width="80" align="center">规格</uni-th>
<uni-th width="100" align="center">数量</uni-th>
</uni-tr>
<uni-tr v-for="(item, index) in selectList" :key="index">
<uni-td width="120" align="center">{{item.name}}</uni-td>
<uni-td width="80" align="center">{{ item.spec }}</uni-td>
<uni-td width="100" align="center">
<u-input placeholder="数量" v-model="item.number" type="number" maxlength="3" @blur="formatNumber(item)"
:custom-style="checkNumber(item.number)">
<u-text :text="item.unit" slot="suffix" margin="0 3px 0 0"></u-text>
</u-input>
</uni-td>
</uni-tr>
</uni-table>
</view>
<!-- 物料选择弹框 -->
<u-popup :show="selectorShow" mode="center" @close="selectorShow = false" closeable>
<view class="select-box">
<uni-section title="选择物料" type="line">
<uni-data-select :localdata="selectRange" @change="changeRestore"></uni-data-select>
</uni-section>
</view>
</u-popup>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
export default {
// model: {
// prop: 'formData',
// event: 'changeData',
// },
inject: ['checkData'],
props: {
// formData: {
// type: Number,
// require: false,
// default: 100
// }
name: {
type: String,
require: true,
},
picklist: {
type: Array,
require: false,
default: () => []
},
title: {
type: String,
require: false,
default: "上报记录",
},
},
options: {
styleIsolation: 'shared',
},
data() {
return {
selectorShow: false,
datalist: [], //
selectList: [], //
selectedIndexs: [], //
}
},
created() {
//
this.datalist = uni.$u.deepClone(this.picklist);
},
computed: {
//
selectRange() {
return this.datalist.reduce((result, item) => {
let obj = {
value: item.id,
text: item.name,
disable: item.disable ? true : false
};
result.push(obj);
return result;
}, []);
}
},
methods: {
//
formatNumber(item) {
item.number = this.$base.filterNonNumeric(item.number + '');
},
//
checkNumber(val) {
this.checkData(this,val);
},
//
selectRowChange(e) {
this.selectedIndexs = e.detail.index;
// console.log(this.selectedIndexs);
},
//
addRow() {
this.selectorShow = true;
},
//
changeRestore(val) {
let index = this.datalist.findIndex(item => item.id == val);
if (index != -1) {
let obj = Object.assign({}, this.datalist[index]);
obj.number = 1; // 1
this.selectList.push(obj);
}
this.selectorShow = false;
},
//
removeRows() {
this.$base.removeIndexesItems(this.selectedIndexs, this.selectList);
console.log(this.selectList);
}
},
watch: {
selectList: {
handler(newVal, oldVal) {
console.log(newVal);
let restoreIds = newVal.map(v => v.id);
for (let index in this.datalist) {
let id = this.datalist[index].id;
//
this.$set(this.datalist[index], 'disable', restoreIds.includes(id) ? 1 : 0);
}
//
this.$emit('setFormItem', {name: this.name, value: newVal});
},
deep: true
}
}
}
</script>
<style lang="scss" scoped>
.subject {
margin: 10px 0;
@include flex-space-between(row);
.options {
@include flex-space-around(row);
::v-deep {
.u-button {
width: 30px;
height: 30px;
min-width: 30px;
margin-left: 5px;
}
}
}
}
.select-box {
width: 300px;
max-height: 360px;
background-color: #f5f5f5;
}
</style>

@ -0,0 +1,176 @@
<template>
<view class="container">
<uni-card title="完工报告表" padding="5" spacing="0" margin="0">
<scroll-view class="scrollbox" :scroll-y="true" scroll-top="0">
<view class="report-item">
<text class="uni-my-2">成品完成量</text>
<view class="target">
<view class="imgbox">
<image slot='cover' class="unimg" mode="aspectFill" :src="info.imgurl"></image>
<text class="name">xxx产品</text>
</view>
<view class="number">
<text>计划数量: </text>
<text style="color:#409eff;font-size: 16px;margin-left: 8px;">30</text>
</view>
</view>
<view class="completed">
<view class="completed_item1">完成数量</view>
<view class="completed_item2">
<u-input placeholder="数量" v-model="form.completed" type="number" maxlength="3"
@blur="formatNumber(form.completed)" :custom-style="checkNumber(form.completed)">
<u-text :text="info.unit" slot="suffix" margin="0 3px 0 0"></u-text>
</u-input>
</view>
</view>
</view>
<view class="report-item">
<reportItem :picklist="picklist" title="领料返库" name="restore" @setFormItem="setFormItem"></reportItem>
</view>
<view class="report-item">
<reportItem :picklist="picklist" title="加工货损" name="damage" @setFormItem="setFormItem"></reportItem>
</view>
</scroll-view>
<!-- 提交申请 -->
<view slot="actions" class="actions">
<u-button type="primary" :hairline="true" text="提交报告" @click="submitReport"></u-button>
<u-button type="error" :hairline="true" text="再看看" @click="closeReport"></u-button>
</view>
</uni-card>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
import reportItem from './work-report-item.vue';
export default {
components: {
reportItem
},
inject: ['checkData'],
props: {
picklist: {
type: Array,
require: false,
default: () => []
},
info: {
type: Object,
require: false,
default: () => []
},
show: {
type: Boolean,
require: false,
default: false
}
},
options: {
styleIsolation: 'shared',
},
data() {
return {
restoreSelectorShow: false,
damageSelectorShow: false,
form: {
completed: 1,
restore: [],
damage: [],
},
}
},
methods: {
//
submitReport() {
let params = uni.$u.deepClone(this.form);
console.log(params); // todo
this.$emit("update:show", false);
},
//
closeReport() {
this.$emit("update:show", false);
},
//
setFormItem(obj) {
this.form[obj.name] = obj.value;
},
//
formatNumber(number) {
this.form.completed = this.$base.filterNonNumeric(number + '');
return this.form.completed;
},
//
checkNumber(val) {
this.checkData(this,val);
}
},
// watch:{
// form: {
// handler(newVal, oldVal) {
// console.log(newVal);
// },
// deep: true
// }
// }
}
</script>
<style lang="scss" scoped>
@import "../../styles/base.scss";
.scrollbox {
height: 500px;
overflow: hidden;
}
.report-item {
margin-bottom: 15px;
.target {
margin-top: 10px;
@include flex-space-between(row);
.imgbox {
@include imgbox(30, 30);
}
.number {
font-size: 14px;
}
}
.completed {
@include flex-space-between(row);
height: 45px;
border: 1px solid #e4e4e4;
margin-top: 10px;
&_item1 {
flex: 1;
background-color: #f5f5f5;
color: #666;
font-weight: 700;
line-height: 45px;
padding: 0 10px;
}
&_item2 {
flex: 1;
padding: 0 10px;
}
}
}
.actions {
margin-bottom: 20px;
@include flex-space-around(row);
::v-deep {
.u-button {
width: 100px;
height: 35px;
}
}
}
</style>

@ -1,52 +1,6 @@
<template>
<view class="container">
<!-- 购物车为空 -->
<view v-if="!hasLogin || cartList.length === 0">
<view class="cart-empty">
<u-empty text="去逛逛添点什么吧" width="500rpx" height="500rpx" icon="/static/images/empty/cart.png"></u-empty>
</view>
</view>
<!-- 购物车列表 -->
<scroll-view v-if="hasLogin && cartList.length > 0" class="cart-product" scroll-y="true">
<yd-cart-product :product-list="cartList" @productCheckedChange="handleProductCheckedChange" @productCountChange="handleProductCountChange"></yd-cart-product>
</scroll-view>
<!-- 未登录 -->
<view v-if="!hasLogin" class="login-tips-box">
<view class="login-tips">
<navigator url="/pages/login/social" open-type="navigate" hover-class="none">
<view class="login-link">登录查看</view>
</navigator>
</view>
</view>
<!-- 底部菜单 -->
<view class="cart-btn-container">
<view class="btn-box">
<view class="product-check-info">
<view class="check-all-btn" @click.stop="handleCheckAllProduct">
<u-icon v-if="isCheckAll" name="checkmark-circle-fill" color="#3c9cff" size="22"></u-icon>
<view v-else class="un-check-box"></view>
</view>
<view class="info-text">合计</view>
<view>
<yd-text-price color="red" size="15" intSize="20" :price="totalAmount"></yd-text-price>
</view>
</view>
<view v-if="checkedProduct.length > 0" class="cart-btn-group">
<u-button type="warning" shape="circle" size="small" text="移除" @click="handleRemoveProduct"></u-button>
<view class="btn-gap"></view>
<u-button style="margin-left: 10px" class="main-btn" type="primary" shape="circle" size="small" text="去结算" @click="handleCheckoutProduct"></u-button>
</view>
<view v-else class="cart-btn-group">
<u-button type="warning" shape="circle" size="small" text="移除" disabled></u-button>
<view class="btn-gap"></view>
<u-button style="margin-left: 10px" class="main-btn" type="primary" shape="circle" size="small" text="去结算" disabled></u-button>
</view>
</view>
</view>
消息列表
</view>
</template>
@ -54,172 +8,15 @@
export default {
data() {
return {
title: '',
cartList: [],
checkedNumber: 0,
totalAmount: 0
}
},
computed: {
checkedProduct() {
return this.cartList.filter(item => {
return item.checked
})
},
isCheckAll() {
if (this.cartList.length < 1) {
return false
}
return this.cartList.every(item => {
return item.checked
})
},
hasLogin() {
return this.$store.getters.hasLogin
}
},
onShow() {
if (this.hasLogin) {
this.loadCartDetailData()
} else {
this.cartList =[]
}
},
methods: {
loadCartDetailData() {
this.$store.dispatch('CartProductDetail').then(res => {
this.cartList = res.data || []
})
},
/** 商品全选/取消全选 */
handleCheckAllProduct() {
if (this.cartList.length < 1) {
return
}
const productIds = this.cartList.map(item => {
return item.productId
})
this.$store.dispatch('CartProductCheckChange', { productIds, checked: !this.isCheckAll }).then(res => {
this.cartList = res.data || []
})
},
/** 商品单选/取消单选 */
handleProductCheckedChange(productId, checked) {
this.$store.dispatch('CartProductCheckChange', { productIds: [productId], checked: checked }).then(res => {
this.cartList = res.data || []
})
},
/** 修改购物车商品数量 */
handleProductCountChange(productId, number) {
this.$store.dispatch('CartProductCountChange', { productIds: [productId], productCount: number }).then(res => {
this.cartList = res.data || []
})
},
/** 移除购物车商品 */
handleRemoveProduct() {
if (this.checkedProduct < 1) {
return
}
const productIds = this.checkedProduct.map(item => {
return item.productId
})
uni.showModal({
title: '确定要移除选中的商品?',
cancelText: '取消',
confirmText: '移除',
success: res => {
if (res.confirm) {
this.$store.dispatch('CartProductCountChange', { productIds: productIds, productCount: 0 }).then(res => {
this.cartList = res.data || []
})
} else if (res.cancel) {
//console.log('')
}
}
})
},
/** 购物车提交结算 */
handleCheckoutProduct() {
if (this.checkedProduct < 1) {
return
}
const checkedProduct = this.checkedProduct.map(item => {
return { productId: item.productId, productCount: item.productCount, sellPrice: item.sellPrice }
})
uni.$u.route('/pages/checkout/checkout', {
checkedProduct: JSON.stringify(checkedProduct)
})
}
}
}
</script>
<style lang="scss" scoped>
.login-tips-box {
padding-top: 100rpx;
.login-tips {
@include flex-center;
color: #939393;
font-size: 24rpx;
letter-spacing: 5rpx;
}
.login-link {
width: 160rpx;
height: 50rpx;
line-height: 50rpx;
border-radius: 50rpx;
border: 1px solid #777;
color: #777;
text-align: center;
}
}
.cart-btn-container {
position: fixed;
bottom: 0;
left: 0;
.btn-box {
background: $custom-bg-color;
border-top: $custom-border-style;
width: 750rpx;
@include flex-space-between();
height: 100rpx;
.product-check-info {
@include flex-left;
.check-all-btn {
padding: 20rpx;
.un-check-box {
width: 20px;
height: 20px;
border: 1px solid #939393;
border-radius: 50%;
}
}
.info-text {
font-size: 26rpx;
font-weight: bold;
color: #666666;
}
}
.cart-btn-group {
@include flex-right();
width: 360rpx;
padding-right: 10px;
.btn-gap {
width: 20rpx;
}
}
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

@ -1,3 +1,11 @@
@font-face {
font-family: "Alibaba PuHuiTi 2.0 55 Regular";
font-weight: 400;
src: url("https://ttf.cjyx.cc/Alibaba_PuHuiTi_2.0_55_Regular_55_Regular.ttf") format("truetype");
font-display: swap;
}
.row-between {
display: flex;
align-items: center;
@ -9,3 +17,18 @@
font-size: #{$size}px;
}
@mixin imgbox($width: 30, $height: 30) {
@include flex-center(row);
.unimg {
height: #{$height}px;
width: #{$width}px;
margin-right: 5px;
}
.name {
color: #333;
font-size: 16px;
}
}

@ -23,6 +23,9 @@ $custom-bg-color: #ffffff;
/* 边框样式 */
$custom-border-style: 1rpx solid #f3f3f3;
/* 字体样式 */
$custom-ttf: 'Alibaba PuHuiTi 2.0 55 Regular';
@mixin flex-left($direction: row) {
display: flex;
@ -70,3 +73,6 @@ $custom-border-style: 1rpx solid #f3f3f3;
-webkit-line-clamp: $row;
text-overflow: ellipsis;
}

@ -0,0 +1,35 @@
## 1.0.62023-04-12
- 修复 微信小程序点击时会改变背景颜色的 bug
## 1.0.52023-02-03
- 修复 禁用时会显示清空按钮
## 1.0.42023-02-02
- 优化 查询条件短期内多次变更只查询最后一次变更后的结果
- 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue
## 1.0.32023-01-16
- 修复 不关联服务空间报错的问题
## 1.0.22023-01-14
- 新增 属性 `format` 可用于格式化显示选项内容
## 1.0.12022-12-06
- 修复 当where变化时数据不会自动更新的问题
## 0.1.92022-09-05
- 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框
## 0.1.82022-08-29
- 修复 点击的位置不准确
## 0.1.72022-08-12
- 新增 支持 disabled 属性
## 0.1.62022-07-06
- 修复 pc端宽度异常的bug
## 0.1.5
- 修复 pc端宽度异常的bug
## 0.1.42022-07-05
- 优化 显示样式
## 0.1.32022-06-02
- 修复 localdata 赋值不生效的 bug
- 新增 支持 uni.scss 修改颜色
- 新增 支持选项禁用(数据选项设置 disabled: true 即禁用)
## 0.1.22022-05-08
- 修复 当 value 为 0 时选择不生效的 bug
## 0.1.12022-05-07
- 新增 记住上次的选项(仅 collection 存在时有效)
## 0.1.02022-04-22
- 初始化

@ -0,0 +1,517 @@
<template>
<view class="uni-stat__select">
<span v-if="label" class="uni-label-text hide-on-phone">{{label + ''}}</span>
<view class="uni-stat-box" :class="{'uni-stat__actived': current}">
<view class="uni-select" :class="{'uni-select--disabled':disabled}">
<view class="uni-select__input-box" @click="toggleSelector">
<view v-if="current" class="uni-select__input-text">{{current}}</view>
<view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view>
<view v-if="current && clear && !disabled" @click.stop="clearVal" >
<uni-icons type="clear" color="#c0c4cc" size="24"/>
</view>
<view v-else>
<uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" />
</view>
</view>
<view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" />
<view class="uni-select__selector" v-if="showSelector">
<view class="uni-popper__arrow"></view>
<scroll-view scroll-y="true" class="uni-select__selector-scroll">
<view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0">
<text>{{emptyTips}}</text>
</view>
<view v-else class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index"
@click="change(item)">
<text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text>
</view>
</scroll-view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* DataChecklist 数据选择器
* @description 通过数据渲染的下拉框组件
* @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select
* @property {String} value 默认值
* @property {Array} localdata 本地数据 格式 [{text:'',value:''}]
* @property {Boolean} clear 是否可以清空已选项
* @property {Boolean} emptyText 没有数据时显示的文字 本地数据无效
* @property {String} label 左侧标题
* @property {String} placeholder 输入框的提示文字
* @property {Boolean} disabled 是否禁用
* @event {Function} change 选中发生变化触发
*/
export default {
name: "uni-data-select",
mixins: [uniCloud.mixinDatacom || {}],
props: {
localdata: {
type: Array,
default () {
return []
}
},
value: {
type: [String, Number],
default: ''
},
modelValue: {
type: [String, Number],
default: ''
},
label: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '请选择'
},
emptyTips: {
type: String,
default: '无选项'
},
clear: {
type: Boolean,
default: true
},
defItem: {
type: Number,
default: 0
},
disabled: {
type: Boolean,
default: false
},
// field="_id as value, version as text, uni_platform as label" format="{label} - {text}"
format: {
type: String,
default: ''
},
},
data() {
return {
showSelector: false,
current: '',
mixinDatacomResData: [],
apps: [],
channels: [],
cacheKey: "uni-data-select-lastSelectedValue",
};
},
created() {
this.debounceGet = this.debounce(() => {
this.query();
}, 300);
if (this.collection && !this.localdata.length) {
this.debounceGet();
}
},
computed: {
typePlaceholder() {
const text = {
'opendb-stat-app-versions': '版本',
'opendb-app-channels': '渠道',
'opendb-app-list': '应用'
}
const common = this.placeholder
const placeholder = text[this.collection]
return placeholder ?
common + placeholder :
common
},
valueCom(){
// #ifdef VUE3
return this.modelValue;
// #endif
// #ifndef VUE3
return this.value;
// #endif
}
},
watch: {
localdata: {
immediate: true,
handler(val, old) {
if (Array.isArray(val) && old !== val) {
this.mixinDatacomResData = val
}
}
},
valueCom(val, old) {
this.initDefVal()
},
mixinDatacomResData: {
immediate: true,
handler(val) {
if (val.length) {
this.initDefVal()
}
}
}
},
methods: {
debounce(fn, time = 100){
let timer = null
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, time)
}
},
//
query(){
this.mixinDatacomEasyGet();
},
//
onMixinDatacomPropsChange(){
if (this.collection) {
this.debounceGet();
}
},
initDefVal() {
let defValue = ''
if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) {
defValue = this.valueCom
} else {
let strogeValue
if (this.collection) {
strogeValue = this.getCache()
}
if (strogeValue || strogeValue === 0) {
defValue = strogeValue
} else {
let defItem = ''
if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) {
defItem = this.mixinDatacomResData[this.defItem - 1].value
}
defValue = defItem
}
if (defValue || defValue === 0) {
this.emit(defValue)
}
}
const def = this.mixinDatacomResData.find(item => item.value === defValue)
this.current = def ? this.formatItemName(def) : ''
},
/**
* @param {[String, Number]} value
* 判断用户给的 value 是否同时为禁用状态
*/
isDisabled(value) {
let isDisabled = false;
this.mixinDatacomResData.forEach(item => {
if (item.value === value) {
isDisabled = item.disable
}
})
return isDisabled;
},
clearVal() {
this.emit('')
if (this.collection) {
this.removeCache()
}
},
change(item) {
if (!item.disable) {
this.showSelector = false
this.current = this.formatItemName(item)
this.emit(item.value)
}
},
emit(val) {
this.$emit('input', val)
this.$emit('update:modelValue', val)
this.$emit('change', val)
if (this.collection) {
this.setCache(val);
}
},
toggleSelector() {
if (this.disabled) {
return
}
this.showSelector = !this.showSelector
},
formatItemName(item) {
let {
text,
value,
channel_code
} = item
channel_code = channel_code ? `(${channel_code})` : ''
if (this.format) {
//
let str = "";
str = this.format;
for (let key in item) {
str = str.replace(new RegExp(`{${key}}`,"g"),item[key]);
}
return str;
} else {
return this.collection.indexOf('app-list') > 0 ?
`${text}(${value})` :
(
text ?
text :
`未命名${channel_code}`
)
}
},
//
getLoadData(){
return this.mixinDatacomResData;
},
// key
getCurrentCacheKey(){
return this.collection;
},
//
getCache(name=this.getCurrentCacheKey()){
let cacheData = uni.getStorageSync(this.cacheKey) || {};
return cacheData[name];
},
//
setCache(value, name=this.getCurrentCacheKey()){
let cacheData = uni.getStorageSync(this.cacheKey) || {};
cacheData[name] = value;
uni.setStorageSync(this.cacheKey, cacheData);
},
//
removeCache(name=this.getCurrentCacheKey()){
let cacheData = uni.getStorageSync(this.cacheKey) || {};
delete cacheData[name];
uni.setStorageSync(this.cacheKey, cacheData);
},
}
}
</script>
<style lang="scss">
$uni-base-color: #6a6a6a !default;
$uni-main-color: #333 !default;
$uni-secondary-color: #909399 !default;
$uni-border-3: #e5e5e5;
/* #ifndef APP-NVUE */
@media screen and (max-width: 500px) {
.hide-on-phone {
display: none;
}
}
/* #endif */
.uni-stat__select {
display: flex;
align-items: center;
// padding: 15px;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
width: 100%;
flex: 1;
box-sizing: border-box;
}
.uni-stat-box {
width: 100%;
flex: 1;
}
.uni-stat__actived {
width: 100%;
flex: 1;
// outline: 1px solid #2979ff;
}
.uni-label-text {
font-size: 14px;
font-weight: bold;
color: $uni-base-color;
margin: auto 0;
margin-right: 5px;
}
.uni-select {
font-size: 14px;
border: 1px solid $uni-border-3;
box-sizing: border-box;
border-radius: 4px;
padding: 0 5px;
padding-left: 10px;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
user-select: none;
/* #endif */
flex-direction: row;
align-items: center;
border-bottom: solid 1px $uni-border-3;
width: 100%;
flex: 1;
height: 35px;
&--disabled {
background-color: #f5f7fa;
cursor: not-allowed;
}
}
.uni-select__label {
font-size: 16px;
// line-height: 22px;
height: 35px;
padding-right: 10px;
color: $uni-secondary-color;
}
.uni-select__input-box {
height: 35px;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
}
.uni-select__input {
flex: 1;
font-size: 14px;
height: 22px;
line-height: 22px;
}
.uni-select__input-plac {
font-size: 14px;
color: $uni-secondary-color;
}
.uni-select__selector {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
position: absolute;
top: calc(100% + 12px);
left: 0;
width: 100%;
background-color: #FFFFFF;
border: 1px solid #EBEEF5;
border-radius: 6px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 3;
padding: 4px 0;
}
.uni-select__selector-scroll {
/* #ifndef APP-NVUE */
max-height: 200px;
box-sizing: border-box;
/* #endif */
}
/* #ifdef H5 */
@media (min-width: 768px) {
.uni-select__selector-scroll {
max-height: 600px;
}
}
/* #endif */
.uni-select__selector-empty,
.uni-select__selector-item {
/* #ifndef APP-NVUE */
display: flex;
cursor: pointer;
/* #endif */
line-height: 35px;
font-size: 14px;
text-align: center;
/* border-bottom: solid 1px $uni-border-3; */
padding: 0px 10px;
}
.uni-select__selector-item:hover {
background-color: #f9f9f9;
}
.uni-select__selector-empty:last-child,
.uni-select__selector-item:last-child {
/* #ifndef APP-NVUE */
border-bottom: none;
/* #endif */
}
.uni-select__selector__disabled {
opacity: 0.4;
cursor: default;
}
/* picker 弹出层通用的指示小三角 */
.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;
}
.uni-select__input-text {
// width: 280px;
width: 100%;
color: $uni-main-color;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
}
.uni-select__input-placeholder {
color: $uni-base-color;
font-size: 12px;
}
.uni-select--mask {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 2;
}
</style>

@ -0,0 +1,85 @@
{
"id": "uni-data-select",
"displayName": "uni-data-select 下拉框选择器",
"version": "1.0.6",
"description": "通过数据驱动的下拉框选择器",
"keywords": [
"uni-ui",
"select",
"uni-data-select",
"下拉框",
"下拉选"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "^3.1.1"
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-load-more"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "u",
"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",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

@ -0,0 +1,8 @@
## DataSelect 下拉框选择器
> **组件名uni-data-select**
> 代码块: `uDataSelect`
当选项过多时,使用下拉菜单展示并选择内容
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-select)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

@ -0,0 +1,140 @@
## 2.2.242023-06-02
- 修复 部分情况修改时间开始、结束时间显示异常的Bug [详情](https://ask.dcloud.net.cn/question/171146)
- 优化 当前月可以选择上月、下月的日期
## 2.2.232023-05-02
- 修复 部分情况修改时间,开始时间未更新 [详情](https://github.com/dcloudio/uni-ui/issues/737)
- 修复 部分平台及设备第一次点击无法显示弹框
- 修复 ios 日期格式未补零显示及使用异常 [详情](https://ask.dcloud.net.cn/question/162979)
## 2.2.222023-03-30
- 修复 日历 picker 修改年月后自动选中当月1日 [详情](https://ask.dcloud.net.cn/question/165937)
- 修复 小程序端 低版本 ios NaN [详情](https://ask.dcloud.net.cn/question/162979)
## 2.2.212023-02-20
- 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362)
## 2.2.202023-02-17
- 优化 值为空依然选中当天问题
- 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间
- 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间
- 优化 字节小程序日期时间范围选择,底部日期换行问题
## 2.2.192023-02-09
- 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686)
## 2.2.182023-02-08
- 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684)
- 优化 PC端输入日期格式错误时返回当前日期时间
- 优化 PC端输入日期时间超出 start、end 限制的Bug
- 优化 移动端日期时间范围用法时间展示不完整问题
## 2.2.172023-02-04
- 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679)
- 修复 vue3 time-picker 无法显示绑定时分秒的Bug
## 2.2.162023-02-02
- 修复 字节小程序报错的Bug
## 2.2.152023-02-02
- 修复 某些情况切换月份错误的Bug
## 2.2.142023-01-30
- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/162033)
## 2.2.132023-01-10
- 修复 多次加载组件造成内存占用的Bug
## 2.2.122022-12-01
- 修复 vue3 下 i18n 国际化初始值不正确的Bug
## 2.2.112022-09-19
- 修复 支付宝小程序样式错乱的Bug [详情](https://github.com/dcloudio/uni-app/issues/3861)
## 2.2.102022-09-19
- 修复 反向选择日期范围日期显示异常的Bug [详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false)
## 2.2.92022-09-16
- 可以使用 uni-scss 控制主题色
## 2.2.82022-09-08
- 修复 close事件无效的Bug
## 2.2.72022-09-05
- 修复 移动端 maskClick 无效的Bug [详情](https://ask.dcloud.net.cn/question/140824)
## 2.2.62022-06-30
- 优化 组件样式调整了组件图标大小、高度、颜色等与uni-ui风格保持一致
## 2.2.52022-06-24
- 修复 日历顶部年月及底部确认未国际化的Bug
## 2.2.42022-03-31
- 修复 Vue3 下动态赋值,单选类型未响应的Bug
## 2.2.32022-03-28
- 修复 Vue3 下动态赋值未响应的Bug
## 2.2.22021-12-10
- 修复 clear-icon 属性在小程序平台不生效的Bug
## 2.2.12021-12-10
- 修复 日期范围选在小程序平台必须多点击一次才能取消选中状态的Bug
## 2.2.02021-11-19
- 优化 组件UI并提供设计资源 [详情](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.52021-11-09
- 新增 提供组件设计资源,组件样式调整
## 2.1.42021-09-10
- 修复 hide-second 在移动端的Bug
- 修复 单选赋默认值时赋值日期未高亮的Bug
- 修复 赋默认值时移动端未正确显示时间的Bug
## 2.1.32021-09-09
- 新增 hide-second 属性,支持只使用时分,隐藏秒
## 2.1.22021-09-03
- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次
- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法
- 优化 调整字号大小,美化日历界面
- 修复 因国际化导致的 placeholder 失效的Bug
## 2.1.12021-08-24
- 新增 支持国际化
- 优化 范围选择器在 pc 端过宽的问题
## 2.1.02021-08-09
- 新增 适配 vue3
## 2.0.192021-08-09
- 新增 支持作为 uni-forms 子组件相关功能
- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的Bug
## 2.0.182021-08-05
- 修复 type 属性动态赋值无效的Bug
- 修复 ‘确认’按钮被 tabbar 遮盖 bug
- 修复 组件未赋值时范围选左、右日历相同的Bug
## 2.0.172021-08-04
- 修复 范围选未正确显示当前值的Bug
- 修复 h5 平台(移动端)报错 'cale' of undefined 的Bug
## 2.0.162021-07-21
- 新增 return-type 属性支持返回 date 日期对象
## 2.0.152021-07-14
- 修复 单选日期类型初始赋值后不在当前日历的Bug
- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效)
- 优化 移动端移除显示框的清空按钮,无实际用途
## 2.0.142021-07-14
- 修复 组件赋值为空界面未更新的Bug
- 修复 start 和 end 不能动态赋值的Bug
- 修复 范围选类型用户选择后再次选择右侧日历结束日期显示不正确的Bug
## 2.0.132021-07-08
- 修复 范围选择不能动态赋值的Bug
## 2.0.122021-07-08
- 修复 范围选择的初始时间在一个月内时造成无法选择的bug
## 2.0.112021-07-08
- 优化 弹出层在超出视窗边缘定位不准确的问题
## 2.0.102021-07-08
- 修复 范围起始点样式的背景色与今日样式的字体前景色融合导致日期字体看不清的Bug
- 优化 弹出层在超出视窗边缘被遮盖的问题
## 2.0.92021-07-07
- 新增 maskClick 事件
- 修复 特殊情况日历 rpx 布局错误的Bugrpx -> px
- 修复 范围选择时清空返回值不合理的bug['', ''] -> []
## 2.0.82021-07-07
- 新增 日期时间显示框支持插槽
## 2.0.72021-07-01
- 优化 添加 uni-icons 依赖
## 2.0.62021-05-22
- 修复 图标在小程序上不显示的Bug
- 优化 重命名引用组件,避免潜在组件命名冲突
## 2.0.52021-05-20
- 优化 代码目录扁平化
## 2.0.42021-05-12
- 新增 组件示例地址
## 2.0.32021-05-10
- 修复 ios 下不识别 '-' 日期格式的Bug
- 优化 pc 下弹出层添加边框和阴影
## 2.0.22021-05-08
- 修复 在 admin 中获取弹出层定位错误的bug
## 2.0.12021-05-08
- 修复 type 属性向下兼容,默认值从 date 变更为 datetime
## 2.0.02021-04-30
- 支持日历形式的日期+时间的范围选择
> 注意此版本不向后兼容不再支持单独时间选择type=time及相关的 hide-second 属性(时间选可使用内置组件 picker
## 1.0.62021-03-18
- 新增 hide-second 属性,时间支持仅选择时、分
- 修复 选择跟显示的日期不一样的Bug
- 修复 chang事件触发2次的Bug
- 修复 分、秒 end 范围错误的Bug
- 优化 更好的 nvue 适配

@ -0,0 +1,177 @@
<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--today': weeks.isToday}"></view>
</view>
</template>
<script>
export default {
props: {
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
checkHover: {
type: Boolean,
default: false
}
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
},
handleMousemove(weeks) {
this.$emit('handleMouse', weeks)
}
}
}
</script>
<style lang="scss" >
$uni-primary: #007aff !default;
.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: darken($color: $uni-primary, $amount: 40%);
}
.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 {
cursor: default;
}
.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
color: #D1D1D1;
}
.uni-calendar-item--today {
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: $uni-primary;
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: $uni-primary;
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>

@ -0,0 +1,929 @@
<template>
<view class="uni-calendar" @mouseleave="leaveCale">
<view v-if="!insert && show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
@click="maskClick"></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 class="uni-calendar__header-btn-box" @click.stop="changeMonth('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||'') + yearText + ( nowDate.month||'') + monthText}}</text>
</picker>
<view class="uni-calendar__header-btn-box" @click.stop="changeMonth('next')">
<view class="uni-calendar__header-btn uni-calendar--right"></view>
</view>
<view v-if="!insert" class="dialog-close" @click="close">
<view class="dialog-close-plus" data-id="close"></view>
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
</view>
</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" :checkHover="range" @change="choiceDate"
@handleMouse="handleMouse">
</calendar-item>
</view>
</view>
</view>
<view v-if="!insert && !range && hasTime" 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="timepickerStartTime" :end="timepickerEndTime" v-model="time"
:disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
</time-picker>
</view>
<view v-if="!insert && range && hasTime" 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="timepickerStartTime" v-model="timeRange.startTime" :border="false"
:hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style">
</time-picker>
</view>
<view style="line-height: 50px;">
<uni-icons type="arrowthinright" color="#999"></uni-icons>
</view>
<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="timepickerEndTime" 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-datetime-picker--btn" @click="confirm">{{confirmText}}</view>
</view>
</view>
</view>
</template>
<script>
import { Calendar, getDate, getTime } from './util.js';
import calendarItem from './calendar-item.vue'
import timePicker from './time-picker.vue'
import { initVueI18n } from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
const { t } = initVueI18n(i18nMessages)
/**
* Calendar 日历
* @description 日历组件可以查看日期选择任意范围内的日期打点操作常用场景如酒店日期预订火车机票选择购买日期上下班打卡等
* @tutorial https://ext.dcloud.net.cn/plugin?id=56
* @property {String} date 自定义当前时间默认为今天
* @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 是否选择月份为背景
* @property {[String} defaultValue 选择器打开时默认显示的时间
* @event {Function} change 日期改变`insert :ture` 时生效
* @event {Function} confirm 确认选择`insert :false` 时生效
* @event {Function} monthSwitch 切换月份时触发
* @example <uni-calendar :insert="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 []
}
},
startDate: {
type: String,
default: ''
},
endDate: {
type: String,
default: ''
},
startPlaceholder: {
type: String,
default: ''
},
endPlaceholder: {
type: String,
default: ''
},
range: {
type: Boolean,
default: false
},
hasTime: {
type: Boolean,
default: false
},
insert: {
type: Boolean,
default: true
},
showMonth: {
type: Boolean,
default: true
},
clearDate: {
type: Boolean,
default: true
},
checkHover: {
type: Boolean,
default: true
},
hideSecond: {
type: [Boolean],
default: false
},
pleStatus: {
type: Object,
default () {
return {
before: '',
after: '',
data: [],
fulldate: ''
}
}
},
defaultValue: {
type: [String, Object, Array],
default: ''
}
},
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) {
if (!this.range) {
this.tempSingleDate = newVal
setTimeout(() => {
this.init(newVal)
}, 100)
}
}
},
defTime: {
immediate: true,
handler(newVal) {
if (!this.range) {
this.time = newVal
} else {
this.timeRange.startTime = newVal.start
this.timeRange.endTime = newVal.end
}
}
},
startDate(val) {
// watch created
if(!this.cale){
return
}
this.cale.setStartDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
endDate(val) {
// watch created
if(!this.cale){
return
}
this.cale.setEndDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
selected(newVal) {
// watch created
if(!this.cale){
return
}
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.weeks = this.cale.weeks
},
pleStatus: {
immediate: true,
handler(newVal) {
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 {
// watch created
if(!this.cale){
return
}
this.cale.setDefaultMultiple(before, after)
if (which === 'left' && before) {
this.setDate(before)
this.weeks = this.cale.weeks
} else if(after) {
this.setDate(after)
this.weeks = this.cale.weeks
}
this.cale.lastHover = true
}
}, 16)
}
}
},
computed: {
timepickerStartTime() {
const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
return activeDate === this.startDate ? this.selectableTimes.start : ''
},
timepickerEndTime() {
const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
return activeDate === this.endDate ? this.selectableTimes.end : ''
},
/**
* 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")
},
yearText() {
return t("uni-datetime-picker.year")
},
monthText() {
return t("uni-datetime-picker.month")
},
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")
},
confirmText() {
return t("uni-calender.confirm")
},
},
created() {
//
this.cale = new Calendar({
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
range: this.range,
})
//
this.init(this.date)
},
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
},
//
maskClick() {
this.close()
this.$emit('maskClose')
},
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(new Date())
},
bindDateChange(e) {
const value = e.detail.value + '-1'
this.setDate(value)
},
/**
* 初始化日期显示
* @param {Object} date
*/
init(date) {
// watch created
if(!this.cale){
return
}
this.cale.setDate(date || new Date())
this.weeks = this.cale.weeks
this.nowDate = this.cale.getInfo(date)
this.calendar = {...this.nowDate}
if(!date){
// date
this.calendar.fullDate = ''
if(this.defaultValue && !this.range){
//
const defaultDate = new Date(this.defaultValue)
const fullDate = getDate(defaultDate)
const year = defaultDate.getFullYear()
const month = defaultDate.getMonth()+1
const date = defaultDate.getDate()
const day = defaultDate.getDay()
this.calendar = {
fullDate,
year,
month,
date,
day
},
this.tempSingleDate = fullDate
this.time = getTime(defaultDate, this.hideSecond)
}
}
},
/**
* 打开日历弹窗
*/
open() {
//
if (this.clearDate && !this.insert) {
this.cale.cleanMultipleStatus()
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) {
if(!this.range){
if(!this.calendar.fullDate){
this.calendar = this.cale.getInfo(new Date())
this.tempSingleDate = this.calendar.fullDate
}
if(this.hasTime && !this.time) {
this.time = getTime(new Date(), this.hideSecond)
}
}
let {
year,
month,
date,
fullDate,
extraInfo
} = this.calendar
this.$emit(name, {
range: this.cale.multipleStatus,
year,
month,
date,
time: this.time,
timeRange: this.timeRange,
fulldate: fullDate,
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
const beforeDate = new Date(this.cale.multipleStatus.before).getTime()
const afterDate = new Date(this.cale.multipleStatus.after).getTime()
//
if (beforeDate > afterDate && afterDate) {
this.tempRange.before = this.cale.multipleStatus.after
this.tempRange.after = this.cale.multipleStatus.before
} else {
this.tempRange.before = this.cale.multipleStatus.before
this.tempRange.after = this.cale.multipleStatus.after
}
this.change()
},
changeMonth(type) {
let newDate
if(type === 'pre') {
newDate = this.cale.getPreMonthObj(this.nowDate.fullDate).fullDate
} else if(type === 'next') {
newDate = this.cale.getNextMonthObj(this.nowDate.fullDate).fullDate
}
this.setDate(newDate)
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" >
$uni-primary: #007aff !default;
.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: $uni-primary;
/* #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;
/* #ifdef MP-TOUTIAO */
font-size: 16px;
/* #endif */
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: $uni-primary;
color: #fff;
font-size: 16px;
letter-spacing: 2px;
}
/* #ifndef APP-NVUE */
.uni-datetime-picker--btn:active {
opacity: 0.7;
}
/* #endif */
</style>

@ -0,0 +1,22 @@
{
"uni-datetime-picker.selectDate": "select date",
"uni-datetime-picker.selectTime": "select time",
"uni-datetime-picker.selectDateTime": "select date and time",
"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-datetime-picker.year": "-",
"uni-datetime-picker.month": "",
"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",
"uni-calender.confirm": "confirm"
}

@ -0,0 +1,8 @@
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
}

@ -0,0 +1,22 @@
{
"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-datetime-picker.year": "年",
"uni-datetime-picker.month": "月",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六",
"uni-calender.confirm": "确认"
}

@ -0,0 +1,22 @@
{
"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-datetime-picker.year": "年",
"uni-datetime-picker.month": "月",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六",
"uni-calender.confirm": "確認"
}

@ -0,0 +1,934 @@
<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>
</view>
</template>
<script>
import { initVueI18n } from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
const { t } = initVueI18n(i18nMessages)
import { fixIosDateFormat } from './util'
/**
* 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',
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: {
// #ifndef VUE3
value: {
handler(newVal) {
if (newVal) {
this.parseValue(fixIosDateFormat(newVal))
this.initTime(false)
} else {
this.time = ''
this.parseValue(Date.now())
}
},
immediate: true
},
// #endif
// #ifdef VUE3
modelValue: {
handler(newVal) {
if (newVal) {
this.parseValue(fixIosDateFormat(newVal))
this.initTime(false)
} else {
this.time = ''
this.parseValue(Date.now())
}
},
immediate: true
},
// #endif
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(fixIosDateFormat(newVal), 'start')
},
immediate: true
},
end: {
handler(newVal) {
this.parseDatetimeRange(fixIosDateFormat(newVal), 'end')
},
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)) {
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
}
},
/**
* 解析可选择时间范围 startend年月日字符串时间戳
* @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)) {
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();
},
// iOSsafari
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 = fixIosDateFormat(this.time)
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 lang="scss">
$uni-primary: #007aff !default;
.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: $uni-primary;
}
.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;
line-height: 50px
}
.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>

@ -0,0 +1,453 @@
class Calendar {
constructor({
selected,
startDate,
endDate,
range,
} = {}) {
// 当前日期
this.date = this.getDateObj(new Date()) // 当前初入日期
// 打点信息
this.selected = selected || [];
// 起始时间
this.startDate = startDate
// 终止时间
this.endDate = endDate
// 是否范围选择
this.range = range
// 多选状态
this.cleanMultipleStatus()
// 每周日期
this.weeks = {}
this.lastHover = false
}
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
const selectDate = this.getDateObj(date)
this.getWeeks(selectDate.fullDate)
}
/**
* 清理多选状态
*/
cleanMultipleStatus() {
this.multipleStatus = {
before: '',
after: '',
data: []
}
}
setStartDate(startDate) {
this.startDate = startDate
}
setEndDate(endDate) {
this.endDate = endDate
}
getPreMonthObj(date){
date = fixIosDateFormat(date)
date = new Date(date)
const oldMonth = date.getMonth()
date.setMonth(oldMonth - 1)
const newMonth = date.getMonth()
if(oldMonth !== 0 && newMonth - oldMonth === 0){
date.setMonth(newMonth - 1)
}
return this.getDateObj(date)
}
getNextMonthObj(date){
date = fixIosDateFormat(date)
date = new Date(date)
const oldMonth = date.getMonth()
date.setMonth(oldMonth + 1)
const newMonth = date.getMonth()
if(newMonth - oldMonth > 1){
date.setMonth(newMonth - 1)
}
return this.getDateObj(date)
}
/**
* 获取指定格式Date对象
*/
getDateObj(date) {
date = fixIosDateFormat(date)
date = new Date(date)
return {
fullDate: getDate(date),
year: date.getFullYear(),
month: addZero(date.getMonth() + 1),
date: addZero(date.getDate()),
day: date.getDay()
}
}
/**
* 获取上一个月日期集合
*/
getPreMonthDays(amount, dateObj) {
const result = []
for (let i = amount - 1; i >= 0; i--) {
const month = dateObj.month > 1 ? dateObj.month -1 : 12
const year = month === 12 ? dateObj.year - 1 : dateObj.year
const date = new Date(year,month,-i).getDate()
const fullDate = `${year}-${addZero(month)}-${addZero(date)}`
let multiples = this.multipleStatus.data
let multiplesStatus = -1
if (this.range && multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, fullDate)
})
}
const checked = multiplesStatus !== -1
// 获取打点信息
const extraInfo = this.selected && this.selected.find((item) => {
if (this.dateEqual(fullDate, item.date)) {
return item
}
})
result.push({
fullDate,
year,
month,
date,
multiple: this.range ? checked : false,
beforeMultiple: this.isLogicBefore(fullDate, this.multipleStatus.before, this.multipleStatus.after),
afterMultiple: this.isLogicAfter(fullDate, this.multipleStatus.before, this.multipleStatus.after),
disable: (this.startDate && !dateCompare(this.startDate, fullDate)) || (this.endDate && !dateCompare(fullDate,this.endDate)),
isToday: fullDate === this.date.fullDate,
userChecked: false,
extraInfo
})
}
return result
}
/**
* 获取本月日期集合
*/
getCurrentMonthDays(amount, dateObj) {
const result = []
const fullDate = this.date.fullDate
for (let i = 1; i <= amount; i++) {
const currentDate = `${dateObj.year}-${dateObj.month}-${addZero(i)}`
const isToday = fullDate === currentDate
// 获取打点信息
const extraInfo = this.selected && this.selected.find((item) => {
if (this.dateEqual(currentDate, item.date)) {
return item
}
})
// 日期禁用
let disableBefore = true
let disableAfter = true
if (this.startDate) {
disableBefore = dateCompare(this.startDate, currentDate)
}
if (this.endDate) {
disableAfter = dateCompare(currentDate, this.endDate)
}
let multiples = this.multipleStatus.data
let multiplesStatus = -1
if (this.range && multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, currentDate)
})
}
const checked = multiplesStatus !== -1
result.push({
fullDate: currentDate,
year: dateObj.year,
month: dateObj.month,
date: i,
multiple: this.range ? checked : false,
beforeMultiple: this.isLogicBefore(currentDate, this.multipleStatus.before, this.multipleStatus.after),
afterMultiple: this.isLogicAfter(currentDate, this.multipleStatus.before, this.multipleStatus.after),
disable: (this.startDate && !dateCompare(this.startDate, currentDate)) || (this.endDate && !dateCompare(currentDate,this.endDate)),
isToday,
userChecked: false,
extraInfo
})
}
return result
}
/**
* 获取下一个月日期集合
*/
_getNextMonthDays(amount, dateObj) {
const result = []
const month = dateObj.month + 1
for (let i = 1; i <= amount; i++) {
const month = dateObj.month === 12 ? 1 : dateObj.month*1 + 1
const year = month === 1 ? dateObj.year + 1 : dateObj.year
const fullDate = `${year}-${addZero(month)}-${addZero(i)}`
let multiples = this.multipleStatus.data
let multiplesStatus = -1
if (this.range && multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, fullDate)
})
}
const checked = multiplesStatus !== -1
// 获取打点信息
const extraInfo = this.selected && this.selected.find((item) => {
if (this.dateEqual(fullDate, item.date)) {
return item
}
})
result.push({
fullDate,
year,
date: i,
month,
multiple: this.range ? checked : false,
beforeMultiple: this.isLogicBefore(fullDate, this.multipleStatus.before, this.multipleStatus.after),
afterMultiple: this.isLogicAfter(fullDate, this.multipleStatus.before, this.multipleStatus.after),
disable: (this.startDate && !dateCompare(this.startDate, fullDate)) || (this.endDate && !dateCompare(fullDate,this.endDate)),
isToday: fullDate === this.date.fullDate,
userChecked: false,
extraInfo
})
}
return result
}
/**
* 获取当前日期详情
* @param {Object} date
*/
getInfo(date) {
if (!date) {
date = new Date()
}
return this.calendar.find(item => item.fullDate === this.getDateObj(date).fullDate)
}
/**
* 比较时间是否相等
*/
dateEqual(before, after) {
before = new Date(fixIosDateFormat(before))
after = new Date(fixIosDateFormat(after))
return before.valueOf() === after.valueOf()
}
/**
* 比较真实起始日期
*/
isLogicBefore(currentDate, before, after) {
let logicBefore = before
if (before && after) {
logicBefore = dateCompare(before, after) ? before : after
}
return this.dateEqual(logicBefore, currentDate)
}
isLogicAfter(currentDate, before, after) {
let logicAfter = after
if (before && after) {
logicAfter = dateCompare(before, after) ? after : before
}
return this.dateEqual(logicAfter, currentDate)
}
/**
* 获取日期范围内所有日期
* @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.getDateObj(new Date(parseInt(k))).fullDate)
}
return arr
}
/**
* 获取多选状态
*/
setMultiple(fullDate) {
if (!this.range) return
let {
before,
after
} = this.multipleStatus
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 (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.getWeeks(fullDate)
}
/**
* 鼠标 hover 更新多选状态
*/
setHoverMultiple(fullDate) {
if (!this.range || this.lastHover) return
const { before } = this.multipleStatus
if (!before) {
this.multipleStatus.before = fullDate
} else {
this.multipleStatus.after = fullDate
if (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.getWeeks(fullDate)
}
/**
* 更新默认值多选状态
*/
setDefaultMultiple(before, after) {
this.multipleStatus.before = before
this.multipleStatus.after = after
if (before && after) {
if (dateCompare(before, after)) {
this.multipleStatus.data = this.geDateAll(before, after);
this.getWeeks(after)
} else {
this.multipleStatus.data = this.geDateAll(after, before);
this.getWeeks(before)
}
}
}
/**
* 获取每周数据
* @param {Object} dateData
*/
getWeeks(dateData) {
const {
year,
month,
} = this.getDateObj(dateData)
const preMonthDayAmount = new Date(year, month - 1, 1).getDay()
const preMonthDays = this.getPreMonthDays(preMonthDayAmount, this.getDateObj(dateData))
const currentMonthDayAmount = new Date(year, month, 0).getDate()
const currentMonthDays = this.getCurrentMonthDays(currentMonthDayAmount, this.getDateObj(dateData))
const nextMonthDayAmount = 42 - preMonthDayAmount - currentMonthDayAmount
const nextMonthDays = this._getNextMonthDays(nextMonthDayAmount, this.getDateObj(dateData))
const calendarDays = [...preMonthDays, ...currentMonthDays, ...nextMonthDays]
const weeks = new Array(6)
for (let i = 0; i < calendarDays.length; i++) {
const index = Math.floor(i / 7)
if(!weeks[index]){
weeks[index] = new Array(7)
}
weeks[index][i % 7] = calendarDays[i]
}
this.calendar = calendarDays
this.weeks = weeks
}
}
function getDateTime(date, hideSecond){
return `${getDate(date)} ${getTime(date, hideSecond)}`
}
function getDate(date) {
date = fixIosDateFormat(date)
date = new Date(date)
const year = date.getFullYear()
const month = date.getMonth()+1
const day = date.getDate()
return `${year}-${addZero(month)}-${addZero(day)}`
}
function getTime(date, hideSecond){
date = fixIosDateFormat(date)
date = new Date(date)
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return hideSecond ? `${addZero(hour)}:${addZero(minute)}` : `${addZero(hour)}:${addZero(minute)}:${addZero(second)}`
}
function addZero(num) {
if(num < 10){
num = `0${num}`
}
return num
}
function getDefaultSecond(hideSecond) {
return hideSecond ? '00:00' : '00:00:00'
}
function dateCompare(startDate, endDate) {
startDate = new Date(fixIosDateFormat(startDate))
endDate = new Date(fixIosDateFormat(endDate))
return startDate <= endDate
}
function checkDate(date){
const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g
return date.match(dateReg)
}
const dateTimeReg = /^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])( [0-5]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9])?$/
function fixIosDateFormat(value) {
if (typeof value === 'string' && dateTimeReg.test(value)) {
value = value.replace(/-/g, '/')
}
return value
}
export {Calendar, getDateTime, getDate, getTime, addZero, getDefaultSecond, dateCompare, checkDate, fixIosDateFormat}

@ -0,0 +1,87 @@
{
"id": "uni-datetime-picker",
"displayName": "uni-datetime-picker 日期选择器",
"version": "2.2.24",
"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": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"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"
}
}
}
}
}

@ -0,0 +1,21 @@
> `重要通知:组件升级更新 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,19 @@
## 1.3.32022-01-20
- 新增 showText属性 ,是否显示文本
## 1.3.22022-01-19
- 修复 nvue 平台下不显示文本的bug
## 1.3.12022-01-19
- 修复 微信小程序平台样式选择器报警告的问题
## 1.3.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-load-more](https://uniapp.dcloud.io/component/uniui/uni-load-more)
## 1.2.12021-08-24
- 新增 支持国际化
## 1.2.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.82021-05-12
- 新增 组件示例地址
## 1.1.72021-03-30
- 修复 uni-load-more 在首页使用时h5 平台报 'uni is not defined' 的 bug
## 1.1.62021-02-05
- 调整为uni_modules目录规范

@ -0,0 +1,5 @@
{
"uni-load-more.contentdown": "Pull up to show more",
"uni-load-more.contentrefresh": "loading...",
"uni-load-more.contentnomore": "No more data"
}

@ -0,0 +1,8 @@
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
}

@ -0,0 +1,5 @@
{
"uni-load-more.contentdown": "上拉显示更多",
"uni-load-more.contentrefresh": "正在加载...",
"uni-load-more.contentnomore": "没有更多数据了"
}

@ -0,0 +1,5 @@
{
"uni-load-more.contentdown": "上拉顯示更多",
"uni-load-more.contentrefresh": "正在加載...",
"uni-load-more.contentnomore": "沒有更多數據了"
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,86 @@
{
"id": "uni-load-more",
"displayName": "uni-load-more 加载更多",
"version": "1.3.3",
"description": "LoadMore 组件,常用在列表里面,做滚动加载使用。",
"keywords": [
"uni-ui",
"uniui",
"加载更多",
"load-more"
],
"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"],
"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"
}
}
}
}
}

@ -0,0 +1,14 @@
### LoadMore 加载更多
> **组件名uni-load-more**
> 代码块: `uLoadMore`
用于列表中,做滚动加载使用,展示 loading 的各种状态。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-load-more)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

@ -0,0 +1,27 @@
## 1.2.32023-03-28
- 修复 在vue3模式下可能会出现错误的问题
## 1.2.22022-11-29
- 优化 主题样式
## 1.2.12022-06-06
- 修复 微信小程序存在无使用组件的问题
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-table](https://uniapp.dcloud.io/component/uniui/uni-table)
## 1.1.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.72021-07-08
- 新增 uni-th 支持 date 日期筛选范围
## 1.0.62021-07-05
- 新增 uni-th 支持 range 筛选范围
## 1.0.52021-06-28
- 新增 uni-th 筛选功能
## 1.0.42021-05-12
- 新增 示例地址
- 修复 示例项目缺少组件的Bug
## 1.0.32021-04-16
- 新增 sortable 属性,是否开启单列排序
- 优化 表格多选逻辑
## 1.0.22021-03-22
- uni-tr 添加 disabled 属性,用于 type=selection 时,设置某行是否可由全选按钮控制
## 1.0.12021-02-05
- 调整为uni_modules目录规范

@ -0,0 +1,455 @@
<template>
<view class="uni-table-scroll" :class="{ 'table--border': border, 'border-none': !noData }">
<!-- #ifdef H5 -->
<table class="uni-table" border="0" cellpadding="0" cellspacing="0" :class="{ 'table--stripe': stripe }" :style="{ 'min-width': minWidth + 'px' }">
<slot></slot>
<tr v-if="noData" class="uni-table-loading">
<td class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</td>
</tr>
<view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view>
</table>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="uni-table" :style="{ 'min-width': minWidth + 'px' }" :class="{ 'table--stripe': stripe }">
<slot></slot>
<view v-if="noData" class="uni-table-loading">
<view class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</view>
</view>
<view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view>
</view>
<!-- #endif -->
</view>
</template>
<script>
/**
* Table 表格
* @description 用于展示多条结构类似的数据
* @tutorial https://ext.dcloud.net.cn/plugin?id=3270
* @property {Boolean} border 是否带有纵向边框
* @property {Boolean} stripe 是否显示斑马线
* @property {Boolean} type 是否开启多选
* @property {String} emptyText 空数据时显示的文本内容
* @property {Boolean} loading 显示加载中
* @event {Function} selection-change 开启多选时当选择项发生变化时会触发该事件
*/
export default {
name: 'uniTable',
options: {
virtualHost: true
},
emits:['selection-change'],
props: {
data: {
type: Array,
default() {
return []
}
},
// 线
border: {
type: Boolean,
default: false
},
// 线
stripe: {
type: Boolean,
default: false
},
//
type: {
type: String,
default: ''
},
//
emptyText: {
type: String,
default: '没有更多数据'
},
loading: {
type: Boolean,
default: false
},
rowKey: {
type: String,
default: ''
}
},
data() {
return {
noData: true,
minWidth: 0,
multiTableHeads: []
}
},
watch: {
loading(val) {},
data(newVal) {
let theadChildren = this.theadChildren
let rowspan = 1
if (this.theadChildren) {
rowspan = this.theadChildren.rowspan
}
// this.trChildren.length - rowspan
this.noData = false
// this.noData = newVal.length === 0
}
},
created() {
// tr
this.trChildren = []
this.thChildren = []
this.theadChildren = null
this.backData = []
this.backIndexData = []
},
methods: {
isNodata() {
let theadChildren = this.theadChildren
let rowspan = 1
if (this.theadChildren) {
rowspan = this.theadChildren.rowspan
}
this.noData = this.trChildren.length - rowspan <= 0
},
/**
* 选中所有
*/
selectionAll() {
let startIndex = 1
let theadChildren = this.theadChildren
if (!this.theadChildren) {
theadChildren = this.trChildren[0]
} else {
startIndex = theadChildren.rowspan - 1
}
let isHaveData = this.data && this.data.length > 0
theadChildren.checked = true
theadChildren.indeterminate = false
this.trChildren.forEach((item, index) => {
if (!item.disabled) {
item.checked = true
if (isHaveData && item.keyValue) {
const row = this.data.find(v => v[this.rowKey] === item.keyValue)
if (!this.backData.find(v => v[this.rowKey] === row[this.rowKey])) {
this.backData.push(row)
}
}
if (index > (startIndex - 1) && this.backIndexData.indexOf(index - startIndex) === -1) {
this.backIndexData.push(index - startIndex)
}
}
})
// this.backData = JSON.parse(JSON.stringify(this.data))
this.$emit('selection-change', {
detail: {
value: this.backData,
index: this.backIndexData
}
})
},
/**
* 用于多选表格切换某一行的选中状态如果使用了第二个参数则是设置这一行选中与否selected true 则选中
*/
toggleRowSelection(row, selected) {
// if (!this.theadChildren) return
row = [].concat(row)
this.trChildren.forEach((item, index) => {
// if (item.keyValue) {
const select = row.findIndex(v => {
//
if (typeof v === 'number') {
return v === index - 1
} else {
return v[this.rowKey] === item.keyValue
}
})
let ischeck = item.checked
if (select !== -1) {
if (typeof selected === 'boolean') {
item.checked = selected
} else {
item.checked = !item.checked
}
if (ischeck !== item.checked) {
this.check(item.rowData||item, item.checked, item.rowData?item.keyValue:null, true)
}
}
// }
})
this.$emit('selection-change', {
detail: {
value: this.backData,
index:this.backIndexData
}
})
},
/**
* 用于多选表格清空用户的选择
*/
clearSelection() {
let theadChildren = this.theadChildren
if (!this.theadChildren) {
theadChildren = this.trChildren[0]
}
// if (!this.theadChildren) return
theadChildren.checked = false
theadChildren.indeterminate = false
this.trChildren.forEach(item => {
// if (item.keyValue) {
item.checked = false
// }
})
this.backData = []
this.backIndexData = []
this.$emit('selection-change', {
detail: {
value: [],
index: []
}
})
},
/**
* 用于多选表格切换所有行的选中状态
*/
toggleAllSelection() {
let list = []
let startIndex = 1
let theadChildren = this.theadChildren
if (!this.theadChildren) {
theadChildren = this.trChildren[0]
} else {
startIndex = theadChildren.rowspan - 1
}
this.trChildren.forEach((item, index) => {
if (!item.disabled) {
if (index > (startIndex - 1) ) {
list.push(index-startIndex)
}
}
})
this.toggleRowSelection(list)
},
/**
* 选中\取消选中
* @param {Object} child
* @param {Object} check
* @param {Object} rowValue
*/
check(child, check, keyValue, emit) {
let theadChildren = this.theadChildren
if (!this.theadChildren) {
theadChildren = this.trChildren[0]
}
let childDomIndex = this.trChildren.findIndex((item, index) => child === item)
if(childDomIndex < 0){
childDomIndex = this.data.findIndex(v=>v[this.rowKey] === keyValue) + 1
}
const dataLen = this.trChildren.filter(v => !v.disabled && v.keyValue).length
if (childDomIndex === 0) {
check ? this.selectionAll() : this.clearSelection()
return
}
if (check) {
if (keyValue) {
this.backData.push(child)
}
this.backIndexData.push(childDomIndex - 1)
} else {
const index = this.backData.findIndex(v => v[this.rowKey] === keyValue)
const idx = this.backIndexData.findIndex(item => item === childDomIndex - 1)
if (keyValue) {
this.backData.splice(index, 1)
}
this.backIndexData.splice(idx, 1)
}
const domCheckAll = this.trChildren.find((item, index) => index > 0 && !item.checked && !item.disabled)
if (!domCheckAll) {
theadChildren.indeterminate = false
theadChildren.checked = true
} else {
theadChildren.indeterminate = true
theadChildren.checked = false
}
if (this.backIndexData.length === 0) {
theadChildren.indeterminate = false
}
if (!emit) {
this.$emit('selection-change', {
detail: {
value: this.backData,
index: this.backIndexData
}
})
}
}
}
}
</script>
<style lang="scss">
$border-color: #ebeef5;
.uni-table-scroll {
width: 100%;
/* #ifndef APP-NVUE */
overflow-x: auto;
/* #endif */
}
.uni-table {
position: relative;
width: 100%;
border-radius: 5px;
// box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1);
background-color: #fff;
/* #ifndef APP-NVUE */
box-sizing: border-box;
display: table;
overflow-x: auto;
::v-deep .uni-table-tr:nth-child(n + 2) {
&:hover {
background-color: #f5f7fa;
}
}
::v-deep .uni-table-thead {
.uni-table-tr {
// background-color: #f5f7fa;
&:hover {
background-color:#fafafa;
}
}
}
/* #endif */
}
.table--border {
border: 1px $border-color solid;
border-right: none;
}
.border-none {
/* #ifndef APP-NVUE */
border-bottom: none;
/* #endif */
}
.table--stripe {
/* #ifndef APP-NVUE */
::v-deep .uni-table-tr:nth-child(2n + 3) {
background-color: #fafafa;
}
/* #endif */
}
/* 表格加载、无数据样式 */
.uni-table-loading {
position: relative;
/* #ifndef APP-NVUE */
display: table-row;
/* #endif */
height: 50px;
line-height: 50px;
overflow: hidden;
box-sizing: border-box;
}
.empty-border {
border-right: 1px $border-color solid;
}
.uni-table-text {
position: absolute;
right: 0;
left: 0;
text-align: center;
font-size: 14px;
color: #999;
}
.uni-table-mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(255, 255, 255, 0.8);
z-index: 99;
/* #ifndef APP-NVUE */
display: flex;
margin: auto;
transition: all 0.5s;
/* #endif */
justify-content: center;
align-items: center;
}
.uni-table--loader {
width: 30px;
height: 30px;
border: 2px solid #aaa;
// border-bottom-color: transparent;
border-radius: 50%;
/* #ifndef APP-NVUE */
animation: 2s uni-table--loader linear infinite;
/* #endif */
position: relative;
}
@keyframes uni-table--loader {
0% {
transform: rotate(360deg);
}
10% {
border-left-color: transparent;
}
20% {
border-bottom-color: transparent;
}
30% {
border-right-color: transparent;
}
40% {
border-top-color: transparent;
}
50% {
transform: rotate(0deg);
}
60% {
border-top-color: transparent;
}
70% {
border-left-color: transparent;
}
80% {
border-bottom-color: transparent;
}
90% {
border-right-color: transparent;
}
100% {
transform: rotate(-360deg);
}
}
</style>

@ -0,0 +1,29 @@
<template>
<!-- #ifdef H5 -->
<tbody>
<slot></slot>
</tbody>
<!-- #endif -->
<!-- #ifndef H5 -->
<view><slot></slot></view>
<!-- #endif -->
</template>
<script>
export default {
name: 'uniBody',
options: {
virtualHost: true
},
data() {
return {
}
},
created() {},
methods: {}
}
</script>
<style>
</style>

@ -0,0 +1,90 @@
<template>
<!-- #ifdef H5 -->
<td class="uni-table-td" :rowspan="rowspan" :colspan="colspan" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}">
<slot></slot>
</td>
<!-- #endif -->
<!-- #ifndef H5 -->
<!-- :class="{'table--border':border}" -->
<view class="uni-table-td" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}">
<slot></slot>
</view>
<!-- #endif -->
</template>
<script>
/**
* Td 单元格
* @description 表格中的标准单元格组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=3270
* @property {Number} align = [left|center|right] 单元格对齐方式
*/
export default {
name: 'uniTd',
options: {
virtualHost: true
},
props: {
width: {
type: [String, Number],
default: ''
},
align: {
type: String,
default: 'left'
},
rowspan: {
type: [Number,String],
default: 1
},
colspan: {
type: [Number,String],
default: 1
}
},
data() {
return {
border: false
};
},
created() {
this.root = this.getTable()
this.border = this.root.border
},
methods: {
/**
* 获取父元素实例
*/
getTable() {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== 'uniTable') {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
},
}
}
</script>
<style lang="scss">
$border-color:#EBEEF5;
.uni-table-td {
display: table-cell;
padding: 8px 10px;
font-size: 14px;
border-bottom: 1px $border-color solid;
font-weight: 400;
color: #606266;
line-height: 23px;
box-sizing: border-box;
}
.table--border {
border-right: 1px $border-color solid;
}
</style>

@ -0,0 +1,511 @@
<template>
<view class="uni-filter-dropdown">
<view class="dropdown-btn" @click="onDropdown">
<view class="icon-select" :class="{active: canReset}" v-if="isSelect || isRange"></view>
<view class="icon-search" :class="{active: canReset}" v-if="isSearch">
<view class="icon-search-0"></view>
<view class="icon-search-1"></view>
</view>
<view class="icon-calendar" :class="{active: canReset}" v-if="isDate">
<view class="icon-calendar-0"></view>
<view class="icon-calendar-1"></view>
</view>
</view>
<view class="uni-dropdown-cover" v-if="isOpened" @click="handleClose"></view>
<view class="dropdown-popup dropdown-popup-right" v-if="isOpened" @click.stop>
<!-- select-->
<view v-if="isSelect" class="list">
<label class="flex-r a-i-c list-item" v-for="(item,index) in dataList" :key="index"
@click="onItemClick($event, index)">
<check-box class="check" :checked="item.checked" />
<view class="checklist-content">
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
</view>
</label>
</view>
<view v-if="isSelect" class="flex-r opera-area">
<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleSelectReset">
{{resource.reset}}</view>
<view class="flex-f btn btn-submit" @click="handleSelectSubmit">{{resource.submit}}</view>
</view>
<!-- search -->
<view v-if="isSearch" class="search-area">
<input class="search-input" v-model="filterValue" />
</view>
<view v-if="isSearch" class="flex-r opera-area">
<view class="flex-f btn btn-submit" @click="handleSearchSubmit">{{resource.search}}</view>
<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleSearchReset">
{{resource.reset}}</view>
</view>
<!-- range -->
<view v-if="isRange">
<view class="input-label">{{resource.gt}}</view>
<input class="input" v-model="gtValue" />
<view class="input-label">{{resource.lt}}</view>
<input class="input" v-model="ltValue" />
</view>
<view v-if="isRange" class="flex-r opera-area">
<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleRangeReset">
{{resource.reset}}</view>
<view class="flex-f btn btn-submit" @click="handleRangeSubmit">{{resource.submit}}</view>
</view>
<!-- date -->
<view v-if="isDate">
<uni-datetime-picker ref="datetimepicker" :value="dateRange" type="datetimerange" return-type="timestamp" @change="datetimechange" @maskClick="timepickerclose">
<view></view>
</uni-datetime-picker>
</view>
</view>
</view>
</template>
<script>
import checkBox from '../uni-tr/table-checkbox.vue'
const resource = {
"reset": "重置",
"search": "搜索",
"submit": "确定",
"filter": "筛选",
"gt": "大于等于",
"lt": "小于等于",
"date": "日期范围"
}
const DropdownType = {
Select: "select",
Search: "search",
Range: "range",
Date: "date",
Timestamp: "timestamp"
}
export default {
name: 'FilterDropdown',
emits:['change'],
components: {
checkBox
},
options: {
virtualHost: true
},
props: {
filterType: {
type: String,
default: DropdownType.Select
},
filterData: {
type: Array,
default () {
return []
}
},
mode: {
type: String,
default: 'default'
},
map: {
type: Object,
default () {
return {
text: 'text',
value: 'value'
}
}
},
filterDefaultValue: {
type: [Array,String],
default () {
return ""
}
}
},
computed: {
canReset() {
if (this.isSearch) {
return this.filterValue.length > 0
}
if (this.isSelect) {
return this.checkedValues.length > 0
}
if (this.isRange) {
return (this.gtValue.length > 0 && this.ltValue.length > 0)
}
if (this.isDate) {
return this.dateSelect.length > 0
}
return false
},
isSelect() {
return this.filterType === DropdownType.Select
},
isSearch() {
return this.filterType === DropdownType.Search
},
isRange() {
return this.filterType === DropdownType.Range
},
isDate() {
return (this.filterType === DropdownType.Date || this.filterType === DropdownType.Timestamp)
}
},
watch: {
filterData(newVal) {
this._copyFilters()
},
indeterminate(newVal) {
this.isIndeterminate = newVal
}
},
data() {
return {
resource,
enabled: true,
isOpened: false,
dataList: [],
filterValue: this.filterDefaultValue,
checkedValues: [],
gtValue: '',
ltValue: '',
dateRange: [],
dateSelect: []
};
},
created() {
this._copyFilters()
},
methods: {
_copyFilters() {
let dl = JSON.parse(JSON.stringify(this.filterData))
for (let i = 0; i < dl.length; i++) {
if (dl[i].checked === undefined) {
dl[i].checked = false
}
}
this.dataList = dl
},
openPopup() {
this.isOpened = true
if (this.isDate) {
this.$nextTick(() => {
if (!this.dateRange.length) {
this.resetDate()
}
this.$refs.datetimepicker.show()
})
}
},
closePopup() {
this.isOpened = false
},
handleClose(e) {
this.closePopup()
},
resetDate() {
let date = new Date()
let dateText = date.toISOString().split('T')[0]
this.dateRange = [dateText + ' 0:00:00', dateText + ' 23:59:59']
},
onDropdown(e) {
this.openPopup()
},
onItemClick(e, index) {
let items = this.dataList
let listItem = items[index]
if (listItem.checked === undefined) {
items[index].checked = true
} else {
items[index].checked = !listItem.checked
}
let checkvalues = []
for (let i = 0; i < items.length; i++) {
const item = items[i]
if (item.checked) {
checkvalues.push(item.value)
}
}
this.checkedValues = checkvalues
},
datetimechange(e) {
this.closePopup()
this.dateRange = e
this.dateSelect = e
this.$emit('change', {
filterType: this.filterType,
filter: e
})
},
timepickerclose(e) {
this.closePopup()
},
handleSelectSubmit() {
this.closePopup()
this.$emit('change', {
filterType: this.filterType,
filter: this.checkedValues
})
},
handleSelectReset() {
if (!this.canReset) {
return;
}
var items = this.dataList
for (let i = 0; i < items.length; i++) {
let item = items[i]
this.$set(item, 'checked', false)
}
this.checkedValues = []
this.handleSelectSubmit()
},
handleSearchSubmit() {
this.closePopup()
this.$emit('change', {
filterType: this.filterType,
filter: this.filterValue
})
},
handleSearchReset() {
if (!this.canReset) {
return;
}
this.filterValue = ''
this.handleSearchSubmit()
},
handleRangeSubmit(isReset) {
this.closePopup()
this.$emit('change', {
filterType: this.filterType,
filter: isReset === true ? [] : [parseInt(this.gtValue), parseInt(this.ltValue)]
})
},
handleRangeReset() {
if (!this.canReset) {
return;
}
this.gtValue = ''
this.ltValue = ''
this.handleRangeSubmit(true)
}
}
}
</script>
<style lang="scss">
$uni-primary: #1890ff !default;
.flex-r {
display: flex;
flex-direction: row;
}
.flex-f {
flex: 1;
}
.a-i-c {
align-items: center;
}
.j-c-c {
justify-content: center;
}
.icon-select {
width: 14px;
height: 16px;
border: solid 6px transparent;
border-top: solid 6px #ddd;
border-bottom: none;
background-color: #ddd;
background-clip: content-box;
box-sizing: border-box;
}
.icon-select.active {
background-color: $uni-primary;
border-top-color: $uni-primary;
}
.icon-search {
width: 12px;
height: 16px;
position: relative;
}
.icon-search-0 {
border: 2px solid #ddd;
border-radius: 8px;
width: 7px;
height: 7px;
}
.icon-search-1 {
position: absolute;
top: 8px;
right: 0;
width: 1px;
height: 7px;
background-color: #ddd;
transform: rotate(-45deg);
}
.icon-search.active .icon-search-0 {
border-color: $uni-primary;
}
.icon-search.active .icon-search-1 {
background-color: $uni-primary;
}
.icon-calendar {
color: #ddd;
width: 14px;
height: 16px;
}
.icon-calendar-0 {
height: 4px;
margin-top: 3px;
margin-bottom: 1px;
background-color: #ddd;
border-radius: 2px 2px 1px 1px;
position: relative;
}
.icon-calendar-0:before, .icon-calendar-0:after {
content: '';
position: absolute;
top: -3px;
width: 4px;
height: 3px;
border-radius: 1px;
background-color: #ddd;
}
.icon-calendar-0:before {
left: 2px;
}
.icon-calendar-0:after {
right: 2px;
}
.icon-calendar-1 {
height: 9px;
background-color: #ddd;
border-radius: 1px 1px 2px 2px;
}
.icon-calendar.active {
color: $uni-primary;
}
.icon-calendar.active .icon-calendar-0,
.icon-calendar.active .icon-calendar-1,
.icon-calendar.active .icon-calendar-0:before,
.icon-calendar.active .icon-calendar-0:after {
background-color: $uni-primary;
}
.uni-filter-dropdown {
position: relative;
font-weight: normal;
}
.dropdown-popup {
position: absolute;
top: 100%;
background-color: #fff;
box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d;
min-width: 150px;
z-index: 1000;
}
.dropdown-popup-left {
left: 0;
}
.dropdown-popup-right {
right: 0;
}
.uni-dropdown-cover {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: transparent;
z-index: 100;
}
.list {
margin-top: 5px;
margin-bottom: 5px;
}
.list-item {
padding: 5px 10px;
text-align: left;
}
.list-item:hover {
background-color: #f0f0f0;
}
.check {
margin-right: 5px;
}
.search-area {
padding: 10px;
}
.search-input {
font-size: 12px;
border: 1px solid #f0f0f0;
border-radius: 3px;
padding: 2px 5px;
min-width: 150px;
text-align: left;
}
.input-label {
margin: 10px 10px 5px 10px;
text-align: left;
}
.input {
font-size: 12px;
border: 1px solid #f0f0f0;
border-radius: 3px;
margin: 10px;
padding: 2px 5px;
min-width: 150px;
text-align: left;
}
.opera-area {
cursor: default;
border-top: 1px solid #ddd;
padding: 5px;
}
.opera-area .btn {
font-size: 12px;
border-radius: 3px;
margin: 5px;
padding: 4px 4px;
}
.btn-default {
border: 1px solid #ddd;
}
.btn-default.disable {
border-color: transparent;
}
.btn-submit {
background-color: $uni-primary;
color: #ffffff;
}
</style>

@ -0,0 +1,285 @@
<template>
<!-- #ifdef H5 -->
<th :rowspan="rowspan" :colspan="colspan" class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: customWidth + 'px', 'text-align': align }">
<view class="uni-table-th-row">
<view class="uni-table-th-content" :style="{ 'justify-content': contentAlign }" @click="sort">
<slot></slot>
<view v-if="sortable" class="arrow-box">
<text class="arrow up" :class="{ active: ascending }" @click.stop="ascendingFn"></text>
<text class="arrow down" :class="{ active: descending }" @click.stop="descendingFn"></text>
</view>
</view>
<dropdown v-if="filterType || filterData.length" :filterDefaultValue="filterDefaultValue" :filterData="filterData" :filterType="filterType" @change="ondropdown"></dropdown>
</view>
</th>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: customWidth + 'px', 'text-align': align }"><slot></slot></view>
<!-- #endif -->
</template>
<script>
// #ifdef H5
import dropdown from './filter-dropdown.vue'
// #endif
/**
* Th 表头
* @description 表格内的表头单元格组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=3270
* @property {Number | String} width 单元格宽度支持纯数字携带单位px或rpx
* @property {Boolean} sortable 是否启用排序
* @property {Number} align = [left|center|right] 单元格对齐方式
* @value left 单元格文字左侧对齐
* @value center 单元格文字居中
* @value right 单元格文字右侧对齐
* @property {Array} filterData 筛选数据
* @property {String} filterType [search|select] 筛选类型
* @value search 关键字搜素
* @value select 条件选择
* @event {Function} sort-change 排序触发事件
*/
export default {
name: 'uniTh',
options: {
virtualHost: true
},
components: {
// #ifdef H5
dropdown
// #endif
},
emits:['sort-change','filter-change'],
props: {
width: {
type: [String, Number],
default: ''
},
align: {
type: String,
default: 'left'
},
rowspan: {
type: [Number, String],
default: 1
},
colspan: {
type: [Number, String],
default: 1
},
sortable: {
type: Boolean,
default: false
},
filterType: {
type: String,
default: ""
},
filterData: {
type: Array,
default () {
return []
}
},
filterDefaultValue: {
type: [Array,String],
default () {
return ""
}
}
},
data() {
return {
border: false,
ascending: false,
descending: false
}
},
computed: {
// propswidth th(px)
customWidth(){
if(typeof this.width === 'number'){
return this.width
} else if(typeof this.width === 'string') {
let regexHaveUnitPx = new RegExp(/^[1-9][0-9]*px$/g)
let regexHaveUnitRpx = new RegExp(/^[1-9][0-9]*rpx$/g)
let regexHaveNotUnit = new RegExp(/^[1-9][0-9]*$/g)
if (this.width.match(regexHaveUnitPx) !== null) { // px
return this.width.replace('px', '')
} else if (this.width.match(regexHaveUnitRpx) !== null) { // rpx
let numberRpx = Number(this.width.replace('rpx', ''))
let widthCoe = uni.getSystemInfoSync().screenWidth / 750
return Math.round(numberRpx * widthCoe)
} else if (this.width.match(regexHaveNotUnit) !== null) { // rpxpx String
return this.width
} else { //
return ''
}
} else {
return ''
}
},
contentAlign() {
let align = 'left'
switch (this.align) {
case 'left':
align = 'flex-start'
break
case 'center':
align = 'center'
break
case 'right':
align = 'flex-end'
break
}
return align
}
},
created() {
this.root = this.getTable('uniTable')
this.rootTr = this.getTable('uniTr')
this.rootTr.minWidthUpdate(this.customWidth ? this.customWidth : 140)
this.border = this.root.border
this.root.thChildren.push(this)
},
methods: {
sort() {
if (!this.sortable) return
this.clearOther()
if (!this.ascending && !this.descending) {
this.ascending = true
this.$emit('sort-change', { order: 'ascending' })
return
}
if (this.ascending && !this.descending) {
this.ascending = false
this.descending = true
this.$emit('sort-change', { order: 'descending' })
return
}
if (!this.ascending && this.descending) {
this.ascending = false
this.descending = false
this.$emit('sort-change', { order: null })
}
},
ascendingFn() {
this.clearOther()
this.ascending = !this.ascending
this.descending = false
this.$emit('sort-change', { order: this.ascending ? 'ascending' : null })
},
descendingFn() {
this.clearOther()
this.descending = !this.descending
this.ascending = false
this.$emit('sort-change', { order: this.descending ? 'descending' : null })
},
clearOther() {
this.root.thChildren.map(item => {
if (item !== this) {
item.ascending = false
item.descending = false
}
return item
})
},
ondropdown(e) {
this.$emit("filter-change", e)
},
/**
* 获取父元素实例
*/
getTable(name) {
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
}
}
}
</script>
<style lang="scss">
$border-color: #ebeef5;
$uni-primary: #007aff !default;
.uni-table-th {
padding: 12px 10px;
/* #ifndef APP-NVUE */
display: table-cell;
box-sizing: border-box;
/* #endif */
font-size: 14px;
font-weight: bold;
color: #909399;
border-bottom: 1px $border-color solid;
}
.uni-table-th-row {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.table--border {
border-right: 1px $border-color solid;
}
.uni-table-th-content {
display: flex;
align-items: center;
flex: 1;
}
.arrow-box {
}
.arrow {
display: block;
position: relative;
width: 10px;
height: 8px;
// border: 1px red solid;
left: 5px;
overflow: hidden;
cursor: pointer;
}
.down {
top: 3px;
::after {
content: '';
width: 8px;
height: 8px;
position: absolute;
left: 2px;
top: -5px;
transform: rotate(45deg);
background-color: #ccc;
}
&.active {
::after {
background-color: $uni-primary;
}
}
}
.up {
::after {
content: '';
width: 8px;
height: 8px;
position: absolute;
left: 2px;
top: 5px;
transform: rotate(45deg);
background-color: #ccc;
}
&.active {
::after {
background-color: $uni-primary;
}
}
}
</style>

@ -0,0 +1,129 @@
<template>
<!-- #ifdef H5 -->
<thead class="uni-table-thead">
<tr class="uni-table-tr">
<th :rowspan="rowspan" colspan="1" class="checkbox" :class="{ 'tr-table--border': border }">
<table-checkbox :indeterminate="indeterminate" :checked="checked" @checkboxSelected="checkboxSelected"></table-checkbox>
</th>
</tr>
<slot></slot>
</thead>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="uni-table-thead"><slot></slot></view>
<!-- #endif -->
</template>
<script>
import tableCheckbox from '../uni-tr/table-checkbox.vue'
export default {
name: 'uniThead',
components: {
tableCheckbox
},
options: {
virtualHost: true
},
data() {
return {
border: false,
selection: false,
rowspan: 1,
indeterminate: false,
checked: false
}
},
created() {
this.root = this.getTable()
// #ifdef H5
this.root.theadChildren = this
// #endif
this.border = this.root.border
this.selection = this.root.type
},
methods: {
init(self) {
this.rowspan++
},
checkboxSelected(e) {
this.indeterminate = false
const backIndexData = this.root.backIndexData
const data = this.root.trChildren.filter(v => !v.disabled && v.keyValue)
if (backIndexData.length === data.length) {
this.checked = false
this.root.clearSelection()
} else {
this.checked = true
this.root.selectionAll()
}
},
/**
* 获取父元素实例
*/
getTable(name = 'uniTable') {
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
}
}
}
</script>
<style lang="scss">
$border-color: #ebeef5;
.uni-table-thead {
display: table-header-group;
}
.uni-table-tr {
/* #ifndef APP-NVUE */
display: table-row;
transition: all 0.3s;
box-sizing: border-box;
/* #endif */
border: 1px red solid;
background-color: #fafafa;
}
.checkbox {
padding: 0 8px;
width: 26px;
padding-left: 12px;
/* #ifndef APP-NVUE */
display: table-cell;
vertical-align: middle;
/* #endif */
color: #333;
font-weight: 500;
border-bottom: 1px $border-color solid;
font-size: 14px;
// text-align: center;
}
.tr-table--border {
border-right: 1px $border-color solid;
}
/* #ifndef APP-NVUE */
.uni-table-tr {
::v-deep .uni-table-th {
&.table--border:last-child {
// border-right: none;
}
}
::v-deep .uni-table-td {
&.table--border:last-child {
// border-right: none;
}
}
}
/* #endif */
</style>

@ -0,0 +1,179 @@
<template>
<view class="uni-table-checkbox" @click="selected">
<view v-if="!indeterminate" class="checkbox__inner" :class="{'is-checked':isChecked,'is-disable':isDisabled}">
<view class="checkbox__inner-icon"></view>
</view>
<view v-else class="checkbox__inner checkbox--indeterminate">
<view class="checkbox__inner-icon"></view>
</view>
</view>
</template>
<script>
export default {
name: 'TableCheckbox',
emits:['checkboxSelected'],
props: {
indeterminate: {
type: Boolean,
default: false
},
checked: {
type: [Boolean,String],
default: false
},
disabled: {
type: Boolean,
default: false
},
index: {
type: Number,
default: -1
},
cellData: {
type: Object,
default () {
return {}
}
}
},
watch:{
checked(newVal){
if(typeof this.checked === 'boolean'){
this.isChecked = newVal
}else{
this.isChecked = true
}
},
indeterminate(newVal){
this.isIndeterminate = newVal
}
},
data() {
return {
isChecked: false,
isDisabled: false,
isIndeterminate:false
}
},
created() {
if(typeof this.checked === 'boolean'){
this.isChecked = this.checked
}
this.isDisabled = this.disabled
},
methods: {
selected() {
if (this.isDisabled) return
this.isIndeterminate = false
this.isChecked = !this.isChecked
this.$emit('checkboxSelected', {
checked: this.isChecked,
data: this.cellData
})
}
}
}
</script>
<style lang="scss">
$uni-primary: #007aff !default;
$border-color: #DCDFE6;
$disable:0.4;
.uni-table-checkbox {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
position: relative;
margin: 5px 0;
cursor: pointer;
//
.checkbox__inner {
/* #ifndef APP-NVUE */
flex-shrink: 0;
box-sizing: border-box;
/* #endif */
position: relative;
width: 16px;
height: 16px;
border: 1px solid $border-color;
border-radius: 2px;
background-color: #fff;
z-index: 1;
.checkbox__inner-icon {
position: absolute;
/* #ifdef APP-NVUE */
top: 2px;
/* #endif */
/* #ifndef APP-NVUE */
top: 2px;
/* #endif */
left: 5px;
height: 7px;
width: 3px;
border: 1px solid #fff;
border-left: 0;
border-top: 0;
opacity: 0;
transform-origin: center;
transform: rotate(45deg);
box-sizing: content-box;
}
&.checkbox--indeterminate {
border-color: $uni-primary;
background-color: $uni-primary;
.checkbox__inner-icon {
position: absolute;
opacity: 1;
transform: rotate(0deg);
height: 2px;
top: 0;
bottom: 0;
margin: auto;
left: 0px;
right: 0px;
bottom: 0;
width: auto;
border: none;
border-radius: 2px;
transform: scale(0.5);
background-color: #fff;
}
}
&:hover{
border-color: $uni-primary;
}
//
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
background-color: #F2F6FC;
border-color: $border-color;
}
//
&.is-checked {
border-color: $uni-primary;
background-color: $uni-primary;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
//
&.is-disable {
opacity: $disable;
}
}
}
}
</style>

@ -0,0 +1,171 @@
<template>
<!-- #ifdef H5 -->
<tr class="uni-table-tr">
<th v-if="selection === 'selection' && ishead" class="checkbox" :class="{ 'tr-table--border': border }">
<table-checkbox :checked="checked" :indeterminate="indeterminate" :disabled="disabled" @checkboxSelected="checkboxSelected"></table-checkbox>
</th>
<slot></slot>
<!-- <uni-th class="th-fixed">123</uni-th> -->
</tr>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="uni-table-tr">
<view v-if="selection === 'selection' " class="checkbox" :class="{ 'tr-table--border': border }">
<table-checkbox :checked="checked" :indeterminate="indeterminate" :disabled="disabled" @checkboxSelected="checkboxSelected"></table-checkbox>
</view>
<slot></slot>
</view>
<!-- #endif -->
</template>
<script>
import tableCheckbox from './table-checkbox.vue'
/**
* Tr 表格行组件
* @description 表格行组件 仅包含 th,td 组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=
*/
export default {
name: 'uniTr',
components: { tableCheckbox },
props: {
disabled: {
type: Boolean,
default: false
},
keyValue: {
type: [String, Number],
default: ''
}
},
options: {
virtualHost: true
},
data() {
return {
value: false,
border: false,
selection: false,
widthThArr: [],
ishead: true,
checked: false,
indeterminate:false
}
},
created() {
this.root = this.getTable()
this.head = this.getTable('uniThead')
if (this.head) {
this.ishead = false
this.head.init(this)
}
this.border = this.root.border
this.selection = this.root.type
this.root.trChildren.push(this)
const rowData = this.root.data.find(v => v[this.root.rowKey] === this.keyValue)
if(rowData){
this.rowData = rowData
}
this.root.isNodata()
},
mounted() {
if (this.widthThArr.length > 0) {
const selectionWidth = this.selection === 'selection' ? 50 : 0
this.root.minWidth = this.widthThArr.reduce((a, b) => Number(a) + Number(b)) + selectionWidth
}
},
// #ifndef VUE3
destroyed() {
const index = this.root.trChildren.findIndex(i => i === this)
this.root.trChildren.splice(index, 1)
this.root.isNodata()
},
// #endif
// #ifdef VUE3
unmounted() {
const index = this.root.trChildren.findIndex(i => i === this)
this.root.trChildren.splice(index, 1)
this.root.isNodata()
},
// #endif
methods: {
minWidthUpdate(width) {
this.widthThArr.push(width)
},
//
checkboxSelected(e) {
let rootData = this.root.data.find(v => v[this.root.rowKey] === this.keyValue)
this.checked = e.checked
this.root.check(rootData||this, e.checked,rootData? this.keyValue:null)
},
change(e) {
this.root.trChildren.forEach(item => {
if (item === this) {
this.root.check(this, e.detail.value.length > 0 ? true : false)
}
})
},
/**
* 获取父元素实例
*/
getTable(name = 'uniTable') {
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
}
}
}
</script>
<style lang="scss">
$border-color: #ebeef5;
.uni-table-tr {
/* #ifndef APP-NVUE */
display: table-row;
transition: all 0.3s;
box-sizing: border-box;
/* #endif */
}
.checkbox {
padding: 0 8px;
width: 26px;
padding-left: 12px;
/* #ifndef APP-NVUE */
display: table-cell;
vertical-align: middle;
/* #endif */
color: #333;
font-weight: 500;
border-bottom: 1px $border-color solid;
font-size: 14px;
// text-align: center;
}
.tr-table--border {
border-right: 1px $border-color solid;
}
/* #ifndef APP-NVUE */
.uni-table-tr {
::v-deep .uni-table-th {
&.table--border:last-child {
// border-right: none;
}
}
::v-deep .uni-table-td {
&.table--border:last-child {
// border-right: none;
}
}
}
/* #endif */
</style>

@ -0,0 +1,9 @@
{
"filter-dropdown.reset": "Reset",
"filter-dropdown.search": "Search",
"filter-dropdown.submit": "Submit",
"filter-dropdown.filter": "Filter",
"filter-dropdown.gt": "Greater or equal to",
"filter-dropdown.lt": "Less than or equal to",
"filter-dropdown.date": "Date"
}

@ -0,0 +1,9 @@
{
"filter-dropdown.reset": "Reiniciar",
"filter-dropdown.search": "Búsqueda",
"filter-dropdown.submit": "Entregar",
"filter-dropdown.filter": "Filtrar",
"filter-dropdown.gt": "Mayor o igual a",
"filter-dropdown.lt": "Menos que o igual a",
"filter-dropdown.date": "Fecha"
}

@ -0,0 +1,9 @@
{
"filter-dropdown.reset": "Réinitialiser",
"filter-dropdown.search": "Chercher",
"filter-dropdown.submit": "Soumettre",
"filter-dropdown.filter": "Filtre",
"filter-dropdown.gt": "Supérieur ou égal à",
"filter-dropdown.lt": "Inférieur ou égal à",
"filter-dropdown.date": "Date"
}

@ -0,0 +1,12 @@
import en from './en.json'
import es from './es.json'
import fr from './fr.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
es,
fr,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}

@ -0,0 +1,9 @@
{
"filter-dropdown.reset": "重置",
"filter-dropdown.search": "搜索",
"filter-dropdown.submit": "确定",
"filter-dropdown.filter": "筛选",
"filter-dropdown.gt": "大于等于",
"filter-dropdown.lt": "小于等于",
"filter-dropdown.date": "日期范围"
}

@ -0,0 +1,9 @@
{
"filter-dropdown.reset": "重置",
"filter-dropdown.search": "搜索",
"filter-dropdown.submit": "確定",
"filter-dropdown.filter": "篩選",
"filter-dropdown.gt": "大於等於",
"filter-dropdown.lt": "小於等於",
"filter-dropdown.date": "日期範圍"
}

@ -0,0 +1,83 @@
{
"id": "uni-table",
"displayName": "uni-table 表格",
"version": "1.2.3",
"description": "表格组件,多用于展示多条结构类似的数据,如",
"keywords": [
"uni-ui",
"uniui",
"table",
"表格"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-scss","uni-datetime-picker"],
"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",
"字节跳动": "n",
"QQ": "y"
},
"快应用": {
"华为": "n",
"联盟": "n"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

@ -0,0 +1,13 @@
## Table 表单
> 组件名:``uni-table``,代码块: `uTable`
用于展示多条结构类似的数据
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-table)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

@ -5,5 +5,17 @@ export default {
// 填充对象至数组到指定长度
fillArrayWithObject: function(obj, length) {
return Array.from({ length }, () => obj);
},
// 删除数组指定索引元素
removeIndexesItems: function(indexesToDelete, arr) {
// 从后往前删除元素,避免索引变化
indexesToDelete.sort((a, b) => b - a);
for (let i = 0; i < indexesToDelete.length; i++) {
arr.splice(indexesToDelete[i], 1);
}
},
// 正则过滤非数字
filterNonNumeric: function(str) {
return Number.parseInt(str.replace(/\D/g, ''));
}
}
}

Loading…
Cancel
Save