This commit is contained in:
shijing 2023-03-02 10:50:27 +08:00
commit 4b0112fe15
318 changed files with 12668 additions and 0 deletions

81
app.js Normal file
View File

@ -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
}
})

106
app.json Normal file
View File

@ -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"
}

6
app.wxss Normal file

File diff suppressed because one or more lines are too long

View File

@ -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
}

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -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>

View File

@ -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; */
}

View File

@ -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);
}
}
});

View File

@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"van-icon": "./vant/icon/index",
"van-uploader": "./vant/uploader/index",
"time-picker": "./components/timePicker/timePicker"
}
}

View File

@ -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>

View File

@ -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;
}

View File

@ -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;

View File

@ -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";

View File

@ -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';

View File

@ -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 };

View File

@ -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 };

View File

@ -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}

View File

@ -0,0 +1 @@
.van-clearfix:after{display:table;clear:both;content:""}

View File

@ -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}

View File

@ -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}

View File

@ -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;

View File

@ -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;
}

View File

@ -0,0 +1 @@
export {};

View File

@ -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');
}
}
});

View File

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"van-info": "../info/index"
}
}

View File

@ -0,0 +1,20 @@
<!-- <wxs src="../wxs/utils.wxs" module="utils" /> -->
<view
class="custom-class {{ classPrefix }} {{ isImageName ? 'van-icon--image' : classPrefix + '-' + name }}"
style="color: {{ color }};font-size: {{ size }}rpx;{{ customStyle }}"
bind:tap="onClick"
>
<!-- <van-info
wx:if="{{ info !== null || dot }}"
dot="{{ dot }}"
info="{{ info }}"
custom-class="van-icon__info"
/> -->
<image
wx:if="{{ isImageName }}"
src="{{ name }}"
mode="aspectFit"
class="van-icon__image"
/>
</view>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,12 @@
import { VantComponent } from '../common/component';
VantComponent({
props: {
dot: Boolean,
info: null,
customStyle: String
},
data: {
// classBem: utils.bem('info', { dot })
}
});

View File

@ -0,0 +1,3 @@
{
"component": true
}

View File

@ -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> -->

View File

@ -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)}

View File

@ -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();
});
}
}
});

View File

@ -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'
}
}
});

View File

@ -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
});
});
}
});

View File

@ -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
});
}
}
}
});

View File

@ -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());
}
}
}
});

View File

@ -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);
}
}
}

View File

@ -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;
});
}

View File

@ -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);
}
}
});

View File

@ -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' : '';
}
}
});

View File

@ -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
});
}
}
}
});
};

View File

@ -0,0 +1 @@
export {};

View File

@ -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' });
}
});
}
}
});

View File

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"van-icon": "../icon/index"
}
}

View File

@ -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>

View File

@ -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}

View File

@ -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 {};

View File

@ -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;
}

View File

@ -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
};

View File

@ -0,0 +1,5 @@
function isArray(array) {
return array && array.constructor === 'Array';
}
module.exports.isArray = isArray;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
};

View File

@ -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(/&amp;/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}}});

View File

@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node/node"}}

View File

@ -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>

View File

@ -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}

View File

@ -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})}}});

View File

@ -0,0 +1 @@
{"component":true,"usingComponents":{"node":"./node"}}

View File

@ -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>

View File

@ -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}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,100 @@
/*
audio 扩展包
githubhttps://github.com/jin-yufeng/Parser
docshttps://jin-yufeng.github.io/Parser
authorJinYufeng
*/
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;
}
}
})

View File

@ -0,0 +1,3 @@
{
"component": true
}

View File

@ -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>

View File

@ -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;
}

View File

@ -0,0 +1,102 @@
/*
解析和匹配 Css 的选择器
githubhttps://github.com/jin-yufeng/Parser
docshttps://jin-yufeng.github.io/Parser
authorJinYufeng
update2020/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;
}
}

View File

@ -0,0 +1,528 @@
/*
html 解析为适用于小程序 rich-text DOM 结构
githubhttps://github.com/jin-yufeng/Parser
docshttps://jin-yufeng.github.io/Parser
authorJinYufeng
update2020/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;

View File

@ -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(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/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;
}

View File

@ -0,0 +1,198 @@
/*
emoji 扩展包
githubhttps://github.com/jin-yufeng/Parser
docshttps://jin-yufeng.github.io/Parser
authorJinYufeng
*/
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
}

File diff suppressed because one or more lines are too long

View File

@ -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;
}

312
components/parser/parser.js Normal file
View File

@ -0,0 +1,312 @@
/*
parser 主组件
githubhttps://github.com/jin-yufeng/Parser
docshttps://jin-yufeng.github.io/Parser
authorJinYufeng
update2020/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(/&nbsp;/g, '\u00A0').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/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()
})
}
}
}
})

View File

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"trees": "./trees/trees"
}
}

View File

@ -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" />

View File

@ -0,0 +1,16 @@
:host {
display: block;
overflow: scroll;
-webkit-overflow-scrolling: touch;
}
.top {
display: inherit;
}
@keyframes show {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

View File

@ -0,0 +1,134 @@
/*
trees 递归子组件
githubhttps://github.com/jin-yufeng/Parser
docshttps://jin-yufeng.github.io/Parser
authorJinYufeng
update2020/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
})
}
}
})

View File

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"trees": "./trees",
"myAudio": "../audio/audio"
}
}

View File

@ -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>

View File

@ -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%;
}

1
images/avg.svg Normal file
View File

@ -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

BIN
images/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

1
images/candidate.svg Normal file
View File

@ -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

1
images/class.svg Normal file
View File

@ -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

1
images/count.svg Normal file
View File

@ -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

1
images/cuoti.svg Normal file
View File

@ -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

1
images/cuoti_.svg Normal file
View File

@ -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

BIN
images/data.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
images/datac.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

1
images/exam.svg Normal file
View File

@ -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

1
images/examtest.svg Normal file
View File

@ -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

1
images/excel.svg Normal file
View File

@ -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

1
images/file.svg Normal file
View File

@ -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

Some files were not shown because too many files have changed in this diff Show More