Merge branch 'master' of https://e.coding.net/ctcdevteam/examtest
This commit is contained in:
commit
398fa371ed
|
@ -31,7 +31,11 @@
|
|||
"pages/qtest/form",
|
||||
"pages/main/start",
|
||||
"pages/exam/index",
|
||||
"pages/exam/note"
|
||||
"pages/exam/note",
|
||||
"pages/admin/index",
|
||||
"pages/admin/login",
|
||||
"pages/admin/exam/add",
|
||||
"pages/admin/exam/add2"
|
||||
],
|
||||
"window": {
|
||||
"backgroundTextStyle": "light",
|
||||
|
@ -88,7 +92,7 @@
|
|||
},
|
||||
"plugins": {
|
||||
"tencentvideo": {
|
||||
"version": "1.3.15",
|
||||
"version": "1.3.31",
|
||||
"provider": "wxa75efa648b60994b"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -0,0 +1,566 @@
|
|||
|
||||
Component({
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
properties: {
|
||||
pickerShow: {
|
||||
type: Boolean,
|
||||
observer:function(val){ //弹出动画
|
||||
// console.log(this.data);
|
||||
if(val){
|
||||
let animation = wx.createAnimation({
|
||||
duration: 500,
|
||||
timingFunction: "ease"
|
||||
});
|
||||
let animationOpacity = wx.createAnimation({
|
||||
duration: 500,
|
||||
timingFunction: "ease"
|
||||
});
|
||||
setTimeout(() => {
|
||||
animation.bottom(0).step();
|
||||
animationOpacity.opacity(0.7).step();
|
||||
this.setData({
|
||||
animationOpacity: animationOpacity.export(),
|
||||
animationData: animation.export()
|
||||
})
|
||||
}, 0);
|
||||
}else{
|
||||
let animation = wx.createAnimation({
|
||||
duration: 100,
|
||||
timingFunction: "ease"
|
||||
});
|
||||
let animationOpacity = wx.createAnimation({
|
||||
duration: 500,
|
||||
timingFunction: "ease"
|
||||
});
|
||||
animation.bottom(-320).step();
|
||||
animationOpacity.opacity(0).step();
|
||||
this.setData({
|
||||
animationOpacity: animationOpacity.export(),
|
||||
animationData: animation.export()
|
||||
});
|
||||
}
|
||||
|
||||
// 在picker滚动未停止前点确定,会使startValue数组各项归零,发生错误,这里判断并重新初始化
|
||||
// 微信新增了picker滚动的回调函数,已进行兼容
|
||||
if(this.data.startValue&&this.data.endValue){
|
||||
let s = 0, e = 0;
|
||||
let conf = this.data.config;
|
||||
|
||||
this.data.startValue.map(val => {
|
||||
if (val == 0) {
|
||||
s++
|
||||
}
|
||||
})
|
||||
this.data.endValue.map(val => {
|
||||
if (val == 0) {
|
||||
e++;
|
||||
}
|
||||
});
|
||||
let tmp={
|
||||
hour:4,
|
||||
minute:5,
|
||||
second:6
|
||||
}
|
||||
let n = tmp[conf.column];
|
||||
if (s>=n || e>=n) {
|
||||
this.initPick(this.data.config);
|
||||
this.setData({
|
||||
startValue: this.data.startValue,
|
||||
endValue: this.data.endValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
config: Object
|
||||
},
|
||||
|
||||
/**
|
||||
* 组件的初始数据
|
||||
*/
|
||||
data: {
|
||||
// pickerShow:true
|
||||
// limitStartTime: new Date().getTime()-1000*60*60*24*30,
|
||||
// limitEndTime: new Date().getTime(),
|
||||
// yearStart:2000,
|
||||
// yearEnd:2100
|
||||
},
|
||||
detached: function() {
|
||||
console.log("dele");
|
||||
},
|
||||
attached: function() {},
|
||||
ready: function() {
|
||||
this.readConfig();
|
||||
this.initPick(this.data.config || null);
|
||||
this.setData({
|
||||
startValue: this.data.startValue,
|
||||
endValue: this.data.endValue,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {
|
||||
//阻止滑动事件
|
||||
onCatchTouchMove(e) {
|
||||
|
||||
},
|
||||
//读取配置项
|
||||
readConfig() {
|
||||
let limitEndTime = new Date().getTime();
|
||||
let limitStartTime = new Date().getTime() - 1000 * 60 * 60 * 24 * 30;
|
||||
if (this.data.config) {
|
||||
let conf = this.data.config;
|
||||
|
||||
if (typeof conf.dateLimit == "number") {
|
||||
limitStartTime =
|
||||
new Date().getTime() - 1000 * 60 * 60 * 24 * conf.dateLimit;
|
||||
}
|
||||
if(conf.limitStartTime){
|
||||
|
||||
limitStartTime = new Date(conf.limitStartTime.replace(/-/g,'/')).getTime();
|
||||
}
|
||||
|
||||
if (conf.limitEndTime) {
|
||||
limitEndTime = new Date(conf.limitEndTime.replace(/-/g, '/')).getTime();
|
||||
}
|
||||
|
||||
this.setData({
|
||||
yearStart: conf.yearStart || 2000,
|
||||
yearEnd: conf.yearEnd || 2100,
|
||||
endDate: conf.endDate || false,
|
||||
dateLimit: conf.dateLimit || false,
|
||||
hourColumn:
|
||||
conf.column == "hour" ||
|
||||
conf.column == "minute" ||
|
||||
conf.column == "second",
|
||||
minColumn: conf.column == "minute" || conf.column == "second",
|
||||
secColumn: conf.column == "second"
|
||||
});
|
||||
}
|
||||
|
||||
let limitStartTimeArr = formatTime(limitStartTime);
|
||||
let limitEndTimeArr = formatTime(limitEndTime);
|
||||
|
||||
this.setData({
|
||||
limitStartTime,
|
||||
limitStartTimeArr,
|
||||
limitEndTime,
|
||||
limitEndTimeArr
|
||||
});
|
||||
},
|
||||
//滚动开始
|
||||
handlePickStart:function(e){
|
||||
this.setData({
|
||||
isPicking:true
|
||||
})
|
||||
},
|
||||
//滚动结束
|
||||
handlePickEnd:function(e){
|
||||
this.setData({
|
||||
isPicking:false
|
||||
})
|
||||
},
|
||||
onConfirm: function() {
|
||||
//滚动未结束时不能确认
|
||||
if(this.data.isPicking){return}
|
||||
let startTime = new Date(this.data.startPickTime.replace(/-/g, "/"));
|
||||
let endTime = new Date(this.data.endPickTime.replace(/-/g, "/"));
|
||||
if (startTime <= endTime || !this.data.endDate) {
|
||||
this.setData({
|
||||
startTime,
|
||||
endTime
|
||||
});
|
||||
let startArr = formatTime(startTime).arr;
|
||||
let endArr = formatTime(endTime).arr;
|
||||
let format0 = function(num){
|
||||
return num<10?'0'+num:num
|
||||
}
|
||||
|
||||
let startTimeBack =
|
||||
startArr[0] +
|
||||
"-" +
|
||||
format0(startArr[1]) +
|
||||
"-" +
|
||||
format0(startArr[2]) +
|
||||
" " +
|
||||
(this.data.hourColumn ? format0(startArr[3]) : "00") +
|
||||
":" +
|
||||
(this.data.minColumn ? format0(startArr[4]) : "00") +
|
||||
":" +
|
||||
(this.data.secColumn ? format0(startArr[5]) : "00");
|
||||
|
||||
let endTimeBack =
|
||||
endArr[0] +
|
||||
"-" +
|
||||
format0(endArr[1]) +
|
||||
"-" +
|
||||
format0(endArr[2]) +
|
||||
" " +
|
||||
(this.data.hourColumn ? format0(endArr[3]) : "00") +
|
||||
":" +
|
||||
(this.data.minColumn ? format0(endArr[4]) : "00") +
|
||||
":" +
|
||||
(this.data.secColumn ? format0(endArr[5]) : "00");
|
||||
|
||||
let time = {
|
||||
startTime: startTimeBack,
|
||||
endTime: endTimeBack
|
||||
};
|
||||
|
||||
//触发自定义事件
|
||||
this.triggerEvent("setPickerTime", time);
|
||||
this.triggerEvent("hidePicker", {});
|
||||
} else {
|
||||
wx.showToast({
|
||||
icon: "none",
|
||||
title: "时间不合理"
|
||||
});
|
||||
}
|
||||
},
|
||||
hideModal: function() {
|
||||
|
||||
this.triggerEvent("hidePicker", {});
|
||||
},
|
||||
changeStartDateTime: function(e) {
|
||||
let val = e.detail.value;
|
||||
|
||||
this.compareTime(val, "start");
|
||||
},
|
||||
|
||||
changeEndDateTime: function(e) {
|
||||
let val = e.detail.value;
|
||||
this.compareTime(val, "end");
|
||||
},
|
||||
//比较时间是否在范围内
|
||||
compareTime(val, type) {
|
||||
let h = val[3] ? this.data.HourList[val[3]] : "00";
|
||||
let m = val[4] ? this.data.MinuteList[val[4]] : "00";
|
||||
let s = val[5] ? this.data.SecondList[val[5]] : "00";
|
||||
let time =
|
||||
this.data.YearList[val[0]] +
|
||||
"-" +
|
||||
this.data.MonthList[val[1]] +
|
||||
"-" +
|
||||
this.data.DayList[val[2]] +
|
||||
" " +
|
||||
h +
|
||||
":" +
|
||||
m +
|
||||
":" +
|
||||
s;
|
||||
|
||||
let start = this.data.limitStartTime;
|
||||
let end = this.data.limitEndTime;
|
||||
let timeNum = new Date(time.replace(/-/g, '/')).getTime();
|
||||
let year, month, day, hour, min, sec, limitDate;
|
||||
let tempArr = []
|
||||
|
||||
if (!this.data.dateLimit){
|
||||
limitDate = [
|
||||
this.data.YearList[val[0]],
|
||||
this.data.MonthList[val[1]],
|
||||
this.data.DayList[val[2]],
|
||||
this.data.HourList[val[3]],
|
||||
this.data.MinuteList[val[4]],
|
||||
this.data.SecondList[val[5]]]
|
||||
} else if (type == "start" && timeNum > new Date(this.data.endPickTime.replace(/-/g, '/')) && this.data.config.endDate) {
|
||||
limitDate = formatTime(this.data.endPickTime).arr;
|
||||
|
||||
} else if (type == "end" && timeNum < new Date(this.data.startPickTime.replace(/-/g, '/'))) {
|
||||
limitDate = formatTime(this.data.startPickTime).arr;
|
||||
|
||||
} else if (timeNum < start) {
|
||||
limitDate = this.data.limitStartTimeArr.arr;
|
||||
|
||||
} else if (timeNum > end) {
|
||||
limitDate = this.data.limitEndTimeArr.arr;
|
||||
|
||||
} else {
|
||||
limitDate = [
|
||||
this.data.YearList[val[0]],
|
||||
this.data.MonthList[val[1]],
|
||||
this.data.DayList[val[2]],
|
||||
this.data.HourList[val[3]],
|
||||
this.data.MinuteList[val[4]],
|
||||
this.data.SecondList[val[5]]
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
year = limitDate[0];
|
||||
month = limitDate[1];
|
||||
day = limitDate[2];
|
||||
hour = limitDate[3];
|
||||
min = limitDate[4];
|
||||
sec = limitDate[5];
|
||||
|
||||
if (type == "start") {
|
||||
this.setStartDate(year, month, day, hour, min, sec);
|
||||
} else if (type == "end") {
|
||||
this.setEndDate(year, month, day, hour, min, sec);
|
||||
}
|
||||
},
|
||||
getDays: function(year, month) {
|
||||
let daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||
if (month === 2) {
|
||||
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
|
||||
? 29
|
||||
: 28;
|
||||
} else {
|
||||
return daysInMonth[month - 1];
|
||||
}
|
||||
},
|
||||
initPick: function(initData) {
|
||||
const date = initData.initStartTime ? new Date(initData.initStartTime.replace(/-/g, '/')): new Date();
|
||||
const endDate = initData.initEndTime ? new Date(initData.initEndTime.replace(/-/g, '/')) : new Date();
|
||||
// const startDate = new Date(date.getTime() - 1000 * 60 * 60 * 24);
|
||||
const startDate = date;
|
||||
const startYear = date.getFullYear();
|
||||
const startMonth = date.getMonth() + 1;
|
||||
const startDay = date.getDate();
|
||||
const startHour = date.getHours();
|
||||
const startMinute = date.getMinutes();
|
||||
const startSecond = date.getSeconds();
|
||||
|
||||
const endYear = endDate.getFullYear();
|
||||
const endMonth = endDate.getMonth() + 1;
|
||||
const endDay = endDate.getDate();
|
||||
const endHour = endDate.getHours();
|
||||
const endMinute = endDate.getMinutes();
|
||||
const endSecond = endDate.getSeconds();
|
||||
|
||||
let YearList = [];
|
||||
let MonthList = [];
|
||||
let DayList = [];
|
||||
let HourList = [];
|
||||
let MinuteList = [];
|
||||
let SecondList = [];
|
||||
|
||||
//设置年份列表
|
||||
for (let i = this.data.yearStart; i <= this.data.yearEnd; i++) {
|
||||
YearList.push(i);
|
||||
}
|
||||
|
||||
// 设置月份列表
|
||||
for (let i = 1; i <= 12; i++) {
|
||||
MonthList.push(i);
|
||||
}
|
||||
// 设置日期列表
|
||||
for (let i = 1; i <= 31; i++) {
|
||||
DayList.push(i);
|
||||
}
|
||||
// 设置时列表
|
||||
for (let i = 0; i <= 23; i++) {
|
||||
if (0 <= i && i < 10) {
|
||||
i = "0" + i;
|
||||
}
|
||||
HourList.push(i);
|
||||
}
|
||||
// 分|秒
|
||||
for (let i = 0; i <= 59; i++) {
|
||||
if (0 <= i && i < 10) {
|
||||
i = "0" + i;
|
||||
}
|
||||
MinuteList.push(i);
|
||||
SecondList.push(i);
|
||||
}
|
||||
|
||||
this.setData({
|
||||
YearList,
|
||||
MonthList,
|
||||
DayList,
|
||||
HourList,
|
||||
MinuteList,
|
||||
SecondList
|
||||
});
|
||||
|
||||
this.setStartDate(startYear, startMonth, startDay, startHour, startMinute, startSecond);
|
||||
this.setEndDate(endYear, endMonth, endDay, endHour, endMinute, endSecond);
|
||||
|
||||
//!!!
|
||||
// setTimeout(() => {
|
||||
// this.setStartDate(nowYear, nowMonth, nowDay, nowHour, nowMinute)
|
||||
// this.setEndDate(nowYear, nowMonth, nowDay, nowHour, nowMinute)
|
||||
// }, 0);
|
||||
},
|
||||
setPickerDateArr(type, year, month, day, hour, minute, second) {
|
||||
let yearIdx = 0;
|
||||
let monthIdx = 0;
|
||||
let dayIdx = 0;
|
||||
let hourIdx = 0;
|
||||
let minuteIdx = 0;
|
||||
let secondIdx = 0;
|
||||
|
||||
this.data.YearList.map((v, idx) => {
|
||||
if (parseInt(v) === year) {
|
||||
yearIdx = idx;
|
||||
}
|
||||
});
|
||||
|
||||
this.data.MonthList.map((v, idx) => {
|
||||
if (parseInt(v) === month) {
|
||||
monthIdx = idx;
|
||||
}
|
||||
});
|
||||
|
||||
// 重新设置日期列表
|
||||
let DayList = [];
|
||||
for (let i = 1; i <= this.getDays(year, month); i++) {
|
||||
DayList.push(i);
|
||||
}
|
||||
|
||||
DayList.map((v, idx) => {
|
||||
if (parseInt(v) === day) {
|
||||
dayIdx = idx;
|
||||
}
|
||||
});
|
||||
if (type == "start") {
|
||||
this.setData({ startDayList: DayList });
|
||||
} else if (type == "end") {
|
||||
this.setData({ endDayList: DayList });
|
||||
}
|
||||
|
||||
this.data.HourList.map((v, idx) => {
|
||||
if (parseInt(v) === parseInt(hour)) {
|
||||
hourIdx = idx;
|
||||
}
|
||||
});
|
||||
|
||||
this.data.MinuteList.map((v, idx) => {
|
||||
if (parseInt(v) === parseInt(minute)) {
|
||||
minuteIdx = idx;
|
||||
}
|
||||
});
|
||||
this.data.SecondList.map((v, idx) => {
|
||||
if (parseInt(v) === parseInt(second)) {
|
||||
secondIdx = idx;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
yearIdx,
|
||||
monthIdx,
|
||||
dayIdx,
|
||||
hourIdx,
|
||||
minuteIdx,
|
||||
secondIdx
|
||||
};
|
||||
},
|
||||
setStartDate: function(year, month, day, hour, minute, second) {
|
||||
let pickerDateArr = this.setPickerDateArr(
|
||||
"start",
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
hour,
|
||||
minute,
|
||||
second
|
||||
);
|
||||
this.setData({
|
||||
startYearList: this.data.YearList,
|
||||
startMonthList: this.data.MonthList,
|
||||
// startDayList: this.data.DayList,
|
||||
startHourList: this.data.HourList,
|
||||
startMinuteList: this.data.MinuteList,
|
||||
startSecondList: this.data.SecondList,
|
||||
startValue: [
|
||||
pickerDateArr.yearIdx,
|
||||
pickerDateArr.monthIdx,
|
||||
pickerDateArr.dayIdx,
|
||||
pickerDateArr.hourIdx,
|
||||
pickerDateArr.minuteIdx,
|
||||
pickerDateArr.secondIdx
|
||||
],
|
||||
startPickTime:
|
||||
this.data.YearList[pickerDateArr.yearIdx] +
|
||||
"-" +
|
||||
this.data.MonthList[pickerDateArr.monthIdx] +
|
||||
"-" +
|
||||
this.data.DayList[pickerDateArr.dayIdx] +
|
||||
" " +
|
||||
this.data.HourList[pickerDateArr.hourIdx] +
|
||||
":" +
|
||||
this.data.MinuteList[pickerDateArr.minuteIdx] +
|
||||
":" +
|
||||
this.data.SecondList[pickerDateArr.secondIdx]
|
||||
});
|
||||
},
|
||||
setEndDate: function(year, month, day, hour, minute, second) {
|
||||
let pickerDateArr = this.setPickerDateArr(
|
||||
"end",
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
hour,
|
||||
minute,
|
||||
second
|
||||
);
|
||||
|
||||
this.setData({
|
||||
endYearList: this.data.YearList,
|
||||
endMonthList: this.data.MonthList,
|
||||
// endDayList: this.data.DayList,
|
||||
endHourList: this.data.HourList,
|
||||
endMinuteList: this.data.MinuteList,
|
||||
endSecondList: this.data.SecondList,
|
||||
endValue: [
|
||||
pickerDateArr.yearIdx,
|
||||
pickerDateArr.monthIdx,
|
||||
pickerDateArr.dayIdx,
|
||||
pickerDateArr.hourIdx,
|
||||
pickerDateArr.minuteIdx,
|
||||
pickerDateArr.secondIdx
|
||||
],
|
||||
endPickTime:
|
||||
this.data.YearList[pickerDateArr.yearIdx] +
|
||||
"-" +
|
||||
this.data.MonthList[pickerDateArr.monthIdx] +
|
||||
"-" +
|
||||
this.data.DayList[pickerDateArr.dayIdx] +
|
||||
" " +
|
||||
this.data.HourList[pickerDateArr.hourIdx] +
|
||||
":" +
|
||||
this.data.MinuteList[pickerDateArr.minuteIdx] +
|
||||
":" +
|
||||
this.data.SecondList[pickerDateArr.secondIdx]
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function formatTime(date) {
|
||||
|
||||
if (typeof date == 'string' || 'number') {
|
||||
try {
|
||||
date = date.replace(/-/g, '/')//兼容ios
|
||||
} catch (error) {
|
||||
}
|
||||
date = new Date(date)
|
||||
}
|
||||
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth() + 1
|
||||
const day = date.getDate()
|
||||
const hour = date.getHours()
|
||||
const minute = date.getMinutes()
|
||||
const second = date.getSeconds()
|
||||
|
||||
return {
|
||||
str: [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':'),
|
||||
arr: [year, month, day, hour, minute, second]
|
||||
}
|
||||
}
|
||||
function formatNumber(n) {
|
||||
n = n.toString()
|
||||
return n[1] ? n : '0' + n
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<!--components/timePicker/timePicker.wxml-->
|
||||
<!-- 自定义时间筛选器 -->
|
||||
<view hidden="{{!pickerShow}}">
|
||||
<view class="picker-container {{pickerShow?'show_picker':'hide_picker'}}" animation="{{animationData}}">
|
||||
|
||||
<view class="btn-box" catchtouchmove="onCatchTouchMove">
|
||||
<view class="pick_btn" bindtap="hideModal">取消</view>
|
||||
<view class='pick_btn' style="color: #19f" bindtap="onConfirm">确定</view>
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<picker-view class='sensorTypePicker' indicator-style='height: 35px;' bindchange="changeStartDateTime"
|
||||
value="{{startValue}}" style="height: {{endDate?'120px':'250px'}};" bindpickstart="handlePickStart" bindpickend="handlePickEnd">
|
||||
<picker-view-column style="min-width: 70px;flex-shrink: 0">
|
||||
<view class='picker-item' wx:for="{{startYearList}}" wx:key='*this'>{{item}}年</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class='picker-item' wx:for="{{startMonthList}}" wx:key='*this'>{{item}}月</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class='picker-item' wx:for="{{startDayList}}" wx:key='*this'>{{item}}日</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column hidden="{{!hourColumn}}">
|
||||
<view class='picker-item' wx:for="{{startHourList}}" wx:key='*this'>{{item}}时</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column hidden="{{!minColumn}}">
|
||||
<view class='picker-item' wx:for="{{startMinuteList}}" wx:key='*this'>{{item}}分</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column hidden="{{!secColumn}}">
|
||||
<view class='picker-item' wx:for="{{startSecondList}}" wx:key='*this'>{{item}}秒</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{endDate}}">
|
||||
<view class='to' style='margin-top: 4px;margin-bottom: 4px;'>至</view>
|
||||
<picker-view class='sensorTypePicker' indicator-style='height: 35px;' bindchange="changeEndDateTime" bindpickstart="handlePickStart" bindpickend="handlePickEnd"
|
||||
value="{{endValue}}">
|
||||
<picker-view-column style="min-width: 70px;flex-shrink: 0">
|
||||
<view class='picker-item' wx:for="{{endYearList}}" wx:key='*this' style="min-width: 70px;">{{item}}年</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class='picker-item' wx:for="{{endMonthList}}" wx:key='*this'>{{item}}月</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view class='picker-item' wx:for="{{endDayList}}" wx:key='*this'>{{item}}日</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column hidden="{{!hourColumn}}" >
|
||||
<view class='picker-item' wx:for="{{endHourList}}" wx:key='*this'>{{item}}时</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column hidden="{{!minColumn}}">
|
||||
<view class='picker-item' wx:for="{{endMinuteList}}" wx:key='*this'>{{item}}分</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column hidden="{{!secColumn}}">
|
||||
<view class='picker-item' wx:for="{{startSecondList}}" wx:key='*this'>{{item}}秒</view>
|
||||
</picker-view-column>
|
||||
|
||||
|
||||
</picker-view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- <view class='sure' bindtap="onConfirm">确定</view> -->
|
||||
|
||||
</view>
|
||||
<!-- 遮罩 -->
|
||||
<view class="sensorType-screen" bindtap="hideModal" catchtouchmove="onCatchTouchMove" animation="{{animationOpacity}}"/>
|
||||
</view>
|
|
@ -0,0 +1,96 @@
|
|||
/* components/timePicker/timePicker.wxss */
|
||||
|
||||
.picker-item{
|
||||
line-height: 100rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 自定义时间 */
|
||||
.picker-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* justify-content: center; */
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
bottom: -640rpx;
|
||||
left: 0;
|
||||
/* height: 0; */
|
||||
transition: height 0.5s;
|
||||
z-index: 2000;
|
||||
background: white;
|
||||
border-top: 1px solid #EFEFF4;
|
||||
}
|
||||
.sensorType-screen{
|
||||
width: 100vw;
|
||||
/* height:400rpx; */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #000;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
z-index: 1999;
|
||||
color: #fff;
|
||||
}
|
||||
.sensorTypePicker{
|
||||
width: 690rpx;
|
||||
height: 240rpx;
|
||||
/* padding: 45px 0; */
|
||||
}
|
||||
.picker-item{
|
||||
line-height: 100rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
.box{
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
/* 至 */
|
||||
.to{
|
||||
width:100%;
|
||||
display: flex;
|
||||
justify-content: center;align-items: center;
|
||||
color:rgb(138,138,138);
|
||||
/* font-size:30rpx; */
|
||||
}
|
||||
|
||||
/* 确定 */
|
||||
.sure{
|
||||
width:100%;
|
||||
height:90rpx;
|
||||
border-top: 2rpx solid #EFEFF4;
|
||||
display: flex;justify-content: center;align-items: center;
|
||||
color: rgb(36,123,255);
|
||||
font-size:16px;
|
||||
}
|
||||
|
||||
.btn-box{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2rpx solid #eee;
|
||||
}
|
||||
.pick_btn{
|
||||
padding: 14rpx 30rpx;
|
||||
color: #ccc;
|
||||
/* background-color: #159; */
|
||||
}
|
||||
|
||||
.show_picker{
|
||||
/* height: 320px; */
|
||||
}
|
||||
.hide_picker{
|
||||
/* height: 0; */
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
// components/dynamicForm/index.js
|
||||
import formatTime from './utils/formatTime';
|
||||
|
||||
Component({
|
||||
/**
|
||||
* 组件的属性列表
|
||||
*/
|
||||
properties: {
|
||||
formData: Array,
|
||||
showSubmitBtn: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
toSubmit: Number
|
||||
},
|
||||
//监听数据变化, 当toSubmit 值变化时, 代表父组件点击提交案例事件
|
||||
observers: {
|
||||
'toSubmit': function (e) {
|
||||
if (e) {
|
||||
this.formSubmit();
|
||||
}
|
||||
},
|
||||
'formData': function () {
|
||||
this.formInit();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 组件的初始数据
|
||||
*/
|
||||
data: {
|
||||
pickerMap: {},
|
||||
fileMap: {},
|
||||
inputMap: {}
|
||||
},
|
||||
lifetimes: {
|
||||
attached: function () {
|
||||
this.formInit();
|
||||
},
|
||||
moved: function () { },
|
||||
detached: function () { },
|
||||
},
|
||||
pageLifetimes: {
|
||||
// 组件所在页面的生命周期函数
|
||||
show: function () { },
|
||||
hide: function () { },
|
||||
resize: function () { },
|
||||
},
|
||||
/**
|
||||
* 组件的方法列表
|
||||
*/
|
||||
methods: {
|
||||
//表单初始化
|
||||
formInit() {
|
||||
|
||||
const pickerMap = {}, fileMap = {}, inputMap = {}, dateMap = {};//存储各表单变化后的值,表单id为索引
|
||||
const pickers = [], files = [], inputs = [], datePickers = [];
|
||||
this.data.formData.forEach(val => {
|
||||
switch (val.type) {
|
||||
case 'picker':
|
||||
pickers.push(val);
|
||||
break;
|
||||
case 'file':
|
||||
files.push(val);
|
||||
break;
|
||||
case 'input':
|
||||
case 'textarea':
|
||||
inputs.push(val);
|
||||
break;
|
||||
case 'date':
|
||||
datePickers.push(val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
pickers.forEach(val => {
|
||||
pickerMap[val.id] = {
|
||||
original: val,
|
||||
hasChoose: val.defaultIdx != 'undefined',
|
||||
error:null,
|
||||
idx: val.defaultIdx || 0
|
||||
};
|
||||
});
|
||||
files.forEach(val => {
|
||||
fileMap[val.id] = {
|
||||
original: val,
|
||||
error: null,
|
||||
list: val.fileList
|
||||
};
|
||||
});
|
||||
inputs.forEach(val => {
|
||||
inputMap[val.id] = {
|
||||
original: val,
|
||||
value: val.defaultValue || '',
|
||||
placeholder: val.placeholder,
|
||||
error: null,
|
||||
rules: val.rules ? val.rules.map(val => {
|
||||
val.regular = new RegExp(val.regular);
|
||||
return val;
|
||||
}) : []
|
||||
};
|
||||
});
|
||||
datePickers.forEach(val => {
|
||||
dateMap[val.id] = {
|
||||
original: val,
|
||||
config: val.config,
|
||||
completeTime: val.completeTime,
|
||||
show: false,
|
||||
hasChoose: !!val.config.initStartTime,
|
||||
error: null,
|
||||
startDate: val.config.initStartTime || formatTime(),
|
||||
endDate: val.config.initEndTime || formatTime()
|
||||
};
|
||||
if (!val.completeTime){
|
||||
dateMap[val.id].startDate = dateMap[val.id].startDate.split(' ')[0];
|
||||
dateMap[val.id].endDate = dateMap[val.id].endDate.split(' ')[0];
|
||||
}
|
||||
});
|
||||
this.setData({
|
||||
pickers,
|
||||
inputs,
|
||||
datePickers,
|
||||
files,
|
||||
pickerMap,
|
||||
inputMap,
|
||||
fileMap,
|
||||
dateMap
|
||||
});
|
||||
},
|
||||
//提交表单
|
||||
formSubmit() {
|
||||
let formData = {};
|
||||
const { pickerMap, inputMap, dateMap, fileMap } = this.data;
|
||||
for (let i in this.data) { //获取表单数据后缀为Map
|
||||
if (i.match(/Map$/)) {
|
||||
formData = Object.assign({}, formData, this.data[i]);
|
||||
}
|
||||
}
|
||||
let hasError = false;
|
||||
for (let i in formData) {//循环验证所有表单数据规则
|
||||
let info = formData[i];
|
||||
if (info.original.type === 'input' || info.original.type === 'textarea') {
|
||||
if (!info.value){
|
||||
if (info.original.isRequired){
|
||||
info.error = info.original.lable + '不可为空';
|
||||
hasError = true;
|
||||
}
|
||||
} else if (info.rules){
|
||||
for (let val of info.rules) {
|
||||
if (!info.value.match(val.regular)) {
|
||||
info.error = val.tips || '格式有误';
|
||||
hasError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setData({
|
||||
[`inputMap.${i}`]: info
|
||||
});
|
||||
} else if (info.original.type === 'file') {
|
||||
if (info.list.length === 0 && info.original.isRequired) {
|
||||
let error = '请选择文件';
|
||||
if (info.original.accept === 'video') {
|
||||
error = '请选择视频';
|
||||
} else if (info.original.accept === 'image') {
|
||||
error = '请选择图片';
|
||||
}
|
||||
info.error = error;
|
||||
hasError = true;
|
||||
this.setData({
|
||||
[`fileMap.${i}`]: info
|
||||
});
|
||||
}
|
||||
} else if (info.original.type === 'picker' || info.original.type === 'date'){
|
||||
if (!info.hasChoose && info.original.isRequired){
|
||||
info.error = '请选择' + info.original.lable;
|
||||
hasError = true;
|
||||
this.setData({
|
||||
[`${info.original.type}Map.${i}`]: info
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasError) {
|
||||
wx.showToast({
|
||||
title: '表单填写有误',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.triggerEvent('dynamicFormSubmit', formData);
|
||||
console.log(formData);
|
||||
|
||||
},
|
||||
//更新数据劫持
|
||||
updateData(key,val){
|
||||
this.setData({
|
||||
[key]: val
|
||||
});
|
||||
this.triggerEvent('dynamicFormChange', { key, val});
|
||||
},
|
||||
//显示选择器
|
||||
datePickerShow(e) {
|
||||
if (e.target.dataset.disabled) {
|
||||
return;
|
||||
}
|
||||
this.setData({
|
||||
[`dateMap.${e.target.dataset.id}.show`]: true
|
||||
});
|
||||
},
|
||||
//隐藏时间选择器
|
||||
datePickerHide(id) {
|
||||
if (typeof id === 'object') {
|
||||
id = id.target.id;
|
||||
}
|
||||
this.setData({
|
||||
[`dateMap.${id}.show`]: false
|
||||
});
|
||||
},
|
||||
//设置选择器时间
|
||||
setPickerTime(e) {
|
||||
const {dateMap} = this.data;
|
||||
const { startTime, endTime } = e.detail;
|
||||
const date = dateMap[e.target.id];
|
||||
if (!date.hasChoose){
|
||||
date.hasChoose = true;
|
||||
date.error = null;
|
||||
}
|
||||
date.show = false;
|
||||
date.startDate = date.completeTime ? startTime :startTime.split(' ')[0];
|
||||
date.endDate = date.completeTime ?endTime :endTime.split(' ')[0];
|
||||
this.updateData(`dateMap.${e.target.id}`,date);
|
||||
},
|
||||
//输入框
|
||||
onInput(e) {
|
||||
const { value } = e.detail;
|
||||
const info = this.data.inputMap[e.target.id] || {};
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
info.value = e.detail.value;
|
||||
info.error = null;
|
||||
if (info.rules && info.value) {
|
||||
for (let val of info.rules) {
|
||||
if (!info.value.match(val.regular)) {
|
||||
info.error = val.tips || '格式有误';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.updateData(`inputMap.${e.target.id}`, info);
|
||||
},
|
||||
//picker选择
|
||||
onPickerChange(e) {
|
||||
const { id } = e.target;
|
||||
const picker = this.data.pickerMap[id];
|
||||
if(!picker.hasChoose){
|
||||
picker.hasChoose = true;
|
||||
picker.error = null;
|
||||
}
|
||||
picker.idx = e.detail.value;
|
||||
picker.data = this.data.pickers.filter(val => val.id === id)[0].range[e.detail.value];
|
||||
this.updateData(`pickerMap.${e.target.id}`, picker);
|
||||
},
|
||||
// 选择文件
|
||||
onFileRead(e) {
|
||||
console.log(e);
|
||||
for (let val of e.detail.file) {
|
||||
const size = this.data.fileMap[e.target.id].original.maxSize;
|
||||
if (val.size > size * 1024 * 1024) {
|
||||
wx.showToast({
|
||||
title: `请选择${size}MB以内的文件`,
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
const files = this.data.fileMap[e.target.id];
|
||||
files.error = null;
|
||||
files.list = files.list.concat(e.detail.file);
|
||||
this.updateData(`fileMap.${e.target.id}`, files);
|
||||
},
|
||||
//删除文件
|
||||
onFileDelete(e) {
|
||||
console.log(e);
|
||||
const files = this.data.fileMap[e.target.id].list;
|
||||
files.splice(e.detail.index, 1);
|
||||
this.updateData(`fileMap.${e.target.id}.list`, files);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"van-icon": "./vant/icon/index",
|
||||
"van-uploader": "./vant/uploader/index",
|
||||
"time-picker": "./components/timePicker/timePicker"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<!--components/dynamicForm/index.wxml-->
|
||||
|
||||
<view class="form-box">
|
||||
<block wx:for="{{formData}}" wx:key="id">
|
||||
<!-- input输入框 -->
|
||||
<view class="form-row ipt-row " wx:if="{{item.type==='input'}}">
|
||||
<view class="form-lable {{item.isRequired?'required':'' +''}}">{{item.lable}}</view>
|
||||
<view style="width:100%">
|
||||
<input wx:if="{{!item.disabled}}" class="field {{item.disabled?'disabled':''}}"
|
||||
type="{{inputMap[item.id].original.inputType || 'text'}}" maxlength="{{item.maxLength || -1}}"
|
||||
bindinput="onInput" id="{{item.id}}" value="{{item.defaultValue || ''}}"
|
||||
disabled="{{item.disabled}}" placeholder="{{inputMap[item.id].placeholder || '请填写内容'}}" />
|
||||
<view class="field disabled" wx:else>{{item.defaultValue || ''}}</view>
|
||||
<view class="error-info" wx:if="{{inputMap[item.id].error}}">{{inputMap[item.id].error}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- picker选择器 -->
|
||||
<view class="form-row flex-start" wx:elif="{{item.type==='picker'}}">
|
||||
<view class="form-lable {{item.isRequired?'required':''}}">{{item.lable}}</view>
|
||||
<view style="width:100%">
|
||||
<view wx:if="{{!item.disabled}}" class="picker-row {{item.disabled?'disabled':''}}">
|
||||
<picker class="field" range="{{item.range}}" disabled="{{item.disabled}}" id="{{item.id}}" value="{{pickerMap[item.id].idx}}"
|
||||
range-key="name" bindchange="onPickerChange">
|
||||
{{pickerMap[item.id].hasChoose?item.range[pickerMap[item.id].idx].name:'请选择'}}
|
||||
</picker>
|
||||
<van-icon class="row-icon" name="arrow"></van-icon>
|
||||
</view>
|
||||
<view class="field disabled" wx:else>{{pickerMap[item.id].hasChoose?item.range[pickerMap[item.id].idx].name:''}}</view>
|
||||
<view class="error-info" wx:if="{{pickerMap[item.id].error}}">{{pickerMap[item.id].error}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 日期选择器 -->
|
||||
<view class="form-row flex-start " wx:elif="{{item.type==='date'}}">
|
||||
<view class="form-lable {{item.isRequired?'required':''}}">{{item.lable}}</view>
|
||||
<view style="width:100%">
|
||||
<view class="picker-row">
|
||||
<view class="field {{item.disabled?'disabled':''}}" bindtap="datePickerShow" data-disabled="{{item.disabled}}" data-id="{{item.id}}">{{dateMap[item.id].hasChoose?(dateMap[item.id].config.endDate?dateMap[item.id].startDate+' ~ ' + dateMap[item.id].endDate: dateMap[item.id].startDate):'请选择'}}</view>
|
||||
<time-picker pickerShow="{{dateMap[item.id].show}}" id="{{item.id}}" wx:if="{{!isPickerRender}}" bind:hidePicker="datePickerHide"
|
||||
bind:setPickerTime="setPickerTime" config="{{dateMap[item.id].original.config}}"></time-picker>
|
||||
<van-icon class="row-icon" name="arrow"></van-icon>
|
||||
</view>
|
||||
<view class="error-info" wx:if="{{dateMap[item.id].error}}">{{dateMap[item.id].error}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 文本框 -->
|
||||
<view class="textarea-box" wx:elif="{{item.type==='textarea'}}">
|
||||
<view class="flex mb-24">
|
||||
<view class="area-lable {{item.isRequired?'required':''}}">{{item.lable}}</view>
|
||||
<view class="error-info" wx:if="{{inputMap[item.id].error}}">{{inputMap[item.id].error}}</view>
|
||||
</view>
|
||||
<view class="text-area {{item.disabled?'disabled':''}}">
|
||||
<textarea style="width:100%" value="{{item.defaultValue || ''}}" disabled="{{item.disabled}}" placeholder="{{inputMap[item.id].placeholder || '请填写内容'}}"
|
||||
id="{{item.id}}" bindinput="onInput" auto-height="{{true}}" maxlength="{{item.maxLength || -1}}" name=""
|
||||
cols="30" rows="10"></textarea>
|
||||
<view wx:if="{{item.maxLength}}" class="text-num">{{inputMap[item.id].value.length||0}}/{{item.maxLength}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 文件上传 -->
|
||||
<view class="img-box" wx:elif="{{item.type==='file'}}">
|
||||
<!-- <view class="area-lable mb-24 {{item.isRequired?'required':''}}">{{item.lable}}</view> -->
|
||||
<view class="flex mb-24">
|
||||
<view class="area-lable {{item.isRequired?'required':''}}">{{item.lable}}</view>
|
||||
<view class="error-info" wx:if="{{fileMap[item.id].error}}">{{fileMap[item.id].error}}</view>
|
||||
</view>
|
||||
<van-uploader file-list="{{ fileMap[item.id].list }}" preview-size="196rpx" max-count="{{fileMap[item.id].original.maxCount || 9}}"
|
||||
disabled="{{fileMap[item.id].original.disabled || false}}" accept="{{fileMap[item.id].original.accept || 'image'}}" id="{{item.id}}" multiple image-fit="aspectFill"
|
||||
bind:after-read="onFileRead" bind:delete="onFileDelete" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<button wx:if="{{showSubmitBtn}}" class='form-btn' bindtap="formSubmit">提交</button>
|
|
@ -0,0 +1,119 @@
|
|||
/* components/dynamicForm/index.wxss */
|
||||
.page{
|
||||
background-color: #eeeeef;
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
.flex{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.form-box{
|
||||
padding: 0 30rpx 0px;
|
||||
padding-right: 0;
|
||||
margin: 30rpx;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid #eee;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
.form-row{
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 25rpx 0;
|
||||
font-size: 30rpx;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.ipt-row{
|
||||
align-items: flex-start;
|
||||
}
|
||||
.flex-start{
|
||||
align-items: flex-start;
|
||||
}
|
||||
.picker-row{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.form-row:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
.form-lable{
|
||||
position: relative;
|
||||
min-width: 120rpx;
|
||||
max-width: 200rpx;
|
||||
word-break: break-all;
|
||||
padding-right: 30rpx;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
.field{
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
min-height: 45rpx;
|
||||
padding-right: 30rpx;
|
||||
box-sizing: border-box;
|
||||
line-height: 45rpx;
|
||||
}
|
||||
.error-info{
|
||||
font-size: 24rpx;
|
||||
color: #f00;
|
||||
}
|
||||
.required::before{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: -18rpx;
|
||||
transform: translateY(-35%);
|
||||
content: '*';
|
||||
color: #f00;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
.form-btn{
|
||||
margin: 60rpx auto;
|
||||
width: 400rpx;
|
||||
background-color: #17e;
|
||||
color: #fff;
|
||||
}
|
||||
.row-icon{
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
.img-box{
|
||||
padding-top: 25rpx;
|
||||
/* padding-bottom: 25rpx; */
|
||||
}
|
||||
.mb-24{
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.area-lable{
|
||||
position: relative;
|
||||
font-size: 30rpx;
|
||||
margin-right: 20rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.textarea-box{
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
.text-area{
|
||||
position: relative;
|
||||
width: 630rpx;
|
||||
padding: 20rpx;
|
||||
padding-bottom: 60rpx;
|
||||
font-size: 30rpx;
|
||||
min-height: 200rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8rpx;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.text-num{
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
bottom: 10px;
|
||||
text-align: right;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.disabled{
|
||||
opacity: 0.3;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
const formatTime = date => {
|
||||
if (!date) {
|
||||
date = new Date();
|
||||
}
|
||||
if(typeof date === 'string'){
|
||||
date = new Date(date);
|
||||
if(!date){
|
||||
date = new Date(date.replace(/-/g, '/'));//兼容IOS new Date()
|
||||
}
|
||||
}
|
||||
if(typeof date === 'number'){
|
||||
date = new Date(date);
|
||||
}
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate();
|
||||
const hour = date.getHours();
|
||||
const minute = date.getMinutes();
|
||||
const second = date.getSeconds();
|
||||
|
||||
return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':');
|
||||
};
|
||||
|
||||
const formatNumber = n => {
|
||||
n = n.toString();
|
||||
return n[1] ? n : '0' + n;
|
||||
};
|
||||
|
||||
export default formatTime;
|
|
@ -0,0 +1,7 @@
|
|||
export declare const RED = "#ee0a24";
|
||||
export declare const BLUE = "#1989fa";
|
||||
export declare const WHITE = "#fff";
|
||||
export declare const GREEN = "#07c160";
|
||||
export declare const ORANGE = "#ff976a";
|
||||
export declare const GRAY = "#323233";
|
||||
export declare const GRAY_DARK = "#969799";
|
|
@ -0,0 +1,7 @@
|
|||
export const RED = '#ee0a24';
|
||||
export const BLUE = '#1989fa';
|
||||
export const WHITE = '#fff';
|
||||
export const GREEN = '#07c160';
|
||||
export const ORANGE = '#ff976a';
|
||||
export const GRAY = '#323233';
|
||||
export const GRAY_DARK = '#969799';
|
|
@ -0,0 +1,3 @@
|
|||
import { VantComponentOptions, CombinedComponentInstance } from '../definitions/index';
|
||||
declare function VantComponent<Data, Props, Methods>(vantOptions?: VantComponentOptions<Data, Props, Methods, CombinedComponentInstance<Data, Props, Methods>>): void;
|
||||
export { VantComponent };
|
|
@ -0,0 +1,48 @@
|
|||
import { basic } from '../mixins/basic';
|
||||
import { observe } from '../mixins/observer/index';
|
||||
function mapKeys(source, target, map) {
|
||||
Object.keys(map).forEach(key => {
|
||||
if (source[key]) {
|
||||
target[map[key]] = source[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
function VantComponent(vantOptions = {}) {
|
||||
const options = {};
|
||||
mapKeys(vantOptions, options, {
|
||||
data: 'data',
|
||||
props: 'properties',
|
||||
mixins: 'behaviors',
|
||||
methods: 'methods',
|
||||
beforeCreate: 'created',
|
||||
created: 'attached',
|
||||
mounted: 'ready',
|
||||
relations: 'relations',
|
||||
destroyed: 'detached',
|
||||
classes: 'externalClasses'
|
||||
});
|
||||
const { relation } = vantOptions;
|
||||
if (relation) {
|
||||
options.relations = Object.assign(options.relations || {}, {
|
||||
[`../${relation.name}/index`]: relation
|
||||
});
|
||||
}
|
||||
// add default externalClasses
|
||||
options.externalClasses = options.externalClasses || [];
|
||||
options.externalClasses.push('custom-class');
|
||||
// add default behaviors
|
||||
options.behaviors = options.behaviors || [];
|
||||
options.behaviors.push(basic);
|
||||
// map field to form-field behavior
|
||||
if (vantOptions.field) {
|
||||
options.behaviors.push('wx://form-field');
|
||||
}
|
||||
// add default options
|
||||
options.options = {
|
||||
multipleSlots: true,
|
||||
addGlobalClass: true
|
||||
};
|
||||
observe(vantOptions, options);
|
||||
Component(options);
|
||||
}
|
||||
export { VantComponent };
|
|
@ -0,0 +1 @@
|
|||
.van-ellipsis{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.van-multi-ellipsis--l2{-webkit-line-clamp:2}.van-multi-ellipsis--l2,.van-multi-ellipsis--l3{display:-webkit-box;overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical}.van-multi-ellipsis--l3{-webkit-line-clamp:3}.van-clearfix:after{display:table;clear:both;content:""}.van-hairline,.van-hairline--bottom,.van-hairline--left,.van-hairline--right,.van-hairline--surround,.van-hairline--top,.van-hairline--top-bottom{position:relative}.van-hairline--bottom:after,.van-hairline--left:after,.van-hairline--right:after,.van-hairline--surround:after,.van-hairline--top-bottom:after,.van-hairline--top:after,.van-hairline:after{position:absolute;box-sizing:border-box;-webkit-transform-origin:center;transform-origin:center;content:" ";pointer-events:none;top:-50%;right:-50%;bottom:-50%;left:-50%;border:0 solid #eee;-webkit-transform:scale(.5);transform:scale(.5)}.van-hairline--top:after{border-top-width:1px}.van-hairline--left:after{border-left-width:1px}.van-hairline--right:after{border-right-width:1px}.van-hairline--bottom:after{border-bottom-width:1px}.van-hairline--top-bottom:after{border-width:1px 0}.van-hairline--surround:after{border-width:1px}
|
|
@ -0,0 +1 @@
|
|||
.van-clearfix:after{display:table;clear:both;content:""}
|
|
@ -0,0 +1 @@
|
|||
.van-ellipsis{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.van-multi-ellipsis--l2{-webkit-line-clamp:2}.van-multi-ellipsis--l2,.van-multi-ellipsis--l3{display:-webkit-box;overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical}.van-multi-ellipsis--l3{-webkit-line-clamp:3}
|
|
@ -0,0 +1 @@
|
|||
.van-hairline,.van-hairline--bottom,.van-hairline--left,.van-hairline--right,.van-hairline--surround,.van-hairline--top,.van-hairline--top-bottom{position:relative}.van-hairline--bottom:after,.van-hairline--left:after,.van-hairline--right:after,.van-hairline--surround:after,.van-hairline--top-bottom:after,.van-hairline--top:after,.van-hairline:after{position:absolute;box-sizing:border-box;-webkit-transform-origin:center;transform-origin:center;content:" ";pointer-events:none;top:-50%;right:-50%;bottom:-50%;left:-50%;border:0 solid #eee;-webkit-transform:scale(.5);transform:scale(.5)}.van-hairline--top:after{border-top-width:1px}.van-hairline--left:after{border-left-width:1px}.van-hairline--right:after{border-right-width:1px}.van-hairline--bottom:after{border-bottom-width:1px}.van-hairline--top-bottom:after{border-width:1px 0}.van-hairline--surround:after{border-width:1px}
|
|
@ -0,0 +1,8 @@
|
|||
/// <reference types="miniprogram-api-typings" />
|
||||
export declare function isDef(value: any): boolean;
|
||||
export declare function isObj(x: any): boolean;
|
||||
export declare function isNumber(value: any): boolean;
|
||||
export declare function range(num: number, min: number, max: number): number;
|
||||
export declare function nextTick(fn: Function): void;
|
||||
export declare function getSystemInfoSync(): WechatMiniprogram.GetSystemInfoSuccessCallbackResult;
|
||||
export declare function addUnit(value?: string | number): string | undefined;
|
|
@ -0,0 +1,32 @@
|
|||
export function isDef(value) {
|
||||
return value !== undefined && value !== null;
|
||||
}
|
||||
export function isObj(x) {
|
||||
const type = typeof x;
|
||||
return x !== null && (type === 'object' || type === 'function');
|
||||
}
|
||||
export function isNumber(value) {
|
||||
return /^\d+(\.\d+)?$/.test(value);
|
||||
}
|
||||
export function range(num, min, max) {
|
||||
return Math.min(Math.max(num, min), max);
|
||||
}
|
||||
export function nextTick(fn) {
|
||||
setTimeout(() => {
|
||||
fn();
|
||||
}, 1000 / 30);
|
||||
}
|
||||
let systemInfo = null;
|
||||
export function getSystemInfoSync() {
|
||||
if (systemInfo == null) {
|
||||
systemInfo = wx.getSystemInfoSync();
|
||||
}
|
||||
return systemInfo;
|
||||
}
|
||||
export function addUnit(value) {
|
||||
if (!isDef(value)) {
|
||||
return undefined;
|
||||
}
|
||||
value = String(value);
|
||||
return isNumber(value) ? `${value}px` : value;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export {};
|
|
@ -0,0 +1,27 @@
|
|||
import { VantComponent } from '../common/component';
|
||||
VantComponent({
|
||||
props: {
|
||||
dot: Boolean,
|
||||
info: null,
|
||||
size: null,
|
||||
color: String,
|
||||
customStyle: String,
|
||||
classPrefix: {
|
||||
type: String,
|
||||
value: 'van-icon'
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
observer(val) {
|
||||
this.setData({
|
||||
isImageName: val.indexOf('/') !== -1
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('click');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"van-info": "../info/index"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<!-- <wxs src="../wxs/utils.wxs" module="utils" /> -->
|
||||
|
||||
<view
|
||||
class="custom-class {{ classPrefix }} {{ isImageName ? 'van-icon--image' : classPrefix + '-' + name }}"
|
||||
style="color: {{ color }};font-size: {{ size }}rpx;{{ customStyle }}"
|
||||
bind:tap="onClick"
|
||||
>
|
||||
<!-- <van-info
|
||||
wx:if="{{ info !== null || dot }}"
|
||||
dot="{{ dot }}"
|
||||
info="{{ info }}"
|
||||
custom-class="van-icon__info"
|
||||
/> -->
|
||||
<image
|
||||
wx:if="{{ isImageName }}"
|
||||
src="{{ name }}"
|
||||
mode="aspectFit"
|
||||
class="van-icon__image"
|
||||
/>
|
||||
</view>
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
export {};
|
|
@ -0,0 +1,12 @@
|
|||
import { VantComponent } from '../common/component';
|
||||
|
||||
VantComponent({
|
||||
props: {
|
||||
dot: Boolean,
|
||||
info: null,
|
||||
customStyle: String
|
||||
},
|
||||
data: {
|
||||
// classBem: utils.bem('info', { dot })
|
||||
}
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<!-- <wxs src="../wxs/utils.wxs" module="utils" /> -->
|
||||
|
||||
<view
|
||||
wx:if="{{ info !== null && info !== '' || dot }}"
|
||||
class="custom-class van-info "
|
||||
style="{{ customStyle }}"
|
||||
>{{ dot ? '' : info }}</view>
|
||||
<!-- <view>{{ utils.bem('info', { dot }) }}</view> -->
|
|
@ -0,0 +1 @@
|
|||
@import '../common/index.wxss';.van-info{position:absolute;top:0;right:0;box-sizing:border-box;white-space:nowrap;text-align:center;-webkit-transform:translate(50%,-50%);transform:translate(50%,-50%);-webkit-transform-origin:100%;transform-origin:100%;min-width:16px;min-width:var(--info-size,16px);padding:0 3px;padding:var(--info-padding,0 3px);color:#fff;color:var(--info-color,#fff);font-weight:500;font-weight:var(--info-font-weight,500);font-size:12px;font-size:var(--info-font-size,12px);font-family:PingFang SC,Helvetica Neue,Arial,sans-serif;font-family:var(--info-font-family,PingFang SC,Helvetica Neue,Arial,sans-serif);line-height:14px;line-height:calc(var(--info-size, 16px) - var(--info-border-width, 1px)*2);background-color:#ee0a24;background-color:var(--info-background-color,#ee0a24);border:1px solid #fff;border:var(--info-border-width,1px) solid var(--white,#fff);border-radius:16px;border-radius:var(--info-size,16px)}.van-info--dot{min-width:0;border-radius:100%;width:8px;width:var(--info-dot-size,8px);height:8px;height:var(--info-dot-size,8px);background-color:#ee0a24;background-color:var(--info-dot-color,#ee0a24)}
|
|
@ -0,0 +1,22 @@
|
|||
export var basic = Behavior({
|
||||
methods: {
|
||||
$emit: function $emit() {
|
||||
this.triggerEvent.apply(this, arguments);
|
||||
},
|
||||
getRect: function getRect(selector, all) {
|
||||
var _this = this;
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
wx.createSelectorQuery().in(_this)[all ? 'selectAll' : 'select'](selector).boundingClientRect(function (rect) {
|
||||
if (all && Array.isArray(rect) && rect.length) {
|
||||
resolve(rect);
|
||||
}
|
||||
|
||||
if (!all && rect) {
|
||||
resolve(rect);
|
||||
}
|
||||
}).exec();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
export var button = Behavior({
|
||||
properties: {
|
||||
id: String,
|
||||
sessionFrom: String,
|
||||
appParameter: String,
|
||||
sendMessageImg: String,
|
||||
sendMessagePath: String,
|
||||
showMessageCard: String,
|
||||
sendMessageTitle: String,
|
||||
lang: {
|
||||
type: String,
|
||||
value: 'en'
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
var isIPhoneX = null;
|
||||
|
||||
function getIsIPhoneX() {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (isIPhoneX !== null) {
|
||||
resolve(isIPhoneX);
|
||||
} else {
|
||||
wx.getSystemInfo({
|
||||
success: function success(_ref) {
|
||||
var model = _ref.model,
|
||||
screenHeight = _ref.screenHeight;
|
||||
var iphoneX = /iphone x/i.test(model);
|
||||
var iphoneNew = /iPhone11/i.test(model) && screenHeight === 812;
|
||||
isIPhoneX = iphoneX || iphoneNew;
|
||||
resolve(isIPhoneX);
|
||||
},
|
||||
fail: reject
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export var iphonex = Behavior({
|
||||
properties: {
|
||||
safeAreaInsetBottom: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
}
|
||||
},
|
||||
created: function created() {
|
||||
var _this = this;
|
||||
|
||||
getIsIPhoneX().then(function (isIPhoneX) {
|
||||
_this.set({
|
||||
isIPhoneX: isIPhoneX
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
export var link = Behavior({
|
||||
properties: {
|
||||
url: String,
|
||||
linkType: {
|
||||
type: String,
|
||||
value: 'navigateTo'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
jumpLink: function jumpLink(urlKey) {
|
||||
if (urlKey === void 0) {
|
||||
urlKey = 'url';
|
||||
}
|
||||
|
||||
var url = this.data[urlKey];
|
||||
|
||||
if (url) {
|
||||
wx[this.data.linkType]({
|
||||
url: url
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,43 @@
|
|||
export var behavior = Behavior({
|
||||
created: function created() {
|
||||
var _this = this;
|
||||
|
||||
if (!this.$options) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cache = {};
|
||||
|
||||
var _this$$options = this.$options(),
|
||||
computed = _this$$options.computed;
|
||||
|
||||
var keys = Object.keys(computed);
|
||||
|
||||
this.calcComputed = function () {
|
||||
var needUpdate = {};
|
||||
keys.forEach(function (key) {
|
||||
var value = computed[key].call(_this);
|
||||
|
||||
if (cache[key] !== value) {
|
||||
cache[key] = needUpdate[key] = value;
|
||||
}
|
||||
});
|
||||
return needUpdate;
|
||||
};
|
||||
},
|
||||
attached: function attached() {
|
||||
this.set();
|
||||
},
|
||||
methods: {
|
||||
// set data and set computed data
|
||||
set: function set(data, callback) {
|
||||
if (data) {
|
||||
this.setData(data, callback);
|
||||
}
|
||||
|
||||
if (this.calcComputed) {
|
||||
this.setData(this.calcComputed());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
import { behavior } from './behavior';
|
||||
import { observeProps } from './props';
|
||||
export function observe(vantOptions, options) {
|
||||
var watch = vantOptions.watch,
|
||||
computed = vantOptions.computed;
|
||||
options.behaviors.push(behavior);
|
||||
|
||||
if (watch) {
|
||||
var props = options.properties || {};
|
||||
Object.keys(watch).forEach(function (key) {
|
||||
if (key in props) {
|
||||
var prop = props[key];
|
||||
|
||||
if (prop === null || !('type' in prop)) {
|
||||
prop = {
|
||||
type: prop
|
||||
};
|
||||
}
|
||||
|
||||
prop.observer = watch[key];
|
||||
props[key] = prop;
|
||||
}
|
||||
});
|
||||
options.properties = props;
|
||||
}
|
||||
|
||||
if (computed) {
|
||||
options.methods = options.methods || {};
|
||||
|
||||
options.methods.$options = function () {
|
||||
return vantOptions;
|
||||
};
|
||||
|
||||
if (options.properties) {
|
||||
observeProps(options.properties);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
export function observeProps(props) {
|
||||
if (!props) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(props).forEach(function (key) {
|
||||
var prop = props[key];
|
||||
|
||||
if (prop === null || !('type' in prop)) {
|
||||
prop = {
|
||||
type: prop
|
||||
};
|
||||
}
|
||||
|
||||
var _prop = prop,
|
||||
observer = _prop.observer;
|
||||
|
||||
prop.observer = function () {
|
||||
if (observer) {
|
||||
if (typeof observer === 'string') {
|
||||
observer = this[observer];
|
||||
}
|
||||
|
||||
observer.apply(this, arguments);
|
||||
}
|
||||
|
||||
this.set();
|
||||
};
|
||||
|
||||
props[key] = prop;
|
||||
});
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
export var openType = Behavior({
|
||||
properties: {
|
||||
openType: String
|
||||
},
|
||||
methods: {
|
||||
bindGetUserInfo: function bindGetUserInfo(event) {
|
||||
this.$emit('getuserinfo', event.detail);
|
||||
},
|
||||
bindContact: function bindContact(event) {
|
||||
this.$emit('contact', event.detail);
|
||||
},
|
||||
bindGetPhoneNumber: function bindGetPhoneNumber(event) {
|
||||
this.$emit('getphonenumber', event.detail);
|
||||
},
|
||||
bindOpenSetting: function bindOpenSetting(event) {
|
||||
this.$emit('opensetting', event.detail);
|
||||
},
|
||||
bindError: function bindError(event) {
|
||||
this.$emit('error', event.detail);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
export var touch = Behavior({
|
||||
methods: {
|
||||
touchStart: function touchStart(event) {
|
||||
this.direction = '';
|
||||
this.deltaX = 0;
|
||||
this.deltaY = 0;
|
||||
this.offsetX = 0;
|
||||
this.offsetY = 0;
|
||||
this.startX = event.touches[0].clientX;
|
||||
this.startY = event.touches[0].clientY;
|
||||
},
|
||||
touchMove: function touchMove(event) {
|
||||
var touch = event.touches[0];
|
||||
this.deltaX = touch.clientX - this.startX;
|
||||
this.deltaY = touch.clientY - this.startY;
|
||||
this.offsetX = Math.abs(this.deltaX);
|
||||
this.offsetY = Math.abs(this.deltaY);
|
||||
this.direction = this.offsetX > this.offsetY ? 'horizontal' : this.offsetX < this.offsetY ? 'vertical' : '';
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
export var transition = function transition(showDefaultValue) {
|
||||
return Behavior({
|
||||
properties: {
|
||||
customStyle: String,
|
||||
show: {
|
||||
type: Boolean,
|
||||
value: showDefaultValue,
|
||||
observer: 'observeShow'
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
value: 300
|
||||
}
|
||||
},
|
||||
data: {
|
||||
type: '',
|
||||
inited: false,
|
||||
display: false,
|
||||
supportAnimation: true
|
||||
},
|
||||
attached: function attached() {
|
||||
if (this.data.show) {
|
||||
this.show();
|
||||
}
|
||||
|
||||
this.detectSupport();
|
||||
},
|
||||
methods: {
|
||||
detectSupport: function detectSupport() {
|
||||
var _this = this;
|
||||
|
||||
wx.getSystemInfo({
|
||||
success: function success(info) {
|
||||
if (info && info.system && info.system.indexOf('iOS 8') === 0) {
|
||||
_this.set({
|
||||
supportAnimation: false
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
observeShow: function observeShow(value) {
|
||||
if (value) {
|
||||
this.show();
|
||||
} else {
|
||||
if (this.data.supportAnimation) {
|
||||
this.set({
|
||||
type: 'leave'
|
||||
});
|
||||
} else {
|
||||
this.set({
|
||||
display: false
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
show: function show() {
|
||||
this.set({
|
||||
inited: true,
|
||||
display: true,
|
||||
type: 'enter'
|
||||
});
|
||||
},
|
||||
onAnimationEnd: function onAnimationEnd() {
|
||||
if (!this.data.show) {
|
||||
this.set({
|
||||
display: false
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export {};
|
|
@ -0,0 +1,180 @@
|
|||
import { VantComponent } from '../common/component';
|
||||
import { isImageFile } from './utils';
|
||||
|
||||
VantComponent({
|
||||
props: {
|
||||
disabled: Boolean,
|
||||
multiple: Boolean,
|
||||
uploadText: String,
|
||||
useBeforeRead: Boolean,
|
||||
previewSize: {
|
||||
type: null,
|
||||
value: 90
|
||||
},
|
||||
name: {
|
||||
type: [Number, String],
|
||||
value: ''
|
||||
},
|
||||
accept: {
|
||||
type: String,
|
||||
value: 'image'
|
||||
},
|
||||
sizeType: {
|
||||
type: Array,
|
||||
value: ['original', 'compressed']
|
||||
},
|
||||
capture: {
|
||||
type: Array,
|
||||
value: ['album', 'camera']
|
||||
},
|
||||
fileList: {
|
||||
type: Array,
|
||||
value: [],
|
||||
observer: 'formatFileList'
|
||||
},
|
||||
maxSize: {
|
||||
type: Number,
|
||||
value: Number.MAX_VALUE
|
||||
},
|
||||
maxCount: {
|
||||
type: Number,
|
||||
value: 100
|
||||
},
|
||||
deletable: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
previewImage: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
previewFullImage: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
imageFit: {
|
||||
type: String,
|
||||
value: 'scaleToFill'
|
||||
}
|
||||
},
|
||||
data: {
|
||||
lists: [],
|
||||
computedPreviewSize: '',
|
||||
isInCount: true
|
||||
},
|
||||
methods: {
|
||||
formatFileList() {
|
||||
const { fileList = [], maxCount } = this.data;
|
||||
const lists = fileList.map(item => (Object.assign(Object.assign({}, item), { isImage: typeof item.isImage === 'undefined' ? isImageFile(item) : item.isImage })));
|
||||
this.setData({ lists, isInCount: lists.length < maxCount });
|
||||
},
|
||||
startUpload() {
|
||||
if (this.data.disabled)
|
||||
return;
|
||||
const { name = '', capture, maxCount, multiple, maxSize, accept, sizeType,videoCfg={}, lists, useBeforeRead = false // 是否定义了 beforeRead
|
||||
} = this.data;
|
||||
let chooseFile = null;
|
||||
const newMaxCount = maxCount - lists.length;
|
||||
// 设置为只选择图片的时候使用 chooseImage 来实现
|
||||
if (accept === 'image') {
|
||||
chooseFile = new Promise((resolve, reject) => {
|
||||
wx.chooseImage({
|
||||
count: multiple ? (newMaxCount > 9 ? 9 : newMaxCount) : 1,
|
||||
sourceType: capture,
|
||||
sizeType,
|
||||
success: resolve,
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (accept === 'video'){
|
||||
chooseFile = new Promise((resolve, reject) => {
|
||||
wx.chooseMedia({
|
||||
count: videoCfg.count || 9,
|
||||
mediaType: ['video'],
|
||||
sourceType: videoCfg.sourceType || ['album', 'camera'],
|
||||
maxDuration: videoCfg.maxDuration || 10,
|
||||
camera: videoCfg.camera || 'back',
|
||||
success: (res)=>{
|
||||
res.isVideo = true;
|
||||
resolve(res);
|
||||
},
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
chooseFile = new Promise((resolve, reject) => {
|
||||
wx.chooseMessageFile({
|
||||
count: multiple ? newMaxCount : 1,
|
||||
type: 'file',
|
||||
success: resolve,
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
}
|
||||
chooseFile
|
||||
.then((res) => {
|
||||
const file = multiple ? res.tempFiles : res.tempFiles[0];
|
||||
// 检查文件大小
|
||||
if (file instanceof Array) {
|
||||
const sizeEnable = file.every(item => item.size <= maxSize);
|
||||
if (!sizeEnable) {
|
||||
this.$emit('oversize', { name });
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (file.size > maxSize) {
|
||||
this.$emit('oversize', { name });
|
||||
return;
|
||||
}
|
||||
let upData = { file, name };
|
||||
if(res.isVideo){
|
||||
file.map(val=>{
|
||||
val.isVideo = true;
|
||||
return val;
|
||||
});
|
||||
}
|
||||
// 触发上传之前的钩子函数
|
||||
if (useBeforeRead) {
|
||||
this.$emit('before-read', {
|
||||
file,
|
||||
name,
|
||||
callback: (result) => {
|
||||
if (result) {
|
||||
// 开始上传
|
||||
this.$emit('after-read', upData);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.$emit('after-read', upData);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.$emit('error', error);
|
||||
});
|
||||
},
|
||||
deleteItem(event) {
|
||||
const { index } = event.currentTarget.dataset;
|
||||
this.$emit('delete', { index, name: this.data.name });
|
||||
},
|
||||
doPreviewImage(event) {
|
||||
if (!this.data.previewFullImage)
|
||||
return;
|
||||
const curUrl = event.currentTarget.dataset.url;
|
||||
const images = this.data.lists
|
||||
.filter(item => item.isImage)
|
||||
.map(item => item.url || item.path);
|
||||
this.$emit('click-preview', { url: curUrl, name: this.data.name });
|
||||
wx.previewImage({
|
||||
urls: images,
|
||||
current: curUrl,
|
||||
fail() {
|
||||
wx.showToast({ title: '预览图片失败', icon: 'none' });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"van-icon": "../icon/index"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<!-- <wxs src="../wxs/utils.wxs" module="utils" /> -->
|
||||
|
||||
<view class="van-uploader">
|
||||
<view class="van-uploader__wrapper">
|
||||
<!-- 预览样式 -->
|
||||
<view
|
||||
wx:if="{{ previewImage }}"
|
||||
wx:for="{{ lists }}"
|
||||
wx:key="index"
|
||||
class="van-uploader__preview"
|
||||
>
|
||||
<image
|
||||
wx:if="{{ item.isImage }}"
|
||||
mode="{{ imageFit }}"
|
||||
src="{{ item.url || item.path }}"
|
||||
alt="{{ item.name || ('图片' + index) }}"
|
||||
class="van-uploader__preview-image"
|
||||
style="width: {{ previewSize }}; height: {{ previewSize }};"
|
||||
data-url="{{ item.url || item.path }}"
|
||||
bind:tap="doPreviewImage"
|
||||
/>
|
||||
<view
|
||||
wx:elif="{{ item.isVideo }}"
|
||||
class="van-uploader__preview"
|
||||
style="width:280rpx; height:280rpx;"
|
||||
class="van-uploader__preview"
|
||||
>
|
||||
<video class="van-uploader__preview-video" data-url="{{ item.url || item.path }}" style="width:280rpx; height:280rpx;" src="{{ item.url || item.tempFilePath }}" controls></video>
|
||||
</view>
|
||||
<view
|
||||
wx:else
|
||||
class="van-uploader__file"
|
||||
style="width: {{ previewSize }}; height: {{ previewSize }};"
|
||||
>
|
||||
<van-icon name="description" class="van-uploader__file-icon" />
|
||||
<view class="van-uploader__file-name van-ellipsis">{{ item.name || item.url || item.path }}</view>
|
||||
</view>
|
||||
<van-icon
|
||||
wx:if="{{ deletable && !disabled }}"
|
||||
name="clear"
|
||||
size='40'
|
||||
class="van-uploader__preview-delete"
|
||||
data-index="{{ index }}"
|
||||
bind:tap="deleteItem"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 上传样式 -->
|
||||
<block wx:if="{{ isInCount && !disabled }}">
|
||||
<view class="van-uploader__slot" bind:tap="startUpload">
|
||||
<slot />
|
||||
</view>
|
||||
|
||||
<!-- 默认上传样式 -->
|
||||
<view
|
||||
class="van-uploader__upload"
|
||||
style="width: {{ previewSize }}; height: {{ previewSize }};"
|
||||
bind:tap="startUpload"
|
||||
>
|
||||
<van-icon name="plus" size='40' class="van-uploader__upload-icon" />
|
||||
<text wx:if="{{ uploadText }}" class="van-uploader__upload-text">{{ uploadText }}</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
|
@ -0,0 +1,3 @@
|
|||
@import '../common/index.wxss';.van-uploader{position:relative;display:inline-block}.van-uploader__wrapper{display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap}.van-uploader__slot:empty{display:none}.van-uploader__slot:not(:empty)+.van-uploader__upload{display:none!important}.van-uploader__upload{position:relative;display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;box-sizing:border-box;width:80px;height:80px;margin:0 20rpx 20rpx 0;background-color:#fff;border:2px dashed #e0e0f0;border-radius:8px}.van-uploader__upload-icon{display:inline-block;width:48rpx;height:48rpx;color:#969799;font-size:24px}.van-uploader__upload-text{margin-top:8px;color:#969799;font-size:12px}.van-uploader__preview{position:relative;margin:0 20rpx 20rpx 0}.van-uploader__preview-image{display:block;width:80px;height:80px;border-radius:8px}
|
||||
.van-uploader__preview-video{display:block;width:80px;height:80px;border-radius:8px;overflow: hidden}
|
||||
.van-uploader__preview-delete{position:absolute;top:-8px;right:-8px;color:#969799;font-size:18px;background-color:#fff;border-radius:100%}.van-uploader__file{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;width:80px;height:80px;background-color:#f7f8fa;border-radius:8px}.van-uploader__file-icon{display:inline-block;width:20px;height:20px;color:#646566;font-size:20px}.van-uploader__file-name{box-sizing:border-box;width:100%;margin-top:8px;padding:0 5px;color:#646566;font-size:12px;text-align:center}
|
|
@ -0,0 +1,12 @@
|
|||
interface File {
|
||||
path: string;
|
||||
url: string;
|
||||
size: number;
|
||||
name: string;
|
||||
type: string;
|
||||
time: number;
|
||||
image: boolean;
|
||||
}
|
||||
export declare function isImageUrl(url: string): boolean;
|
||||
export declare function isImageFile(item: File): boolean;
|
||||
export {};
|
|
@ -0,0 +1,16 @@
|
|||
const IMAGE_EXT = ['jpeg', 'jpg', 'gif', 'png', 'svg'];
|
||||
export function isImageUrl(url) {
|
||||
return IMAGE_EXT.some(ext => url.indexOf(`.${ext}`) !== -1);
|
||||
}
|
||||
export function isImageFile(item) {
|
||||
if (item.type) {
|
||||
return item.type.indexOf('image') === 0;
|
||||
}
|
||||
if (item.path) {
|
||||
return isImageUrl(item.path);
|
||||
}
|
||||
if (item.url) {
|
||||
return isImageUrl(item.url);
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/* eslint-disable */
|
||||
var REGEXP = getRegExp('^\d+(\.\d+)?$');
|
||||
|
||||
function addUnit(value) {
|
||||
if (value == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return REGEXP.test('' + value) ? value + 'px' : value;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addUnit: addUnit
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
function isArray(array) {
|
||||
return array && array.constructor === 'Array';
|
||||
}
|
||||
|
||||
module.exports.isArray = isArray;
|
|
@ -0,0 +1,38 @@
|
|||
var array = require('./array.wxs');
|
||||
var object = require('./object.wxs');
|
||||
var PREFIX = 'van-';
|
||||
|
||||
function join(name, mods) {
|
||||
name = PREFIX + name;
|
||||
mods = mods.map(function(mod) {
|
||||
return name + '--' + mod;
|
||||
});
|
||||
mods.unshift(name);
|
||||
return mods.join(' ');
|
||||
}
|
||||
|
||||
function traversing(mods, conf) {
|
||||
if (!conf) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof conf === 'string' || typeof conf === 'number') {
|
||||
mods.push(conf);
|
||||
} else if (array.isArray(conf)) {
|
||||
conf.forEach(function(item) {
|
||||
traversing(mods, item);
|
||||
});
|
||||
} else if (typeof conf === 'object') {
|
||||
object.keys(conf).forEach(function(key) {
|
||||
conf[key] && mods.push(key);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function bem(name, conf) {
|
||||
var mods = [];
|
||||
traversing(mods, conf);
|
||||
return join(name, mods);
|
||||
}
|
||||
|
||||
module.exports.bem = bem;
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* Simple memoize
|
||||
* wxs doesn't support fn.apply, so this memoize only support up to 2 args
|
||||
*/
|
||||
|
||||
function isPrimitive(value) {
|
||||
var type = typeof value;
|
||||
return (
|
||||
type === 'boolean' ||
|
||||
type === 'number' ||
|
||||
type === 'string' ||
|
||||
type === 'undefined' ||
|
||||
value === null
|
||||
);
|
||||
}
|
||||
|
||||
// mock simple fn.call in wxs
|
||||
function call(fn, args) {
|
||||
if (args.length === 2) {
|
||||
return fn(args[0], args[1]);
|
||||
}
|
||||
|
||||
if (args.length === 1) {
|
||||
return fn(args[0]);
|
||||
}
|
||||
|
||||
return fn();
|
||||
}
|
||||
|
||||
function serializer(args) {
|
||||
if (args.length === 1 && isPrimitive(args[0])) {
|
||||
return args[0];
|
||||
}
|
||||
var obj = {};
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
obj['key' + i] = args[i];
|
||||
}
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
|
||||
function memoize(fn) {
|
||||
var cache = {};
|
||||
|
||||
return function() {
|
||||
var key = serializer(arguments);
|
||||
if (cache[key] === undefined) {
|
||||
cache[key] = call(fn, arguments);
|
||||
}
|
||||
|
||||
return cache[key];
|
||||
};
|
||||
}
|
||||
|
||||
module.exports.memoize = memoize;
|
|
@ -0,0 +1,13 @@
|
|||
/* eslint-disable */
|
||||
var REGEXP = getRegExp('{|}|"', 'g');
|
||||
|
||||
function keys(obj) {
|
||||
return JSON.stringify(obj)
|
||||
.replace(REGEXP, '')
|
||||
.split(',')
|
||||
.map(function(item) {
|
||||
return item.split(':')[0];
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.keys = keys;
|
|
@ -0,0 +1,10 @@
|
|||
/* eslint-disable */
|
||||
var bem = require('./bem.wxs').bem;
|
||||
var memoize = require('./memoize.wxs').memoize;
|
||||
var addUnit = require('./add-unit.wxs').addUnit;
|
||||
|
||||
module.exports = {
|
||||
bem: memoize(bem),
|
||||
memoize: memoize,
|
||||
addUnit: addUnit
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621915089686" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2807" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M762.78272 154.624h-44.032a96.81408 96.81408 0 0 0-46.92992-51.31776A98.06848 98.06848 0 0 0 578.92352 35.84H445.07648a98.06848 98.06848 0 0 0-92.88192 67.49184A96.81408 96.81408 0 0 0 305.25952 154.624h-44.032C176.47104 154.624 107.52 226.26816 107.52 314.31168v519.30624C107.52 921.6512 176.47104 993.28 261.21728 993.28h501.56544C847.52896 993.28 916.48 921.6512 916.48 833.61792V314.31168c0-88.04352-68.95104-159.68768-153.69728-159.68768z m-372.736 8.86784a34.92864 34.92864 0 0 0 27.21792-31.744 27.96544 27.96544 0 0 1 27.80672-26.0352h133.84704a27.96032 27.96032 0 0 1 27.80672 26.0352 34.93376 34.93376 0 0 0 27.21792 31.744 27.93984 27.93984 0 0 1-6.144 55.1936H396.19072a27.93984 27.93984 0 0 1-6.13888-55.168z m456.59648 670.12608c0 49.50016-37.62176 89.77408-83.86048 89.77408H261.21728c-46.23872 0-83.86048-40.27392-83.86048-89.77408V314.31168c0-49.50528 37.62176-89.7792 83.86048-89.7792h43.32544a97.84832 97.84832 0 0 0 91.648 64.06144h231.62368a97.8432 97.8432 0 0 0 91.648-64.06144h43.33056c46.23872 0 83.86048 40.27392 83.86048 89.7792v519.30624zM739.84 375.65952H279.79264a34.944 34.944 0 0 0 0 69.888H739.84a34.944 34.944 0 0 0 0-69.888z m-460.04736 254.64832h324.74112a34.944 34.944 0 0 0 0-69.88288H279.79264a34.944 34.944 0 0 0 0 69.88288z m383.24736 114.87744H279.79264a34.944 34.944 0 0 0 0 69.88288H663.04a34.944 34.944 0 0 0 0-69.88288z" fill="#595BB3" p-id="2808"></path></svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621915136519" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4543" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M692.949333 838.101333c30.933333 17.408 61.653333 26.197333 92.117334 26.197334 30.464 0 61.184-8.789333 92.117333-26.197334V960l-83.712-44.202667a15.530667 15.530667 0 0 0-14.250667-0.128l-86.4 44.202667v-121.770667h0.128zM810.666667 106.666667a42.666667 42.666667 0 0 1 42.666666 42.666666v298.666667h-128a170.666667 170.666667 0 0 0-170.453333 162.133333l-0.213333 8.533334v298.666666H213.333333a42.666667 42.666667 0 0 1-42.666666-42.666666v-725.333334a42.666667 42.666667 0 0 1 42.666666-42.666666h597.333334z m-25.6 426.666666c84.906667 0 153.6 68.138667 153.6 152.405334 0 84.224-68.693333 152.362667-153.6 152.362666s-153.6-68.138667-153.6-152.362666c0-84.266667 68.693333-152.405333 153.6-152.405334z m0 60.885334l-32.554667 47.018666-55.04 16.213334 35.114667 45.226666-1.536 57.002667 54.016-19.029333 54.186666 19.029333-1.536-57.002667 35.114667-45.226666-55.210667-16.213334-32.554666-47.018666zM490.666667 405.333333h-170.666667a21.333333 21.333333 0 0 0-20.992 17.493334L298.666667 426.666667v21.333333a21.333333 21.333333 0 0 0 17.493333 20.992L320 469.333333h170.666667a21.333333 21.333333 0 0 0 20.992-17.493333L512 448V426.666667a21.333333 21.333333 0 0 0-21.333333-21.333334z m128-170.666666h-298.666667a21.333333 21.333333 0 0 0-20.992 17.493333L298.666667 256v21.333333a21.333333 21.333333 0 0 0 17.493333 20.992L320 298.666667h298.666667a21.333333 21.333333 0 0 0 20.992-17.493334L640 277.333333V256a21.333333 21.333333 0 0 0-21.333333-21.333333z" fill="#d81e06" p-id="4544"></path></svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,68 @@
|
|||
// pages/admin/exam/add.js
|
||||
Page({
|
||||
|
||||
/**
|
||||
* 页面的初始数据
|
||||
*/
|
||||
data: {
|
||||
form:{
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad: function (options) {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面初次渲染完成
|
||||
*/
|
||||
onReady: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面隐藏
|
||||
*/
|
||||
onHide: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面卸载
|
||||
*/
|
||||
onUnload: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面相关事件处理函数--监听用户下拉动作
|
||||
*/
|
||||
onPullDownRefresh: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面上拉触底事件的处理函数
|
||||
*/
|
||||
onReachBottom: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户点击右上角分享
|
||||
*/
|
||||
onShareAppMessage: function () {
|
||||
|
||||
}
|
||||
})
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"usingComponents": {}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<!--pages/admin/exam/add.wxml-->
|
||||
<view class="page__title" style="text-align:center">新建考试</view>
|
||||
<view class="weui-cells__title">基本信息</view>
|
||||
<view class="weui-cells weui-cells_form">
|
||||
<view class="weui-cell weui-cell_active">
|
||||
<view class="weui-cell__hd"><label class="weui-label">考试名称</label></view>
|
||||
<view class="weui-cell__bd">
|
||||
<input class="weui-input" placeholder="填写考试名称" placeholder-class="weui-input__placeholder"
|
||||
value="{{form.name}}" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="weui-cell weui-cell_active">
|
||||
<view class="weui-cell__hd">
|
||||
<label class="weui-label">考试地点</label>
|
||||
</view>
|
||||
<view class="weui-cell__bd">
|
||||
<input class="weui-input" placeholder="填写考试地点" placeholder-class="weui-input__placeholder" type="number"
|
||||
value="{{form.place}}" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="weui-cell weui-cell_active">
|
||||
<view class="weui-cell__hd">
|
||||
<label class="weui-label">工作类别</label>
|
||||
</view>
|
||||
<view class="weui-cell__bd">
|
||||
<input class="weui-input" placeholder="填写您的快递单号" placeholder-class="weui-input__placeholder"
|
||||
bindinput="kdInput" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="weui-cell weui-cell_active">
|
||||
<view class="weui-cell__hd">
|
||||
<label class="weui-label">参考机会</label>
|
||||
</view>
|
||||
<view class="weui-cell__bd">
|
||||
<input class="weui-input" placeholder="填写可参考次数" placeholder-class="weui-input__placeholder"
|
||||
value="3" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="weui-cell weui-cell_active">
|
||||
<view class="weui-cell__hd">
|
||||
<label class="weui-label">开启时间</label>
|
||||
</view>
|
||||
<view class="weui-cell__bd">
|
||||
<input class="weui-input" placeholder="选择考试开启时间" placeholder-class="weui-input__placeholder"
|
||||
bindinput="addressInput" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="weui-cell weui-cell_active">
|
||||
<view class="weui-cell__hd">
|
||||
<label class="weui-label">关闭时间</label>
|
||||
</view>
|
||||
<view class="weui-cell__bd">
|
||||
<input class="weui-input" placeholder="选择考试关闭时间" placeholder-class="weui-input__placeholder"
|
||||
bindinput="addressInput" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="weui-cells__title">监考人信息</view>
|
||||
<view class="weui-cells weui-cells_form">
|
||||
<view class="weui-cell weui-cell_active">
|
||||
<view class="weui-cell__hd">
|
||||
<label class="weui-label">姓名</label>
|
||||
</view>
|
||||
<view class="weui-cell__bd">
|
||||
<input class="weui-input" placeholder="填写监考人姓名" placeholder-class="weui-input__placeholder"
|
||||
value="{{form.proctor_name}}" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="weui-cell weui-cell_active">
|
||||
<view class="weui-cell__hd">
|
||||
<label class="weui-label">联系方式</label>
|
||||
</view>
|
||||
<view class="weui-cell__bd">
|
||||
<input class="weui-input" placeholder="填写监考人联系方式" placeholder-class="weui-input__placeholder"
|
||||
value="{{form.proctor_phone}}" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view style="margin-top:16rpx">
|
||||
<a class="weui-btn weui-btn_primary" bindtap="submit">确定</a>
|
||||
</view>
|
|
@ -0,0 +1 @@
|
|||
/* pages/admin/exam/add.wxss */
|
|
@ -0,0 +1,146 @@
|
|||
const app = getApp()
|
||||
const api = require("../../../utils/request.js");
|
||||
Page({
|
||||
data: {
|
||||
formData: [
|
||||
{
|
||||
type: 'input',
|
||||
id:'name',
|
||||
lable:'考试名称',
|
||||
isRequired: true,//是否必填
|
||||
maxLength: 20,//最大长度
|
||||
defaultValue:'',//初始值
|
||||
rules:[//规则验证数组
|
||||
{
|
||||
regular: '^\\S*$',//正则字符串
|
||||
tips: '不能有空格'//错误提示
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
id:'place',
|
||||
lable:'考试地点',
|
||||
isRequired: true,//是否必填
|
||||
maxLength: 50,//最大长度
|
||||
defaultValue:'',//初始值
|
||||
rules:[//规则验证数组
|
||||
{
|
||||
regular: '^\\S*$',//正则字符串
|
||||
tips: '不能有空格'//错误提示
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
id: 'chance',
|
||||
lable: '考试机会',
|
||||
defaultValue: 3,
|
||||
inputType: 'digit', //对应input组件type值(text,number)
|
||||
placeholder: '请填写数字',
|
||||
isRequired: true,//是否必填
|
||||
//disabled:true,
|
||||
rules: [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'picker',
|
||||
id: 'workscope',
|
||||
lable: '工作类别',
|
||||
defaultIdx:0,//默认选择索引
|
||||
// disabled:true,
|
||||
isRequired:true,
|
||||
range:[
|
||||
{
|
||||
id: 0,
|
||||
name: '正常'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: '异常'
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'date',
|
||||
id: 'daterange',
|
||||
lable: '开关时间',
|
||||
isRequired: true,
|
||||
/* 显示完整时间包含时分秒;当使用endDate的时候关闭,不要同时打开, 否则日期将会换行;
|
||||
与config中的colum属性共同设置
|
||||
*/
|
||||
completeTime:true, //显示完整时间, 包含时分秒
|
||||
config: {
|
||||
endDate: true,
|
||||
dateLimit: true,
|
||||
// initStartTime: "2020-01-01 12:32:44",
|
||||
// initEndTime: "2020-12-01 12:32:44",
|
||||
column: "minute",//day、hour、minute、secend
|
||||
limitStartTime: "2000-01-01 00:00:59",
|
||||
limitEndTime: "2100-01-01 00:00:59"
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
id:'proctor_name',
|
||||
lable:'监考人姓名',
|
||||
isRequired: true,//是否必填
|
||||
maxLength: 50,//最大长度
|
||||
defaultValue:'',//初始值
|
||||
rules:[//规则验证数组
|
||||
{
|
||||
regular: '^\\S*$',//正则字符串
|
||||
tips: '不能有空格'//错误提示
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
id:'proctor_phone',
|
||||
lable:'监考人联系方式',
|
||||
isRequired: true,//是否必填
|
||||
maxLength: 50,//最大长度
|
||||
defaultValue:'',//初始值
|
||||
rules:[//规则验证数组
|
||||
{
|
||||
regular: '^\\S*$',//正则字符串
|
||||
tips: '不能有空格'//错误提示
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
toSubmit: Math.random()
|
||||
},
|
||||
onFormSubmit(e){
|
||||
console.log('表单提交: ', e);
|
||||
let x = {};
|
||||
x.opentime = e.daterange.startDate;
|
||||
x.endtime = e.daterange.endDate;
|
||||
x.name = e.name
|
||||
x.place = e.place
|
||||
x.chance = e.chance
|
||||
x.workscope = e.workscope
|
||||
x.proctor_name = e.proctor_name
|
||||
x.proctor_phone = e.proctor_phone
|
||||
console.log(x)
|
||||
},
|
||||
onFormChange(e){
|
||||
console.log('表单变化: ',e);
|
||||
},
|
||||
//变更数值, 触发表单提交事件
|
||||
toSubmitChange(){
|
||||
this.setData({
|
||||
toSubmit: Math.random()
|
||||
})
|
||||
},
|
||||
onLoad: function () {
|
||||
api.requesta('/examtest/workscope/?can_exam=true', 'GET').then(res=>{
|
||||
this.data.formData[3].range=res.data
|
||||
this.setData({
|
||||
formData: this.data.formData
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"usingComponents": {
|
||||
"d-form": "/components/dynamicForm/index"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<!--pages/admin/exam/add2.wxml-->
|
||||
<d-form formData="{{formData}}" showSubmitBtn="{{false}}" toSubmit="{{toSubmit}}" bind:dynamicFormChange="onFormChange" bind:dynamicFormSubmit="onFormSubmit"></d-form>
|
||||
|
||||
<button bindtap="toSubmitChange" type="primary">确认</button>
|
|
@ -0,0 +1 @@
|
|||
/* pages/admin/exam/add2.wxss */
|
|
@ -0,0 +1,86 @@
|
|||
// pages/admin/index.js
|
||||
const api = require("../../utils/request.js");
|
||||
Page({
|
||||
|
||||
/**
|
||||
* 页面的初始数据
|
||||
*/
|
||||
data: {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad: function (options) {
|
||||
this.setData({
|
||||
admininfo:getApp().globalData.admininfo
|
||||
})
|
||||
},
|
||||
logout: function(){
|
||||
wx.redirectTo({
|
||||
url: '/pages/admin/login?type=nologin',
|
||||
})
|
||||
},
|
||||
bindmp: function(){
|
||||
wx.login({
|
||||
success: res => {
|
||||
// 发送 res.code 到后台换取 openId, sessionKey, unionId
|
||||
api.requesta('/rbac/user/bindmp/','POST', {code:res.code}).then(res=>{
|
||||
getApp().globalData.admininfo.mpopenid = res.data.mpopenid
|
||||
wx.redirectTo({
|
||||
url: '/pages/admin/index',
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 生命周期函数--监听页面初次渲染完成
|
||||
*/
|
||||
onReady: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面隐藏
|
||||
*/
|
||||
onHide: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面卸载
|
||||
*/
|
||||
onUnload: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面相关事件处理函数--监听用户下拉动作
|
||||
*/
|
||||
onPullDownRefresh: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面上拉触底事件的处理函数
|
||||
*/
|
||||
onReachBottom: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户点击右上角分享
|
||||
*/
|
||||
onShareAppMessage: function () {
|
||||
|
||||
}
|
||||
})
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "管理员控制台"
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<!--pages/admin/index.wxml-->
|
||||
<view class="page">
|
||||
<view class="page__bd">
|
||||
<view class="weui-cells weui-cells_after-title" style="margin-top:0px">
|
||||
<view class="weui-panel__bd">
|
||||
<view class="weui-media-box weui-media-box_appmsg">
|
||||
<view class="weui-media-box__bd weui-media-box__bd_in-appmsg">
|
||||
<view class="weui-media-box__title">
|
||||
<span>账号:</span>
|
||||
<span style="font-weight:bold;">{{admininfo.username}}</span>
|
||||
</view>
|
||||
<view class="weui-media-box__desc">昵称:
|
||||
<span>{{ admininfo.name }}</span>
|
||||
</view>
|
||||
<view class="weui-media-box__desc">角色:
|
||||
<span>{{ admininfo.roles }}</span>
|
||||
</view>
|
||||
<view class="weui-media-box__desc" wx:if="{{admininfo.mpopenid}}">
|
||||
已绑定微信
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view style="text-align:right;padding-right:8rpx">
|
||||
<button type="primary" size="mini" bindtap="bindmp" wx:if="{{!admininfo.mpopenid}}">绑定微信</button>
|
||||
<button type="warn" size="mini" bindtap="logout" style="margin-left:4rpx">退出</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="weui-cells__title">功能列表</view>
|
||||
<view class="weui-cells weui-cells_after-title">
|
||||
<view class="weui-grids">
|
||||
<navigator class="weui-grid" url="/pages/admin/exam/add2">
|
||||
<view class="weui-grid__icon">
|
||||
<image src="/images/suiji.svg" alt></image>
|
||||
</view>
|
||||
<view class="weui-grid__label">发布考试</view>
|
||||
</navigator>
|
||||
<a class="weui-grid" bindtap="goYati">
|
||||
<view class="weui-grid__icon">
|
||||
<image src="/images/examtest.svg" alt></image>
|
||||
</view>
|
||||
<view class="weui-grid__label">考试记录</view>
|
||||
</a>
|
||||
<navigator class="weui-grid" url="/pages/cuoti/index">
|
||||
<view class="weui-grid__icon">
|
||||
<image src="/images/candidate.svg" alt></image>
|
||||
</view>
|
||||
<view class="weui-grid__label">出证记录</view>
|
||||
</navigator>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
|
@ -0,0 +1,9 @@
|
|||
/* pages/admin/index.wxss */
|
||||
.weui-grids {
|
||||
border-top:none;
|
||||
}
|
||||
.weui-grid {
|
||||
width: 25%;
|
||||
border-right:none;
|
||||
border-bottom:none;
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
// pages/admin/login.js
|
||||
const api = require("../../utils/request.js");
|
||||
Page({
|
||||
|
||||
/**
|
||||
* 页面的初始数据
|
||||
*/
|
||||
data: {
|
||||
form:{
|
||||
username:'',
|
||||
password:''
|
||||
}
|
||||
},
|
||||
usernameChange: function (e) {
|
||||
this.data.form.username = e.detail.value
|
||||
},
|
||||
passwordChange: function (e) {
|
||||
this.data.form.password = e.detail.value
|
||||
},
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad: function (options) {
|
||||
if(options.type=='nologin'){
|
||||
|
||||
}else{
|
||||
this.mplogin()
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面初次渲染完成
|
||||
*/
|
||||
onReady: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面隐藏
|
||||
*/
|
||||
onHide: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面卸载
|
||||
*/
|
||||
onUnload: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面相关事件处理函数--监听用户下拉动作
|
||||
*/
|
||||
onPullDownRefresh: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面上拉触底事件的处理函数
|
||||
*/
|
||||
onReachBottom: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户点击右上角分享
|
||||
*/
|
||||
onShareAppMessage: function () {
|
||||
|
||||
},
|
||||
denglu: function ( ) {
|
||||
var form = this.data.form
|
||||
api.requesta('/token/', 'POST', form).then(res => {
|
||||
getApp().globalData.admintoken = res.data.token;
|
||||
api.requesta('/rbac/user/info/', 'GET').then(res=>{
|
||||
getApp().globalData.admininfo = res.data
|
||||
wx.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'none'
|
||||
})
|
||||
wx.redirectTo({
|
||||
url: '/pages/admin/index',
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
mplogin: function(){
|
||||
wx.showLoading({
|
||||
title: '自动登陆中',
|
||||
mask:true
|
||||
})
|
||||
wx.login({
|
||||
success: res => {
|
||||
// 发送 res.code 到后台换取 openId, sessionKey, unionId
|
||||
api.requesta('/rbac/user/mplogin/','POST', {code:res.code}).then(res=>{
|
||||
getApp().globalData.admintoken = res.data.token;
|
||||
api.requesta('/rbac/user/info/', 'GET').then(res=>{
|
||||
getApp().globalData.admininfo = res.data
|
||||
wx.redirectTo({
|
||||
url: '/pages/admin/index',
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "管理员登录"
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<!--pages/admin/login.wxml-->
|
||||
<view class="page">
|
||||
<view class="page__hd" >
|
||||
<view class="page__title" style="text-align:center">中科辐射学堂</view>
|
||||
<view class="page__desc" style="text-align:center">管理员登录</view>
|
||||
</view>
|
||||
<view class="page__bd">
|
||||
<view class="weui-cells weui-cells_after-title">
|
||||
<view class="weui-cell weui-cell_input">
|
||||
<view class="weui-cell__hd">
|
||||
<view class="weui-label">账号</view>
|
||||
</view>
|
||||
<view class="weui-cell__bd">
|
||||
<input class="weui-input" placeholder="请输入管理员账号" bindinput="usernameChange" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="weui-cell weui-cell_input">
|
||||
<view class="weui-cell__hd">
|
||||
<view class="weui-label">密码</view>
|
||||
</view>
|
||||
<view class="weui-cell__bd">
|
||||
<input class="weui-input" placeholder="请输入密码" bindinput="passwordChange" type="password"/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<button class="weui-btn" type="primary" bindtap="denglu">登陆</button>
|
||||
</view>
|
||||
<view class="weui-footer weui-footer_fixed-bottom">
|
||||
<!-- <view class="weui-footer__text" bindtap="intro">点击下载系统/小程序文档介绍</view> -->
|
||||
<view class="weui-footer__text">Copyright © 2018-2021 国检集团</view>
|
||||
<view class="weui-footer__text">中存大数据提供技术支持</view>
|
||||
</view>
|
|
@ -0,0 +1 @@
|
|||
/* pages/admin/login.wxss */
|
|
@ -107,6 +107,11 @@ Page({
|
|||
url: '/pages/main/main',
|
||||
})
|
||||
},
|
||||
goadmin: function(){
|
||||
wx.redirectTo({
|
||||
url: '/pages/admin/login',
|
||||
})
|
||||
},
|
||||
denglu: function (data) {
|
||||
api.request('/crm/consumer/register/', 'POST', data).then(res => {
|
||||
getApp().onLaunch()
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
</view>
|
||||
<view class="weui-footer weui-footer_fixed-bottom">
|
||||
<!-- <view class="weui-footer__text" bindtap="intro">点击下载系统/小程序文档介绍</view> -->
|
||||
<view class="weui-footer__text">Copyright © 2018-2020 国检集团</view>
|
||||
<view style="color:blue" bindtap="goadmin">管理员入口</view>
|
||||
<view class="weui-footer__text">Copyright © 2018-2021 国检集团</view>
|
||||
<view class="weui-footer__text">中存大数据提供技术支持</view>
|
||||
</view>
|
|
@ -72,6 +72,13 @@
|
|||
<view class="weui-cell__ft weui-cell__ft_in-access" style="color:red"></view>
|
||||
</navigator>
|
||||
</view>
|
||||
<view class="weui-cells__title">管理员操作台</view>
|
||||
<view class="weui-cells weui-cells_after-title">
|
||||
<navigator url="/pages/admin/login" class="weui-cell weui-cell_access">
|
||||
<view class="weui-cell__bd">管理员入口</view>
|
||||
<view class="weui-cell__ft weui-cell__ft_in-access" style="color:red"></view>
|
||||
</navigator>
|
||||
</view>
|
||||
<view class="weui-footer weui-footer_fixed-bottom">
|
||||
<!-- <view class="weui-footer__text" bindtap="intro">点击下载系统/小程序文档介绍</view> -->
|
||||
<view class="weui-footer__text">更多服务请联系课程顾问</view>
|
||||
|
|
|
@ -21,13 +21,15 @@
|
|||
"checkSiteMap": true,
|
||||
"uploadWithSourceMap": true,
|
||||
"compileHotReLoad": false,
|
||||
"useMultiFrameRuntime": false,
|
||||
"useMultiFrameRuntime": true,
|
||||
"useApiHook": true,
|
||||
"useApiHostProcess": false,
|
||||
"babelSetting": {
|
||||
"ignore": [],
|
||||
"disablePlugins": [],
|
||||
"outputPath": ""
|
||||
},
|
||||
"enableEngineNative": false,
|
||||
"bundle": false,
|
||||
"useIsolateContext": true,
|
||||
"useCompilerModule": true,
|
||||
|
@ -38,7 +40,7 @@
|
|||
"minifyWXSS": true
|
||||
},
|
||||
"compileType": "miniprogram",
|
||||
"libVersion": "2.14.1",
|
||||
"libVersion": "2.16.1",
|
||||
"appid": "wxf1e9471c93f05ad6",
|
||||
"projectname": "test_mini",
|
||||
"debugOptions": {
|
||||
|
|
|
@ -51,6 +51,57 @@ function request(url, method, data) {
|
|||
return promise;
|
||||
}
|
||||
|
||||
function requesta(url, method, data) {
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
wx.showNavigationBarLoading();
|
||||
wx.request({
|
||||
url: getApp().globalData.host + url,
|
||||
method: method,
|
||||
data: data,
|
||||
header:{
|
||||
'Authorization': 'JWT ' + getApp().globalData.admintoken
|
||||
},
|
||||
success: (res => {
|
||||
if (res.data.code >= 200 && res.data.code < 400) {
|
||||
resolve(res.data);
|
||||
}else if(res.data.code == 401){
|
||||
|
||||
}
|
||||
else {
|
||||
var msg = '请求错误'
|
||||
if(res.data.msg){
|
||||
msg = res.data.msg
|
||||
}
|
||||
if (msg.indexOf('该操作的权限')!=-1){
|
||||
msg = '权限不足或账户过期,请联系课程顾问'
|
||||
}
|
||||
wx.showToast({
|
||||
title: msg,
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
})
|
||||
}
|
||||
|
||||
}),
|
||||
fail: (res => {
|
||||
wx.showToast({
|
||||
title: '请求出错',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
})
|
||||
console.log(res)
|
||||
reject('网络出错');
|
||||
}),
|
||||
complete: function () {
|
||||
wx.hideNavigationBarLoading();
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
return promise;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
request: request
|
||||
request: request,
|
||||
requesta: requesta
|
||||
}
|
|
@ -16,8 +16,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
|
||||
from rest_framework_jwt.serializers import (jwt_encode_handler,
|
||||
jwt_payload_handler)
|
||||
from rest_framework_jwt.serializers import jwt_encode_handler
|
||||
from rest_framework_jwt.settings import api_settings
|
||||
|
||||
from crm.zhenzismsclient import ZhenziSmsClient
|
||||
|
@ -35,16 +34,12 @@ from rbac.models import UserProfile
|
|||
from django.http import Http404
|
||||
from .spider import getZs
|
||||
import time
|
||||
appid = 'wxf1e9471c93f05ad6'
|
||||
secret = '4bf7f9bd6c52634586bbe792a1f0a834'
|
||||
sms_appid = '100172'
|
||||
sms_appsecret = '00b8681c-0ce6-41c8-a867-904c1891c78a'
|
||||
sms_url = 'https://sms.zhenzikj.com'
|
||||
from server.config import *
|
||||
|
||||
def jwt_payload_handler(user):
|
||||
def my_payload_handler(user, dtype="admin"):
|
||||
payload = {
|
||||
'user_id': user.pk,
|
||||
'type':'consumer',
|
||||
'type':dtype,
|
||||
'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA
|
||||
}
|
||||
if api_settings.JWT_ALLOW_REFRESH:
|
||||
|
@ -571,7 +566,7 @@ class ConsumerMPLoginView(APIView):
|
|||
session_key = info['session_key']
|
||||
consumer = Consumer.objects.get_or_create(openid = openid)[0]
|
||||
serializer = ConsumerDetailSerializer(instance=consumer)
|
||||
payload = jwt_payload_handler(consumer)
|
||||
payload = my_payload_handler(consumer, 'consumer')
|
||||
token = jwt_encode_handler(payload)
|
||||
return Response({"token":token,"session_key":session_key, "openid":openid, "userinfo":serializer.data})
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.0.4 on 2021-05-25 02:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('rbac', '0005_userprofile_bcompany'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='userprofile',
|
||||
name='mpopenid',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, unique=True, verbose_name='小程序openid'),
|
||||
),
|
||||
]
|
|
@ -152,6 +152,7 @@ class UserProfile(AbstractUser):
|
|||
|
||||
pname = models.CharField('所属省份', max_length=100, null=True, blank=True)
|
||||
bcompany = models.ForeignKey('crm.company', verbose_name='所属公司', null=True, blank=True, on_delete=models.SET_NULL)
|
||||
mpopenid = models.CharField(max_length=100, verbose_name='小程序openid', unique=True, null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "用户信息"
|
||||
|
|
|
@ -1,28 +1,35 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
from operator import itemgetter
|
||||
|
||||
import requests
|
||||
# import jwt
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import authenticate,login,logout
|
||||
from django.contrib.auth.hashers import check_password
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.hashers import check_password, make_password
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.filters import SearchFilter, OrderingFilter
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action, authentication_classes
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.generics import ListAPIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from rest_framework import status
|
||||
|
||||
from utils.custom import CommonPagination
|
||||
from utils.child import get_child_queryset
|
||||
from ..models import UserProfile, Menu, Organization
|
||||
from utils.custom import CommonPagination
|
||||
|
||||
from ..models import Menu, Organization, UserProfile
|
||||
from ..permission import (RbacPermission, get_all_menu_queryset,
|
||||
get_permission_list)
|
||||
from ..serializers.menu_serializer import MenuSerializer
|
||||
from ..serializers.user_serializer import UserListSerializer, UserCreateSerializer, UserModifySerializer, \
|
||||
UserInfoListSerializer
|
||||
from ..permission import get_all_menu_queryset,get_permission_list,RbacPermission
|
||||
from ..serializers.user_serializer import (UserCreateSerializer,
|
||||
UserInfoListSerializer,
|
||||
UserListSerializer,
|
||||
UserModifySerializer)
|
||||
from server.config import *
|
||||
from rest_framework_jwt.serializers import jwt_encode_handler
|
||||
from crm.views import my_payload_handler
|
||||
|
||||
class UserLogoutView(APIView):
|
||||
authentication_classes = ()
|
||||
|
@ -47,6 +54,8 @@ class UserInfoView(APIView):
|
|||
# 'avatar': request._request._current_scheme_host + '/media/' + str(user.image),
|
||||
'avatar': user.avatar,
|
||||
'perms': perms,
|
||||
'roles':user.roles.values_list('name', flat=True),
|
||||
'mpopenid': user.mpopenid
|
||||
}
|
||||
|
||||
return Response(data)
|
||||
|
@ -59,6 +68,7 @@ class UserInfoView(APIView):
|
|||
|
||||
from utils.pagination import PageOrNot
|
||||
|
||||
|
||||
class UserViewSet(PageOrNot, ModelViewSet):
|
||||
"""
|
||||
用户管理:增删改查
|
||||
|
@ -135,4 +145,39 @@ class UserViewSet(PageOrNot, ModelViewSet):
|
|||
return Response({'error': '新密码两次输入不一致!'})
|
||||
else:
|
||||
return Response({'error':'旧密码错误!'})
|
||||
|
||||
@action(methods=['post'], detail=False, permission_classes=[], authentication_classes=[])
|
||||
def mplogin(self, request, pk=None):
|
||||
"""
|
||||
小程序登录
|
||||
"""
|
||||
code = request.data['code']
|
||||
info = requests.get('https://api.weixin.qq.com/sns/jscode2session?appid='+appid+'&secret='+secret+'&js_code=' +
|
||||
code+'&grant_type=authorization_code').content.decode('utf-8')
|
||||
info = json.loads(info)
|
||||
openid = info['openid']
|
||||
session_key = info['session_key']
|
||||
try:
|
||||
user = UserProfile.objects.get(mpopenid = openid)
|
||||
payload = my_payload_handler(user)
|
||||
token = jwt_encode_handler(payload)
|
||||
return Response({"token":token,"session_key":session_key, "openid":openid})
|
||||
except:
|
||||
return Response({'error':'自动登录失败!'})
|
||||
|
||||
@action(methods=['post'], detail=False, permission_classes=[IsAuthenticated])
|
||||
def bindmp(self, request, pk=None):
|
||||
"""
|
||||
绑定微信
|
||||
"""
|
||||
user = request.user
|
||||
code = request.data['code']
|
||||
info = requests.get('https://api.weixin.qq.com/sns/jscode2session?appid='+appid+'&secret='+secret+'&js_code=' +
|
||||
code+'&grant_type=authorization_code').content.decode('utf-8')
|
||||
info = json.loads(info)
|
||||
openid = info['openid']
|
||||
user.mpopenid = openid
|
||||
user.save()
|
||||
return Response({'mpopenid':openid})
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
appid = 'wxf1e9471c93f05ad6'
|
||||
secret = '4bf7f9bd6c52634586bbe792a1f0a834'
|
||||
sms_appid = '100172'
|
||||
sms_appsecret = '00b8681c-0ce6-41c8-a867-904c1891c78a'
|
||||
sms_url = 'https://sms.zhenzikj.com'
|
|
@ -45,7 +45,11 @@ class FitJSONRenderer(JSONRenderer):
|
|||
response = renderer_context.get("response")
|
||||
response_body.code = response.status_code
|
||||
if response_body.code >= 400: # drf异常
|
||||
response_body.msg = data['detail'] if 'detail' in data else data
|
||||
if isinstance(data, dict):
|
||||
data = data[list(data.keys())[0]]
|
||||
if isinstance(data, list):
|
||||
data = data[0]
|
||||
response_body.msg = data
|
||||
elif data and 'error' in data and data['error']:# 自传异常,key为error
|
||||
response_body.code = data.get("code",400)
|
||||
response_body.msg = data.get("error", "")
|
||||
|
|
Loading…
Reference in New Issue