first
|
@ -0,0 +1,81 @@
|
|||
const api = require("utils/request.js");
|
||||
//app.js
|
||||
App({
|
||||
onLaunch: function () {
|
||||
// 展示本地存储能力
|
||||
var logs = wx.getStorageSync('logs') || []
|
||||
logs.unshift(Date.now())
|
||||
wx.setStorageSync('logs', logs)
|
||||
|
||||
// 登录
|
||||
wx.showLoading({
|
||||
title: '加载中...',
|
||||
mask:true,
|
||||
icon:'none'
|
||||
})
|
||||
wx.login({
|
||||
success: res => {
|
||||
// 发送 res.code 到后台换取 openId, sessionKey, unionId
|
||||
api.request('/crm/consumer/mplogin/','POST', {code:res.code}).then(res=>{
|
||||
if(res.code==200){
|
||||
this.globalData.token = res.data.token
|
||||
this.globalData.session_key = res.data.session_key
|
||||
this.globalData.userinfo = res.data.userinfo
|
||||
//console.log(res.data.userinfo.perms)
|
||||
// var pages = getCurrentPages() //获取加载的页面
|
||||
// var currentPage = pages[pages.length - 1] //获取当前页面的对象
|
||||
// currentPage.showExp()
|
||||
|
||||
if(res.data.userinfo.role_name == '游客' && this.globalData.rlogin){
|
||||
//匿名用户
|
||||
wx.reLaunch({
|
||||
url: '/pages/login/login2',
|
||||
})
|
||||
}
|
||||
}
|
||||
}).then(res=>{
|
||||
api.request('/crm/consumer/process/', 'GET').then(res => {
|
||||
|
||||
for (var key in res.data.process) {
|
||||
try {
|
||||
if(res.data.process[key]){
|
||||
wx.setStorageSync(key.toString(), res.data.process[key])
|
||||
}
|
||||
} catch (e) { }
|
||||
}
|
||||
wx.hideLoading()
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
// 获取用户信息
|
||||
wx.getSetting({
|
||||
success: res => {
|
||||
if (res.authSetting['scope.userInfo']) {
|
||||
// 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
|
||||
wx.getUserInfo({
|
||||
success: res => {
|
||||
// 可以将 res 发送给后台解码出 unionId
|
||||
this.globalData.userInfo = res.userInfo
|
||||
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
|
||||
// 所以此处加入 callback 以防止这种情况
|
||||
if (this.userInfoReadyCallback) {
|
||||
this.userInfoReadyCallback(res)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
globalData: {
|
||||
userInfo: {},
|
||||
userinfo: {}, // 服务器传回的消费者信息
|
||||
// host: 'https://apitest.ahctc.cn',
|
||||
mediahost: 'https://apitest.ahctc.cn',
|
||||
host: 'http://127.0.0.1:8000',
|
||||
//mediahost: 'http://127.0.0.1:8000',
|
||||
token : '',
|
||||
rlogin:true
|
||||
}
|
||||
})
|
|
@ -0,0 +1,106 @@
|
|||
{
|
||||
"pages": [
|
||||
"pages/main/main",
|
||||
"pages/index/index",
|
||||
"pages/logs/logs",
|
||||
"pages/login/login",
|
||||
"pages/login/login2",
|
||||
"pages/lianxi/index",
|
||||
"pages/subject/index",
|
||||
"pages/lianxi/main",
|
||||
"pages/about/about",
|
||||
"pages/cuoti/index",
|
||||
"pages/moni/index",
|
||||
"pages/moni/note",
|
||||
"pages/test/test",
|
||||
"pages/test/result",
|
||||
"pages/test/detail",
|
||||
"pages/test/sheet",
|
||||
"pages/test/list",
|
||||
"pages/my/index",
|
||||
"pages/collect/main",
|
||||
"pages/workscope/index",
|
||||
"pages/yati/index",
|
||||
"pages/article/index",
|
||||
"pages/article/detail",
|
||||
"pages/quota/quota",
|
||||
"pages/material/index",
|
||||
"pages/question/detail",
|
||||
"pages/candidate/index",
|
||||
"pages/candidate/detail",
|
||||
"pages/material/video",
|
||||
"pages/qtest/form",
|
||||
"pages/main/start",
|
||||
"pages/exam/index",
|
||||
"pages/exam/note",
|
||||
"pages/admin/index",
|
||||
"pages/admin/login",
|
||||
"pages/admin/exam/add",
|
||||
"pages/admin/exam/add2",
|
||||
"pages/admin/exam/detail",
|
||||
"pages/admin/exam/index",
|
||||
"pages/candidate/show",
|
||||
"pages/admin/candidate/index",
|
||||
"pages/candidate/my",
|
||||
"pages/admin/exam/upimg",
|
||||
"pages/video/index",
|
||||
"pages/video/play"
|
||||
],
|
||||
"window": {
|
||||
"backgroundTextStyle": "light",
|
||||
"navigationBarBackgroundColor": "#fff",
|
||||
"navigationBarTitleText": "中科辐射学堂",
|
||||
"navigationBarTextStyle": "black"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#7A7E83",
|
||||
"selectedColor": "#1296db",
|
||||
"borderStyle": "black",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/main/main",
|
||||
"iconPath": "images/home.png",
|
||||
"selectedIconPath": "images/homec.png",
|
||||
"text": "主页"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/article/index",
|
||||
"iconPath": "images/news.png",
|
||||
"selectedIconPath": "images/newsc.png",
|
||||
"text": "资讯"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/material/index",
|
||||
"iconPath": "images/data.png",
|
||||
"selectedIconPath": "images/datac.png",
|
||||
"text": "资料"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/material/video",
|
||||
"iconPath": "images/play.png",
|
||||
"selectedIconPath": "images/playc.png",
|
||||
"text": "视频"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/my/index",
|
||||
"iconPath": "images/me.png",
|
||||
"selectedIconPath": "images/mec.png",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"useExtendedLib": {
|
||||
"kbone": true,
|
||||
"weui": true
|
||||
},
|
||||
"usingComponents": {},
|
||||
"plugins": {
|
||||
"tencentvideo": {
|
||||
"version": "1.4.0",
|
||||
"provider": "wxa75efa648b60994b"
|
||||
}
|
||||
},
|
||||
"style": "v2",
|
||||
"sitemapLocation": "sitemap.json"
|
||||
}
|
|
@ -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>
|
|
@ -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,8 @@
|
|||
"use strict";function e(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}/*!
|
||||
* mp-html v2.0.5
|
||||
* https://github.com/jin-yufeng/mp-html
|
||||
*
|
||||
* Released under the MIT license
|
||||
* Author: Jin Yufeng
|
||||
*/
|
||||
var t=require("./parser"),n=[];Component({data:{nodes:[]},properties:{content:{type:String,value:"",observer:function(e){this.setContent(e)}},copyLink:{type:Boolean,value:!0},domain:String,errorImg:String,lazyLoad:Boolean,loadingImg:String,pauseVideo:{type:Boolean,value:!0},previewImg:{type:Boolean,value:!0},scrollTable:Boolean,selectable:null,setTitle:{type:Boolean,value:!0},showImgMenu:{type:Boolean,value:!0},tagStyle:Object,useAnchor:null},created:function(){this.plugins=[];for(var e=n.length;e--;)this.plugins.push(new n[e](this))},detached:function(){clearInterval(this._timer),this._hook("onDetached")},methods:{in:function(e,t,n){e&&t&&n&&(this._in={page:e,selector:t,scrollTop:n})},navigateTo:function(t,n){var o=this;return new Promise(function(i,r){if(!o.data.useAnchor)return r("Anchor is disabled");var a=wx.createSelectorQuery().in(o._in?o._in.page:o).select((o._in?o._in.selector:"._root")+(t?"".concat(">>>","#").concat(t):"")).boundingClientRect();o._in?a.select(o._in.selector).scrollOffset().select(o._in.selector).boundingClientRect():a.selectViewport().scrollOffset(),a.exec(function(t){if(!t[0])return r("Label not found");var a=t[1].scrollTop+t[0].top-(t[2]?t[2].top:0)+(n||parseInt(o.data.useAnchor)||0);o._in?o._in.page.setData(e({},o._in.scrollTop,a)):wx.pageScrollTo({scrollTop:a,duration:300}),i()})})},getText:function(e){var t="";return function e(n){for(var o=0;o<n.length;o++){var i=n[o];if("text"==i.type)t+=i.text.replace(/&/g,"&");else if("br"==i.name)t+="\n";else{var r="p"==i.name||"div"==i.name||"tr"==i.name||"li"==i.name||"h"==i.name[0]&&i.name[1]>"0"&&i.name[1]<"7";r&&t&&"\n"!=t[t.length-1]&&(t+="\n"),i.children&&e(i.children),r&&"\n"!=t[t.length-1]?t+="\n":"td"!=i.name&&"th"!=i.name||(t+="\t")}}}(e||this.data.nodes),t},getRect:function(){var e=this;return new Promise(function(t,n){wx.createSelectorQuery().in(e).select("._root").boundingClientRect().exec(function(e){return e[0]?t(e[0]):n("Root label not found")})})},setContent:function(e,n){var o=this;this.imgList&&n||(this.imgList=[]),this._videos=[];var i={},r=new t(this).parse(e);if(n)for(var a=this.data.nodes.length,l=r.length;l--;)i["nodes[".concat(a+l,"]")]=r[l];else i.nodes=r;this.setData(i,function(){o._hook("onLoad"),o.triggerEvent("load")});var s;clearInterval(this._timer),this._timer=setInterval(function(){o.getRect().then(function(e){e.height==s&&(o.triggerEvent("ready",e),clearInterval(o._timer)),s=e.height}).catch(function(){})},350)},_hook:function(e){for(var t=n.length;t--;)this.plugins[t][e]&&this.plugins[t][e]()},_add:function(e){e.detail.root=this}}});
|
|
@ -0,0 +1 @@
|
|||
{"component":true,"usingComponents":{"node":"./node/node"}}
|
|
@ -0,0 +1 @@
|
|||
<view class="_root {{selectable?'_select':''}}"><slot wx:if="{{!nodes[0]}}"/><node id="_root" childs="{{nodes}}" opts="{{[lazyLoad,loadingImg,errorImg,showImgMenu]}}" catchadd="_add"/></view>
|
|
@ -0,0 +1 @@
|
|||
._root{padding:1px 0;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}._select{-webkit-user-select:text;user-select:text}
|
|
@ -0,0 +1 @@
|
|||
"use strict";function t(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}Component({data:{ctrl:{}},properties:{childs:Array,opts:Array},attached:function(){this.triggerEvent("add",this,{bubbles:!0,composed:!0})},methods:{noop:function(){},getNode:function(t){for(var e=t.split("_"),i=this.data.childs[e[0]],r=1;r<e.length;r++)i=i.children[e[r]];return i},play:function(t){if(this.root.data.pauseVideo){for(var e=!1,i=t.target.id,r=this.root._videos.length;r--;)this.root._videos[r].id==i?e=!0:this.root._videos[r].pause();if(!e){var s=wx.createVideoContext(i,this);s.id=i,this.root._videos.push(s)}}},imgTap:function(t){var e=this.getNode(t.target.dataset.i);if(e.a)return this.linkTap(e.a);if(!e.attrs.ignore&&(this.root.triggerEvent("imgtap",e.attrs),this.root.data.previewImg)){var i=this.root.imgList[e.i];wx.previewImage({current:i,urls:this.root.imgList})}},imgLoad:function(e){var i,r=e.target.dataset.i,s=this.getNode(r);s.w?(this.data.opts[1]&&!this.data.ctrl[r]||-1==this.data.ctrl[r])&&(i=1):i=e.detail.width,i&&this.setData(t({},"ctrl."+r,i))},linkTap:function(t){var e=t.currentTarget?this.getNode(t.currentTarget.dataset.i):{},i=e.attrs||t,r=i.href;this.root.triggerEvent("linktap",Object.assign({innerText:this.root.getText(e.children||[])},i)),r&&("#"==r[0]?this.root.navigateTo(r.substring(1)).catch(function(){}):r.includes("://")?this.root.data.copyLink&&wx.setClipboardData({data:r,success:function(){return wx.showToast({title:"链接已复制"})}}):wx.navigateTo({url:r,fail:function(){wx.switchTab({url:r,fail:function(){}})}}))},mediaError:function(e){var i=e.target.dataset.i,r=this.getNode(i);if("video"==r.name||"audio"==r.name){var s=(this.data.ctrl[i]||0)+1;if(s>r.src.length&&(s=0),s<r.src.length)return this.setData(t({},"ctrl."+i,s))}else"img"==r.name&&this.data.opts[2]&&this.setData(t({},"ctrl."+i,-1));this.root&&this.root.triggerEvent("error",{source:r.name,attrs:r.attrs,errMsg:e.detail.errMsg})}}});
|
|
@ -0,0 +1 @@
|
|||
{"component":true,"usingComponents":{"node":"./node"}}
|
|
@ -0,0 +1 @@
|
|||
<wxs module="use">var e={abbr:!0,b:!0,big:!0,code:!0,del:!0,em:!0,i:!0,ins:!0,label:!0,q:!0,small:!0,span:!0,strong:!0,sub:!0,sup:!0};module.exports=function(n){return!(e[n.name]||n.children&&-1!=(n.attrs.style||"").indexOf("inline"))&&!n.c};</wxs><template name="el"><block wx:if="{{n.name=='img'}}"><image wx:if="{{(opts[1]&&!ctrl[i])||ctrl[i]<0}}" class="_img" style="{{n.attrs.style}}" src="{{ctrl[i]<0?opts[2]:opts[1]}}" mode="widthFix"/><image id="{{n.attrs.id}}" class="_img {{n.attrs.class}}" style="{{ctrl[i]==-1?'display:none;':''}}width:{{ctrl[i]||1}}px;height:1px;{{n.attrs.style}}" src="{{n.attrs.src}}" mode="{{n.h?'':'widthFix'}}" lazy-load="{{opts[0]}}" webp="{{n.webp}}" show-menu-by-longpress="{{opts[3]&&!n.attrs.ignore}}" data-i="{{i}}" bindload="imgLoad" binderror="mediaError" catchtap="imgTap" bindlongpress="noop"/></block><text wx:elif="{{n.type=='text'}}" user-select="{{n.us}}" decode>{{n.text}}</text><text wx:elif="{{n.name=='br'}}">\n</text><view wx:elif="{{n.name=='a'}}" id="{{n.attrs.id}}" class="{{n.attrs.href?'_a ':''}}{{n.attrs.class}}" hover-class="_hover" style="display:inline;{{n.attrs.style}}" data-i="{{i}}" catchtap="linkTap"><node childs="{{n.children}}" opts="{{opts}}" style="display:inherit"/></view><video wx:elif="{{n.name=='video'}}" id="{{n.attrs.id}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" autoplay="{{n.attrs.autoplay}}" controls="{{n.attrs.controls}}" loop="{{n.attrs.loop}}" muted="{{n.attrs.muted}}" poster="{{n.attrs.poster}}" src="{{n.src[ctrl[i]||0]}}" data-i="{{i}}" bindplay="play" binderror="mediaError"/><audio wx:elif="{{n.name=='audio'}}" id="{{n.attrs.id}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" author="{{n.attrs.author}}" controls="{{n.attrs.controls}}" loop="{{n.attrs.loop}}" name="{{n.attrs.name}}" poster="{{n.attrs.poster}}" src="{{n.src[ctrl[i]||0]}}" data-i="{{i}}" bindplay="play" binderror="mediaError"/><rich-text wx:else id="{{n.attrs.id}}" style="{{n.f}}" nodes="{{[n]}}"/></template><block wx:for="{{childs}}" wx:for-item="n1" wx:for-index="i1" wx:key="i1"><template wx:if="{{use(n1)}}" is="el" data="{{n:n1,i:''+i1,opts:opts,ctrl:ctrl}}"/><view wx:else id="{{n1.attrs.id}}" class="_{{n1.name}} {{n1.attrs.class}}" style="{{n1.attrs.style}}"><block wx:for="{{n1.children}}" wx:for-item="n2" wx:for-index="i2" wx:key="i2"><template wx:if="{{use(n2)}}" is="el" data="{{n:n2,i:i1+'_'+i2,opts:opts,ctrl:ctrl}}"/><view wx:else id="{{n2.attrs.id}}" class="_{{n2.name}} {{n2.attrs.class}}" style="{{n2.attrs.style}}"><block wx:for="{{n2.children}}" wx:for-item="n3" wx:for-index="i3" wx:key="i3"><template wx:if="{{use(n3)}}" is="el" data="{{n:n3,i:i1+'_'+i2+'_'+i3,opts:opts,ctrl:ctrl}}"/><view wx:else id="{{n3.attrs.id}}" class="_{{n3.name}} {{n3.attrs.class}}" style="{{n3.attrs.style}}"><block wx:for="{{n3.children}}" wx:for-item="n4" wx:for-index="i4" wx:key="i4"><template wx:if="{{use(n4)}}" is="el" data="{{n:n4,i:i1+'_'+i2+'_'+i3+'_'+i4,opts:opts,ctrl:ctrl}}"/><view wx:else id="{{n4.attrs.id}}" class="_{{n4.name}} {{n4.attrs.class}}" style="{{n4.attrs.style}}"><block wx:for="{{n4.children}}" wx:for-item="n5" wx:for-index="i5" wx:key="i5"><template wx:if="{{use(n5)}}" is="el" data="{{n:n5,i:i1+'_'+i2+'_'+i3+'_'+i4+'_'+i5,opts:opts,ctrl:ctrl}}"/><node wx:else id="{{n5.attrs.id}}" class="_{{n5.name}} {{n5.attrs.class}}" style="{{n5.attrs.style}}" childs="{{n5.children}}" opts="{{opts}}"/></block></view></block></view></block></view></block></view></block>
|
|
@ -0,0 +1 @@
|
|||
._a{padding:1.5px 0 1.5px 0;color:#366092;word-break:break-all}._hover{text-decoration:underline;opacity:.7}._img{max-width:100%;-webkit-touch-callout:none}._b,._strong{font-weight:700}._code{font-family:monospace}._del{text-decoration:line-through}._em,._i{font-style:italic}._h1{font-size:2em}._h2{font-size:1.5em}._h3{font-size:1.17em}._h5{font-size:.83em}._h6{font-size:.67em}._h1,._h2,._h3,._h4,._h5,._h6{display:block;font-weight:700}._ins{text-decoration:underline}._li{display:list-item}._ol{list-style-type:decimal}._ol,._ul{display:block;padding-left:40px;margin:1em 0}._q::before{content:'"'}._q::after{content:'"'}._sub{font-size:smaller;vertical-align:sub}._sup{font-size:smaller;vertical-align:super}._tbody,._tfoot,._thead{display:table-row-group}._tr{display:table-row}._td,._th{display:table-cell;vertical-align:middle}._th{font-weight:700;text-align:center}._ul{list-style-type:disc}._ul ._ul{margin:0;list-style-type:circle}._ul ._ul ._ul{list-style-type:square}._abbr,._b,._code,._del,._em,._i,._ins,._label,._q,._span,._strong,._sub,._sup{display:inline}._blockquote,._div,._p{display:block}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
audio 扩展包
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
*/
|
||||
Component({
|
||||
data: {
|
||||
time: '00:00'
|
||||
},
|
||||
properties: {
|
||||
author: String,
|
||||
autoplay: Boolean,
|
||||
controls: Boolean,
|
||||
loop: Boolean,
|
||||
name: String,
|
||||
poster: String,
|
||||
src: {
|
||||
type: String,
|
||||
observer(src) {
|
||||
this.setSrc(src);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this._ctx = wx.createInnerAudioContext();
|
||||
this._ctx.onError((err) => {
|
||||
this.setData({
|
||||
error: true
|
||||
})
|
||||
this.triggerEvent('error', err);
|
||||
})
|
||||
this._ctx.onTimeUpdate(() => {
|
||||
var time = this._ctx.currentTime,
|
||||
min = parseInt(time / 60),
|
||||
sec = Math.ceil(time % 60),
|
||||
data = {};
|
||||
data.time = (min > 9 ? min : '0' + min) + ':' + (sec > 9 ? sec : '0' + sec);
|
||||
if (!this.lastTime) data.value = time / this._ctx.duration * 100; // 不在拖动状态下
|
||||
this.setData(data);
|
||||
})
|
||||
this._ctx.onEnded(() => {
|
||||
this.setData({
|
||||
playing: false
|
||||
})
|
||||
})
|
||||
},
|
||||
detached() {
|
||||
this._ctx.destroy();
|
||||
},
|
||||
pageLifetimes: {
|
||||
show() {
|
||||
if (this.data.playing && this._ctx.paused)
|
||||
this._ctx.play();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 设置源
|
||||
setSrc(src) {
|
||||
this._ctx.autoplay = this.data.autoplay;
|
||||
this._ctx.loop = this.data.loop;
|
||||
this._ctx.src = src;
|
||||
},
|
||||
// 播放
|
||||
play() {
|
||||
this._ctx.play();
|
||||
this.setData({
|
||||
playing: true
|
||||
})
|
||||
this.triggerEvent('play');
|
||||
},
|
||||
// 暂停
|
||||
pause() {
|
||||
this._ctx.pause();
|
||||
this.setData({
|
||||
playing: false
|
||||
})
|
||||
this.triggerEvent('pause');
|
||||
},
|
||||
// 移动进度条
|
||||
seek(sec) {
|
||||
this._ctx.seek(sec);
|
||||
},
|
||||
// 内部方法
|
||||
_seeking(e) {
|
||||
if (e.timeStamp - this.lastTime < 200) return;
|
||||
var time = Math.round(e.detail.value / 100 * this._ctx.duration),
|
||||
min = parseInt(time / 60),
|
||||
sec = time % 60;
|
||||
this.setData({
|
||||
time: (min > 9 ? min : '0' + min) + ':' + (sec > 9 ? sec : '0' + sec)
|
||||
})
|
||||
this.lastTime = e.timeStamp;
|
||||
},
|
||||
_seeked(e) {
|
||||
this.seek(e.detail.value / 100 * this._ctx.duration);
|
||||
this.lastTime = void 0;
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"component": true
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<view wx:if="{{controls}}" class="contain">
|
||||
<slider class="slider" activeColor="#585959" block-size="12" disabled="{{error}}" value="{{value}}" bindchanging="_seeking" bindchange="_seeked" />
|
||||
<view class="poster" style="background-image:url('{{poster}}')">
|
||||
<view class="button" bindtap="{{playing?'pause':'play'}}">
|
||||
<view class="{{playing?'pause':'play'}}" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="title">
|
||||
<view class="name">{{name||'未知音频'}}</view>
|
||||
<view class="author">{{author||'未知作者'}}</view>
|
||||
</view>
|
||||
<view class="time">{{time}}</view>
|
||||
</view>
|
|
@ -0,0 +1,75 @@
|
|||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
.author {
|
||||
color: #888;
|
||||
font-size: 28rpx;
|
||||
width: 140rpx;
|
||||
}
|
||||
.author, .name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.button {
|
||||
align-items: center;
|
||||
background-color: rgb(0, 0, 0, 0.2);
|
||||
border: 3.5rpx solid white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
height: 47rpx;
|
||||
justify-content: center;
|
||||
opacity: 0.9;
|
||||
overflow: hidden;
|
||||
width: 47rpx;
|
||||
}
|
||||
.contain {
|
||||
background-color: #fcfcfc;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: 650rpx;
|
||||
}
|
||||
.name {
|
||||
font-size: 33rpx;
|
||||
line-height: 80rpx;
|
||||
width: 320rpx;
|
||||
}
|
||||
.pause {
|
||||
width: 16.5rpx;
|
||||
height: 16.5rpx;
|
||||
background-color: white;
|
||||
}
|
||||
.play {
|
||||
border-bottom: 10rpx solid transparent;
|
||||
border-left: 18rpx solid white;
|
||||
border-top: 10rpx solid transparent;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
.poster {
|
||||
align-items: center;
|
||||
background-color: #e6e6e6;
|
||||
background-size: contain;
|
||||
display: flex;
|
||||
height: 152.35rpx;
|
||||
justify-content: center;
|
||||
width: 152.35rpx;
|
||||
}
|
||||
.slider {
|
||||
position: absolute;
|
||||
bottom: 18.75rpx;
|
||||
margin: 0;
|
||||
right: 35.15rpx;
|
||||
width: 316.4rpx;
|
||||
}
|
||||
.title {
|
||||
flex: 1;
|
||||
margin: 10rpx 0 0 35.15rpx;
|
||||
text-align: left;
|
||||
}
|
||||
.time {
|
||||
color: #888;
|
||||
font-size: 28rpx;
|
||||
margin: 16.4rpx 35.15rpx 0 0;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
解析和匹配 Css 的选择器
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
update:2020/03/15
|
||||
*/
|
||||
var cfg = require('./config.js');
|
||||
class CssHandler {
|
||||
constructor(tagStyle) {
|
||||
var styles = Object.assign({}, cfg.userAgentStyles);
|
||||
for (var item in tagStyle)
|
||||
styles[item] = (styles[item] ? styles[item] + ';' : '') + tagStyle[item];
|
||||
this.styles = styles;
|
||||
}
|
||||
getStyle = data => this.styles = new CssParser(data, this.styles).parse();
|
||||
match(name, attrs) {
|
||||
var tmp, matched = (tmp = this.styles[name]) ? tmp + ';' : '';
|
||||
if (attrs.class) {
|
||||
var items = attrs.class.split(' ');
|
||||
for (var i = 0, item; item = items[i]; i++)
|
||||
if (tmp = this.styles['.' + item])
|
||||
matched += tmp + ';';
|
||||
}
|
||||
if (tmp = this.styles['#' + attrs.id])
|
||||
matched += tmp + ';';
|
||||
return matched;
|
||||
}
|
||||
}
|
||||
module.exports = CssHandler;
|
||||
class CssParser {
|
||||
constructor(data, init) {
|
||||
this.data = data;
|
||||
this.floor = 0;
|
||||
this.i = 0;
|
||||
this.list = [];
|
||||
this.res = init;
|
||||
this.state = this.Space;
|
||||
}
|
||||
parse() {
|
||||
for (var c; c = this.data[this.i]; this.i++)
|
||||
this.state(c);
|
||||
return this.res;
|
||||
}
|
||||
section = () => this.data.substring(this.start, this.i);
|
||||
isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
// 状态机
|
||||
Space(c) {
|
||||
if (c == '.' || c == '#' || this.isLetter(c)) {
|
||||
this.start = this.i;
|
||||
this.state = this.Name;
|
||||
} else if (c == '/' && this.data[this.i + 1] == '*')
|
||||
this.Comment();
|
||||
else if (!cfg.blankChar[c] && c != ';')
|
||||
this.state = this.Ignore;
|
||||
}
|
||||
Comment() {
|
||||
this.i = this.data.indexOf('*/', this.i) + 1;
|
||||
if (!this.i) this.i = this.data.length;
|
||||
this.state = this.Space;
|
||||
}
|
||||
Ignore(c) {
|
||||
if (c == '{') this.floor++;
|
||||
else if (c == '}' && !--this.floor) this.state = this.Space;
|
||||
}
|
||||
Name(c) {
|
||||
if (cfg.blankChar[c]) {
|
||||
this.list.push(this.section());
|
||||
this.state = this.NameSpace;
|
||||
} else if (c == '{') {
|
||||
this.list.push(this.section());
|
||||
this.Content();
|
||||
} else if (c == ',') {
|
||||
this.list.push(this.section());
|
||||
this.Comma();
|
||||
} else if (!this.isLetter(c) && (c < '0' || c > '9') && c != '-' && c != '_')
|
||||
this.state = this.Ignore;
|
||||
}
|
||||
NameSpace(c) {
|
||||
if (c == '{') this.Content();
|
||||
else if (c == ',') this.Comma();
|
||||
else if (!cfg.blankChar[c]) this.state = this.Ignore;
|
||||
}
|
||||
Comma() {
|
||||
while (cfg.blankChar[this.data[++this.i]]);
|
||||
if (this.data[this.i] == '{') this.Content();
|
||||
else {
|
||||
this.start = this.i--;
|
||||
this.state = this.Name;
|
||||
}
|
||||
}
|
||||
Content() {
|
||||
this.start = ++this.i;
|
||||
if ((this.i = this.data.indexOf('}', this.i)) == -1) this.i = this.data.length;
|
||||
var content = this.section();
|
||||
for (var i = 0, item; item = this.list[i++];)
|
||||
if (this.res[item]) this.res[item] += ';' + content;
|
||||
else this.res[item] = content;
|
||||
this.list = [];
|
||||
this.state = this.Space;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
将 html 解析为适用于小程序 rich-text 的 DOM 结构
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
update:2020/04/26
|
||||
*/
|
||||
var cfg = require('./config.js'),
|
||||
blankChar = cfg.blankChar,
|
||||
CssHandler = require('./CssHandler.js'),
|
||||
screenWidth = wx.getSystemInfoSync().screenWidth;
|
||||
try {
|
||||
var emoji = require('./emoji.js');
|
||||
} catch (e) {}
|
||||
class MpHtmlParser {
|
||||
constructor(data, options = {}) {
|
||||
this.attrs = {};
|
||||
this.compress = options.compress;
|
||||
this.CssHandler = new CssHandler(options.tagStyle, screenWidth);
|
||||
this.data = data;
|
||||
this.domain = options.domain;
|
||||
this.DOM = [];
|
||||
this.i = this.start = this.audioNum = this.imgNum = this.videoNum = 0;
|
||||
this.protocol = this.domain && this.domain.includes('://') ? this.domain.split('://')[0] : 'http';
|
||||
this.state = this.Text;
|
||||
this.STACK = [];
|
||||
this.useAnchor = options.useAnchor;
|
||||
this.xml = options.xml;
|
||||
}
|
||||
parse() {
|
||||
if (emoji) this.data = emoji.parseEmoji(this.data);
|
||||
for (var c; c = this.data[this.i]; this.i++)
|
||||
this.state(c);
|
||||
if (this.state == this.Text) this.setText();
|
||||
while (this.STACK.length) this.popNode(this.STACK.pop());
|
||||
if (this.DOM.length) {
|
||||
this.DOM[0].PoweredBy = 'Parser';
|
||||
if (this.title) this.DOM[0].title = this.title;
|
||||
}
|
||||
return this.DOM;
|
||||
}
|
||||
// 设置属性
|
||||
setAttr() {
|
||||
var name = this.getName(this.attrName);
|
||||
if (cfg.trustAttrs[name]) {
|
||||
var val = this.attrVal;
|
||||
if (val) {
|
||||
if (name == 'src') this.attrs[name] = this.getUrl(this.decode(val, 'amp'));
|
||||
else if (name == 'href' || name == 'style') this.attrs[name] = this.decode(val, 'amp');
|
||||
else this.attrs[name] = val;
|
||||
} else if (cfg.boolAttrs[name]) this.attrs[name] = 'T';
|
||||
}
|
||||
this.attrVal = '';
|
||||
while (blankChar[this.data[this.i]]) this.i++;
|
||||
if (this.isClose()) this.setNode();
|
||||
else {
|
||||
this.start = this.i;
|
||||
this.state = this.AttrName;
|
||||
}
|
||||
}
|
||||
// 设置文本节点
|
||||
setText() {
|
||||
var back, text = this.section();
|
||||
if (!text) return;
|
||||
text = (cfg.onText && cfg.onText(text, () => back = true)) || text;
|
||||
if (back) {
|
||||
this.data = this.data.substr(0, this.start) + text + this.data.substr(this.i);
|
||||
let j = this.start + text.length;
|
||||
for (this.i = this.start; this.i < j; this.i++) this.state(this.data[this.i]);
|
||||
return;
|
||||
}
|
||||
if (!this.pre) {
|
||||
// 合并空白符
|
||||
var tmp = [];
|
||||
for (let i = text.length, c; c = text[--i];)
|
||||
if (!blankChar[c] || (!blankChar[tmp[0]] && (c = ' '))) tmp.unshift(c);
|
||||
text = tmp.join('');
|
||||
if (text == ' ') return;
|
||||
}
|
||||
this.siblings().push({
|
||||
type: 'text',
|
||||
text: this.decode(text)
|
||||
});
|
||||
}
|
||||
// 设置元素节点
|
||||
setNode() {
|
||||
var node = {
|
||||
name: this.getName(this.tagName),
|
||||
attrs: this.attrs
|
||||
},
|
||||
close = cfg.selfClosingTags[node.name] || (this.xml && this.data[this.i] == '/');
|
||||
this.attrs = {};
|
||||
if (!cfg.ignoreTags[node.name]) {
|
||||
this.matchAttr(node);
|
||||
if (!close) {
|
||||
node.children = [];
|
||||
if (node.name == 'pre' && cfg.highlight) {
|
||||
this.remove(node);
|
||||
this.pre = node.pre = true;
|
||||
}
|
||||
this.siblings().push(node);
|
||||
this.STACK.push(node);
|
||||
} else if (!cfg.filter || cfg.filter(node, this) != false)
|
||||
this.siblings().push(node);
|
||||
} else {
|
||||
if (!close) this.remove(node);
|
||||
else if (node.name == 'source') {
|
||||
var parent = this.parent(),
|
||||
attrs = node.attrs;
|
||||
if (parent && attrs.src)
|
||||
if (parent.name == 'video' || parent.name == 'audio')
|
||||
parent.attrs.source.push(attrs.src);
|
||||
else {
|
||||
var i, media = attrs.media;
|
||||
if (parent.name == 'picture' && !parent.attrs.src && (!media || (media.includes('px') &&
|
||||
(((i = media.indexOf('min-width')) != -1 && (i = media.indexOf(':', i + 8)) != -1 && screenWidth > parseInt(media.substr(i + 1))) ||
|
||||
((i = media.indexOf('max-width')) != -1 && (i = media.indexOf(':', i + 8)) != -1 && screenWidth < parseInt(media.substr(i + 1)))))))
|
||||
parent.attrs.src = attrs.src;
|
||||
}
|
||||
} else if (node.name == 'base' && !this.domain) this.domain = node.attrs.href;
|
||||
}
|
||||
if (this.data[this.i] == '/') this.i++;
|
||||
this.start = this.i + 1;
|
||||
this.state = this.Text;
|
||||
}
|
||||
// 移除标签
|
||||
remove(node) {
|
||||
var name = node.name,
|
||||
j = this.i;
|
||||
while (1) {
|
||||
if ((this.i = this.data.indexOf('</', this.i + 1)) == -1) {
|
||||
if (name == 'pre' || name == 'svg') this.i = j;
|
||||
else this.i = this.data.length;
|
||||
return;
|
||||
}
|
||||
this.start = (this.i += 2);
|
||||
while (!blankChar[this.data[this.i]] && !this.isClose()) this.i++;
|
||||
if (this.getName(this.section()) == name) {
|
||||
// 代码块高亮
|
||||
if (name == 'pre') {
|
||||
this.data = this.data.substr(0, j + 1) + cfg.highlight(this.data.substring(j + 1, this.i - 5), node.attrs) + this.data.substr(this.i - 5);
|
||||
return this.i = j;
|
||||
} else if (name == 'style')
|
||||
this.CssHandler.getStyle(this.data.substring(j + 1, this.i - 7));
|
||||
else if (name == 'title')
|
||||
this.title = this.data.substring(j + 1, this.i - 7);
|
||||
if ((this.i = this.data.indexOf('>', this.i)) == -1) this.i = this.data.length;
|
||||
// 处理 svg
|
||||
if (name == 'svg') {
|
||||
var src = this.data.substring(j, this.i + 1);
|
||||
if (!node.attrs.xmlns) src = ' xmlns="http://www.w3.org/2000/svg"' + src;
|
||||
var i = j;
|
||||
while (this.data[j] != '<') j--;
|
||||
src = this.data.substring(j, i) + src;
|
||||
var parent = this.parent();
|
||||
if (node.attrs.width == '100%' && parent && (parent.attrs.style || '').includes('inline'))
|
||||
parent.attrs.style = 'width:300px;max-width:100%;' + parent.attrs.style;
|
||||
this.siblings().push({
|
||||
name: 'img',
|
||||
attrs: {
|
||||
src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
|
||||
ignore: 'T'
|
||||
}
|
||||
})
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理属性
|
||||
matchAttr(node) {
|
||||
var attrs = node.attrs,
|
||||
style = this.CssHandler.match(node.name, attrs, node) + (attrs.style || ''),
|
||||
styleObj = {};
|
||||
if (attrs.id) {
|
||||
if (this.compress & 1) attrs.id = void 0;
|
||||
else if (this.useAnchor) this.bubble();
|
||||
}
|
||||
if ((this.compress & 2) && attrs.class) attrs.class = void 0;
|
||||
switch (node.name) {
|
||||
case 'a':
|
||||
case 'ad':
|
||||
this.bubble();
|
||||
break;
|
||||
case 'font':
|
||||
if (attrs.color) {
|
||||
styleObj['color'] = attrs.color;
|
||||
attrs.color = void 0;
|
||||
}
|
||||
if (attrs.face) {
|
||||
styleObj['font-family'] = attrs.face;
|
||||
attrs.face = void 0;
|
||||
}
|
||||
if (attrs.size) {
|
||||
var size = parseInt(attrs.size);
|
||||
if (size < 1) size = 1;
|
||||
else if (size > 7) size = 7;
|
||||
var map = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
|
||||
styleObj['font-size'] = map[size - 1];
|
||||
attrs.size = void 0;
|
||||
}
|
||||
break;
|
||||
case 'video':
|
||||
case 'audio':
|
||||
if (!attrs.id) attrs.id = node.name + (++this[`${node.name}Num`]);
|
||||
else this[`${node.name}Num`]++;
|
||||
if (node.name == 'video') {
|
||||
if (this.videoNum > 3)
|
||||
node.lazyLoad = 1;
|
||||
if (attrs.width) {
|
||||
styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px');
|
||||
attrs.width = void 0;
|
||||
}
|
||||
if (attrs.height) {
|
||||
styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px');
|
||||
attrs.height = void 0;
|
||||
}
|
||||
}
|
||||
attrs.source = [];
|
||||
if (attrs.src) attrs.source.push(attrs.src);
|
||||
if (!attrs.controls && !attrs.autoplay)
|
||||
console.warn(`存在没有 controls 属性的 ${node.name} 标签,可能导致无法播放`, node);
|
||||
this.bubble();
|
||||
break;
|
||||
case 'td':
|
||||
case 'th':
|
||||
if (attrs.colspan || attrs.rowspan)
|
||||
for (var k = this.STACK.length, item; item = this.STACK[--k];)
|
||||
if (item.name == 'table') {
|
||||
item.c = void 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attrs.align) {
|
||||
styleObj['text-align'] = attrs.align;
|
||||
attrs.align = void 0;
|
||||
}
|
||||
// 压缩 style
|
||||
var styles = style.split(';');
|
||||
style = '';
|
||||
for (var i = 0, len = styles.length; i < len; i++) {
|
||||
var info = styles[i].split(':');
|
||||
if (info.length < 2) continue;
|
||||
let key = info[0].trim().toLowerCase(),
|
||||
value = info.slice(1).join(':').trim();
|
||||
if (value.includes('-webkit') || value.includes('-moz') || value.includes('-ms') || value.includes('-o') || value.includes('safe'))
|
||||
style += `;${key}:${value}`;
|
||||
else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import'))
|
||||
styleObj[key] = value;
|
||||
}
|
||||
if (node.name == 'img' || node.name == 'picture') {
|
||||
if (attrs['data-src']) {
|
||||
attrs.src = attrs.src || attrs['data-src'];
|
||||
attrs['data-src'] = void 0;
|
||||
}
|
||||
if ((attrs.src || node.name == 'picture') && !attrs.ignore) {
|
||||
if (this.bubble())
|
||||
attrs.i = (this.imgNum++).toString();
|
||||
else attrs.ignore = 'T';
|
||||
}
|
||||
if (attrs.ignore) styleObj['max-width'] = '100%';
|
||||
var width;
|
||||
if(styleObj.width) width = styleObj.width;
|
||||
else if(attrs.width) width = attrs.width.includes('%') ? attrs.width : attrs.width + 'px';
|
||||
if (width) {
|
||||
styleObj.width = width;
|
||||
attrs.width = '100%';
|
||||
if (parseInt(width) > screenWidth) {
|
||||
styleObj.height = '';
|
||||
if (attrs.height) attrs.height = void 0;
|
||||
}
|
||||
}
|
||||
if (styleObj.height) {
|
||||
attrs.height = styleObj.height;
|
||||
styleObj.height = '';
|
||||
} else if (attrs.height && !attrs.height.includes('%'))
|
||||
attrs.height += 'px';
|
||||
}
|
||||
for (var key in styleObj) {
|
||||
var value = styleObj[key];
|
||||
if (key.includes('flex') || key == 'order' || key == 'self-align') node.c = 1;
|
||||
// 填充链接
|
||||
if (value.includes('url')) {
|
||||
var j = value.indexOf('(');
|
||||
if (j++ != -1) {
|
||||
while (value[j] == '"' || value[j] == "'" || blankChar[value[j]]) j++;
|
||||
value = value.substr(0, j) + this.getUrl(value.substr(j));
|
||||
}
|
||||
}
|
||||
// 转换 rpx
|
||||
else if (value.includes('rpx'))
|
||||
value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * screenWidth / 750 + 'px');
|
||||
else if (key == 'white-space' && value.includes('pre'))
|
||||
this.pre = node.pre = true;
|
||||
style += `;${key}:${value}`;
|
||||
}
|
||||
style = style.substr(1);
|
||||
if (style) attrs.style = style;
|
||||
}
|
||||
// 节点出栈处理
|
||||
popNode(node) {
|
||||
// 空白符处理
|
||||
if (node.pre) {
|
||||
node.pre = this.pre = void 0;
|
||||
for (let i = this.STACK.length; i--;)
|
||||
if (this.STACK[i].pre)
|
||||
this.pre = true;
|
||||
}
|
||||
if (node.name == 'head' || (cfg.filter && cfg.filter(node, this) == false))
|
||||
return this.siblings().pop();
|
||||
var attrs = node.attrs;
|
||||
// 替换一些标签名
|
||||
if (node.name == 'picture') {
|
||||
node.name = 'img';
|
||||
if (!attrs.src && (node.children[0] || '').name == 'img')
|
||||
attrs.src = node.children[0].attrs.src;
|
||||
return node.children = void 0;
|
||||
}
|
||||
if (cfg.blockTags[node.name]) node.name = 'div';
|
||||
else if (!cfg.trustTags[node.name]) node.name = 'span';
|
||||
// 处理列表
|
||||
if (node.c && (node.name == 'ul' || node.name == 'ol')) {
|
||||
if ((node.attrs.style || '').includes('list-style:none')) {
|
||||
for (let i = 0, child; child = node.children[i++];)
|
||||
if (child.name == 'li')
|
||||
child.name = 'div';
|
||||
} else if (node.name == 'ul') {
|
||||
var floor = 1;
|
||||
for (let i = this.STACK.length; i--;)
|
||||
if (this.STACK[i].name == 'ul') floor++;
|
||||
if (floor != 1)
|
||||
for (let i = node.children.length; i--;)
|
||||
node.children[i].floor = floor;
|
||||
} else {
|
||||
for (let i = 0, num = 1, child; child = node.children[i++];)
|
||||
if (child.name == 'li') {
|
||||
child.type = 'ol';
|
||||
child.num = ((num, type) => {
|
||||
if (type == 'a') return String.fromCharCode(97 + (num - 1) % 26);
|
||||
if (type == 'A') return String.fromCharCode(65 + (num - 1) % 26);
|
||||
if (type == 'i' || type == 'I') {
|
||||
num = (num - 1) % 99 + 1;
|
||||
var one = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'],
|
||||
ten = ['X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
|
||||
res = (ten[Math.floor(num / 10) - 1] || '') + (one[num % 10 - 1] || '');
|
||||
if (type == 'i') return res.toLowerCase();
|
||||
return res;
|
||||
}
|
||||
return num;
|
||||
})(num++, attrs.type) + '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理表格的边框
|
||||
if (node.name == 'table') {
|
||||
var padding = attrs.cellpadding,
|
||||
spacing = attrs.cellspacing,
|
||||
border = attrs.border;
|
||||
if (node.c) {
|
||||
this.bubble();
|
||||
attrs.style = (attrs.style || '') + ';display:table';
|
||||
if (!padding) padding = 2;
|
||||
if (!spacing) spacing = 2;
|
||||
}
|
||||
if (border) attrs.style = `border:${border}px solid gray;${attrs.style || ''}`;
|
||||
if (spacing) attrs.style = `border-spacing:${spacing}px;${attrs.style || ''}`;
|
||||
if (border || padding || node.c)
|
||||
(function f(ns) {
|
||||
for (var i = 0, n; n = ns[i]; i++) {
|
||||
if(n.type == 'text') continue;
|
||||
var style = n.attrs.style || '';
|
||||
if (node.c && n.name[0] == 't') {
|
||||
n.c = 1;
|
||||
style += ';display:table-' + (n.name == 'th' || n.name == 'td' ? 'cell' : (n.name == 'tr' ? 'row' : 'row-group'));
|
||||
}
|
||||
if (n.name == 'th' || n.name == 'td') {
|
||||
if (border) style = `border:${border}px solid gray;${style}`;
|
||||
if (padding) style = `padding:${padding}px;${style}`;
|
||||
} else f(n.children || []);
|
||||
if (style) n.attrs.style = style;
|
||||
}
|
||||
})(node.children)
|
||||
}
|
||||
this.CssHandler.pop && this.CssHandler.pop(node);
|
||||
// 自动压缩
|
||||
if (node.name == 'div' && !Object.keys(attrs).length) {
|
||||
var siblings = this.siblings();
|
||||
if (node.children.length == 1 && node.children[0].name == 'div')
|
||||
siblings[siblings.length - 1] = node.children[0];
|
||||
}
|
||||
}
|
||||
// 工具函数
|
||||
bubble() {
|
||||
for (var i = this.STACK.length, item; item = this.STACK[--i];) {
|
||||
if (cfg.richOnlyTags[item.name]) {
|
||||
if (item.name == 'table' && !Object.hasOwnProperty.call(item, 'c')) item.c = 1;
|
||||
return false;
|
||||
}
|
||||
item.c = 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
decode(val, amp) {
|
||||
var i = -1,
|
||||
j, en;
|
||||
while (1) {
|
||||
if ((i = val.indexOf('&', i + 1)) == -1) break;
|
||||
if ((j = val.indexOf(';', i + 2)) == -1) break;
|
||||
if (val[i + 1] == '#') {
|
||||
en = parseInt((val[i + 2] == 'x' ? '0' : '') + val.substring(i + 2, j));
|
||||
if (!isNaN(en)) val = val.substr(0, i) + String.fromCharCode(en) + val.substr(j + 1);
|
||||
} else {
|
||||
en = val.substring(i + 1, j);
|
||||
if (cfg.entities[en] || en == amp)
|
||||
val = val.substr(0, i) + (cfg.entities[en] || '&') + val.substr(j + 1);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
getUrl(url) {
|
||||
if (url[0] == '/') {
|
||||
if (url[1] == '/') url = this.protocol + ':' + url;
|
||||
else if (this.domain) url = this.domain + url;
|
||||
} else if (this.domain && url.indexOf('data:') != 0 && !url.includes('://'))
|
||||
url = this.domain + '/' + url;
|
||||
return url;
|
||||
}
|
||||
getName = val => this.xml ? val : val.toLowerCase();
|
||||
isClose = () => this.data[this.i] == '>' || (this.data[this.i] == '/' && this.data[this.i + 1] == '>');
|
||||
section = () => this.data.substring(this.start, this.i);
|
||||
parent = () => this.STACK[this.STACK.length - 1];
|
||||
siblings = () => this.STACK.length ? this.parent().children : this.DOM;
|
||||
// 状态机
|
||||
Text(c) {
|
||||
if (c == '<') {
|
||||
var next = this.data[this.i + 1],
|
||||
isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
if (isLetter(next)) {
|
||||
this.setText();
|
||||
this.start = this.i + 1;
|
||||
this.state = this.TagName;
|
||||
} else if (next == '/') {
|
||||
this.setText();
|
||||
if (isLetter(this.data[++this.i + 1])) {
|
||||
this.start = this.i + 1;
|
||||
this.state = this.EndTag;
|
||||
} else this.Comment();
|
||||
} else if (next == '!') {
|
||||
this.setText();
|
||||
this.Comment();
|
||||
}
|
||||
}
|
||||
}
|
||||
Comment() {
|
||||
var key;
|
||||
if (this.data.substring(this.i + 2, this.i + 4) == '--') key = '-->';
|
||||
else if (this.data.substring(this.i + 2, this.i + 9) == '[CDATA[') key = ']]>';
|
||||
else key = '>';
|
||||
if ((this.i = this.data.indexOf(key, this.i + 2)) == -1) this.i = this.data.length;
|
||||
else this.i += key.length - 1;
|
||||
this.start = this.i + 1;
|
||||
this.state = this.Text;
|
||||
}
|
||||
TagName(c) {
|
||||
if (blankChar[c]) {
|
||||
this.tagName = this.section();
|
||||
while (blankChar[this.data[this.i]]) this.i++;
|
||||
if (this.isClose()) this.setNode();
|
||||
else {
|
||||
this.start = this.i;
|
||||
this.state = this.AttrName;
|
||||
}
|
||||
} else if (this.isClose()) {
|
||||
this.tagName = this.section();
|
||||
this.setNode();
|
||||
}
|
||||
}
|
||||
AttrName(c) {
|
||||
var blank = blankChar[c];
|
||||
if (blank) {
|
||||
this.attrName = this.section();
|
||||
c = this.data[this.i];
|
||||
}
|
||||
if (c == '=') {
|
||||
if (!blank) this.attrName = this.section();
|
||||
while (blankChar[this.data[++this.i]]);
|
||||
this.start = this.i--;
|
||||
this.state = this.AttrValue;
|
||||
} else if (blank) this.setAttr();
|
||||
else if (this.isClose()) {
|
||||
this.attrName = this.section();
|
||||
this.setAttr();
|
||||
}
|
||||
}
|
||||
AttrValue(c) {
|
||||
if (c == '"' || c == "'") {
|
||||
this.start++;
|
||||
if ((this.i = this.data.indexOf(c, this.i + 1)) == -1) return this.i = this.data.length;
|
||||
this.attrVal = this.section();
|
||||
this.i++;
|
||||
} else {
|
||||
for (; !blankChar[this.data[this.i]] && !this.isClose(); this.i++);
|
||||
this.attrVal = this.section();
|
||||
}
|
||||
this.setAttr();
|
||||
}
|
||||
EndTag(c) {
|
||||
if (blankChar[c] || c == '>' || c == '/') {
|
||||
var name = this.getName(this.section());
|
||||
for (var i = this.STACK.length; i--;)
|
||||
if (this.STACK[i].name == name) break;
|
||||
if (i != -1) {
|
||||
var node;
|
||||
while ((node = this.STACK.pop()).name != name);
|
||||
this.popNode(node);
|
||||
} else if (name == 'p' || name == 'br')
|
||||
this.siblings().push({
|
||||
name,
|
||||
attrs: {}
|
||||
});
|
||||
this.i = this.data.indexOf('>', this.i);
|
||||
this.start = this.i + 1;
|
||||
if (this.i == -1) this.i = this.data.length;
|
||||
else this.state = this.Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = MpHtmlParser;
|
|
@ -0,0 +1,102 @@
|
|||
/* 配置文件 */
|
||||
const canIUse = wx.canIUse('editor'); // 高基础库标识,用于兼容
|
||||
const Prism = require('./prism.js');
|
||||
module.exports = {
|
||||
// 过滤器函数
|
||||
filter(node, cxt) {
|
||||
if (node.name == 'pre')
|
||||
cxt.bubble(); // 使得 pre 不被 rich-text 包含(为实现长按复制)
|
||||
},
|
||||
// 代码高亮函数
|
||||
highlight(content, attrs) {
|
||||
var info = content.match(/<code.*?language-([a-z-]+).*?>([\s\S]+)<\/code.*?>/m);
|
||||
if (!info) return content;
|
||||
var lan = info[1];
|
||||
content = info[2].replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
|
||||
attrs['data-content'] = content; // 记录原始内容,长按复制时使用
|
||||
switch (lan) {
|
||||
case 'js':
|
||||
case 'javascript':
|
||||
content = Prism.highlight(content, Prism.languages.javascript, 'javascript');
|
||||
break;
|
||||
case 'html':
|
||||
case 'html-editor':
|
||||
case 'wxml':
|
||||
case 'vue':
|
||||
content = Prism.highlight(content, Prism.languages.html, 'html');
|
||||
break;
|
||||
case 'json':
|
||||
content = Prism.highlight(content, Prism.languages.json, 'json');
|
||||
break;
|
||||
case 'md':
|
||||
case 'md-editor':
|
||||
case 'markdown':
|
||||
content = Prism.highlight(content, Prism.languages.markdown, 'markdown');
|
||||
break;
|
||||
case 'c':
|
||||
case 'cpp':
|
||||
content = Prism.highlight(content, Prism.languages.clike, 'clike');
|
||||
}
|
||||
// 增加语言显示
|
||||
if (!lan.includes('editor'))
|
||||
content = `<span style="position:absolute;top:3px;right:8px;font-size:.6rem;color:#808080;font-weight:bold">${lan}</span><div style='overflow:auto;max-width:100%;padding-bottom:1em'>${content}</div>`;
|
||||
return content;
|
||||
},
|
||||
// 文本处理函数
|
||||
onText: null,
|
||||
// 实体编码列表
|
||||
entities: {
|
||||
quot: '"',
|
||||
apos: "'",
|
||||
semi: ';',
|
||||
nbsp: '\xA0',
|
||||
ndash: '–',
|
||||
mdash: '—',
|
||||
middot: '·',
|
||||
lsquo: '‘',
|
||||
rsquo: '’',
|
||||
ldquo: '“',
|
||||
rdquo: '”',
|
||||
bull: '•',
|
||||
hellip: '…'
|
||||
},
|
||||
blankChar: makeMap(' ,\xA0,\t,\r,\n,\f'),
|
||||
inlineTags: makeMap('abbr,b,big,code,del,em,i,ins,label,q,small,span,strong'),
|
||||
// 块级标签,将被转为 div
|
||||
blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,section' + (canIUse ? '' : ',pre')),
|
||||
// 将被移除的标签
|
||||
ignoreTags: makeMap('area,base,basefont,canvas,command,embed,frame,iframe,input,isindex,keygen,link,map,meta,param,script,source,style,svg,textarea,title,track,use,wbr' + (canIUse ? ',rp' : '')),
|
||||
// 只能被 rich-text 显示的标签
|
||||
richOnlyTags: makeMap('a,colgroup,fieldset,legend,picture,table' + (canIUse ? ',bdi,bdo,rt,ruby' : '')),
|
||||
// 自闭合的标签
|
||||
selfClosingTags: makeMap('area,base,basefont,br,col,circle,ellipse,embed,frame,hr,img,input,isindex,keygen,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
|
||||
// 信任的属性
|
||||
trustAttrs: makeMap('align,alt,app-id,author,autoplay,border,cellpadding,cellspacing,class,color,colspan,controls,data-src,dir,face,height,href,id,ignore,loop,media,muted,name,path,poster,rowspan,size,span,src,start,style,type,unit-id,width,xmlns'),
|
||||
// bool 型的属性
|
||||
boolAttrs: makeMap('autoplay,controls,ignore,loop,muted'),
|
||||
// 信任的标签
|
||||
trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video' + (canIUse ? ',bdi,bdo,caption,pre,rt,ruby' : '')),
|
||||
// 默认的标签样式
|
||||
userAgentStyles: {
|
||||
address: 'font-style:italic',
|
||||
big: 'display:inline;font-size:1.2em',
|
||||
blockquote: 'background-color:#f6f6f6;border-left:3px solid #dbdbdb;color:#6c6c6c;padding:5px 0 5px 10px',
|
||||
caption: 'display:table-caption;text-align:center',
|
||||
center: 'text-align:center',
|
||||
cite: 'font-style:italic',
|
||||
dd: 'margin-left:40px',
|
||||
mark: 'background-color:yellow',
|
||||
pre: 'font-family:monospace;white-space:pre;overflow:scroll',
|
||||
s: 'text-decoration:line-through',
|
||||
small: 'display:inline;font-size:0.8em',
|
||||
u: 'text-decoration:underline'
|
||||
}
|
||||
}
|
||||
|
||||
function makeMap(str) {
|
||||
var map = {},
|
||||
list = str.split(',');
|
||||
for (var i = list.length; i--;)
|
||||
map[list[i]] = true;
|
||||
return map;
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
emoji 扩展包
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
*/
|
||||
const reg = /\[(\S+?)\]/g;
|
||||
const data = {
|
||||
'笑脸': '😄',
|
||||
'生病': '😷',
|
||||
'破涕为笑': '😂',
|
||||
'吐舌': '😝',
|
||||
'脸红': '😳',
|
||||
'恐惧': '😱',
|
||||
'失望': '😔',
|
||||
'无语': '😒',
|
||||
'眨眼': '😉',
|
||||
'酷': '😎',
|
||||
'哭': '😭',
|
||||
'痴迷': '😍',
|
||||
'吻': '😘',
|
||||
'思考': '🤔',
|
||||
'困惑': '😕',
|
||||
'颠倒': '🙃',
|
||||
'钱': '🤑',
|
||||
'惊讶': '😲',
|
||||
'白眼': '🙄',
|
||||
'叹气': '😤',
|
||||
'睡觉': '😴',
|
||||
'书呆子': '🤓',
|
||||
'愤怒': '😡',
|
||||
'面无表情': '😑',
|
||||
'张嘴': '😮',
|
||||
'量体温': '🤒',
|
||||
'呕吐': '🤮',
|
||||
'光环': '😇',
|
||||
'幽灵': '👻',
|
||||
'外星人': '👽',
|
||||
'机器人': '🤖',
|
||||
'捂眼镜': '🙈',
|
||||
'捂耳朵': '🙉',
|
||||
'捂嘴': '🙊',
|
||||
'婴儿': '👶',
|
||||
'男孩': '👦',
|
||||
'女孩': '👧',
|
||||
'男人': '👨',
|
||||
'女人': '👩',
|
||||
'老人': '👴',
|
||||
'老妇人': '👵',
|
||||
'警察': '👮',
|
||||
'王子': '🤴',
|
||||
'公主': '🤴',
|
||||
'举手': '🙋',
|
||||
'跑步': '🏃',
|
||||
'家庭': '👪',
|
||||
'眼睛': '👀',
|
||||
'鼻子': '👃',
|
||||
'耳朵': '👂',
|
||||
'舌头': '👅',
|
||||
'嘴': '👄',
|
||||
'心': '❤️',
|
||||
'心碎': '💔',
|
||||
'雪人': '☃️',
|
||||
'情书': '💌',
|
||||
'大便': '💩',
|
||||
'闹钟': '⏰',
|
||||
'眼镜': '👓',
|
||||
'雨伞': '☂️',
|
||||
'音乐': '🎵',
|
||||
'话筒': '🎤',
|
||||
'游戏机': '🎮',
|
||||
'喇叭': '📢',
|
||||
'耳机': '🎧',
|
||||
'礼物': '🎁',
|
||||
'电话': '📞',
|
||||
'电脑': '💻',
|
||||
'打印机': '🖨️',
|
||||
'手电筒': '🔦',
|
||||
'灯泡': '💡',
|
||||
'书本': '📖',
|
||||
'信封': '✉️',
|
||||
'药丸': '💊',
|
||||
'口红': '💄',
|
||||
'手机': '📱',
|
||||
'相机': '📷',
|
||||
'电视': '📺',
|
||||
'中': '🀄',
|
||||
'垃圾桶': '🚮',
|
||||
'厕所': '🚾',
|
||||
'感叹号': '❗',
|
||||
'禁': '🈲',
|
||||
'可': '🉑',
|
||||
'彩虹': '🌈',
|
||||
'旋风': '🌀',
|
||||
'雷电': '⚡',
|
||||
'雪花': '❄️',
|
||||
'星星': '⭐',
|
||||
'水滴': '💧',
|
||||
'玫瑰': '🌹',
|
||||
'加油': '💪',
|
||||
'左': '👈',
|
||||
'右': '👉',
|
||||
'上': '👆',
|
||||
'下': '👇',
|
||||
'手掌': '🖐️',
|
||||
'好的': '👌',
|
||||
'好': '👍',
|
||||
'差': '👎',
|
||||
'胜利': '✌',
|
||||
'拳头': '👊',
|
||||
'挥手': '👋',
|
||||
'鼓掌': '👏',
|
||||
'猴子': '🐒',
|
||||
'狗': '🐶',
|
||||
'狼': '🐺',
|
||||
'猫': '🐱',
|
||||
'老虎': '🐯',
|
||||
'马': '🐎',
|
||||
'独角兽': '🦄',
|
||||
'斑马': '🦓',
|
||||
'鹿': '🦌',
|
||||
'牛': '🐮',
|
||||
'猪': '🐷',
|
||||
'羊': '🐏',
|
||||
'长颈鹿': '🦒',
|
||||
'大象': '🐘',
|
||||
'老鼠': '🐭',
|
||||
'蝙蝠': '🦇',
|
||||
'刺猬': '🦔',
|
||||
'熊猫': '🐼',
|
||||
'鸽子': '🕊️',
|
||||
'鸭子': '🦆',
|
||||
'兔子': '🐇',
|
||||
'老鹰': '🦅',
|
||||
'青蛙': '🐸',
|
||||
'蛇': '🐍',
|
||||
'龙': '🐉',
|
||||
'鲸鱼': '🐳',
|
||||
'海豚': '🐬',
|
||||
'足球': '⚽',
|
||||
'棒球': '⚾',
|
||||
'篮球': '🏀',
|
||||
'排球': '🏐',
|
||||
'橄榄球': '🏉',
|
||||
'网球': '🎾',
|
||||
'骰子': '🎲',
|
||||
'鸡腿': '🍗',
|
||||
'蛋糕': '🎂',
|
||||
'啤酒': '🍺',
|
||||
'饺子': '🥟',
|
||||
'汉堡': '🍔',
|
||||
'薯条': '🍟',
|
||||
'意大利面': '🍝',
|
||||
'干杯': '🥂',
|
||||
'筷子': '🥢',
|
||||
'糖果': '🍬',
|
||||
'奶瓶': '🍼',
|
||||
'爆米花': '🍿',
|
||||
'邮局': '🏤',
|
||||
'医院': '🏥',
|
||||
'银行': '🏦',
|
||||
'酒店': '🏨',
|
||||
'学校': '🏫',
|
||||
'城堡': '🏰',
|
||||
'火车': '🚂',
|
||||
'高铁': '🚄',
|
||||
'地铁': '🚇',
|
||||
'公交': '🚌',
|
||||
'救护车': '🚑',
|
||||
'消防车': '🚒',
|
||||
'警车': '🚓',
|
||||
'出租车': '🚕',
|
||||
'汽车': '🚗',
|
||||
'货车': '🚛',
|
||||
'自行车': '🚲',
|
||||
'摩托': '🛵',
|
||||
'红绿灯': '🚥',
|
||||
'帆船': '⛵',
|
||||
'游轮': '🛳️',
|
||||
'轮船': '⛴️',
|
||||
'飞机': '✈️',
|
||||
'直升机': '🚁',
|
||||
'缆车': '🚠',
|
||||
'警告': '⚠️',
|
||||
'禁止': '⛔'
|
||||
}
|
||||
module.exports = {
|
||||
parseEmoji: (text) => text.replace(reg, ($, $1) => {
|
||||
if (data[$1]) {
|
||||
if (data[$1].includes('http'))
|
||||
return `<img src="${data[$1]}" style="width:16px;height:16px;display:inline-block" ignore>`;
|
||||
else return data[$1];
|
||||
} else return $;
|
||||
}),
|
||||
getEmoji: (key) => data[key],
|
||||
setEmoji: (key, emoji) => data[key] = emoji,
|
||||
removeEmoji: (key) => data[key] = void 0
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/* PrismJS 1.19.0
|
||||
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
|
||||
* Based on https://github.com/chriskempson/tomorrow-theme
|
||||
* @author Rose Pritchard
|
||||
*/
|
||||
|
||||
.token.comment,
|
||||
.token.block-comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.token.tag,
|
||||
.token.attr-name,
|
||||
.token.namespace,
|
||||
.token.deleted {
|
||||
color: #e2777a;
|
||||
}
|
||||
|
||||
.token.function-name {
|
||||
color: #6196cc;
|
||||
}
|
||||
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.function {
|
||||
color: #f08d49;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.class-name,
|
||||
.token.constant,
|
||||
.token.symbol {
|
||||
color: #f8c555;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.important,
|
||||
.token.atrule,
|
||||
.token.keyword,
|
||||
.token.builtin {
|
||||
color: #cc99cd;
|
||||
}
|
||||
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.attr-value,
|
||||
.token.regex,
|
||||
.token.variable {
|
||||
color: #7ec699;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url {
|
||||
color: #67cdcc;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.token.inserted {
|
||||
color: green;
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
parser 主组件
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
update:2020/04/25
|
||||
*/
|
||||
var cache = {},
|
||||
Parser = require('./libs/MpHtmlParser.js'),
|
||||
fs = wx.getFileSystemManager && wx.getFileSystemManager();
|
||||
try {
|
||||
var dom = require('./libs/document.js');
|
||||
} catch (e) {}
|
||||
// 计算 cache 的 key
|
||||
function hash(str) {
|
||||
for (var i = str.length, val = 5381; i--;)
|
||||
val += (val << 5) + str.charCodeAt(i);
|
||||
return val;
|
||||
}
|
||||
Component({
|
||||
options: {
|
||||
pureDataPattern: /^[acdgtux]|W/
|
||||
},
|
||||
properties: {
|
||||
'html': {
|
||||
type: null,
|
||||
observer(html) {
|
||||
if (this._refresh) this._refresh = false;
|
||||
else this.setContent(html, false, true);
|
||||
}
|
||||
},
|
||||
'autosetTitle': {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
'autopause': {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
'compress': Number,
|
||||
'domain': String,
|
||||
'gestureZoom': Boolean,
|
||||
'lazyLoad': Boolean,
|
||||
'selectable': Boolean,
|
||||
'tagStyle': Object,
|
||||
'showWithAnimation': Boolean,
|
||||
'useAnchor': Boolean,
|
||||
'useCache': Boolean,
|
||||
'xml': Boolean
|
||||
},
|
||||
relations: {
|
||||
'../parser-group/parser-group': {
|
||||
type: 'ancestor'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 图片数组
|
||||
this.imgList = [];
|
||||
this.imgList.setItem = function (i, src) {
|
||||
if (!i || !src) return;
|
||||
// 去重
|
||||
if (src.indexOf('http') == 0 && this.includes(src)) {
|
||||
var newSrc = '';
|
||||
for (var j = 0, c; c = src[j]; j++) {
|
||||
if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
|
||||
newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
|
||||
}
|
||||
newSrc += src.substr(j);
|
||||
return this[i] = newSrc;
|
||||
}
|
||||
this[i] = src;
|
||||
// 暂存 data src
|
||||
if (src.includes('data:image')) {
|
||||
var info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
|
||||
if (!info) return;
|
||||
var filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
|
||||
fs && fs.writeFile({
|
||||
filePath,
|
||||
data: info[3],
|
||||
encoding: info[2],
|
||||
success: () => this[i] = filePath
|
||||
})
|
||||
}
|
||||
}
|
||||
this.imgList.each = function (f) {
|
||||
for (var i = 0, len = this.length; i < len; i++)
|
||||
this.setItem(i, f(this[i], i, this));
|
||||
}
|
||||
if (dom) this.document = new dom(this);
|
||||
},
|
||||
detached() {
|
||||
// 删除暂存
|
||||
this.imgList.each(src => {
|
||||
if (src && src.includes(wx.env.USER_DATA_PATH) && fs)
|
||||
fs.unlink({
|
||||
filePath: src
|
||||
})
|
||||
})
|
||||
clearInterval(this._timer);
|
||||
},
|
||||
methods: {
|
||||
// 锚点跳转
|
||||
navigateTo(obj) {
|
||||
if (!this.data.useAnchor)
|
||||
return obj.fail && obj.fail({
|
||||
errMsg: 'Anchor is disabled'
|
||||
})
|
||||
this.createSelectorQuery()
|
||||
.select('.top' + (obj.id ? '>>>#' + obj.id : '')).boundingClientRect()
|
||||
.selectViewport().scrollOffset().exec(res => {
|
||||
if (!res[0])
|
||||
return this.group ? this.group.navigateTo(this.i, obj) :
|
||||
obj.fail && obj.fail({
|
||||
errMsg: 'Label not found'
|
||||
});
|
||||
obj.scrollTop = res[1].scrollTop + res[0].top + (obj.offset || 0);
|
||||
wx.pageScrollTo(obj);
|
||||
})
|
||||
},
|
||||
// 获取文本
|
||||
getText(ns = this.data.html) {
|
||||
var txt = '';
|
||||
for (var i = 0, n; n = ns[i++];) {
|
||||
if (n.type == 'text') txt += n.text.replace(/ /g, '\u00A0').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
|
||||
else if (n.type == 'br') txt += '\n';
|
||||
else {
|
||||
// 块级标签前后加换行
|
||||
var br = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] > '0' && n.name[1] < '7');
|
||||
if (br && txt && txt[txt.length - 1] != '\n') txt += '\n';
|
||||
if (n.children) txt += this.getText(n.children);
|
||||
if (br && txt[txt.length - 1] != '\n') txt += '\n';
|
||||
else if (n.name == 'td' || n.name == 'th') txt += '\t';
|
||||
}
|
||||
}
|
||||
return txt;
|
||||
},
|
||||
// 获取视频 context
|
||||
getVideoContext(id) {
|
||||
if (!id) return this.videoContexts;
|
||||
for (var i = this.videoContexts.length; i--;)
|
||||
if (this.videoContexts[i].id == id) return this.videoContexts[i];
|
||||
},
|
||||
// 渲染富文本
|
||||
setContent(html, append, _watch) {
|
||||
var data = {};
|
||||
if (!html) {
|
||||
if (_watch || append) return;
|
||||
data.html = '';
|
||||
} else if (typeof html == 'string') {
|
||||
let parser = new Parser(html, this.data);
|
||||
// 缓存读取
|
||||
if (this.data.useCache) {
|
||||
var hashVal = hash(html);
|
||||
if (cache[hashVal]) data.html = cache[hashVal];
|
||||
else {
|
||||
data.html = parser.parse();
|
||||
cache[hashVal] = data.html;
|
||||
}
|
||||
} else data.html = parser.parse();
|
||||
this._refresh = true;
|
||||
this.triggerEvent('parse', data.html);
|
||||
} else if (html.constructor == Array) {
|
||||
// 转换不符合格式的 array
|
||||
if (html.length && html[0].PoweredBy != 'Parser') {
|
||||
let parser = new Parser('', this.data);
|
||||
(function f(ns) {
|
||||
for (var i = 0, n; n = ns[i]; i++) {
|
||||
if (n.type == 'text') continue;
|
||||
n.attrs = n.attrs || {};
|
||||
for (var key in n.attrs)
|
||||
if (typeof n.attrs[key] != 'string') n.attrs[key] = n.attrs[key].toString();
|
||||
parser.matchAttr(n);
|
||||
if (n.children) {
|
||||
parser.STACK.push(n);
|
||||
f(n.children);
|
||||
parser.popNode(parser.STACK.pop());
|
||||
}
|
||||
}
|
||||
})(html);
|
||||
data.html = html;
|
||||
}
|
||||
if (!_watch) data.html = html;
|
||||
} else if (typeof html == 'object' && html.nodes) {
|
||||
data.html = html.nodes;
|
||||
console.warn('错误的 html 类型:object 类型已废弃');
|
||||
} else
|
||||
return console.warn('错误的 html 类型:' + typeof html);
|
||||
if (append) {
|
||||
this._refresh = true;
|
||||
data.html = (this.data.html || []).concat(data.html);
|
||||
} else if (this.data.showWithAnimation) data.showAm = 'animation: show .5s';
|
||||
if (data.html || data.showAm) this.setData(data);
|
||||
// 设置标题
|
||||
if (this.data.html.length && this.data.html[0].title && this.data.autosetTitle)
|
||||
wx.setNavigationBarTitle({
|
||||
title: this.data.html[0].title
|
||||
})
|
||||
this.imgList.length = 0;
|
||||
this.videoContexts = [];
|
||||
var ns = this.selectAllComponents('.top,.top>>>._node');
|
||||
for (let i = 0, n; n = ns[i++];) {
|
||||
n.top = this;
|
||||
for (var j = 0, item; item = n.data.nodes[j++];) {
|
||||
if (item.c) continue;
|
||||
// 获取图片列表
|
||||
if (item.name == 'img')
|
||||
this.imgList.setItem(item.attrs.i, item.attrs.src);
|
||||
// 音视频控制
|
||||
else if (item.name == 'video' || item.name == 'audio') {
|
||||
var ctx;
|
||||
if (item.name == 'video') ctx = wx.createVideoContext(item.attrs.id, n);
|
||||
else ctx = n.selectComponent('#' + item.attrs.id);
|
||||
if (ctx) {
|
||||
ctx.id = item.attrs.id;
|
||||
this.videoContexts.push(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(wx.nextTick || setTimeout)(() => this.triggerEvent('load'), 50);
|
||||
var height;
|
||||
clearInterval(this._timer);
|
||||
this._timer = setInterval(() => {
|
||||
this.createSelectorQuery().select('.top').boundingClientRect(res => {
|
||||
this.rect = res;
|
||||
if (res.height == height) {
|
||||
this.triggerEvent('ready', res)
|
||||
clearInterval(this._timer);
|
||||
}
|
||||
height = res.height;
|
||||
}).exec();
|
||||
}, 350)
|
||||
},
|
||||
// 预加载
|
||||
preLoad(html, num) {
|
||||
if (typeof html == 'string') {
|
||||
var id = hash(html);
|
||||
html = new Parser(html, this.data).parse();
|
||||
cache[id] = html;
|
||||
}
|
||||
var imgs, wait = [];
|
||||
(function f(ns) {
|
||||
for (var i = 0, n; n = ns[i++];) {
|
||||
if (n.name == 'img' && n.attrs.src && !wait.includes(n.attrs.src))
|
||||
wait.push(n.attrs.src);
|
||||
f(n.children || []);
|
||||
}
|
||||
})(html);
|
||||
if (num) wait = wait.slice(0, num);
|
||||
this._wait = (this._wait || []).concat(wait);
|
||||
if (!this.data.imgs) imgs = this._wait.splice(0, 15);
|
||||
else if (this.data.imgs.length < 15)
|
||||
imgs = this.data.imgs.concat(this._wait.splice(0, 15 - this.data.imgs.length));
|
||||
imgs && this.setData({
|
||||
imgs
|
||||
});
|
||||
},
|
||||
_load(e) {
|
||||
if (this._wait.length)
|
||||
this.setData({
|
||||
[`imgs[${e.target.id}]`]: this._wait.shift()
|
||||
})
|
||||
},
|
||||
// 事件处理
|
||||
_tap(e) {
|
||||
if (this.data.gestureZoom && e.timeStamp - this._lastT < 300) {
|
||||
var initY = e.detail.y - e.currentTarget.offsetTop;
|
||||
if (this._zoom) {
|
||||
this._scaleAm.translateX(0).scale(1).step();
|
||||
wx.pageScrollTo({
|
||||
scrollTop: (initY + this._initY) / 2 - e.touches[0].clientY,
|
||||
duration: 400
|
||||
})
|
||||
} else {
|
||||
var initX = e.detail.x - e.currentTarget.offsetLeft;
|
||||
this._initY = initY;
|
||||
this._scaleAm = wx.createAnimation({
|
||||
transformOrigin: `${initX}px ${this._initY}px 0`,
|
||||
timingFunction: 'ease-in-out'
|
||||
});
|
||||
this._scaleAm.scale(2).step();
|
||||
this._tMax = initX / 2;
|
||||
this._tMin = (initX - this.rect.width) / 2;
|
||||
this._tX = 0;
|
||||
}
|
||||
this._zoom = !this._zoom;
|
||||
this.setData({
|
||||
scaleAm: this._scaleAm.export()
|
||||
})
|
||||
}
|
||||
this._lastT = e.timeStamp;
|
||||
},
|
||||
_touchstart(e) {
|
||||
if (e.touches.length == 1)
|
||||
this._initX = this._lastX = e.touches[0].pageX;
|
||||
},
|
||||
_touchmove(e) {
|
||||
var diff = e.touches[0].pageX - this._lastX;
|
||||
if (this._zoom && e.touches.length == 1 && Math.abs(diff) > 20) {
|
||||
this._lastX = e.touches[0].pageX;
|
||||
if ((this._tX <= this._tMin && diff < 0) || (this._tX >= this._tMax && diff > 0)) return;
|
||||
this._tX += diff * Math.abs(this._lastX - this._initX) * 0.05;
|
||||
if (this._tX < this._tMin) this._tX = this._tMin;
|
||||
if (this._tX > this._tMax) this._tX = this._tMax;
|
||||
this._scaleAm.translateX(this._tX).step();
|
||||
this.setData({
|
||||
scaleAm: this._scaleAm.export()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"trees": "./trees/trees"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<!--parser 主组件-->
|
||||
<slot wx:if="{{!html[0].name&&!html[0].type}}" />
|
||||
<trees class="top" style="{{selectable?'user-select:text;-webkit-user-select:text;':''}}{{showAm}}" animation="{{scaleAm}}" lazy-load="{{lazyLoad}}" nodes="{{html[0].name||html[0].type?html:[]}}" bindtap="_tap" bindtouchstart="_touchstart" bindtouchmove="_touchmove" />
|
||||
<image wx:for="{{imgs}}" wx:key="index" id="{{index}}" src="{{item}}" hidden bindload="_load" />
|
|
@ -0,0 +1,16 @@
|
|||
:host {
|
||||
display: block;
|
||||
overflow: scroll;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.top {
|
||||
display: inherit;
|
||||
}
|
||||
@keyframes show {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
trees 递归子组件
|
||||
github:https://github.com/jin-yufeng/Parser
|
||||
docs:https://jin-yufeng.github.io/Parser
|
||||
author:JinYufeng
|
||||
update:2020/04/25
|
||||
*/
|
||||
Component({
|
||||
data: {
|
||||
canIUse: !!wx.chooseMessageFile,
|
||||
placeholder: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='225'/>",
|
||||
inlineTags: require('../libs/config.js').inlineTags
|
||||
},
|
||||
properties: {
|
||||
nodes: Array,
|
||||
lazyLoad: Boolean
|
||||
},
|
||||
methods: {
|
||||
// 自定义事件
|
||||
copyCode(e) {
|
||||
wx.showActionSheet({
|
||||
itemList: ['复制代码'],
|
||||
success: () =>
|
||||
wx.setClipboardData({
|
||||
data: e.target.dataset.content
|
||||
})
|
||||
})
|
||||
},
|
||||
// 视频播放事件
|
||||
play(e) {
|
||||
this.top.group && this.top.group.pause(this.top.i);
|
||||
if (this.top.videoContexts.length > 1 && this.top.data.autopause)
|
||||
for (var i = this.top.videoContexts.length; i--;)
|
||||
if (this.top.videoContexts[i].id != e.currentTarget.id)
|
||||
this.top.videoContexts[i].pause();
|
||||
},
|
||||
// 图片事件
|
||||
imgtap(e) {
|
||||
var attrs = e.currentTarget.dataset.attrs;
|
||||
if (!attrs.ignore) {
|
||||
var preview = true;
|
||||
this.top.triggerEvent('imgtap', {
|
||||
id: e.currentTarget.id,
|
||||
src: attrs.src,
|
||||
ignore: () => preview = false
|
||||
})
|
||||
if (preview) {
|
||||
if (this.top.group) return this.top.group.preview(this.top.i, attrs.i);
|
||||
var urls = this.top.imgList,
|
||||
current = urls[attrs.i] ? urls[attrs.i] : (urls = [attrs.src], attrs.src);
|
||||
wx.previewImage({
|
||||
current,
|
||||
urls
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
loadImg(e) {
|
||||
var i = e.target.dataset.i;
|
||||
if (this.data.lazyLoad && !this.data.nodes[i].load)
|
||||
this.setData({
|
||||
[`nodes[${i}].load`]: true
|
||||
})
|
||||
},
|
||||
// 链接点击事件
|
||||
linkpress(e) {
|
||||
var jump = true,
|
||||
attrs = e.currentTarget.dataset.attrs;
|
||||
attrs.ignore = () => jump = false;
|
||||
this.top.triggerEvent('linkpress', attrs);
|
||||
if (jump) {
|
||||
if (attrs['app-id'])
|
||||
wx.navigateToMiniProgram({
|
||||
appId: attrs['app-id'],
|
||||
path: attrs.path
|
||||
})
|
||||
else if (attrs.href) {
|
||||
if (attrs.href[0] == '#')
|
||||
this.top.navigateTo({
|
||||
id: attrs.href.substring(1)
|
||||
})
|
||||
else if (attrs.href.indexOf('http') == 0 || attrs.href.indexOf('//') == 0)
|
||||
wx.setClipboardData({
|
||||
data: attrs.href,
|
||||
success: () =>
|
||||
wx.showToast({
|
||||
title: '链接已复制'
|
||||
})
|
||||
})
|
||||
else
|
||||
wx.navigateTo({
|
||||
url: attrs.href,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
// 错误事件
|
||||
error(e) {
|
||||
var context, source = e.target.dataset.source,
|
||||
i = e.target.dataset.i,
|
||||
node = this.data.nodes[i];
|
||||
if (source == 'video' || source == 'audio') {
|
||||
// 加载其他 source
|
||||
var index = (node.i || 0) + 1;
|
||||
if (index < node.attrs.source.length)
|
||||
return this.setData({
|
||||
[`nodes[${i}].i`]: index
|
||||
})
|
||||
if (this.top) context = this.top.getVideoContext(e.target.id);
|
||||
} else if (source == 'img')
|
||||
context = {
|
||||
setSrc: src => {
|
||||
this.setData({
|
||||
[`nodes[${i}].attrs.src`]: src
|
||||
})
|
||||
}
|
||||
}
|
||||
this.top && this.top.triggerEvent('error', {
|
||||
source,
|
||||
target: e.target,
|
||||
context,
|
||||
...e.detail
|
||||
})
|
||||
},
|
||||
// 加载视频
|
||||
loadVideo(e) {
|
||||
var i = e.target.dataset.i;
|
||||
this.setData({
|
||||
[`nodes[${i}].lazyLoad`]: false,
|
||||
[`nodes[${i}].attrs.autoplay`]: true
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"trees": "./trees",
|
||||
"myAudio": "../audio/audio"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<!--trees 递归子组件-->
|
||||
<wxs module="handler">
|
||||
module.exports = {
|
||||
visited: function (e, owner) {
|
||||
if (!e.instance.hasClass('_visited'))
|
||||
e.instance.addClass('_visited')
|
||||
owner.callMethod('linkpress', e);
|
||||
},
|
||||
useRichText: function (item, inlineTags) {
|
||||
return !item.c && !inlineTags[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1
|
||||
}
|
||||
}
|
||||
</wxs>
|
||||
<block wx:for="{{nodes}}" wx:key="index" wx:for-item="n">
|
||||
<!--图片-->
|
||||
<view wx:if="{{n.name=='img'}}" id="{{n.attrs.id}}" class="_img {{n.attrs.class}}" style="{{n.attrs.style}}" data-attrs="{{n.attrs}}" bindtap="imgtap">
|
||||
<rich-text nodes="{{[{attrs:{src:lazyLoad&&!n.load?placeholder:n.attrs.src,alt:n.attrs.alt||'',width:n.attrs.width||'',style:'max-width:100%;display:inherit'+(n.attrs.height?';height:'+n.attrs.height:'')},name:'img'}]}}" />
|
||||
<image class="_image" src="{{lazyLoad&&!n.load?placeholder:n.attrs.src}}" lazy-load="{{lazyLoad}}" show-menu-by-longpress="{{!n.attrs.ignore}}" data-i="{{index}}" data-source="img" bindload="loadImg" binderror="error" />
|
||||
</view>
|
||||
<!--文本-->
|
||||
<text wx:elif="{{n.type=='text'}}" decode>{{n.text}}</text>
|
||||
<text wx:elif="{{n.name=='br'}}">\n</text>
|
||||
<!--链接-->
|
||||
<view wx:elif="{{n.name=='a'}}" class="_a {{n.attrs.class}}" hover-class="_hover" style="{{n.attrs.style}}" data-attrs="{{n.attrs}}" bindtap="{{canIUse?handler.visited:'linkpress'}}">
|
||||
<trees nodes="{{n.children}}" />
|
||||
</view>
|
||||
<!--视频-->
|
||||
<block wx:elif="{{n.name=='video'}}">
|
||||
<view wx:if="{{n.lazyLoad}}" id="{{n.attrs.id}}" class="_video {{n.attrs.class}}" style="{{n.attrs.style}}" data-i="{{index}}" bindtap="loadVideo" />
|
||||
<video wx:else id="{{n.attrs.id}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" autoplay="{{n.attrs.autoplay}}" controls="{{n.attrs.controls}}" loop="{{n.attrs.loop}}" muted="{{n.attrs.muted}}" poster="{{n.attrs.poster}}" src="{{n.attrs.source[n.i||0]}}" unit-id="{{n.attrs['unit-id']}}" data-i="{{index}}" data-source="video" binderror="error" bindplay="play" />
|
||||
</block>
|
||||
<!--音频-->
|
||||
<myAudio wx:elif="{{n.name=='audio'}}" id="{{n.attrs.id}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" author="{{n.attrs.author}}" autoplay="{{n.attrs.autoplay}}" controls="{{n.attrs.controls}}" loop="{{n.attrs.loop}}" name="{{n.attrs.name}}" poster="{{n.attrs.poster}}" src="{{n.attrs.source[n.i||0]}}" data-i="{{index}}" data-source="audio" binderror="error" bindplay="play" />
|
||||
<!--广告-->
|
||||
<ad wx:elif="{{n.name=='ad'}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}" unit-id="{{n.attrs['unit-id']}}" data-source="ad" binderror="error" />
|
||||
<!--列表-->
|
||||
<view wx:elif="{{n.name=='li'}}" class="{{n.attrs.class}}" style="{{n.attrs.style}};display:flex">
|
||||
<view wx:if="{{n.type=='ol'}}" class="_ol-bef">{{n.num}}</view>
|
||||
<view wx:else class="_ul-bef">
|
||||
<view wx:if="{{n.floor%3==0}}" class="_ul-p1">█</view>
|
||||
<view wx:elif="{{n.floor%3==2}}" class="_ul-p2" />
|
||||
<view wx:else class="_ul-p1" style="border-radius:50%">█</view>
|
||||
</view>
|
||||
<trees class="_node _li" lazy-load="{{lazyLoad}}" nodes="{{n.children}}" />
|
||||
</view>
|
||||
<!--自定义标签-->
|
||||
<rich-text wx:elif="{{n.name=='pre'}}" class="_p" nodes="{{[n]}}" data-content="{{n.attrs['data-content']}}" bindlongpress="copyCode" />
|
||||
<!--富文本-->
|
||||
<rich-text wx:elif="{{handler.useRichText(n, inlineTags)}}" id="{{n.attrs.id}}" class="_p __{{n.name}}" nodes="{{[n]}}" />
|
||||
<!--继续递归-->
|
||||
<trees wx:else id="{{n.attrs.id}}" class="_node _{{n.name}} {{n.attrs.class}}" style="{{n.attrs.style}}" lazy-load="{{lazyLoad}}" nodes="{{n.children}}" />
|
||||
</block>
|
|
@ -0,0 +1,136 @@
|
|||
/* 在这里引入自定义样式 */
|
||||
@import "../libs/prism.wxss";
|
||||
|
||||
/* 链接和图片效果 */
|
||||
._a {
|
||||
color: #366092;
|
||||
display: inline;
|
||||
padding: 1.5px 0 1.5px 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
._hover {
|
||||
opacity: 0.7;
|
||||
text-decoration: underline;
|
||||
}
|
||||
._visited {
|
||||
color: #551a8b;
|
||||
}
|
||||
._img {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
/* 内部样式 */
|
||||
:host {
|
||||
display: inline;
|
||||
}
|
||||
._blockquote, ._div, ._p, ._ul, ._ol, ._li {
|
||||
display: block;
|
||||
}
|
||||
._b, ._strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
._code {
|
||||
font-family: monospace;
|
||||
}
|
||||
._del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
._em, ._i {
|
||||
font-style: italic;
|
||||
}
|
||||
._h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
._h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
._h3 {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
._h5 {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
._h6 {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
._h1, ._h2, ._h3, ._h4, ._h5, ._h6 {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
._image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
._ins {
|
||||
text-decoration: underline;
|
||||
}
|
||||
._li {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
}
|
||||
._ol-bef {
|
||||
margin-right: 5px;
|
||||
text-align: right;
|
||||
width: 36px;
|
||||
}
|
||||
._ul-bef {
|
||||
line-height: normal;
|
||||
margin: 0 12px 0 23px;
|
||||
}
|
||||
._ol-bef, ._ul_bef {
|
||||
flex: none;
|
||||
user-select: none;
|
||||
}
|
||||
._ul-p1 {
|
||||
display: inline-block;
|
||||
height: 0.3em;
|
||||
line-height: 0.3em;
|
||||
overflow: hidden;
|
||||
width: 0.3em;
|
||||
}
|
||||
._ul-p2 {
|
||||
border: 0.05em solid black;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
height: 0.23em;
|
||||
width: 0.23em;
|
||||
}
|
||||
._q::before {
|
||||
content: '"';
|
||||
}
|
||||
._q::after {
|
||||
content: '"';
|
||||
}
|
||||
._sub {
|
||||
font-size: smaller;
|
||||
vertical-align: sub;
|
||||
}
|
||||
._sup {
|
||||
font-size: smaller;
|
||||
vertical-align: super;
|
||||
}
|
||||
.__bdi, .__bdo, .__ruby, .__rt {
|
||||
display: inline-block;
|
||||
}
|
||||
._video {
|
||||
background-color: black;
|
||||
display: inline-block;
|
||||
height: 225px;
|
||||
position: relative;
|
||||
width: 300px;
|
||||
}
|
||||
._video::after {
|
||||
border-color: transparent transparent transparent white;
|
||||
border-style: solid;
|
||||
border-width: 15px 0 15px 30px;
|
||||
content: '';
|
||||
left: 50%;
|
||||
margin: -15px 0 0 -15px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
|
@ -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="1604812134932" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9787" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M921.6 0a102.4 102.4 0 0 1 102.4 102.4v819.2a102.4 102.4 0 0 1-102.4 102.4H102.4a102.4 102.4 0 0 1-102.4-102.4V102.4a102.4 102.4 0 0 1 102.4-102.4h819.2zM370.0224 358.4H281.6a25.6 25.6 0 0 0-25.6 25.6v358.4a25.6 25.6 0 0 0 25.6 25.6h88.4224a25.6 25.6 0 0 0 25.6-25.6v-358.4a25.6 25.6 0 0 0-25.6-25.6z m186.2144-102.4H467.7632a25.6 25.6 0 0 0-25.6 25.6v460.8a25.6 25.6 0 0 0 25.6 25.6h88.4736a25.6 25.6 0 0 0 25.6-25.6v-460.8a25.6 25.6 0 0 0-25.6-25.6z m186.1632 256h-88.4224a25.6 25.6 0 0 0-25.6 25.6v204.8a25.6 25.6 0 0 0 25.6 25.6H742.4a25.6 25.6 0 0 0 25.6-25.6v-204.8a25.6 25.6 0 0 0-25.6-25.6z" fill="#4372F4" p-id="9788"></path></svg>
|
After Width: | Height: | Size: 1017 B |
After Width: | Height: | Size: 14 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="1646232016809" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5102" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M937.154 984.333c-1.485 0-2.955-0.46-4.192-1.379l-128.85-94.949-129.726 94.786c-1.238 0.897-2.7 1.359-4.171 1.359-1.089 0-2.191-0.254-3.203-0.764-2.368-1.209-3.868-3.642-3.868-6.305v-191.509c0-2.271 1.089-4.405 2.928-5.733 1.223-0.885 2.673-1.337 4.143-1.337 0.743 0 1.498 0.121 2.22 0.361l45.813 15.172c2.977 0.855 5.21 3.579 5.21 6.794v71.362l76.624-55.959c1.244-0.906 2.708-1.359 4.171-1.359 1.471 0 2.948 0.46 4.192 1.379l75.436 55.583v-71.010c0-3.019 1.917-5.698 4.765-6.681l46.188-15.964c0.75-0.26 1.534-0.389 2.305-0.389 1.451 0 2.884 0.447 4.1 1.314 1.867 1.329 2.969 3.472 2.969 5.755v192.395c0 2.665-1.498 5.113-3.881 6.314-0.996 0.51-2.085 0.764-3.173 0.764zM134.749 951.301c-60.178 0-109.137-48.959-109.137-109.147v-694.208c0-60.188 48.959-109.153 109.137-109.153h605.566c60.199 0 109.173 48.966 109.173 109.153v216.558c0 3.902-3.162 7.069-7.069 7.069h-44.993c-3.909 0-7.069-3.168-7.069-7.069v-216.553c0-27.586-22.447-50.034-50.026-50.034h-605.573c-27.586 0-50.034 22.447-50.034 50.034v694.194c0 27.58 22.447 50.026 50.034 50.026h470.241c3.909 0 7.069 3.162 7.069 7.069v44.987c0 3.909-3.162 7.069-7.069 7.069h-470.247zM803.66 800.678c-112.142 0-203.386-91.229-203.386-203.373s91.238-203.38 203.386-203.38c112.151 0 203.401 91.238 203.401 203.38-0.005 112.142-91.252 203.373-203.401 203.373zM803.66 454.259c-78.879 0-143.053 64.173-143.053 143.046 0 78.879 64.173 143.053 143.053 143.053s143.053-64.173 143.053-143.053c0-78.873-64.173-143.046-143.053-143.046zM193.537 760.867c-3.902 0-7.069-3.162-7.069-7.069v-49.213c0-3.909 3.168-7.069 7.069-7.069h356.111c3.909 0 7.069 3.162 7.069 7.069v49.213c0 3.909-3.162 7.069-7.069 7.069h-356.111zM803.691 685.213c-48.471 0-87.913-39.436-87.913-87.907s39.436-87.907 87.913-87.907c48.465 0 87.907 39.436 87.907 87.907s-39.442 87.907-87.907 87.907zM803.691 546.628c-27.94 0-50.669 22.737-50.669 50.677s22.731 50.677 50.669 50.677c27.934 0 50.663-22.737 50.663-50.677s-22.731-50.677-50.663-50.677zM193.537 604.354c-3.902 0-7.069-3.162-7.069-7.069v-49.213c0-3.909 3.168-7.069 7.069-7.069h356.111c3.909 0 7.069 3.162 7.069 7.069v49.213c0 3.909-3.162 7.069-7.069 7.069h-356.111zM193.537 449.099c-3.902 0-7.069-3.168-7.069-7.069v-49.213c0-3.902 3.168-7.069 7.069-7.069h356.111c3.909 0 7.069 3.168 7.069 7.069v49.213c0 3.902-3.162 7.069-7.069 7.069h-356.111zM193.537 292.578c-3.902 0-7.069-3.168-7.069-7.069v-49.206c0-3.902 3.168-7.069 7.069-7.069h488.028c3.909 0 7.069 3.168 7.069 7.069v49.206c0 3.902-3.162 7.069-7.069 7.069h-488.028z" p-id="5103" fill="#1296db"></path></svg>
|
After Width: | Height: | Size: 2.8 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="1604812004450" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3676" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M368.64 901.888H212.736c-48.64-0.512-87.808-39.936-88.064-88.576v-155.904c0-23.552 9.472-46.08 26.112-62.72 16.64-16.64 39.168-25.856 62.72-25.856h155.904c48.896 0 88.576 39.68 88.832 88.576v155.904c0 23.552-9.728 46.336-26.368 62.976-16.896 16.384-39.68 25.6-63.232 25.6zM783.36 901.888h-155.904c-48.128-1.024-86.784-40.192-87.04-88.576v-155.904c0-23.552 9.472-46.08 26.112-62.72 16.64-16.64 39.168-25.856 62.72-25.856h155.904c23.552 0 46.08 9.216 62.72 25.856s26.112 39.168 26.112 62.72v155.904c0 23.808-9.728 46.592-26.88 63.232-16.896 16.64-39.936 25.856-63.744 25.344zM368.64 487.168H212.736c-48.896-0.512-88.32-40.448-88.064-89.344V241.92c0.768-48.64 40.448-87.808 89.344-87.808h155.904c48.64 1.024 87.552 40.704 87.808 89.344V399.36c-1.024 48.64-40.704 87.552-89.088 87.808zM878.336 257.792l-109.824-109.568c-34.816-34.56-90.88-34.56-125.44 0l-109.824 109.568c-34.304 34.816-34.304 90.624 0 125.44l109.824 109.824c34.816 34.304 90.624 34.304 125.44 0l109.824-109.824c34.56-34.56 34.56-90.624 0-125.44z m-35.072 98.304l-102.4 102.144c-9.216 9.216-22.016 14.592-35.072 14.592s-25.856-5.12-35.072-14.592l-102.4-102.4c-18.944-19.456-18.944-50.688 0-70.144l102.4-102.4c19.456-18.944 50.688-18.944 70.144 0l102.4 102.912c19.2 19.2 19.2 50.432 0 69.888z" fill="#248AFD" p-id="3677"></path></svg>
|
After Width: | Height: | Size: 1.6 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="1604812075816" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6918" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 0a512 512 0 1 0 512 512A511.963875 511.963875 0 0 0 512 0z m0 945.247442a433.247442 433.247442 0 1 1 433.247442-433.247442 433.247442 433.247442 0 0 1-433.247442 433.247442z" fill="#DC9514" p-id="6919"></path><path d="M202.950399 376.531151v48.51591l64.952797-22.036266v228.815918h66.506174V331.916743h-7.04438l-124.414591 44.614408zM545.7769 585.803429q17.159388-17.881888 33.235024-36.811402a275.778452 275.778452 0 0 0 24.926268-34.86065 164.802371 164.802371 0 0 0 15.750512-34.535526 118.128837 118.128837 0 0 0 5.527129-35.799901 119.393212 119.393212 0 0 0-7.477881-43.819657 85.616313 85.616313 0 0 0-22.650391-32.873774 102.1977 102.1977 0 0 0-37.136527-20.62739 163.93537 163.93537 0 0 0-50.900163-7.225006 138.720102 138.720102 0 0 0-52.923163 9.645383 117.297961 117.297961 0 0 0-39.737529 26.082269 114.155084 114.155084 0 0 0-25.070769 38.437028 125.389967 125.389967 0 0 0-8.778381 46.962534H462.400339a109.928456 109.928456 0 0 1 2.564877-24.528893 58.811543 58.811543 0 0 1 8.092005-19.615889 40.67678 40.67678 0 0 1 13.619135-12.968885 37.606153 37.606153 0 0 1 19.001764-4.696253 31.934523 31.934523 0 0 1 27.888521 13.908135q9.645382 13.87201 9.681507 38.509278a74.020179 74.020179 0 0 1-2.023002 16.689763 88.289565 88.289565 0 0 1-6.89988 18.748888 182.864884 182.864884 0 0 1-13.005009 22.108516c-5.382629 8.019756-12.065759 16.906512-20.157765 26.696395l-113.829958 126.943343v50.575037h247.528681v-59.606294h-142.982855zM810.031468 474.393847a65.205673 65.205673 0 0 0-33.235024-25.106893 72.250053 72.250053 0 0 0 17.195513-11.054258 71.130177 71.130177 0 0 0 12.643759-14.450011 62.315671 62.315671 0 0 0 7.875256-16.292387 59.642419 59.642419 0 0 0 2.709377-17.809638 80.378184 80.378184 0 0 0-6.791505-34.102025 68.63755 68.63755 0 0 0-19.001764-24.601143A80.811684 80.811684 0 0 0 762.238058 316.093982a133.120723 133.120723 0 0 0-37.678402-5.057504 108.95308 108.95308 0 0 0-34.752276 5.454879 86.700064 86.700064 0 0 0-28.03302 15.208636 69.865801 69.865801 0 0 0-18.785014 23.734142 68.34855 68.34855 0 0 0-6.936005 31.031398h61.412545a33.271149 33.271149 0 0 1 2.348127-12.932759 30.164397 30.164397 0 0 1 6.285754-9.717633A24.781768 24.781768 0 0 1 715.275524 357.637762a30.164397 30.164397 0 0 1 11.126508-2.131377 29.839272 29.839272 0 0 1 13.00501 2.673252 27.38277 27.38277 0 0 1 9.392506 7.225006 31.862273 31.862273 0 0 1 5.67163 10.837508 50.358287 50.358287 0 0 1 1.842376 13.980385 44.361532 44.361532 0 0 1-7.875256 27.635645c-5.238129 6.89988-13.546885 10.367883-24.781768 10.367883h-29.658647v43.350031h29.658647c11.523883 0 20.519015 3.179002 26.913145 9.609257s9.609257 16.906512 9.609257 31.428773a47.179285 47.179285 0 0 1-2.348127 15.425387 35.005151 35.005151 0 0 1-6.75538 11.885133 28.141396 28.141396 0 0 1-10.837508 7.586256 36.125026 36.125026 0 0 1-14.45001 2.637127 29.730897 29.730897 0 0 1-22.542017-9.898257 33.198899 33.198899 0 0 1-6.430255-10.837508 39.737529 39.737529 0 0 1-2.348126-13.980385h-61.412545a72.972553 72.972553 0 0 0 28.24977 60.184294 84.026812 84.026812 0 0 0 28.900022 14.45001 116.972836 116.972836 0 0 0 33.632399 4.768504 128.099344 128.099344 0 0 0 38.292528-5.418754 90.312566 90.312566 0 0 0 30.742398-15.822762 72.250053 72.250053 0 0 0 20.446765-25.504269 76.296056 76.296056 0 0 0 7.441755-34.535525 70.588302 70.588302 0 0 0-11.018133-39.376279z" fill="#DC9514" p-id="6920"></path></svg>
|
After Width: | Height: | Size: 3.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 class="icon" width="16px" height="16.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#1296db" d="M698.2 469.1c126.5 0 229.4 102.9 229.4 229.4s-102.9 229.5-229.4 229.5S468.8 825.1 468.8 698.6 571.7 469.1 698.2 469.1m0-32.1c-144.5 0-261.6 117.1-261.6 261.6s117.1 261.6 261.6 261.6 261.6-117.1 261.6-261.6-117.1-261.6-261.6-261.6zM531.7 226.3H203.6c-8.9 0-16.1-7.2-16.1-16.1s7.2-16.1 16.1-16.1h328.1c8.9 0 16.1 7.2 16.1 16.1s-7.3 16.1-16.1 16.1zM426.8 469.1H203.6c-8.9 0-16.1-7.2-16.1-16.1s7.2-16.1 16.1-16.1h223.2c8.9 0 16.1 7.2 16.1 16.1s-7.2 16.1-16.1 16.1z" /><path fill="#1296db" d="M722.9 700.6l92.5-92.5c6.3-6.3 6.3-16.4 0-22.7-6.3-6.3-16.4-6.3-22.7 0l-92.5 92.5-92.5-92.5c-6.3-6.3-16.4-6.3-22.7 0s-6.3 16.4 0 22.7l92.5 92.5L585 793.1c-6.3 6.3-6.3 16.4 0 22.7 3.1 3.1 7.2 4.7 11.4 4.7 4.1 0 8.2-1.6 11.4-4.7l92.5-92.5 92.5 92.5c3.1 3.1 7.2 4.7 11.4 4.7 4.1 0 8.2-1.6 11.4-4.7 6.3-6.3 6.3-16.4 0-22.7l-92.7-92.5zM675 226.3h-72.3c-8.9 0-16.1-7.2-16.1-16.1s7.2-16.1 16.1-16.1H675c8.9 0 16.1 7.2 16.1 16.1s-7.3 16.1-16.1 16.1zM325 673.6H203.6c-8.9 0-16.1-7.2-16.1-16.1 0-8.9 7.2-16.1 16.1-16.1H325c8.9 0 16.1 7.2 16.1 16.1 0 8.9-7.2 16.1-16.1 16.1z" /><path fill="#1296db" d="M315.2 957.2H183c-65.5 0-118.8-53.3-118.8-118.8V182.6C64.2 117.1 117.5 63.8 183 63.8h655.8c65.5 0 118.8 53.3 118.8 118.8V336.1c0 8.9-7.2 16.1-16.1 16.1s-16.1-7.2-16.1-16.1V182.6c0-47.8-38.9-86.6-86.7-86.6H183c-47.8 0-86.6 38.9-86.6 86.6v655.8c0 47.8 38.9 86.6 86.6 86.6h132.2c8.9 0 16.1 7.2 16.1 16.1-0.1 8.9-7.3 16.1-16.1 16.1z" /></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="1583918344766" class="icon" viewBox="0 0 1056 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12776" xmlns:xlink="http://www.w3.org/1999/xlink" width="66" height="64"><defs><style type="text/css"></style></defs><path d="M964.218456 512.964218c-15.427495 0-28.926554 13.499058-28.926554 28.926554v260.338983c0 90.636535-73.280603 163.917137-163.917137 163.917138H221.770245c-90.636535 0-163.917137-73.280603-163.917138-163.917138V318.19209C57.853107 227.555556 131.13371 154.274953 221.770245 154.274953h283.480226c15.427495 0 28.926554-13.499058 28.926553-28.926554S520.677966 96.421846 505.250471 96.421846H221.770245C100.278719 96.421846 0 196.700565 0 318.19209v484.037665c0 121.491525 100.278719 221.770245 221.770245 221.770245H771.374765c121.491525 0 221.770245-100.278719 221.770244-221.770245V541.890772c0-17.355932-13.499058-28.926554-28.926553-28.926554zM177.416196 389.544256c0 15.427495 13.499058 28.926554 28.926554 28.926554h354.832391c15.427495 0 28.926554-13.499058 28.926554-28.926554s-13.499058-28.926554-28.926554-28.926554H206.34275c-15.427495 0-28.926554 13.499058-28.926554 28.926554z m609.386064 325.905838H206.34275c-15.427495 0-28.926554 13.499058-28.926554 28.926554s13.499058 28.926554 28.926554 28.926554h580.45951c15.427495 0 28.926554-13.499058 28.926554-28.926554s-13.499058-28.926554-28.926554-28.926554z m0-177.416196H206.34275c-15.427495 0-28.926554 13.499058-28.926554 28.926554s13.499058 28.926554 28.926554 28.926554h580.45951c15.427495 0 28.926554-13.499058 28.926554-28.926554s-13.499058-28.926554-28.926554-28.926554zM752.090395 302.764595c5.785311 5.785311 13.499058 7.713748 21.212807 7.713748s15.427495-1.928437 21.212806-7.713748l42.425612-42.425612 42.425612 42.425612c5.785311 5.785311 13.499058 7.713748 21.212806 7.713748s15.427495-1.928437 21.212806-7.713748c11.570621-11.570621 11.570621-28.926554 0-40.497175l-42.425612-42.425612 42.425612-42.425612c11.570621-11.570621 11.570621-28.926554 0-40.497175s-28.926554-11.570621-40.497175 0l-42.425613 42.425612-42.425612-42.425612c-11.570621-11.570621-28.926554-11.570621-40.497175 0s-11.570621 28.926554 0 40.497175l42.425612 42.425612-42.425612 42.425612c-15.427495 11.570621-15.427495 30.854991-3.856874 40.497175z m84.851225 136.919021c121.491525 0 219.841808-98.350282 219.841807-219.841808S958.433145 0 836.94162 0 617.099812 98.350282 617.099812 219.841808s98.350282 219.841808 219.841808 219.841808z m0-381.830509c88.708098 0 161.988701 73.280603 161.9887 161.988701s-73.280603 161.988701-161.9887 161.9887S674.952919 308.549906 674.952919 219.841808 746.305085 57.853107 836.94162 57.853107z" fill="#A390E8" p-id="12777"></path></svg>
|
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.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="1646232207512" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6280" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M295.581454 931.441151 92.254451 728.115155l562.02665-562.031684 203.325996 203.325996L295.581454 931.441151 295.581454 931.441151zM295.581454 931.441151M988.661735 143.042181 880.643352 35.02984c-26.31967-26.31967-70.851666-24.461989-99.461973 4.153353L679.701633 140.662939l203.325996 203.325996L984.507375 242.510195C1013.111641 213.893847 1014.976371 169.360844 988.661735 143.042181L988.661735 143.042181zM988.661735 143.042181M73.372554 760.101509 1.485823 1022.19971l262.070009-71.887738L73.372554 760.101509zM73.372554 760.101509" p-id="6281" fill="#1296db"></path></svg>
|
After Width: | Height: | Size: 956 B |
|
@ -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 class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M687.29558 28.962342H270.072478c-47.956597 0-86.987919 39.010155-86.98792 86.980864v94.636139h46.016321V115.943911c0-22.598938 18.386772-40.978655 40.971599-40.978655h398.01083l222.976658 225.41788v588.609553c0 22.598232-18.386772 40.978655-40.98571 40.978655H270.072478c-22.584827 0-40.971599-18.380422-40.971599-40.978655V585.258165h-46.016321v303.734524c0 47.962947 39.031322 86.980158 86.98792 86.980158h580.001778c47.962947 0 86.980864-39.017211 86.980863-86.980158V281.474252L687.29558 28.962342z" fill="#509937" /><path d="M548.343509 289.320028h-61.830638v295.938843H283.558463v61.942821H548.343509z" fill="#FFFFFF" /><path d="M420.458074 537.00594V210.096745H94.066051v326.910606h326.392728v-0.001411z m-43.872843-62.638499l-77.224445-0.647699-44.798531-53.974985-44.802765 53.974985-77.232206 0.647699 84.493781-99.85231L132.527284 272.753588l73.63599 0.179211 48.399686 57.197962 48.386281-57.197962 73.636695-0.179211-84.475437 101.760838 84.474732 99.853015z" fill="#509937" /><path d="M471.708909 290.731139v295.938842H268.748151v61.942821h264.789984V290.731139z" fill="#509937" /><path d="M687.361197 61.03336v174.682806c0 35.201568 28.796536 63.991048 63.979054 63.991048h171.780856L687.361197 61.03336z" fill="#509937" /></svg>
|
After Width: | Height: | Size: 1.5 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 class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#13227a" d="M554.666667 384h234.666666L554.666667 149.333333V384M256 85.333333h341.333333l256 256v512a85.333333 85.333333 0 0 1-85.333333 85.333334H256a85.333333 85.333333 0 0 1-85.333333-85.333334V170.666667c0-47.36 37.973333-85.333333 85.333333-85.333334m213.333333 85.333334H256v682.666666h512v-384h-298.666667V170.666667z" /></svg>
|
After Width: | Height: | Size: 604 B |