Commit 6f0b28ff by pangchong

feat: 筛选页开发

parent 6ce64588
{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/ {
// launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数 // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
"version": "0.0", // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
"configurations": [{ "version" : "0.0",
"app-plus" : "configurations" : [
{ {
"launchtype" : "local" "app-plus" : {
}, "launchtype" : "local"
"default" : },
{ "default" : {
"launchtype" : "local" "launchtype" : "local"
}, },
"mp-weixin" : "mp-weixin" : {
{ "launchtype" : "local"
"launchtype" : "local" },
}, "type" : "uniCloud"
"type" : "uniCloud" },
} {
"playground" : "standard",
"type" : "uni-app:app-android"
}
] ]
} }
...@@ -10,6 +10,7 @@ declare module 'vue' { ...@@ -10,6 +10,7 @@ declare module 'vue' {
// 全局组件 // 全局组件
GlobalAlbum: typeof import('./src/mocp/components/global-album/global-album.vue')['default'] GlobalAlbum: typeof import('./src/mocp/components/global-album/global-album.vue')['default']
GlobalButton: typeof import('./src/mocp/components/global-button/global-button.vue')['default'] GlobalButton: typeof import('./src/mocp/components/global-button/global-button.vue')['default']
GlobalCalendar: typeof import('./src/mocp/components/global-calendar/global-calendar.vue')['default']
GlobalCheckbox: typeof import('./src/mocp/components/global-checkbox/global-checkbox.vue')['default'] GlobalCheckbox: typeof import('./src/mocp/components/global-checkbox/global-checkbox.vue')['default']
GlobalDate: typeof import('./src/mocp/components/global-date/global-date.vue')['default'] GlobalDate: typeof import('./src/mocp/components/global-date/global-date.vue')['default']
GlobalEmpty: typeof import('./src/mocp/components/global-empty/global-empty.vue')['default'] GlobalEmpty: typeof import('./src/mocp/components/global-empty/global-empty.vue')['default']
......
...@@ -9,6 +9,15 @@ export const getArrangeWorkListApi = (data, config) => { ...@@ -9,6 +9,15 @@ export const getArrangeWorkListApi = (data, config) => {
}) })
} }
export const getSeatUnitApi = (data, config) => {
return http({
method: 'POST',
url: '/workbench/getSeatUnit',
data,
config
})
}
export const getArrangeWorkDetailApi = (data, config) => { export const getArrangeWorkDetailApi = (data, config) => {
return http({ return http({
method: 'POST', method: 'POST',
......
@mixin global-button-light {
color: $mocp-primary-6;
background-color: $mocp-primary-1;
}
@mixin global-button-light-active {
color: darken($mocp-primary-6, 10%);
background-color: darken($mocp-primary-1, 5%);
border-color: darken($mocp-primary-1, 5%);
}
@mixin global-button-primary { @mixin global-button-primary {
color: #fff; color: #fff;
background-color: $mocp-primary-6; background-color: $mocp-primary-6;
...@@ -38,7 +47,12 @@ ...@@ -38,7 +47,12 @@
padding: 0 20rpx; padding: 0 20rpx;
margin: 0; margin: 0;
font-size: 28rpx; font-size: 28rpx;
border-radius: 52rpx; &.light {
@include global-button-light;
&:active {
@include global-button-light-active;
}
}
&.primary { &.primary {
@include global-button-primary; @include global-button-primary;
&:active { &:active {
......
...@@ -8,6 +8,10 @@ ...@@ -8,6 +8,10 @@
<script setup> <script setup>
import { computed } from 'vue' import { computed } from 'vue'
const ps = defineProps({ const ps = defineProps({
radius: {
type: [String, Number],
default: 52
},
class: { class: {
type: String, type: String,
default: '' default: ''
...@@ -18,7 +22,7 @@ const ps = defineProps({ ...@@ -18,7 +22,7 @@ const ps = defineProps({
return {} return {}
} }
}, },
//default,primary,success,warning,danger,text //default,light,primary,success,warning,danger,text
type: { type: {
type: String, type: String,
default: 'default' default: 'default'
...@@ -61,10 +65,14 @@ const getSize = computed(() => { ...@@ -61,10 +65,14 @@ const getSize = computed(() => {
return 88 + 'rpx' return 88 + 'rpx'
} }
}) })
const getRadius = computed(() => {
return ps.radius + 'rpx'
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import './global-button.scss'; @import './global-button.scss';
.global-button { .global-button {
border-radius: v-bind(getRadius);
height: v-bind(getSize); height: v-bind(getSize);
line-height: v-bind(getSize); line-height: v-bind(getSize);
} }
......
<template>
<!-- 全局日期控件 -->
<view class="date">
<wu-calendar
ref="calendar"
:mode="mode"
:insert="false"
:color="color"
:confirmColor="confirmColor"
@confirm="confirm"
maskClick
lunar
slideSwitchMode="vertical"
rangeSameDay
:startText="startText"
:endText="endText"
operationPosition="bottom"
confirmFullDate
:date="defaultValue"
></wu-calendar>
<view class="date-content" :class="getPickerClass" @tap="open" :style="getStyle">
<text class="date-value">{{ getLabelValue }}</text>
<view class="date-icon">
<view class="date-icon-close" v-if="clearable && !showPlaceholder && !disabled" @tap.stop="clear">
<up-icon name="close-circle-fill" color="#c0c4cc" size="36rpx"></up-icon>
</view>
<global-icon icon="calendar" color="#86909C"></global-icon>
</view>
</view>
</view>
</template>
<script setup>
import { computed, ref, watchEffect } from 'vue'
import Day from 'mocp/utils/dayjs'
const es = defineEmits(['update:startTime', 'update:endTime', 'change'])
const ps = defineProps({
//日历模式single,range,multiple
mode: {
type: String,
default: 'range'
},
//显示为空的value值
emptyValue: {
type: [String, Number],
default: ''
},
startTime: {
type: [String, Number],
default: ''
},
endTime: {
type: [String, Number],
default: ''
},
modelValue: {
type: [String, Number],
default: ''
},
clearable: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: '请选择日期范围'
},
disabled: {
type: Boolean,
default: false
},
pickAlign: {
type: String,
default: 'left'
},
rangeSeparator: {
type: String,
default: '至'
},
format: {
type: String,
default: 'YYYY/MM/DD'
},
startText: {
type: String,
default: '开始'
},
endText: {
type: String,
default: '结束'
},
confirmText: {
type: String,
default: '确认'
},
color: {
type: String,
default: '#165dff'
},
confirmColor: {
type: String,
default: '#165dff'
}
})
//获取下拉框样式
const getStyle = computed(() => {
let pickAlign = 'flex-start'
if (ps.pickAlign == 'center') {
pickAlign = 'center'
} else if (ps.pickAlign == 'right') {
pickAlign = 'flex-end'
}
return {
justifyContent: pickAlign
}
})
//下拉框显示的内容
const labelValue = ref('')
const getLabelValue = computed(() => {
if (labelValue.value) {
return labelValue.value
} else {
return ps.placeholder
}
})
//获取下拉框class
const showPlaceholder = computed(() => {
return !labelValue.value || labelValue.value == ps.emptyValue
})
const getPickerClass = computed(() => {
return {
disabled: ps.disabled,
placeholder: showPlaceholder.value
}
})
const calendar = ref()
const open = () => {
calendar.value?.open()
}
//设置初始值
const defaultValue = ref([Day().format('YYYY-MM-DD'), ''])
watchEffect(() => {
if (ps.modelValue && ps.modelValue != ps.emptyValue) {
if (ps.mode == 'range') {
const range = ps.modelValue.split(ps.rangeSeparator)
labelValue.value = ps.modelValue.join(ps.rangeSeparator)
defaultValue.value = [Day(range[0]).format('YYYY-MM-DD'), Day(range[1]).format('YYYY-MM-DD')]
}
} else if (ps.startTime && ps.endTime) {
labelValue.value = Day(ps.startTime).format('YYYY-MM-DD') + ps.rangeSeparator + Day(ps.endTime).format('YYYY-MM-DD')
defaultValue.value = [Day(ps.startTime).format('YYYY-MM-DD'), Day(ps.endTime).format('YYYY-MM-DD')]
}
})
//点击确定按钮
const confirm = (e) => {
if (ps.mode == 'range') {
const { after, before } = e.range
labelValue.value = Day(before).format(ps.format) + ps.rangeSeparator + Day(after).format(ps.format)
es('update:startTime', Day(before).valueOf())
es('update:endTime', Day(after).valueOf())
es('change', [Day(before).valueOf(), Day(after).valueOf()])
}
}
//点击清空按钮
const clear = () => {
labelValue.value = ''
defaultValue.value = ''
es('update:startTime', ps.emptyValue)
es('update:endTime', ps.emptyValue)
es('change', [ps.emptyValue, ps.emptyValue])
}
</script>
<style lang="scss" scoped>
.date {
flex: auto;
&-content {
display: flex;
align-items: center;
line-height: 48rpx;
&.placeholder {
.date-value {
color: $mocp-text-3;
}
}
&.disabled {
background: #f5f7fa;
}
}
&-value {
color: $mocp-text-4;
margin-right: 8rpx;
}
&-icon {
display: flex;
align-items: center;
&-close {
margin-left: 4rpx;
}
}
}
</style>
...@@ -51,6 +51,7 @@ const getTextStyle = computed(() => { ...@@ -51,6 +51,7 @@ const getTextStyle = computed(() => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
height: 100%;
flex: none; flex: none;
image { image {
margin-bottom: 40rpx; margin-bottom: 40rpx;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
:back-to-top-bottom="backToTopBottom" :back-to-top-bottom="backToTopBottom"
show-refresher-update-time show-refresher-update-time
:hide-empty-view="!isDataList" :hide-empty-view="!isDataList"
@query="query" @query="queryList"
@scroll="scroll" @scroll="scroll"
:paging-style="getPagingStyle" :paging-style="getPagingStyle"
> >
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
</z-paging> </z-paging>
</template> </template>
<script setup> <script setup>
import { filterEmptyValues } from 'mocp/utils/tool'
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
const dataList = ref([]) const dataList = ref([])
...@@ -152,6 +153,18 @@ const ps = defineProps({ ...@@ -152,6 +153,18 @@ const ps = defineProps({
type: Boolean, type: Boolean,
default: false default: false
}, },
//查询参数
params: {
type: Object,
default: () => {
return {}
}
},
//查询接口
api: {
type: Function,
default: null
},
//是否开启下拉刷新 //是否开启下拉刷新
refresherEnabled: { refresherEnabled: {
type: Boolean, type: Boolean,
...@@ -204,23 +217,33 @@ const scroll = () => { ...@@ -204,23 +217,33 @@ const scroll = () => {
}, 500) }, 500)
// #endif // #endif
} }
const complete = (data) => {
paging.value?.complete(data || true)
}
// 获取屏幕边界到安全区域距离 // 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync() const { safeAreaInsets } = uni.getSystemInfoSync()
//列表加载 //列表加载
const query = (pageIndex, pageSize) => { const queryList = (pageIndex, pageSize) => {
es('query', { pageIndex, pageSize })
if (ps.isDataList) { if (ps.isDataList) {
if (!dataList.value.length) { const params = {
complete(dataList.value) pageIndex,
pageSize,
...filterEmptyValues(ps.params)
}
if (Object.prototype.toString.call(ps.api) == '[object Function]') {
ps.api(params, { loading: true })
.then((res) => {
if (res.code == 200) {
paging.value?.complete(res.list || res.data?.list || [])
} else {
uni.$mocpMessage.showToast(res.message)
}
})
.catch(() => {
paging.value?.complete(false)
})
} else {
paging.value?.complete(false)
} }
setTimeout(() => {
complete([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
}, 500)
} else { } else {
complete() es('query', { pageIndex, pageSize })
} }
} }
//点击左边插槽 //点击左边插槽
...@@ -231,7 +254,7 @@ const handleLeftClick = () => { ...@@ -231,7 +254,7 @@ const handleLeftClick = () => {
es('handleLeftClick') es('handleLeftClick')
} }
} }
//保存 //点击右边插槽
const handleRightClick = () => { const handleRightClick = () => {
es('handleRightClick') es('handleRightClick')
} }
...@@ -241,6 +264,14 @@ const handleFooterClick = () => { ...@@ -241,6 +264,14 @@ const handleFooterClick = () => {
} }
// 获取页面栈 // 获取页面栈
const pages = getCurrentPages() const pages = getCurrentPages()
defineExpose({
reload: () => {
paging.value?.reload()
},
updateCache: () => {
paging.value?.updateCache()
}
})
</script> </script>
<script> <script>
export default { export default {
......
<template> <template>
<!-- 全局下拉框控件 --> <!-- 全局下拉框控件 -->
<view class="picker" :style="style"> <view class="picker" :style="style">
<global-popup v-model="show" @close="show = false" :height="592" :customStyle="{ padding: '28rpx 32rpx' }" v-if="filter" :distance="distance">
<template #top>
<view>
<up-search :placeholder="placeholder" :showAction="false" v-model="searchKey"></up-search>
</view>
</template>
<view class="popup-content-list" v-if="getFilterColumns.length">
<view
class="popup-content-item"
:class="{ 'mocp-color-primary-6': modelValue == item[valueField] }"
v-for="(item, index) in getFilterColumns"
:key="index"
@tap="handleChoose(item[valueField])"
>
{{ item[labelField] }}
</view>
</view>
<view style="width: 100%; height: 100%" v-else>
<global-empty></global-empty>
</view>
</global-popup>
<up-picker <up-picker
v-else
:show="show" :show="show"
:columns="[getColumns]" :columns="[getColumns]"
:keyName="labelField" :keyName="labelField"
...@@ -15,7 +37,7 @@ ...@@ -15,7 +37,7 @@
:cancelColor="cancelColor" :cancelColor="cancelColor"
:confirmColor="confirmColor" :confirmColor="confirmColor"
></up-picker> ></up-picker>
<view class="picker-content" :class="getPickerClass" @tap="show = true" :style="getContentStyle"> <view class="picker-content" :class="getPickerClass" @tap="open" :style="getContentStyle">
<text class="picker-value">{{ getLabelValue }}</text> <text class="picker-value">{{ getLabelValue }}</text>
<view class="picker-icon"> <view class="picker-icon">
<view class="picker-icon-close" v-if="clearable && !showPlaceholder && !disabled" @tap.stop="clear"> <view class="picker-icon-close" v-if="clearable && !showPlaceholder && !disabled" @tap.stop="clear">
...@@ -32,8 +54,12 @@ import { computed, ref, watch } from 'vue' ...@@ -32,8 +54,12 @@ import { computed, ref, watch } from 'vue'
import * as dictData from 'mocp/hooks/use-dict/dict-data' import * as dictData from 'mocp/hooks/use-dict/dict-data'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
const es = defineEmits(['update:modelValue', 'change']) const es = defineEmits(['update:modelValue', 'change', 'getScrollTop'])
const ps = defineProps({ const ps = defineProps({
filter: {
type: Boolean,
default: false
},
style: { style: {
type: Object, type: Object,
default: () => { default: () => {
...@@ -131,6 +157,11 @@ const getPickerClass = computed(() => { ...@@ -131,6 +157,11 @@ const getPickerClass = computed(() => {
} }
}) })
const show = ref(false) const show = ref(false)
//打开
const open = () => {
searchKey.value = ''
show.value = true
}
//设置下拉框打开的默认值 //设置下拉框打开的默认值
const defaultIndex = ref(0) const defaultIndex = ref(0)
//获取下拉框的内容Columns //获取下拉框的内容Columns
...@@ -181,6 +212,26 @@ const clear = () => { ...@@ -181,6 +212,26 @@ const clear = () => {
es('update:modelValue', ps.emptyValue) es('update:modelValue', ps.emptyValue)
es('change', ps.emptyValue) es('change', ps.emptyValue)
} }
//弹出层输入框搜索
const searchKey = ref('')
const getFilterColumns = computed(() => {
return getColumns.value.filter((item) => item[ps.labelField].toLowerCase().includes(searchKey.value.toLowerCase()))
})
//弹出层列表点击
const distance = ref(0)
const handleChoose = (value) => {
es('update:modelValue', value)
es('change', value)
show.value = false
}
//打开默认滑动到选中的位置
watch(
defaultIndex,
(value) => {
distance.value = value * 39
},
{ immediate: true }
)
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.picker { .picker {
...@@ -209,5 +260,17 @@ const clear = () => { ...@@ -209,5 +260,17 @@ const clear = () => {
margin-left: 4rpx; margin-left: 4rpx;
} }
} }
.popup-content {
&-list {
padding-top: 20rpx;
color: $mocp-text-4;
}
&-item {
margin-bottom: 40rpx;
&:last-child {
margin-bottom: 0;
}
}
}
} }
</style> </style>
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
<slot name="title" v-if="title"> <slot name="title" v-if="title">
<view class="title">{{ title }}</view> <view class="title">{{ title }}</view>
</slot> </slot>
<slot name="top"></slot>
<scroll-view scroll-y class="content" :scroll-top="scrollTop" @scroll="scroll"> <scroll-view scroll-y class="content" :scroll-top="scrollTop" @scroll="scroll">
<slot></slot> <slot></slot>
</scroll-view> </scroll-view>
...@@ -34,6 +35,10 @@ const ps = defineProps({ ...@@ -34,6 +35,10 @@ const ps = defineProps({
type: [String, Number], type: [String, Number],
default: '' default: ''
}, },
height: {
type: [String, Number],
default: ''
},
title: { title: {
type: String, type: String,
default: '' default: ''
...@@ -64,6 +69,10 @@ const ps = defineProps({ ...@@ -64,6 +69,10 @@ const ps = defineProps({
type: [String, Number], type: [String, Number],
default: 0 default: 0
}, },
distance: {
type: Number,
default: 0
},
customStyle: { customStyle: {
type: Object, type: Object,
default: () => { default: () => {
...@@ -77,6 +86,7 @@ const getCustomStyle = computed(() => { ...@@ -77,6 +86,7 @@ const getCustomStyle = computed(() => {
const style = { const style = {
...ps.customStyle, ...ps.customStyle,
width: ps.width ? ps.width + 'rpx' : 'auto', width: ps.width ? ps.width + 'rpx' : 'auto',
height: ps.height ? ps.height + 'rpx' : 'auto',
background: ps.background background: ps.background
} }
if (ps.mode == 'right') { if (ps.mode == 'right') {
...@@ -101,7 +111,7 @@ watch( ...@@ -101,7 +111,7 @@ watch(
if (value) { if (value) {
scrollTop.value = 0 scrollTop.value = 0
nextTick(() => { nextTick(() => {
scrollTop.value = oldScrollTop.value scrollTop.value = ps.distance > 0 ? ps.distance : oldScrollTop.value
}) })
} }
} }
......
import { getArrangeWorkDetailApi } from 'mocp/api/assign-work' import { getArrangeWorkDetailApi, getSeatUnitApi } from 'mocp/api/assign-work'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import useUserStore from 'mocp/store/user' import useUserStore from 'mocp/store/user'
...@@ -16,10 +16,19 @@ const useAssignWorkStore = defineStore('assignWork', { ...@@ -16,10 +16,19 @@ const useAssignWorkStore = defineStore('assignWork', {
id: '', id: '',
details: undefined, details: undefined,
arrangeWorkExtendList: [], //责任单位列表 arrangeWorkExtendList: [], //责任单位列表
arrangeWorkExtend: undefined //当前选中的责任单位 arrangeWorkExtend: undefined, //当前选中的责任单位
selectList: {
seatList: [] //管理席位
}
} }
}, },
getters: { getters: {
getSeatUnitSelect(state) {
return state.selectList.seatList.map((q) => ({
label: q.adminSeat,
value: q.adminSeat
}))
},
//获取当前选中的责任单位的index //获取当前选中的责任单位的index
getArrangeWorkExtendIndex(state) { getArrangeWorkExtendIndex(state) {
return state.arrangeWorkExtendList.findIndex((item) => item.id == state.arrangeWorkExtend.id) return state.arrangeWorkExtendList.findIndex((item) => item.id == state.arrangeWorkExtend.id)
...@@ -86,6 +95,12 @@ const useAssignWorkStore = defineStore('assignWork', { ...@@ -86,6 +95,12 @@ const useAssignWorkStore = defineStore('assignWork', {
} }
}, },
actions: { actions: {
async getSeatUnit() {
const res = await getSeatUnitApi({ pageIndex: 1, pageSize: 999 })
if (res.code == 200) {
this.selectList.seatList = res.data.list || []
}
},
async getArrangeWorkDetail() { async getArrangeWorkDetail() {
const res = await getArrangeWorkDetailApi({ id: this.id }, { loading: true }) const res = await getArrangeWorkDetailApi({ id: this.id }, { loading: true })
if (res.code == 200) { if (res.code == 200) {
......
...@@ -13,3 +13,15 @@ export const timeStampFormat = (timeStamp, opt) => { ...@@ -13,3 +13,15 @@ export const timeStampFormat = (timeStamp, opt) => {
const format = opt?.format || 'YYYY/MM/DD HH:mm:ss' const format = opt?.format || 'YYYY/MM/DD HH:mm:ss'
return Day(timeStamp).format(format) return Day(timeStamp).format(format)
} }
/**
*接受一个对象并返回一个只包含非空和非 null 值的新对象
*/
export const filterEmptyValues = (obj) => {
return Object.entries(obj)
.filter(([_, value]) => value !== null && value !== undefined && value !== '')
.reduce((acc, [key, value]) => {
acc[key] = value
return acc
}, {})
}
...@@ -48,12 +48,27 @@ ...@@ -48,12 +48,27 @@
} }
}, },
{ {
"path": "pages/modules/mocp/tab/index",
"style": {
"navigationBarTitleText": "tab页面",
"app-plus": {
"bounce": "none" //关闭窗口回弹效果
}
}
},
{
"path": "pages/modules/mocp/panel/appraisal-record/list", "path": "pages/modules/mocp/panel/appraisal-record/list",
"style": { "style": {
"navigationBarTitleText": "考核记录" "navigationBarTitleText": "考核记录"
} }
}, },
{ {
"path": "pages/modules/mocp/panel/appraisal-record/search",
"style": {
"navigationBarTitleText": "筛选"
}
},
{
"path": "pages/modules/mocp/panel/appraisal-record/details", "path": "pages/modules/mocp/panel/appraisal-record/details",
"style": { "style": {
"navigationBarTitleText": "考核记录详情" "navigationBarTitleText": "考核记录详情"
...@@ -78,6 +93,12 @@ ...@@ -78,6 +93,12 @@
} }
}, },
{ {
"path": "pages/modules/mocp/panel/assign-work/search",
"style": {
"navigationBarTitleText": "筛选"
}
},
{
"path": "pages/modules/mocp/panel/assign-work/details", "path": "pages/modules/mocp/panel/assign-work/details",
"style": { "style": {
"navigationBarTitleText": "早会工作详情" "navigationBarTitleText": "早会工作详情"
...@@ -126,18 +147,15 @@ ...@@ -126,18 +147,15 @@
} }
}, },
{ {
"path": "pages/modules/mocp/tab/index", "path": "pages/modules/mocp/panel/move-decision/list",
"style": { "style": {
"navigationBarTitleText": "tab页面", "navigationBarTitleText": "运行决策"
"app-plus": {
"bounce": "none" //关闭窗口回弹效果
}
} }
}, },
{ {
"path": "pages/modules/mocp/panel/move-decision/list", "path": "pages/modules/mocp/panel/move-decision/search",
"style": { "style": {
"navigationBarTitleText": "运行决策" "navigationBarTitleText": "筛选"
} }
}, },
{ {
...@@ -165,7 +183,7 @@ ...@@ -165,7 +183,7 @@
}, },
"condition": { "condition": {
//模式配置,仅开发期间生效 //模式配置,仅开发期间生效
"current": 0, //当前激活的模式(list 的索引项) "current": 1, //当前激活的模式(list 的索引项)
"list": [ "list": [
{ {
"name": "test", //模式名称 "name": "test", //模式名称
......
import { ref } from 'vue'
export const formRef = ref()
// 表单数据
export const formData = ref({
status: null,
valid: null,
department: '',
appraisee: '',
ac: null,
acOwn: '',
acType: '',
startTime: null,
stopTime: null,
eventType: null,
examineType: '',
appealInfo: null
})
export const status = ref([])
import { toRaw } from 'vue'
import { formData, formRef } from './search.compositions'
export const handleReset = () => {
formData.value = {
status: null,
valid: null,
department: '',
appraisee: '',
ac: null,
acOwn: '',
acType: '',
startTime: null,
stopTime: null,
eventType: null,
examineType: '',
appealInfo: null
}
formRef.value?.resetFields()
handleConfirm()
}
export const handleConfirm = () => {
uni.$mocpJump.navigateBack()
uni.$emit('appraisalRecordReload', { params: toRaw(formData.value) })
}
<template> <template>
<global-page-swiper title="考核记录" refresherEnabled loadingMoreEnabled :tabList="tabList" :api="getRqmListApi" ref="paging" :padding="24"> <global-page
title="考核记录"
isDataList
refresherEnabled
loadingMoreEnabled
:params="searchParams"
:api="getRqmListApi"
auto
ref="paging"
:padding="24"
showNavRight
navRightType="icon"
navRightIcon="saixuan-01"
@handleRightClick="handleRightClick"
>
<template #="{ dataList }"> <template #="{ dataList }">
<view class="item" v-for="item in dataList" :key="item.id" @tap="goDetails(item)"> <view class="item" v-for="item in dataList" :key="item.id" @tap="goDetails(item)">
<view class="item-title"> <view class="item-title">
...@@ -15,7 +29,7 @@ ...@@ -15,7 +29,7 @@
</view> </view>
</view> </view>
</template> </template>
</global-page-swiper> </global-page>
</template> </template>
<script setup> <script setup>
...@@ -24,11 +38,6 @@ import { getRqmListApi } from 'mocp/api/appraisal-record' ...@@ -24,11 +38,6 @@ import { getRqmListApi } from 'mocp/api/appraisal-record'
import { onLoad, onUnload } from '@dcloudio/uni-app' import { onLoad, onUnload } from '@dcloudio/uni-app'
import useAppraisalRecordStore from 'mocp/store/appraisal-record' import useAppraisalRecordStore from 'mocp/store/appraisal-record'
const tabList = ref([
{ name: 'OPEN', value: 1 },
{ name: 'CLOSE', value: 0 },
{ name: '全部', value: -1 }
])
//跳转 //跳转
const goDetails = (data) => { const goDetails = (data) => {
uni.$mocpJump.navigateTo('/panel/appraisal-record/details', { uni.$mocpJump.navigateTo('/panel/appraisal-record/details', {
...@@ -40,9 +49,17 @@ const appraisalRecordStore = useAppraisalRecordStore() ...@@ -40,9 +49,17 @@ const appraisalRecordStore = useAppraisalRecordStore()
onLoad(() => { onLoad(() => {
appraisalRecordStore.getRqmOptions() appraisalRecordStore.getRqmOptions()
}) })
//筛选
const handleRightClick = () => {
uni.$mocpJump.navigateTo('/panel/appraisal-record/search')
}
//刷新 //刷新
const searchParams = ref()
const paging = ref() const paging = ref()
uni.$on('appraisalRecordReload', () => { uni.$on('appraisalRecordReload', ({ params }) => {
if (params) {
searchParams.value = params
}
paging.value?.reload() paging.value?.reload()
}) })
onUnload(() => { onUnload(() => {
......
<template>
<global-page :padding="24" title="筛选">
<up-form labelPosition="left" labelWidth="auto" :model="formData" ref="formRef">
<up-form-item label="状态" prop="status" :borderBottom="true">
<global-picker
v-model="formData.status"
pickAlign="right"
clearable
:options="[
{ label: 'OPEN', value: '1' },
{ label: 'CLOSE', value: '0' }
]"
></global-picker>
</up-form-item>
<up-form-item label="是否有效" prop="valid" :borderBottom="true">
<global-picker
v-model="formData.valid"
pickAlign="right"
:options="[
{ label: '有效', value: '1' },
{ label: '无效', value: '0' }
]"
clearable
></global-picker>
</up-form-item>
<up-form-item label="基地/职能部门" prop="department" :borderBottom="true">
<global-picker v-model="formData.department" pickAlign="right" :options="department" clearable filter></global-picker>
</up-form-item>
<up-form-item label="考核对象" prop="appraisee" :borderBottom="true">
<global-picker v-model="formData.appraisee" pickAlign="right" :options="appraisee" clearable filter></global-picker>
</up-form-item>
<up-form-item label="事件类别" prop="eventType" :borderBottom="true">
<global-picker v-model="formData.eventType" pickAlign="right" clearable dictkey="ar_eventType"></global-picker>
</up-form-item>
<up-form-item label="机号" prop="ac" :borderBottom="true">
<global-picker v-model="formData.ac" pickAlign="right" clearable :options="deviceNumList" filter></global-picker>
</up-form-item>
<up-form-item label="日期" :borderBottom="true">
<global-calendar
pickAlign="right"
v-model:startTime="formData.startTime"
v-model:endTime="formData.stopTime"
clearable
></global-calendar>
</up-form-item>
</up-form>
<template #bottom>
<view class="footer-btn">
<up-row gutter="10">
<up-col span="6">
<global-button type="light" size="large" :radius="5" @tap="handleReset">重置</global-button>
</up-col>
<up-col span="6"><global-button type="primary" size="large" :radius="5" @tap="handleConfirm">确定</global-button></up-col>
</up-row>
</view>
</template>
</global-page>
</template>
<script setup>
import { formData, formRef } from './constants/search.compositions'
import useBaseStore from 'mocp/store/base'
import useAppraisalRecordStore from 'mocp/store/appraisal-record'
import { handleConfirm, handleReset } from './constants/search.functionals'
//获取下拉框选项
const {
selectList: { deviceNumList }
} = useBaseStore()
const {
selectList: { appraisee, department }
} = useAppraisalRecordStore()
</script>
<style lang="scss" scoped>
.footer-btn {
padding: 24rpx 32rpx;
background-color: #fff;
}
</style>
import { ref } from 'vue'
export const formRef = ref()
// 表单数据
export const formData = ref({
state: null,
terminal: '',
valid: null,
decisionState: null,
aviation: '',
machineNumber: '',
startTimeS: null,
startTimeE: null
})
export const status = ref([])
import { toRaw } from 'vue'
import { formData, formRef } from './search.compositions'
export const handleReset = () => {
formData.value = {
state: null,
presenter: '',
seat: '',
workType: null,
startTime: null,
stopTime: null
}
formRef.value?.resetFields()
handleConfirm()
}
export const handleConfirm = () => {
uni.$mocpJump.navigateBack()
uni.$emit('assignWorkReload', { params: toRaw(formData.value) })
}
<template> <template>
<global-page-swiper <global-page
title="布置工作" title="布置工作"
isDataList
refresherEnabled refresherEnabled
loadingMoreEnabled loadingMoreEnabled
:tabList="tabList" auto
ref="paging"
:params="searchParams"
:api="getArrangeWorkListApi" :api="getArrangeWorkListApi"
:padding="24" :padding="24"
tabValueField="state" showNavRight
navRightType="icon"
navRightIcon="saixuan-01"
@handleRightClick="handleRightClick"
> >
<template #="{ dataList }"> <template #="{ dataList }">
<view class="item" v-for="item in dataList" :key="item.id" @tap="goDetails(item)"> <view class="item" v-for="item in dataList" :key="item.id" @tap="goDetails(item)">
...@@ -36,25 +42,43 @@ ...@@ -36,25 +42,43 @@
</view> </view>
</view> </view>
</template> </template>
</global-page-swiper> </global-page>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'
import useBaseStore from 'mocp/store/base' import useBaseStore from 'mocp/store/base'
import { timeStampFormat } from 'mocp/utils/tool' import { timeStampFormat } from 'mocp/utils/tool'
import { getArrangeWorkListApi } from 'mocp/api/assign-work' import { getArrangeWorkListApi } from 'mocp/api/assign-work'
import useAssignWorkStore from 'mocp/store/assign-work'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import { ref } from 'vue'
const tabList = ref([
{ name: 'OPEN', value: 1 },
{ name: 'CLOSE', value: 2 },
{ name: '全部', value: null }
])
const baseStore = useBaseStore() const baseStore = useBaseStore()
//加载下拉框数据
const assignWorkStore = useAssignWorkStore()
onLoad(() => {
assignWorkStore.getSeatUnit()
})
//跳转 //跳转
const goDetails = (data) => { const goDetails = (data) => {
uni.$mocpJump.navigateTo('/panel/assign-work/details', { id: data.id }) uni.$mocpJump.navigateTo('/panel/assign-work/details', { id: data.id })
} }
//筛选
const handleRightClick = () => {
uni.$mocpJump.navigateTo('/panel/assign-work/search')
}
//刷新
const searchParams = ref()
const paging = ref()
uni.$on('assignWorkReload', ({ params }) => {
if (params) {
searchParams.value = params
}
paging.value?.reload()
})
onUnload(() => {
uni.$off('assignWorkReload')
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import './constants/list.scss'; @import './constants/list.scss';
......
<template>
<global-page :padding="24" title="筛选">
<up-form labelPosition="left" labelWidth="auto" :model="formData" ref="formRef">
<up-form-item label="任务状态" prop="state" :borderBottom="true">
<global-picker
v-model="formData.state"
pickAlign="right"
clearable
:options="[
{ label: 'OPEN', value: '1' },
{ label: 'CLOSE', value: '2' }
]"
></global-picker>
</up-form-item>
<up-form-item label="提出人" prop="presenter" :borderBottom="true">
<up-input v-model="formData.presenter" inputAlign="right" border="none" placeholder="请输入" clearable></up-input>
</up-form-item>
<up-form-item label="管理席位" prop="seat" :borderBottom="true">
<global-picker
v-model="formData.seat"
pickAlign="right"
:options="assignWorkStore.getSeatUnitSelect"
clearable
filter
></global-picker>
</up-form-item>
<up-form-item label="工作类型" prop="workType" :borderBottom="true">
<global-picker
v-model="formData.workType"
pickAlign="right"
:options="baseStore.getParamsByType('LayoutWorkType')"
clearable
filter
></global-picker>
</up-form-item>
<up-form-item label="开始日期" :borderBottom="true">
<global-calendar
pickAlign="right"
v-model:startTime="formData.startTimeS"
v-model:endTime="formData.startTimeE"
clearable
></global-calendar>
</up-form-item>
</up-form>
<template #bottom>
<view class="footer-btn">
<up-row gutter="10">
<up-col span="6">
<global-button type="light" size="large" :radius="5" @tap="handleReset">重置</global-button>
</up-col>
<up-col span="6"><global-button type="primary" size="large" :radius="5" @tap="handleConfirm">确定</global-button></up-col>
</up-row>
</view>
</template>
</global-page>
</template>
<script setup>
import { formData, formRef } from './constants/search.compositions'
import useBaseStore from 'mocp/store/base'
import useAssignWorkStore from 'mocp/store/assign-work'
import { handleConfirm, handleReset } from './constants/search.functionals'
//获取下拉框选项
const assignWorkStore = useAssignWorkStore()
const baseStore = useBaseStore()
</script>
<style lang="scss" scoped>
.footer-btn {
padding: 24rpx 32rpx;
background-color: #fff;
}
</style>
import { ref } from 'vue'
export const formRef = ref()
// 表单数据
export const formData = ref({
state: null,
presenter: '',
seat: '',
workType: null,
startDateTime: null,
endDateTime: null
})
export const status = ref([])
import { toRaw } from 'vue'
import { formData, formRef } from './search.compositions'
export const handleReset = () => {
formData.value = {
state: null,
presenter: '',
seat: '',
workType: null,
startTime: null,
stopTime: null
}
formRef.value?.resetFields()
handleConfirm()
}
export const handleConfirm = () => {
uni.$mocpJump.navigateBack()
uni.$emit('moveDecisionReload', { params: toRaw(formData.value) })
}
<template> <template>
<global-page-swiper <global-page
title="运行决策" title="运行决策"
isDataList
refresherEnabled refresherEnabled
loadingMoreEnabled loadingMoreEnabled
:tabList="tabList" auto
ref="paging"
:params="searchParams"
showNavRight
navRightType="icon"
navRightIcon="saixuan-01"
@handleRightClick="handleRightClick"
:api="getDecisionApi" :api="getDecisionApi"
:padding="24" :padding="24"
tabValueField="state"
> >
<template #="{ dataList }"> <template #="{ dataList }">
<view class="item" v-for="item in dataList" :key="item.id" @tap="goDetails(item)"> <view class="item" v-for="item in dataList" :key="item.id" @tap="goDetails(item)">
...@@ -33,7 +39,7 @@ ...@@ -33,7 +39,7 @@
</view> </view>
</view> </view>
</template> </template>
</global-page-swiper> </global-page>
</template> </template>
<script setup> <script setup>
...@@ -42,13 +48,8 @@ import { timeStampFormat } from 'mocp/utils/tool' ...@@ -42,13 +48,8 @@ import { timeStampFormat } from 'mocp/utils/tool'
import { getDecisionApi } from 'mocp/api/move-decision' import { getDecisionApi } from 'mocp/api/move-decision'
import { useGetDictByValue } from 'mocp/hooks/use-dict/useDict' import { useGetDictByValue } from 'mocp/hooks/use-dict/useDict'
import useMoveDecisionStore from 'mocp/store/move-decision' import useMoveDecisionStore from 'mocp/store/move-decision'
import { onUnload } from '@dcloudio/uni-app'
const tabList = ref([
{ name: 'OPEN', value: 1 },
{ name: 'CLOSE', value: 3 },
{ name: '跟踪', value: 2 },
{ name: '全部', value: null }
])
//跳转 //跳转
const moveDecisionStore = useMoveDecisionStore() const moveDecisionStore = useMoveDecisionStore()
const goDetails = (data) => { const goDetails = (data) => {
...@@ -56,6 +57,22 @@ const goDetails = (data) => { ...@@ -56,6 +57,22 @@ const goDetails = (data) => {
moveDecisionStore.setState('details', data) moveDecisionStore.setState('details', data)
}) })
} }
//筛选
const handleRightClick = () => {
uni.$mocpJump.navigateTo('/panel/move-decision/search')
}
//刷新
const searchParams = ref()
const paging = ref()
uni.$on('moveDecisionReload', ({ params }) => {
if (params) {
searchParams.value = params
}
paging.value?.reload()
})
onUnload(() => {
uni.$off('moveDecisionReload')
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import './constants/list.scss'; @import './constants/list.scss';
......
<template>
<global-page :padding="24" title="筛选">
<up-form labelPosition="left" labelWidth="auto" :model="formData" ref="formRef">
<up-form-item label="状态" prop="state" :borderBottom="true">
<global-picker
v-model="formData.state"
pickAlign="right"
clearable
:options="[
{ label: 'OPEN', value: 1 },
{ label: '跟踪', value: 2 },
{ label: 'CLOSE', value: 3 }
]"
></global-picker>
</up-form-item>
<up-form-item label="航站" prop="terminal" :borderBottom="true">
<global-picker pickAlign="right" v-model="formData.terminal" clearable :options="baseStore.getTerminalSelect" filter></global-picker>
</up-form-item>
<up-form-item label="是否有效" prop="valid" :borderBottom="true">
<global-picker
v-model="formData.valid"
pickAlign="right"
:options="[
{ label: '有效', value: '1' },
{ label: '无效', value: '0' }
]"
clearable
></global-picker>
</up-form-item>
<up-form-item label="决策结果" prop="decisionState" :borderBottom="true">
<global-picker
v-model="formData.decisionState"
pickAlign="right"
:options="[
{ label: '同意', value: '1' },
{ label: '不同意', value: '0' }
]"
clearable
></global-picker>
</up-form-item>
<up-form-item label="航司" prop="aviation" :borderBottom="true">
<global-picker v-model="formData.aviation" pickAlign="right" :options="baseStore.getAirlineSelect" clearable filter></global-picker>
</up-form-item>
<up-form-item label="机号" prop="machineNumber" :borderBottom="true">
<global-picker pickAlign="right" v-model="formData.machineNumber" :options="deviceNumList" clearable filter></global-picker>
</up-form-item>
<up-form-item label="日期" :borderBottom="true">
<global-calendar
pickAlign="right"
v-model:startTime="formData.startDateTime"
v-model:endTime="formData.endDateTime"
clearable
></global-calendar>
</up-form-item>
</up-form>
<template #bottom>
<view class="footer-btn">
<up-row gutter="10">
<up-col span="6">
<global-button type="light" size="large" :radius="5" @tap="handleReset">重置</global-button>
</up-col>
<up-col span="6"><global-button type="primary" size="large" :radius="5" @tap="handleConfirm">确定</global-button></up-col>
</up-row>
</view>
</template>
</global-page>
</template>
<script setup>
import { formData, formRef } from './constants/search.compositions'
import useBaseStore from 'mocp/store/base'
import { handleConfirm, handleReset } from './constants/search.functionals'
//获取下拉框选项
const baseStore = useBaseStore()
const {
selectList: { deviceNumList }
} = useBaseStore()
</script>
<style lang="scss" scoped>
.footer-btn {
padding: 24rpx 32rpx;
background-color: #fff;
}
</style>
...@@ -55,6 +55,12 @@ ...@@ -55,6 +55,12 @@
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib"> <li class="dib">
<span class="icon iconfont">&#xe6e2;</span>
<div class="name">saixuan</div>
<div class="code-name">&amp;#xe6e2;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6df;</span> <span class="icon iconfont">&#xe6df;</span>
<div class="name">jpg</div> <div class="name">jpg</div>
<div class="code-name">&amp;#xe6df;</div> <div class="code-name">&amp;#xe6df;</div>
...@@ -156,9 +162,9 @@ ...@@ -156,9 +162,9 @@
<pre><code class="language-css" <pre><code class="language-css"
>@font-face { >@font-face {
font-family: 'iconfont'; font-family: 'iconfont';
src: url('iconfont.woff2?t=1717745202331') format('woff2'), src: url('iconfont.woff2?t=1720423308442') format('woff2'),
url('iconfont.woff?t=1717745202331') format('woff'), url('iconfont.woff?t=1720423308442') format('woff'),
url('iconfont.ttf?t=1717745202331') format('truetype'); url('iconfont.ttf?t=1720423308442') format('truetype');
} }
</code></pre> </code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3> <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
...@@ -185,6 +191,15 @@ ...@@ -185,6 +191,15 @@
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib"> <li class="dib">
<span class="icon iconfont icon-saixuan-01"></span>
<div class="name">
saixuan
</div>
<div class="code-name">.icon-saixuan-01
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-jpg"></span> <span class="icon iconfont icon-jpg"></span>
<div class="name"> <div class="name">
jpg jpg
...@@ -339,6 +354,14 @@ ...@@ -339,6 +354,14 @@
<li class="dib"> <li class="dib">
<svg class="icon svg-icon" aria-hidden="true"> <svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-saixuan-01"></use>
</svg>
<div class="name">saixuan</div>
<div class="code-name">#icon-saixuan-01</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-jpg"></use> <use xlink:href="#icon-jpg"></use>
</svg> </svg>
<div class="name">jpg</div> <div class="name">jpg</div>
......
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4550048 */ font-family: "iconfont"; /* Project id 4550048 */
src: url('iconfont.woff2?t=1717745202331') format('woff2'), src: url('iconfont.woff2?t=1720423308442') format('woff2'),
url('iconfont.woff?t=1717745202331') format('woff'), url('iconfont.woff?t=1720423308442') format('woff'),
url('iconfont.ttf?t=1717745202331') format('truetype'); url('iconfont.ttf?t=1720423308442') format('truetype');
} }
.iconfont { .iconfont {
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-saixuan-01:before {
content: "\e6e2";
}
.icon-jpg:before { .icon-jpg:before {
content: "\e6df"; content: "\e6df";
} }
......
window._iconfont_svg_string_4550048='<svg><symbol id="icon-jpg" viewBox="0 0 1024 1024"><path d="M128 128C128 83.2 166.4 44.8 211.2 44.8H640c12.8 0 19.2 6.4 32 12.8l211.2 211.2c6.4 6.4 12.8 19.2 12.8 32V896c0 44.8-38.4 83.2-83.2 83.2H211.2c-44.8 0-83.2-38.4-83.2-83.2V128z" fill="#65C466" ></path><path d="M256 320c0-70.4 57.6-128 128-128s128 57.6 128 128-57.6 128-128 128-128-57.6-128-128zM736 384c19.2 12.8 32 25.6 32 44.8v358.4c0 25.6-19.2 44.8-44.8 44.8H300.8c-25.6 0-44.8-19.2-44.8-44.8V588.8c0-12.8 6.4-32 19.2-38.4 12.8-12.8 32-12.8 44.8-6.4l166.4 64 198.4-211.2c12.8-12.8 38.4-19.2 51.2-12.8z" fill="#FFFFFF" ></path></symbol><symbol id="icon-pdf" viewBox="0 0 1024 1024"><path d="M128 128C128 83.2 166.4 44.8 211.2 44.8H640c12.8 0 19.2 6.4 32 12.8l211.2 211.2c6.4 6.4 12.8 19.2 12.8 32V896c0 44.8-38.4 83.2-83.2 83.2H211.2c-44.8 0-83.2-38.4-83.2-83.2V128z" fill="#F7CE46" ></path><path d="M640 339.2H384c-25.6 0-44.8 19.2-44.8 44.8s19.2 44.8 44.8 44.8h83.2v300.8c0 19.2 19.2 38.4 44.8 38.4s44.8-19.2 44.8-44.8V428.8H640c25.6 0 44.8-19.2 44.8-44.8s-19.2-44.8-44.8-44.8z" fill="#FFFFFF" ></path></symbol><symbol id="icon-video" viewBox="0 0 1024 1024"><path d="M128 128C128 83.2 166.4 44.8 211.2 44.8H640c12.8 0 19.2 6.4 32 12.8l211.2 211.2c6.4 6.4 12.8 19.2 12.8 32V896c0 44.8-38.4 83.2-83.2 83.2H211.2c-44.8 0-83.2-38.4-83.2-83.2V128z" fill="#165DFF" ></path><path d="M256 428.8c0-25.6 19.2-44.8 44.8-44.8h288c25.6 0 44.8 19.2 44.8 44.8v12.8l83.2-32c12.8-6.4 25.6-6.4 38.4 6.4 6.4 6.4 12.8 19.2 12.8 38.4V704c0 12.8-6.4 32-19.2 38.4-12.8 6.4-25.6 12.8-38.4 6.4l-83.2-32v12.8c0 25.6-19.2 44.8-44.8 44.8H300.8c-25.6-6.4-44.8-25.6-44.8-51.2V428.8z" fill="#FFFFFF" ></path></symbol><symbol id="icon-delete-01" viewBox="0 0 1024 1024"><path d="M723.2 192V108.8H300.8V192h-192v83.2h76.8v576c0 38.4 25.6 64 64 64h531.2c38.4 0 64-25.6 64-64v-576h76.8V192h-198.4zM268.8 832V275.2h492.8V832H268.8zM384 384v320h83.2V384H384z m172.8 0v320H640V384H556.8z" ></path></symbol><symbol id="icon-navbarright" viewBox="0 0 1024 1024"><path d="M896 748.8H128V832h768v-83.2z m0-281.6H467.2v83.2H896V467.2zM128 192h768v83.2H128V192z m172.8 448c12.8 6.4 32 0 32-19.2V409.6c0-19.2-19.2-25.6-32-19.2L140.8 499.2c-12.8 6.4-12.8 25.6 0 38.4L300.8 640z" ></path></symbol><symbol id="icon-drive" viewBox="0 0 1024 1024"><path d="M236.8 172.8v684.8h556.8v-448H620.8c-38.4 0-64-25.6-64-64V172.8h-320z m403.2 57.6V320h89.6L640 230.4zM147.2 147.2c0-38.4 25.6-64 64-64h403.2l262.4 262.4v531.2c0 38.4-25.6 64-64 64H211.2c-38.4 0-64-25.6-64-64V147.2z" ></path></symbol><symbol id="icon-safe" viewBox="0 0 1024 1024"><path d="M512 76.8l19.2 12.8c70.4 38.4 153.6 64 217.6 83.2 32 6.4 57.6 12.8 76.8 19.2 12.8 0 19.2 0 25.6 6.4h44.8v371.2l-44.8-6.4h44.8V576c0 6.4-6.4 19.2-12.8 32-6.4 25.6-25.6 64-51.2 108.8-51.2 83.2-147.2 172.8-307.2 224H512l-12.8-6.4c-160-44.8-256-140.8-307.2-224-25.6-32-38.4-70.4-51.2-102.4-6.4-12.8-6.4-25.6-6.4-32v-12.8l44.8-6.4-51.2 6.4V198.4l38.4-6.4h6.4c6.4 0 12.8 0 25.6-6.4 19.2 0 44.8-6.4 76.8-19.2 64-12.8 147.2-38.4 217.6-83.2l19.2-6.4z m-300.8 192v288c0 6.4 6.4 12.8 6.4 25.6 6.4 25.6 19.2 51.2 44.8 89.6 44.8 64 115.2 140.8 249.6 179.2 128-38.4 204.8-115.2 249.6-179.2 19.2-32 32-64 44.8-89.6 6.4-12.8 6.4-19.2 6.4-25.6V268.8c-19.2-6.4-51.2-6.4-83.2-19.2-64-12.8-140.8-38.4-217.6-76.8-76.8 44.8-153.6 64-217.6 83.2-32 6.4-57.6 12.8-83.2 12.8z m518.4 147.2l-243.2 243.2-160-153.6L384 448l96 96 185.6-185.6 64 57.6z" ></path></symbol><symbol id="icon-subscribed" viewBox="0 0 1024 1024"><path d="M234.688 170.688v654.208L512 676.992l277.312 147.904V170.688H234.688z m-85.376-21.312a64 64 0 0 1 64-64h597.376a64 64 0 0 1 64 64v739.52c0 35.456-37.76 58.112-69.056 41.408L512 773.76l-293.632 156.608a46.912 46.912 0 0 1-69.056-41.408V149.376zM471.36 468.48l165.952-165.952 60.352 60.352-226.304 226.304-135.68-135.744 60.288-60.352L471.36 468.48z" fill="#4E5969" ></path></symbol><symbol id="icon-mind-mapping" viewBox="0 0 1024 1024"><path d="M106.688 213.312a85.312 85.312 0 1 1 128 73.92v182.08h106.624v85.376H234.688V768h106.624v85.312h-192V287.232a85.312 85.312 0 0 1-42.624-73.92zM917.312 256H426.688V170.688h490.624V256z m0 298.688H426.688V469.312h490.624v85.376z m0 298.624H426.688V768h490.624v85.312z" fill="#4E5969" ></path></symbol><symbol id="icon-email" viewBox="0 0 1024 1024"><path d="M149.312 128a64 64 0 0 0-64 64v640a64 64 0 0 0 64 64h725.376a64 64 0 0 0 64-64V192a64 64 0 0 0-64-64H149.312z m21.376 682.688V213.312h682.624v597.376H170.688z m88.32-483.072L512 502.784l253.056-175.168 48.576 70.144-265.216 183.552a64 64 0 0 1-72.832 0L210.368 397.76l48.64-70.144z" fill="#4E5969" ></path></symbol><symbol id="icon-idcard" viewBox="0 0 1024 1024"><path d="M85.312 213.312C85.312 166.208 123.52 128 170.688 128h682.624c47.168 0 85.376 38.208 85.376 85.312v597.376c0 47.104-38.208 85.312-85.376 85.312H170.688a85.312 85.312 0 0 1-85.376-85.312V213.312z m768 0H170.688v597.376h682.624V213.312z m-426.624 192h-192V320h192v85.312z m0 149.376h-192V469.312h192v85.376zM341.312 704H234.688V618.688h106.624V704z m277.376-106.688C559.808 597.312 512 645.12 512 704H426.688a192 192 0 0 1 114.24-175.616 128 128 0 1 1 155.456 0A192 192 0 0 1 810.688 704h-85.376c0-58.88-47.744-106.688-106.624-106.688zM576 426.688a42.688 42.688 0 1 1 85.312 0 42.688 42.688 0 0 1-85.312 0z" fill="#4E5969" ></path></symbol><symbol id="icon-calendar" viewBox="0 0 1024 1024"><path d="M256 149.312v-42.688h85.312v42.688h341.376v-42.688H768v42.688h85.312a64 64 0 0 1 64 64v640a64 64 0 0 1-64 64H170.688a64 64 0 0 1-64-64v-640a64 64 0 0 1 64-64H256z m0 85.312H192v192h640v-192h-64v42.688h-85.312v-42.688H341.312v42.688H256v-42.688zM832 512H192v320h640V512z" fill="#4E5969" ></path></symbol><symbol id="icon-message" viewBox="0 0 1024 1024"><path d="M106.688 512a405.312 405.312 0 1 1 810.624 0v13.44a391.936 391.936 0 0 1-391.872 391.872H106.688V512zM512 192a320 320 0 0 0-320 320v320h333.44A306.56 306.56 0 0 0 832 525.44V512a320 320 0 0 0-320-320z m192 192v85.312H320V384h384z m-192 277.312H320V576h192v85.312z" fill="#4E5969" ></path></symbol><symbol id="icon-Vector" viewBox="0 0 1228 1024"><path d="M285.360264 841.131887c15.870413 7.372063 15.870413 22.013799 23.754425 36.553144 0 22.013799-15.768023 43.925207-47.50885 43.925208-23.754425 7.269673-47.611239-14.641736-55.49525-43.925208 0-21.911409 15.870413-43.822818 47.508849-65.734226a263.960804 263.960804 0 0 1 150.615339 0c95.222478 36.553145 182.356164 116.929107 206.110588 168.123988-47.508849-175.49605-253.619438-321.708629-443.859614-241.332667-71.365663 29.283472-103.106489 87.748025-103.106489 153.584641C71.365663 965.637836 134.744926 1024 221.981002 1024c47.508849 0 87.133687-14.641736 110.990501-43.925207 31.740826-51.194881 15.768023-116.929107-47.611239-138.942906z m657.956604 0c-15.870413 7.372063-23.754425 14.641736-23.754424 36.553144 0 22.013799 15.768023 43.925207 47.508849 43.925208 23.754425 7.269673 47.611239-14.641736 55.49525-43.925208 0-21.911409-15.870413-43.822818-47.508849-65.734226a263.960804 263.960804 0 0 0-150.615338 0c-95.222478 36.553145-182.356164 116.929107-213.994601 168.123988 47.508849-175.49605 261.50345-321.708629 451.743626-241.332667 63.481652 22.013799 103.106489 80.478352 95.222478 146.314968-7.986401 73.106289-63.481652 131.673233-150.717729 131.673233-47.508849 0-87.133687-14.641736-110.990501-43.925208-23.754425-43.925207-15.768023-109.659434 47.611239-131.673232zM610.242976 387.750025C784.817518 387.750025 927.446455 307.271673 927.446455 204.881912c0-80.478352-87.236076-146.314969-206.110589-175.496051 142.731327 29.181082 245.735426 109.659434 245.735427 197.40746C967.071293 336.555144 832.428757 409.661434 713.451855 424.30317c-71.365663 7.269673-79.249675 7.269673-103.10649 29.283471-7.884012-22.013799-23.754425-22.013799-87.133686-29.283471C404.234777 409.661434 269.489851 329.183082 269.489851 226.793321c0-87.748025 103.106489-168.226377 245.735426-197.40746C388.466753 58.464554 301.230677 124.40356 301.230677 204.881912c0 102.389761 142.628937 182.868113 309.217078 182.868113z m0 117.031497c31.740826-22.013799 79.249675-29.283472 118.976902-36.655535C903.692031 431.572843 1014.682532 351.29927 1014.682532 241.435056c0-43.822818-15.870413-80.478352-47.611239-109.659434-63.481652-58.566943-158.49935-102.389761-277.476253-117.031496 213.994601 14.641736 372.69873 124.30117 372.698731 255.974402 0 117.031497-118.976902 204.779522-309.217079 248.60234-79.249675 22.013799-118.874513 36.655534-142.731327 73.208679-15.870413-36.553145-55.49525-43.925207-134.744925-73.208679-190.240176-43.822818-309.217078-131.570843-309.217078-248.60234 0-131.673233 158.70413-234.062994 372.69873-255.974402C427.989201 29.385861 332.971503 73.208679 269.489851 131.775622c-31.740826 29.181082-47.508849 73.106289-47.508849 109.659434 0 109.761824 110.990501 190.137786 285.360264 226.690931 31.740826 7.372063 79.249675 14.641736 103.004099 36.655535z m0 175.49605c39.624838-58.566943 118.976902-87.748025 190.240176-109.761824 23.856814-7.269673 31.740826-14.539346 55.49525-14.539346 150.615338-51.194881 245.735426-146.314969 253.721828-255.974402 0-153.584642-198.226577-277.988201-443.962004-299.89961C975.057694 14.744126 1228.677132 175.59844 1228.677132 358.466553c0 124.30117-95.120088 234.062994-277.476252 299.89961-79.249675 21.911409-285.360264 80.375962-340.855515 292.527548-47.508849-204.779522-253.619438-270.616138-332.869113-292.527548C95.120088 585.157484 0 482.767723 0 358.466553 0 175.59844 253.619438 22.013799 562.836516 0.10239 317.10109 14.744126 118.772123 139.045295 118.772123 292.629937c7.986401 109.761824 103.106489 204.779522 245.735426 255.974403 23.856814 7.372063 31.740826 14.641736 55.495251 14.641735 79.249675 29.283472 158.60174 58.464554 190.240176 117.031497z m-110.990501-511.948805C499.354865 109.659434 546.966103 58.464554 618.434157 58.464554 689.59504 58.566943 737.206279 102.492151 737.206279 168.328767c0 58.464554-47.611239 109.659434-118.874512 109.659434-71.365663 0-118.976902-43.822818-118.976902-109.659434z" fill="#DD4012" ></path></symbol></svg>',function(c){var t=(t=document.getElementsByTagName("script"))[t.length-1],e=t.getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var i,o,l,a,n,h=function(t,e){e.parentNode.insertBefore(t,e)};if(e&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}i=function(){var t,e=document.createElement("div");e.innerHTML=c._iconfont_svg_string_4550048,(e=e.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",e=e,(t=document.body).firstChild?h(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(i,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),i()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(l=i,a=c.document,n=!1,v(),a.onreadystatechange=function(){"complete"==a.readyState&&(a.onreadystatechange=null,d())})}function d(){n||(n=!0,l())}function v(){try{a.documentElement.doScroll("left")}catch(t){return void setTimeout(v,50)}d()}}(window); window._iconfont_svg_string_4550048='<svg><symbol id="icon-saixuan-01" viewBox="0 0 1024 1024"><path d="M89.6 172.8c6.4-12.8 19.2-25.6 38.4-25.6h768c19.2 0 32 12.8 38.4 25.6 6.4 12.8 6.4 32-6.4 44.8L633.6 569.6V896c0 12.8-6.4 25.6-19.2 38.4-12.8 6.4-25.6 6.4-38.4 0l-153.6-76.8c-12.8-6.4-25.6-19.2-25.6-38.4v-256L96 217.6c-12.8-12.8-12.8-25.6-6.4-44.8z m128 64l249.6 288c6.4 6.4 12.8 19.2 12.8 25.6v243.2l70.4 32V550.4c0-12.8 6.4-19.2 12.8-25.6l249.6-288H217.6z" ></path></symbol><symbol id="icon-jpg" viewBox="0 0 1024 1024"><path d="M128 128C128 83.2 166.4 44.8 211.2 44.8H640c12.8 0 19.2 6.4 32 12.8l211.2 211.2c6.4 6.4 12.8 19.2 12.8 32V896c0 44.8-38.4 83.2-83.2 83.2H211.2c-44.8 0-83.2-38.4-83.2-83.2V128z" fill="#65C466" ></path><path d="M256 320c0-70.4 57.6-128 128-128s128 57.6 128 128-57.6 128-128 128-128-57.6-128-128zM736 384c19.2 12.8 32 25.6 32 44.8v358.4c0 25.6-19.2 44.8-44.8 44.8H300.8c-25.6 0-44.8-19.2-44.8-44.8V588.8c0-12.8 6.4-32 19.2-38.4 12.8-12.8 32-12.8 44.8-6.4l166.4 64 198.4-211.2c12.8-12.8 38.4-19.2 51.2-12.8z" fill="#FFFFFF" ></path></symbol><symbol id="icon-pdf" viewBox="0 0 1024 1024"><path d="M128 128C128 83.2 166.4 44.8 211.2 44.8H640c12.8 0 19.2 6.4 32 12.8l211.2 211.2c6.4 6.4 12.8 19.2 12.8 32V896c0 44.8-38.4 83.2-83.2 83.2H211.2c-44.8 0-83.2-38.4-83.2-83.2V128z" fill="#F7CE46" ></path><path d="M640 339.2H384c-25.6 0-44.8 19.2-44.8 44.8s19.2 44.8 44.8 44.8h83.2v300.8c0 19.2 19.2 38.4 44.8 38.4s44.8-19.2 44.8-44.8V428.8H640c25.6 0 44.8-19.2 44.8-44.8s-19.2-44.8-44.8-44.8z" fill="#FFFFFF" ></path></symbol><symbol id="icon-video" viewBox="0 0 1024 1024"><path d="M128 128C128 83.2 166.4 44.8 211.2 44.8H640c12.8 0 19.2 6.4 32 12.8l211.2 211.2c6.4 6.4 12.8 19.2 12.8 32V896c0 44.8-38.4 83.2-83.2 83.2H211.2c-44.8 0-83.2-38.4-83.2-83.2V128z" fill="#165DFF" ></path><path d="M256 428.8c0-25.6 19.2-44.8 44.8-44.8h288c25.6 0 44.8 19.2 44.8 44.8v12.8l83.2-32c12.8-6.4 25.6-6.4 38.4 6.4 6.4 6.4 12.8 19.2 12.8 38.4V704c0 12.8-6.4 32-19.2 38.4-12.8 6.4-25.6 12.8-38.4 6.4l-83.2-32v12.8c0 25.6-19.2 44.8-44.8 44.8H300.8c-25.6-6.4-44.8-25.6-44.8-51.2V428.8z" fill="#FFFFFF" ></path></symbol><symbol id="icon-delete-01" viewBox="0 0 1024 1024"><path d="M723.2 192V108.8H300.8V192h-192v83.2h76.8v576c0 38.4 25.6 64 64 64h531.2c38.4 0 64-25.6 64-64v-576h76.8V192h-198.4zM268.8 832V275.2h492.8V832H268.8zM384 384v320h83.2V384H384z m172.8 0v320H640V384H556.8z" ></path></symbol><symbol id="icon-navbarright" viewBox="0 0 1024 1024"><path d="M896 748.8H128V832h768v-83.2z m0-281.6H467.2v83.2H896V467.2zM128 192h768v83.2H128V192z m172.8 448c12.8 6.4 32 0 32-19.2V409.6c0-19.2-19.2-25.6-32-19.2L140.8 499.2c-12.8 6.4-12.8 25.6 0 38.4L300.8 640z" ></path></symbol><symbol id="icon-drive" viewBox="0 0 1024 1024"><path d="M236.8 172.8v684.8h556.8v-448H620.8c-38.4 0-64-25.6-64-64V172.8h-320z m403.2 57.6V320h89.6L640 230.4zM147.2 147.2c0-38.4 25.6-64 64-64h403.2l262.4 262.4v531.2c0 38.4-25.6 64-64 64H211.2c-38.4 0-64-25.6-64-64V147.2z" ></path></symbol><symbol id="icon-safe" viewBox="0 0 1024 1024"><path d="M512 76.8l19.2 12.8c70.4 38.4 153.6 64 217.6 83.2 32 6.4 57.6 12.8 76.8 19.2 12.8 0 19.2 0 25.6 6.4h44.8v371.2l-44.8-6.4h44.8V576c0 6.4-6.4 19.2-12.8 32-6.4 25.6-25.6 64-51.2 108.8-51.2 83.2-147.2 172.8-307.2 224H512l-12.8-6.4c-160-44.8-256-140.8-307.2-224-25.6-32-38.4-70.4-51.2-102.4-6.4-12.8-6.4-25.6-6.4-32v-12.8l44.8-6.4-51.2 6.4V198.4l38.4-6.4h6.4c6.4 0 12.8 0 25.6-6.4 19.2 0 44.8-6.4 76.8-19.2 64-12.8 147.2-38.4 217.6-83.2l19.2-6.4z m-300.8 192v288c0 6.4 6.4 12.8 6.4 25.6 6.4 25.6 19.2 51.2 44.8 89.6 44.8 64 115.2 140.8 249.6 179.2 128-38.4 204.8-115.2 249.6-179.2 19.2-32 32-64 44.8-89.6 6.4-12.8 6.4-19.2 6.4-25.6V268.8c-19.2-6.4-51.2-6.4-83.2-19.2-64-12.8-140.8-38.4-217.6-76.8-76.8 44.8-153.6 64-217.6 83.2-32 6.4-57.6 12.8-83.2 12.8z m518.4 147.2l-243.2 243.2-160-153.6L384 448l96 96 185.6-185.6 64 57.6z" ></path></symbol><symbol id="icon-subscribed" viewBox="0 0 1024 1024"><path d="M234.688 170.688v654.208L512 676.992l277.312 147.904V170.688H234.688z m-85.376-21.312a64 64 0 0 1 64-64h597.376a64 64 0 0 1 64 64v739.52c0 35.456-37.76 58.112-69.056 41.408L512 773.76l-293.632 156.608a46.912 46.912 0 0 1-69.056-41.408V149.376zM471.36 468.48l165.952-165.952 60.352 60.352-226.304 226.304-135.68-135.744 60.288-60.352L471.36 468.48z" fill="#4E5969" ></path></symbol><symbol id="icon-mind-mapping" viewBox="0 0 1024 1024"><path d="M106.688 213.312a85.312 85.312 0 1 1 128 73.92v182.08h106.624v85.376H234.688V768h106.624v85.312h-192V287.232a85.312 85.312 0 0 1-42.624-73.92zM917.312 256H426.688V170.688h490.624V256z m0 298.688H426.688V469.312h490.624v85.376z m0 298.624H426.688V768h490.624v85.312z" fill="#4E5969" ></path></symbol><symbol id="icon-email" viewBox="0 0 1024 1024"><path d="M149.312 128a64 64 0 0 0-64 64v640a64 64 0 0 0 64 64h725.376a64 64 0 0 0 64-64V192a64 64 0 0 0-64-64H149.312z m21.376 682.688V213.312h682.624v597.376H170.688z m88.32-483.072L512 502.784l253.056-175.168 48.576 70.144-265.216 183.552a64 64 0 0 1-72.832 0L210.368 397.76l48.64-70.144z" fill="#4E5969" ></path></symbol><symbol id="icon-idcard" viewBox="0 0 1024 1024"><path d="M85.312 213.312C85.312 166.208 123.52 128 170.688 128h682.624c47.168 0 85.376 38.208 85.376 85.312v597.376c0 47.104-38.208 85.312-85.376 85.312H170.688a85.312 85.312 0 0 1-85.376-85.312V213.312z m768 0H170.688v597.376h682.624V213.312z m-426.624 192h-192V320h192v85.312z m0 149.376h-192V469.312h192v85.376zM341.312 704H234.688V618.688h106.624V704z m277.376-106.688C559.808 597.312 512 645.12 512 704H426.688a192 192 0 0 1 114.24-175.616 128 128 0 1 1 155.456 0A192 192 0 0 1 810.688 704h-85.376c0-58.88-47.744-106.688-106.624-106.688zM576 426.688a42.688 42.688 0 1 1 85.312 0 42.688 42.688 0 0 1-85.312 0z" fill="#4E5969" ></path></symbol><symbol id="icon-calendar" viewBox="0 0 1024 1024"><path d="M256 149.312v-42.688h85.312v42.688h341.376v-42.688H768v42.688h85.312a64 64 0 0 1 64 64v640a64 64 0 0 1-64 64H170.688a64 64 0 0 1-64-64v-640a64 64 0 0 1 64-64H256z m0 85.312H192v192h640v-192h-64v42.688h-85.312v-42.688H341.312v42.688H256v-42.688zM832 512H192v320h640V512z" fill="#4E5969" ></path></symbol><symbol id="icon-message" viewBox="0 0 1024 1024"><path d="M106.688 512a405.312 405.312 0 1 1 810.624 0v13.44a391.936 391.936 0 0 1-391.872 391.872H106.688V512zM512 192a320 320 0 0 0-320 320v320h333.44A306.56 306.56 0 0 0 832 525.44V512a320 320 0 0 0-320-320z m192 192v85.312H320V384h384z m-192 277.312H320V576h192v85.312z" fill="#4E5969" ></path></symbol><symbol id="icon-Vector" viewBox="0 0 1228 1024"><path d="M285.360264 841.131887c15.870413 7.372063 15.870413 22.013799 23.754425 36.553144 0 22.013799-15.768023 43.925207-47.50885 43.925208-23.754425 7.269673-47.611239-14.641736-55.49525-43.925208 0-21.911409 15.870413-43.822818 47.508849-65.734226a263.960804 263.960804 0 0 1 150.615339 0c95.222478 36.553145 182.356164 116.929107 206.110588 168.123988-47.508849-175.49605-253.619438-321.708629-443.859614-241.332667-71.365663 29.283472-103.106489 87.748025-103.106489 153.584641C71.365663 965.637836 134.744926 1024 221.981002 1024c47.508849 0 87.133687-14.641736 110.990501-43.925207 31.740826-51.194881 15.768023-116.929107-47.611239-138.942906z m657.956604 0c-15.870413 7.372063-23.754425 14.641736-23.754424 36.553144 0 22.013799 15.768023 43.925207 47.508849 43.925208 23.754425 7.269673 47.611239-14.641736 55.49525-43.925208 0-21.911409-15.870413-43.822818-47.508849-65.734226a263.960804 263.960804 0 0 0-150.615338 0c-95.222478 36.553145-182.356164 116.929107-213.994601 168.123988 47.508849-175.49605 261.50345-321.708629 451.743626-241.332667 63.481652 22.013799 103.106489 80.478352 95.222478 146.314968-7.986401 73.106289-63.481652 131.673233-150.717729 131.673233-47.508849 0-87.133687-14.641736-110.990501-43.925208-23.754425-43.925207-15.768023-109.659434 47.611239-131.673232zM610.242976 387.750025C784.817518 387.750025 927.446455 307.271673 927.446455 204.881912c0-80.478352-87.236076-146.314969-206.110589-175.496051 142.731327 29.181082 245.735426 109.659434 245.735427 197.40746C967.071293 336.555144 832.428757 409.661434 713.451855 424.30317c-71.365663 7.269673-79.249675 7.269673-103.10649 29.283471-7.884012-22.013799-23.754425-22.013799-87.133686-29.283471C404.234777 409.661434 269.489851 329.183082 269.489851 226.793321c0-87.748025 103.106489-168.226377 245.735426-197.40746C388.466753 58.464554 301.230677 124.40356 301.230677 204.881912c0 102.389761 142.628937 182.868113 309.217078 182.868113z m0 117.031497c31.740826-22.013799 79.249675-29.283472 118.976902-36.655535C903.692031 431.572843 1014.682532 351.29927 1014.682532 241.435056c0-43.822818-15.870413-80.478352-47.611239-109.659434-63.481652-58.566943-158.49935-102.389761-277.476253-117.031496 213.994601 14.641736 372.69873 124.30117 372.698731 255.974402 0 117.031497-118.976902 204.779522-309.217079 248.60234-79.249675 22.013799-118.874513 36.655534-142.731327 73.208679-15.870413-36.553145-55.49525-43.925207-134.744925-73.208679-190.240176-43.822818-309.217078-131.570843-309.217078-248.60234 0-131.673233 158.70413-234.062994 372.69873-255.974402C427.989201 29.385861 332.971503 73.208679 269.489851 131.775622c-31.740826 29.181082-47.508849 73.106289-47.508849 109.659434 0 109.761824 110.990501 190.137786 285.360264 226.690931 31.740826 7.372063 79.249675 14.641736 103.004099 36.655535z m0 175.49605c39.624838-58.566943 118.976902-87.748025 190.240176-109.761824 23.856814-7.269673 31.740826-14.539346 55.49525-14.539346 150.615338-51.194881 245.735426-146.314969 253.721828-255.974402 0-153.584642-198.226577-277.988201-443.962004-299.89961C975.057694 14.744126 1228.677132 175.59844 1228.677132 358.466553c0 124.30117-95.120088 234.062994-277.476252 299.89961-79.249675 21.911409-285.360264 80.375962-340.855515 292.527548-47.508849-204.779522-253.619438-270.616138-332.869113-292.527548C95.120088 585.157484 0 482.767723 0 358.466553 0 175.59844 253.619438 22.013799 562.836516 0.10239 317.10109 14.744126 118.772123 139.045295 118.772123 292.629937c7.986401 109.761824 103.106489 204.779522 245.735426 255.974403 23.856814 7.372063 31.740826 14.641736 55.495251 14.641735 79.249675 29.283472 158.60174 58.464554 190.240176 117.031497z m-110.990501-511.948805C499.354865 109.659434 546.966103 58.464554 618.434157 58.464554 689.59504 58.566943 737.206279 102.492151 737.206279 168.328767c0 58.464554-47.611239 109.659434-118.874512 109.659434-71.365663 0-118.976902-43.822818-118.976902-109.659434z" fill="#DD4012" ></path></symbol></svg>',function(e){var t=(t=document.getElementsByTagName("script"))[t.length-1],c=t.getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var i,o,l,a,n,h=function(t,c){c.parentNode.insertBefore(t,c)};if(c&&!e.__iconfont__svg__cssinject__){e.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}i=function(){var t,c=document.createElement("div");c.innerHTML=e._iconfont_svg_string_4550048,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(t=document.body).firstChild?h(c,t.firstChild):t.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(i,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),i()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(l=i,a=e.document,n=!1,d(),a.onreadystatechange=function(){"complete"==a.readyState&&(a.onreadystatechange=null,v())})}function v(){n||(n=!0,l())}function d(){try{a.documentElement.doScroll("left")}catch(t){return void setTimeout(d,50)}v()}}(window);
\ No newline at end of file \ No newline at end of file
...@@ -6,6 +6,13 @@ ...@@ -6,6 +6,13 @@
"description": "", "description": "",
"glyphs": [ "glyphs": [
{ {
"icon_id": "40993233",
"name": "saixuan",
"font_class": "saixuan-01",
"unicode": "e6e2",
"unicode_decimal": 59106
},
{
"icon_id": "40650590", "icon_id": "40650590",
"name": "jpg", "name": "jpg",
"font_class": "jpg", "font_class": "jpg",
......
## 1.5.2(2024-06-14)
修复错误style语法
## 1.5.1(2024-06-11)
修复弹窗确认与取消设置颜色不生效
## 1.5.0(2024-06-03)
1. 修复monthSwitch事件初始化时被触发的问题
2. monthSwitch事件新增fullDate属性(用来方便直接获取字符串形式的完整日期)
3. 日历新增confirmFullDate属性,用来指定在弹窗模式下的日期点击确认按钮时是否需要选择完整日期
## 1.4.9(2024-05-08)
更新域名
## 1.4.8(2024-04-22)
修复日历内外高度不一致的问题
## 1.4.7(2024-04-16)
增加 operationPosition 属性 用来控制弹窗日历取消和确认按钮的显示位置
## 1.4.6(2024-04-16)
新增属性 actBadgeColor, 当通过 `selected` 属性设置某个日期 `badgeColor`后,如果该日期被选择且主题色与 `badgeColor` 一致时,徽标会显示本颜色
## 1.4.5(2024-04-15)
1. 新增reset方法来重置日历数据
2. 新增插槽-operation 用来自定义弹窗日历确认和取消按钮
3. selected 属性新增 badgeColor 用来指定 badge 颜色
4. close 事件改为弹窗日历点击mask关闭时触发,新增cancel事件,弹窗日历点击取消时触发。
## 1.4.4(2024-01-19)
修复vue2初始化时使用同一引用类型造成的bug
## 1.4.3(2024-01-17)
优化切换折叠状态时的记录方式
## 1.4.2(2024-01-12)
优化 monthShowCurrentMonth 属性, 如果仅显示当月直接不展示该元素。
## 1.4.1(2024-01-12)
优化 monthShowCurrentMonth 属性, 如果仅显示当月直接不展示该元素。
## 1.4.0(2024-01-11)
修复type="week"时找不到
## 1.3.9(2024-01-10)
优化: 将日历日期宽度固定改为跟随父级来决定宽度,避免出现父元素宽度变化带来的对不齐
## 1.3.8(2024-01-04)
1. 增加节日
2. 日历增加自定义高度
3. vue页面日历折叠时增加动效
4. 增加头部插槽
5. 仅显示当月时自动扩大间距,不会再出现下面一排空白的尴尬情况了
## 1.3.7(2023-12-19)
修复回到今日时没有正确记录折叠日期
修复selected变化时无法正常更新打点信息
## 1.3.6(2023-12-13)
修复单选模式下选中时展示的错误的weeks
## 1.3.5(2023-12-13)
修复动态修改date以及其他会触发更新init的函数, swiper不对及只会更新一个下一个数据的bug
## 1.3.4(2023-12-08)
修复selectd没有启用深度监听,导致直接添加数组带来的异常
## 1.3.3(2023-11-23)
1. 修复weeks错误类型提示
2. 修复calendarChange返回month类型不对
## 1.3.2(2023-11-22)
修复将今日日期打点禁用后造成的bug
## 1.3.1(2023-11-21)
1. 修复wu-icon依赖缺失
2. 新增rangeHaveDisableTruncation属性, 用来指定范围选择遇到打点禁用日期是否进行截断
## 1.3.0(2023-11-19)
1. 日历新增类型(周、月日历)
2. 日历新增折叠功能
3. 日历新增以周几开始的属性(周日、周一)
## 1.2.9(2023-11-08)
1. 修复mode变化时,不会正确重置的问题
2. 优化月份大文字显示方式
## 1.2.8(2023-10-22)
新增maskClick用来控制是否点击遮罩层关闭
新增disabledChoice用来控制是否禁止点击日历
## 1.2.7(2023-09-22)
修复传date值情况下不会默认选中的bug
## 1.2.6(2023-09-22)
修改useToday描述
## 1.2.5(2023-09-22)
新增useToday属性用来指定是否使用今天作为默认时间
## 1.2.4(2023-09-21)
修复插入模式下顶部会显示弹窗关闭的内容
## 1.2.3(2023-09-20)
修复恢复默认数据错误的bug
## 1.2.2(2023-09-19)
新增rangeSameDay属性用来指定选日期范围选择时起始日期是否可以为同一天
## 1.2.1(2023-09-18)
1.修复wu-calendar回到今日错误
2.优化wu-calendar picker日期与当前日历日期同步
3.wu-calendar新增mode属性,用来控制单日期、多日期、日期选择范围模式
4.优化wu-calendar date属性来更好的指定多日期、范围日期的默认值
## 1.2.0(2023-09-17)
修复date变化时未成功重置
## 1.1.9(2023-09-17)
1. 新增mode属性用来指定日期选择模式
2. 增加多选
3. 增加范围选择模式、多选模式默认值
## 1.1.8(2023-09-12)
修复回到今日错误
新增日历picker日期与日历同步
## 1.1.7(2023-09-11)
自定义事件声明
## 1.1.6(2023-09-09)
增加 `rangeEndRepick` 属性,用来指定选择完成后点击选择范围内的日期是否可以重选结束日期
## 1.1.5(2023-09-09)
修复每月仅显示当月数据时上方星期错乱的问题
## 1.1.4(2023-09-05)
1. 优化动态计算算法
2. 优化弹窗模式打开缓慢的问题
3. 优化Color方法
## 1.1.3(2023-09-03)
新增插件预览图片
## 1.1.2(2023-09-02)
1. 新增monthShowCurrentMonth 用来设置是否每月仅展示当月数据
2. 修复动态加载时下一月数据更新错误问题
3. 修复confirm和change事件选中的列表中有被禁止的日期的问题
## 1.1.1(2023-08-31)
修复在插入模式中无法滑动的bug
## 1.1.0(2023-08-29)
修复回到今日bug(来自群里464与A毛毛大佬的测试)
## 1.0.9(2023-08-29)
修复依赖缺失
## 1.0.8(2023-08-22)
修复v2初始化时使用不存在的属性导致不可使用的bug(来自wu-ui群内攸宁大佬的反馈)
## 1.0.7(2023-08-21)
阻止默认滑动事件执行
## 1.0.6(2023-08-21)
修复支付宝高度消失的问题
## 1.0.5(2023-08-21)
修改说明
## 1.0.4(2023-08-20)
去除误加打印
## 1.0.3(2023-08-20)
1. 修复初始化时下个月份日期计算不对的问题
2. 增加打点禁用、设置打点徽标位置
## 1.0.2(2023-08-20)
更新展示截图
## 1.0.1(2023-08-20)
修改展示截图及说明
## 1.0.0(2023-08-20)
动态滑动计算的日历插件,还可以设置滑动切换模式、自定义主题颜色、自定义文案、农历显示等功能,可以让您纵享丝滑的使用日历。
{
"wu-calender.ok": "ok",
"wu-calender.cancel": "cancel",
"wu-calender.year": "year",
"wu-calender.month": "month",
"wu-calender.today": "today",
"wu-calender.MON": "MON",
"wu-calender.TUE": "TUE",
"wu-calender.WED": "WED",
"wu-calender.THU": "THU",
"wu-calender.FRI": "FRI",
"wu-calender.SAT": "SAT",
"wu-calender.SUN": "SUN"
}
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}
{
"wu-calender.ok": "确定",
"wu-calender.cancel": "取消",
"wu-calender.year": "年",
"wu-calender.month": "月",
"wu-calender.today": "今日",
"wu-calender.SUN": "日",
"wu-calender.MON": "一",
"wu-calender.TUE": "二",
"wu-calender.WED": "三",
"wu-calender.THU": "四",
"wu-calender.FRI": "五",
"wu-calender.SAT": "六"
}
{
"wu-calender.ok": "確定",
"wu-calender.cancel": "取消",
"wu-calender.year": "年",
"wu-calender.month": "月",
"wu-calender.today": "今日",
"wu-calender.SUN": "日",
"wu-calender.MON": "一",
"wu-calender.TUE": "二",
"wu-calender.WED": "三",
"wu-calender.THU": "四",
"wu-calender.FRI": "五",
"wu-calender.SAT": "六"
}
export default {
props: {
showMonth: {
type: Boolean,
default: false
},
// 折叠状态
FoldStatus: {
type: String,
default: null
},
month: {
type: [Number, String],
default: null
},
color: {
type: String,
default: '#3c9cff'
},
startText: {
type: String,
default: '开始'
},
endText: {
type: String,
default: '结束'
},
weeks: {
type: [Object, Array],
default: ()=> {
return []
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
},
itemHeight: {
type: Number,
default: 64
},
monthShowCurrentMonth: {
type: Boolean,
default: false
},
actBadgeColor: {
type: String,
default: '#fff'
},
}
}
\ No newline at end of file
<template>
<view class="wu-calendar-block">
<view v-if="showMonth && FoldShowMonth" class="wu-calendar__box-bg">
<text class="wu-calendar__box-bg-text">{{month}}</text>
</view>
<!-- 月或周日历 -->
<view class="wu-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
<view class="wu-calendar__weeks-item" v-for="(weeks, weeksIndex) in item" :key="weeksIndex" :style="[weekItemStyle]">
<wu-calendar-item v-if="!monthShowCurrentMonth || !weeks.empty" class="wu-calendar-item--hook" :weeks="weeks" :calendar="calendar"
:selected="selected" :lunar="lunar" @change="choiceDate" :color="color" :actBadgeColor="actBadgeColor"
:startText="startText" :endText="endText" :itemHeight="itemHeight - defaultMargin"></wu-calendar-item>
</view>
</view>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/wu-ui-tools/libs/mixin/mpMixin.js';
import mixin from '@/uni_modules/wu-ui-tools/libs/mixin/mixin.js';
import props from './props.js';
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import i18nMessages from '../i18n/index.js'
const {
t
} = initVueI18n(i18nMessages)
export default {
emits: ['change'],
mixins: [mpMixin, mixin, props],
data() {
return {
FoldShowMonth: false,
// 默认边距
defaultMargin: 8
}
},
mounted() {
this.FoldShowMonth = this.FoldStatus == 'open';
},
computed: {
weekItemStyle() {
let weeksLength = Object.keys(this.weeks).length;
let calendarHeight = this.FoldStatus === 'open' ? this.itemHeight * 6 : this.itemHeight;
let margin = weeksLength && this.weeks[weeksLength - 1][0].empty ? this.itemHeight / (weeksLength - 1) + this.defaultMargin : this.defaultMargin
return {
marginTop: margin / 2 + 'px',
marginBottom: margin / 2 + 'px',
}
}
},
watch: {
FoldStatus(newVal) {
this.$nextTick(()=>{
this.FoldShowMonth = this.FoldStatus == 'open';
})
}
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
}
}
}
</script>
<style lang="scss" scoped>
$wu-text-color-grey: #999;
.wu-calendar-block {
.wu-calendar__weeks {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 0 8rpx;
}
.wu-calendar__weeks-item {
flex: 1;
}
.wu-calendar__box-bg {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.wu-calendar__box-bg-text {
font-size: 100rpx;
transform: scale(4);
font-weight: bold;
color: $wu-text-color-grey;
opacity: 0.1;
text-align: center;
/* #ifndef APP-NVUE */
line-height: 1;
/* #endif */
}
}
</style>
\ No newline at end of file
export default {
props: {
color: {
type: String,
default: '#3c9cff'
},
startText: {
type: String,
default: '开始'
},
endText: {
type: String,
default: '结束'
},
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
},
itemHeight: {
type: Number,
default: 64
},
actBadgeColor: {
type: String,
default: '#fff',
}
}
}
\ No newline at end of file
<template>
<view class="wu-calendar-item__weeks-box" ref="$weeksbox" :style="[calendarItemStyle, {
borderTopLeftRadius: weeks.beforeRange ? '12rpx' : '',
borderBottomLeftRadius: weeks.beforeRange ? '12rpx' : '',
borderTopRightRadius: weeks.afterRange ? '12rpx' : '',
borderBottomRightRadius: weeks.afterRange ? '12rpx' : '',
}]" @click="choiceDate(weeks)">
<view class="wu-calendar-item__weeks-box-item" :style="[actMultipleStyle, {height: itemHeight + 'px'}]">
<!-- 自定义打点上方信息 -->
<text v-if="weeks.extraInfo && weeks.extraInfo.topInfo" class="wu-calendar-item__weeks-lunar-text" :style="[{color: weeks.extraInfo.topInfoColor || '#e43d33'}, calendarItemStyle, actMultipleStyle]">{{weeks.extraInfo.topInfo}}</text>
<!-- 徽标 -->
<text v-if="selected && weeks.extraInfo && weeks.extraInfo.badge" class="wu-calendar-item__weeks-box-circle" :style="[badgeStyle]"></text>
<!-- 日期文字 -->
<text class="wu-calendar-item__weeks-box-text" :style="[calendarItemStyle, actMultipleStyle]">{{weeks.date}}</text>
<!-- 今日的文字 -->
<text v-if="!lunar && !weeks.extraInfo && weeks.isDay && !weeks.beforeRange && !weeks.afterRange" class="wu-calendar-item__weeks-lunar-text" :style="[calendarItemStyle, actMultipleStyle]">{{todayText}}</text>
<!-- 农历文字 -->
<text v-if="lunar && !weeks.extraInfo && !weeks.beforeRange && !weeks.afterRange" class="wu-calendar-item__weeks-lunar-text" :style="[calendarItemStyle, actMultipleStyle]">{{dayText}}</text>
<!-- 选中的文字展示 -->
<text v-if="!weeks.extraInfo && (weeks.beforeRange || weeks.afterRange)" class="wu-calendar-item__weeks-lunar-text" :style="[calendarItemStyle, actMultipleStyle]">{{multipleText}}</text>
<!-- 自定义打点下方信息 -->
<text v-if="weeks.extraInfo && weeks.extraInfo.info" class="wu-calendar-item__weeks-lunar-text" :style="[{color: weeks.extraInfo.infoColor || '#e43d33'}, calendarItemStyle, actMultipleStyle]">{{weeks.extraInfo.info}}</text>
</view>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/wu-ui-tools/libs/mixin/mpMixin.js';
import mixin from '@/uni_modules/wu-ui-tools/libs/mixin/mixin.js';
import props from './props.js';
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import i18nMessages from '../i18n/index.js'
const {
t
} = initVueI18n(i18nMessages)
export default {
emits: ['change'],
mixins: [mpMixin, mixin, props],
computed: {
todayText() {
return t("wu-calender.today")
},
// 每项日历样式
calendarItemStyle() {
let style = {};
let color = this.$w.Color.gradient(this.color, this.$w.Color.isLight(this.color) ? '#000' : '#fff', 100)[6]
// 有顺序别乱动
// 选中的日期范围内的样式
if (this.weeks.rangeMultiple) {
style = {
backgroundColor: this.$w.Color.gradient(this.color, '#fff', 100)[80],
color
}
};
// 今天的日期样式
if (this.weeks.isDay) {
style.color = color;
}
// 禁用的日期样式
if(this.weeks.disable) {
style = {
backgroundColor: 'rgba(249, 249, 249, 0.3)',
color: '#c0c0c0'
}
}
return style;
},
// 选中的日期样式
actMultipleStyle() {
if ((this.weeks.beforeRange || this.weeks.afterRange || this.weeks.multiples || (this.calendar.fullDate === this.weeks
.fullDate && this.weeks.mode === 'single')) && !this.weeks.disable) {
return {
backgroundColor: this.color,
color: '#fff',
borderRadius: '12rpx'
}
}
},
// 徽标样式
badgeStyle() {
let style = {
backgroundColor: this.weeks.disable ? '#c0c0c0' : '#e43d33',
width: '16rpx',
height: '16rpx'
};
if(this.weeks.extraInfo) {
if(this.weeks.extraInfo.badgeColor) {
// 如果当前是选中日期的徽标且徽标颜色与主题色一致 为了保证 徽标颜色可以被看见 再选中时将其设置为 #fff
if ((this.weeks.beforeRange || this.weeks.afterRange || this.weeks.multiples || (this.calendar.fullDate === this.weeks
.fullDate && this.weeks.mode === 'single')) && !this.weeks.disable && this.$w.Color.convertFormat(this.weeks.extraInfo.badgeColor) == this.$w.Color.convertFormat(this.color)) {
style.backgroundColor = this.actBadgeColor;
} else {
style.backgroundColor = this.weeks.extraInfo.badgeColor
}
}
if(this.weeks.extraInfo.badgeSize) {
style.width = this.weeks.extraInfo.badgeSize
style.height = this.weeks.extraInfo.badgeSize
}
if(!this.weeks.extraInfo.badgePosition) {
style.right = '10rpx';
style.top = '10rpx';
} else if(this.weeks.extraInfo.badgePosition == 'top-left'){
style.top = '10rpx';
style.left = '10rpx';
} else if(this.weeks.extraInfo.badgePosition == 'top-center'){
style.top = '10rpx';
style.left = 'center';
} else if(this.weeks.extraInfo.badgePosition == 'top-right'){
style.top = '10rpx';
style.right = '10rpx';
} else if(this.weeks.extraInfo.badgePosition == 'bottom-left'){
style.bottom = '10rpx';
style.left = '10rpx';
} else if(this.weeks.extraInfo.badgePosition == 'bottom-center'){
style.bottom = '10rpx';
style.left = 'center';
} else if(this.weeks.extraInfo.badgePosition == 'bottom-right'){
style.bottom = '10rpx';
style.right = '10rpx';
}
}
return style
},
// 日期文字
dayText() {
let text = '';
if (this.weeks.isDay) {
text = this.todayText
} else if(this.weeks.lunar.festival) {
text = this.weeks.lunar.festival
} else if(this.weeks.lunar.isTerm) {
text = this.weeks.lunar.Term
} else if (this.weeks.lunar.IDayCn === '初一') {
text = this.weeks.lunar.IMonthCn
} else {
text = this.weeks.lunar.IDayCn
}
return text
},
// 选中的文字
multipleText() {
let text = '';
if (this.weeks.afterRange) {
text = this.endText
} else if (this.weeks.beforeRange) {
text = this.startText
}
return text;
}
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/wu-ui-tools/theme.scss';
$wu-font-size-base: 28rpx;
$wu-text-color: #333;
$wu-font-size-sm: 24rpx;
$wu-color-error: #e43d33;
$wu-opacity-disabled: 0.3;
$wu-text-color-disable: #c0c0c0;
.wu-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0 0.5px;
}
.wu-calendar-item__weeks-box-text {
font-size: $wu-font-size-base;
color: $wu-text-color;
}
.wu-calendar-item__weeks-lunar-text {
font-size: $wu-font-size-sm;
color: $wu-text-color;
}
.wu-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
}
.wu-calendar-item__weeks-box-circle {
position: absolute;
border-radius: 16rpx;
background-color: $wu-color-error;
}
.wu-calendar-item--disable {
background-color: rgba(249, 249, 249, $wu-opacity-disabled);
color: $wu-text-color-disable;
}
.wu-calendar-item--extra {
color: $wu-color-error;
opacity: 0.8;
}
.wu-calendar-item--checked {
color: #fff;
}
</style>
\ No newline at end of file
/**
* @1900-2100区间内的公历、农历互转
* @charset UTF-8
* @github https://github.com/jjonline/calendar.js
* @Author Jea杨(JJonline@JJonline.Cn)
* @Time 2014-7-21
* @Time 2016-8-13 Fixed 2033hex、Attribution Annals
* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
* @Version 1.0.3
* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
*/
/* eslint-disable */
var calendar = {
/**
* 农历1900-2100的润大小信息表
* @Array Of Property
* @return Hex
*/
lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0,
0x055d2, // 1900-1909
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
/** Add By JJonline@JJonline.Cn**/
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
0x0d520
], // 2100
/**
* 公历每个月份的天数普通表
* @Array Of Property
* @return Number
*/
solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
/**
* 天干地支之天干速查表
* @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
* @return Cn string
*/
Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
/**
* 天干地支之地支速查表
* @Array Of Property
* @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
* @return Cn string
*/
Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149',
'\u620c', '\u4ea5'
],
/**
* 天干地支之地支速查表<=>生肖
* @Array Of Property
* @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
* @return Cn string
*/
Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21',
'\u72d7', '\u732a'
],
/**
* 24节气速查表
* @Array Of Property
* @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
* @return Cn string
*/
solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206',
'\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3',
'\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206',
'\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'
],
/**
* 1900-2100各年的24节气日期速查表
* @Array Of Property
* @return 0x string For splice
*/
sTermInfo: [
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
'97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
'97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
'97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
'97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
'9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
'7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
'977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
'7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
'7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
'665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'
],
festivals: {
'1-1': '元旦',
'2-14': '情人节',
'3-8': '妇女节',
'3-12': '植树节',
'4-1': '愚人节',
'5-1': '劳动节',
'5-4': '青年节',
'5-12': '护士节',
'6-1': '儿童节',
'8-1': '建军节',
'9-10': '教师节',
'10-1': '国庆',
'11-1': '万圣节',
'12-24': '圣诞节',
'正月初一': '春节',
'二月初二': '龙抬头',
'五月初五': '端午节',
'七月初七': '七夕节',
'七月十五': '中元节',
'八月十五': '中秋节',
'九月初九': '重阳节',
'腊月初八': '腊八节',
'腊月廿三': '小年',
'腊月三十': '除夕',
},
/**
* 数字转中文速查表
* @Array Of Property
* @trans ['日','一','二','三','四','五','六','七','八','九','十']
* @return Cn string
*/
nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d',
'\u5341'
],
/**
* 日期转农历称呼速查表
* @Array Of Property
* @trans ['初','十','廿','卅']
* @return Cn string
*/
nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
/**
* 月份转农历称呼速查表
* @Array Of Property
* @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
* @return Cn string
*/
nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341',
'\u51ac', '\u814a'
],
/**
* 返回农历y年一整年的总天数
* @param lunar Year
* @return Number
* @eg:var count = calendar.lYearDays(1987) ;//count=387
*/
lYearDays: function(y) {
var i;
var sum = 348
for (i = 0x8000; i > 0x8; i >>= 1) {
sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0
}
return (sum + this.leapDays(y))
},
/**
* 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
* @param lunar Year
* @return Number (0-12)
* @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
*/
leapMonth: function(y) { // 闰字编码 \u95f0
return (this.lunarInfo[y - 1900] & 0xf)
},
/**
* 返回农历y年闰月的天数 若该年没有闰月则返回0
* @param lunar Year
* @return Number (0、29、30)
* @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
*/
leapDays: function(y) {
if (this.leapMonth(y)) {
return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
}
return (0)
},
/**
* 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
* @param lunar Year
* @return Number (-1、29、30)
* @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
*/
monthDays: function(y, m) {
if (m > 12 || m < 1) {
return -1
} // 月份参数从1至12,参数错误返回-1
return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
},
/**
* 返回公历(!)y年m月的天数
* @param solar Year
* @return Number (-1、28、29、30、31)
* @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
*/
solarDays: function(y, m) {
if (m > 12 || m < 1) {
return -1
} // 若参数错误 返回-1
var ms = m - 1
if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
} else {
return (this.solarMonth[ms])
}
},
/**
* 农历年份转换为干支纪年
* @param lYear 农历年的年份数
* @return Cn string
*/
toGanZhiYear: function(lYear) {
var ganKey = (lYear - 3) % 10
var zhiKey = (lYear - 3) % 12
if (ganKey == 0) ganKey = 10 // 如果余数为0则为最后一个天干
if (zhiKey == 0) zhiKey = 12 // 如果余数为0则为最后一个地支
return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
},
/**
* 公历月、日判断所属星座
* @param cMonth [description]
* @param cDay [description]
* @return Cn string
*/
toAstro: function(cMonth, cDay) {
var s =
'\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7' // 座
},
/**
* 传入offset偏移量返回干支
* @param offset 相对甲子的偏移量
* @return Cn string
*/
toGanZhi: function(offset) {
return this.Gan[offset % 10] + this.Zhi[offset % 12]
},
/**
* 传入公历(!)y年获得该年第n个节气的公历日期
* @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
* @return day Number
* @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
*/
getTerm: function(y, n) {
if (y < 1900 || y > 2100) {
return -1
}
if (n < 1 || n > 24) {
return -1
}
var _table = this.sTermInfo[y - 1900]
var _info = [
parseInt('0x' + _table.substr(0, 5)).toString(),
parseInt('0x' + _table.substr(5, 5)).toString(),
parseInt('0x' + _table.substr(10, 5)).toString(),
parseInt('0x' + _table.substr(15, 5)).toString(),
parseInt('0x' + _table.substr(20, 5)).toString(),
parseInt('0x' + _table.substr(25, 5)).toString()
]
var _calday = [
_info[0].substr(0, 1),
_info[0].substr(1, 2),
_info[0].substr(3, 1),
_info[0].substr(4, 2),
_info[1].substr(0, 1),
_info[1].substr(1, 2),
_info[1].substr(3, 1),
_info[1].substr(4, 2),
_info[2].substr(0, 1),
_info[2].substr(1, 2),
_info[2].substr(3, 1),
_info[2].substr(4, 2),
_info[3].substr(0, 1),
_info[3].substr(1, 2),
_info[3].substr(3, 1),
_info[3].substr(4, 2),
_info[4].substr(0, 1),
_info[4].substr(1, 2),
_info[4].substr(3, 1),
_info[4].substr(4, 2),
_info[5].substr(0, 1),
_info[5].substr(1, 2),
_info[5].substr(3, 1),
_info[5].substr(4, 2)
]
return parseInt(_calday[n - 1])
},
/**
* 传入农历数字月份返回汉语通俗表示法
* @param lunar month
* @return Cn string
* @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
*/
toChinaMonth: function(m) { // 月 => \u6708
if (m > 12 || m < 1) {
return -1
} // 若参数错误 返回-1
var s = this.nStr3[m - 1]
s += '\u6708' // 加上月字
return s
},
/**
* 传入农历日期数字返回汉字表示法
* @param lunar day
* @return Cn string
* @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
*/
toChinaDay: function(d) { // 日 => \u65e5
var s
switch (d) {
case 10:
s = '\u521d\u5341';
break
case 20:
s = '\u4e8c\u5341';
break
break
case 30:
s = '\u4e09\u5341';
break
break
default:
s = this.nStr2[Math.floor(d / 10)]
s += this.nStr1[d % 10]
}
return (s)
},
/**
* 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
* @param y year
* @return Cn string
* @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
*/
getAnimal: function(y) {
return this.Animals[(y - 4) % 12]
},
/**
* 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
* @param y solar year
* @param m solar month
* @param d solar day
* @return JSON object
* @eg:console.log(calendar.solar2lunar(1987,11,01));
*/
solar2lunar: function(y, m, d) { // 参数区间1900.1.31~2100.12.31
// 年份限定、上限
if (y < 1900 || y > 2100) {
return -1 // undefined转换为数字变为NaN
}
// 公历传参最下限
if (y == 1900 && m == 1 && d < 31) {
return -1
}
// 未传参 获得当天
if (!y) {
var objDate = new Date()
} else {
var objDate = new Date(y, parseInt(m) - 1, d)
}
var i;
var leap = 0;
var temp = 0
// 修正ymd参数
var y = objDate.getFullYear()
var m = objDate.getMonth() + 1
var d = objDate.getDate()
var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0,
31)) / 86400000
for (i = 1900; i < 2101 && offset > 0; i++) {
temp = this.lYearDays(i)
offset -= temp
}
if (offset < 0) {
offset += temp;
i--
}
// 是否今天
var isTodayObj = new Date()
var isToday = false
if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
isToday = true
}
// 星期几
var nWeek = objDate.getDay()
var cWeek = this.nStr1[nWeek]
// 数字表示周几顺应天朝周一开始的惯例
if (nWeek == 0) {
nWeek = 7
}
// 农历年
var year = i
var leap = this.leapMonth(i) // 闰哪个月
var isLeap = false
// 效验闰月
for (i = 1; i < 13 && offset > 0; i++) {
// 闰月
if (leap > 0 && i == (leap + 1) && isLeap == false) {
--i
isLeap = true;
temp = this.leapDays(year) // 计算农历闰月天数
} else {
temp = this.monthDays(year, i) // 计算农历普通月天数
}
// 解除闰月
if (isLeap == true && i == (leap + 1)) {
isLeap = false
}
offset -= temp
}
// 闰月导致数组下标重叠取反
if (offset == 0 && leap > 0 && i == leap + 1) {
if (isLeap) {
isLeap = false
} else {
isLeap = true;
--i
}
}
if (offset < 0) {
offset += temp;
--i
}
// 农历月
var month = i
// 农历日
var day = offset + 1
// 天干地支处理
var sm = m - 1
var gzY = this.toGanZhiYear(year)
// 当月的两个节气
// bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
var firstNode = this.getTerm(y, (m * 2 - 1)) // 返回当月「节」为几日开始
var secondNode = this.getTerm(y, (m * 2)) // 返回当月「节」为几日开始
// 依据12节气修正干支月
var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
if (d >= firstNode) {
gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
}
// 传入的日期的节气与否
var isTerm = false
var Term = null
if (firstNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 2]
}
if (secondNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 1]
}
// 计算农历日期
const IMonthCn = (isLeap ? '\u95f0' : '') + this.toChinaMonth(month)
// 农历日期的汉字表述法
let IDayCn = this.toChinaDay(day)
// 节日
let festival = '';
// 农历的月日汉字表述
let lMDcn = IMonthCn + IDayCn;
// 月份日期
let MD = m + '-' + d;
if (this.festivals.hasOwnProperty(lMDcn)) {
festival = this.festivals[lMDcn]
} else if(this.festivals.hasOwnProperty(MD)) {
festival = this.festivals[MD]
}
// 日柱 当月一日与 1900/1/1 相差天数
var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
var gzD = this.toGanZhi(dayCyclical + d - 1)
// 该日期所属的星座
var astro = this.toAstro(m, d)
return {
'lYear': year,
'lMonth': month,
'lDay': day,
'Animal': this.getAnimal(year),
'IMonthCn': IMonthCn,
'IDayCn': IDayCn,
'cYear': y,
'cMonth': m,
'cDay': d,
'gzYear': gzY,
'gzMonth': gzM,
'gzDay': gzD,
'isToday': isToday,
'isLeap': isLeap,
'nWeek': nWeek,
'ncWeek': '\u661f\u671f' + cWeek,
'isTerm': isTerm,
'Term': Term,
'astro': astro,
'festival': festival
}
},
/**
* 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
* @param y lunar year
* @param m lunar month
* @param d lunar day
* @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
* @return JSON object
* @eg:console.log(calendar.lunar2solar(1987,9,10));
*/
lunar2solar: function(y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
var isLeapMonth = !!isLeapMonth
var leapOffset = 0
var leapMonth = this.leapMonth(y)
var leapDay = this.leapDays(y)
if (isLeapMonth && (leapMonth != m)) {
return -1
} // 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) {
return -1
} // 超出了最大极限值
var day = this.monthDays(y, m)
var _day = day
// bugFix 2016-9-25
// if month is leap, _day use leapDays method
if (isLeapMonth) {
_day = this.leapDays(y, m)
}
if (y < 1900 || y > 2100 || d > _day) {
return -1
} // 参数合法性效验
// 计算农历的时间差
var offset = 0
for (var i = 1900; i < y; i++) {
offset += this.lYearDays(i)
}
var leap = 0;
var isAdd = false
for (var i = 1; i < m; i++) {
leap = this.leapMonth(y)
if (!isAdd) { // 处理闰月
if (leap <= i && leap > 0) {
offset += this.leapDays(y);
isAdd = true
}
}
offset += this.monthDays(y, i)
}
// 转换闰月农历 需补充该年闰月的前一个月的时差
if (isLeapMonth) {
offset += day
}
// 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
var calObj = new Date((offset + d - 31) * 86400000 + stmap)
var cY = calObj.getUTCFullYear()
var cM = calObj.getUTCMonth() + 1
var cD = calObj.getUTCDate()
return this.solar2lunar(cY, cM, cD)
}
}
export default calendar
\ No newline at end of file
export default {
props: {
// 自定义当前时间
date: {
type: [String, Array],
default: ''
},
// 日历类型(默认为month)
type: {
type: String,
default: 'month',
validator(value) {
return ['month', 'week'].includes(value)
}
},
// 日期选择模式
mode: {
type: String,
default: 'single'
},
// 是否使用默认日期(今天,默认为true)
useToday: {
type: Boolean,
default: true
},
// 是否使用折叠功能
fold: {
type: Boolean,
default: null
},
// 主题色
color: {
type: String,
default: '#3c9cff'
},
// 日历中每一项日期的高度(默认70),单位px
itemHeight: {
type: Number,
default: 70
},
// 取消文字的颜色
cancelColor: {
type: String,
default: '#333'
},
// 确定文字的颜色
confirmColor: {
type: String,
default: '#333'
},
// mode=range时,第一个日期底部的提示文字
startText: {
type: String,
default: '开始'
},
// mode=range时,最后一个日期底部的提示文字
endText: {
type: String,
default: '结束'
},
// 日历以周几开始
startWeek: {
type: String,
default: 'sun',
validator(value) {
return ['sun', 'mon'].includes(value)
}
},
// 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
selected: {
type: Array,
default () {
return []
}
},
// 是否显示农历
lunar: {
type: Boolean,
default: false
},
// 日期选择范围-开始日期
startDate: {
type: String,
default: ''
},
// 日期选择范围-结束日期
endDate: {
type: String,
default: ''
},
// 允许日期选择范围内重选结束日期
rangeEndRepick: {
type: Boolean,
default: false
},
// 允许日期选择范围起始日期为同一天
rangeSameDay: {
type: Boolean,
default: false
},
// 允许日期选择范围内遇到打点禁用日期进行截断
rangeHaveDisableTruncation: {
type: Boolean,
default: false
},
// 每月仅显示当月日期
monthShowCurrentMonth: {
type: Boolean,
default: false
},
// 插入模式,可选值,ture:插入模式;false:弹窗模式; 默认为插入模式
insert: {
type: Boolean,
default: true
},
// 滑动切换模式,可选值 horizontal: 横向 vertical:纵向 none: 不使用滑动切换
slideSwitchMode: {
type: String,
default: 'horizontal'
},
// 是否显示月份为背景
showMonth: {
type: Boolean,
default: true
},
// 弹窗模式是否清空上次选择内容
clearDate: {
type: Boolean,
default: true
},
// 是否点击遮罩层关闭
maskClick: {
type: Boolean,
default: false
},
// 是否禁止点击日历
disabledChoice: {
type: Boolean,
default: false
},
// 弹窗日历取消和确认按钮的显示位置
operationPosition: {
type: String,
default: 'top',
validator(value) {
return ['top', 'bottom'].includes(value)
}
},
// 弹窗日历点击确认时是否需要选择完整日期
confirmFullDate: {
type: Boolean,
default: false
},
// 当通过 `selected` 属性设置某个日期 `badgeColor`后,如果该日期被选择且主题色与 `badgeColor` 一致时,徽标会显示本颜色
actBadgeColor: {
type: String,
default: '#fff'
},
...uni.$w?.props?.calendar
}
}
\ No newline at end of file
import calendar from './calendar.js';
import CALENDAR from './calendar.js'
class Calendar {
constructor({
date,
selected,
startDate,
endDate,
mode,
monthShowCurrentMonth,
rangeEndRepick,
rangeSameDay,
rangeHaveDisableTruncation,
type,
foldStatus,
startWeek
} = {}) {
// 当前日期
this.date = this.getDate(new Date()) // 当前初入日期
// 打点信息
this.selected = selected || [];
// 范围开始
this.startDate = startDate
// 范围结束
this.endDate = endDate
// 日历以周几开始
this.startWeek = startWeek
// 日期选择类型
this.mode = mode
// 日历类型
this.type = type
// 折叠状态
this.foldStatus = foldStatus
// 允许范围内重选结束日期
this.rangeEndRepick = rangeEndRepick
// 允许日期选择范围起始日期为同一天
this.rangeSameDay = rangeSameDay
// 日期选择范围内遇到打点禁用日期是否截断
this.rangeHaveDisableTruncation = rangeHaveDisableTruncation
// 每月是否仅显示当月的数据
this.monthShowCurrentMonth = monthShowCurrentMonth
// 清理多选状态
this.cleanRange()
// 每周日期
this.weeks = {}
// 多个日期
this.multiple = [];
}
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.selectDate = this.getDate(date)
this._getWeek(this.selectDate.fullDate)
}
/**
* 清除范围
*/
cleanRange() {
this.rangeStatus = {
before: '',
after: '',
data: []
}
}
/**
* 清除多选
*/
cleanMultiple() {
this.multiple = []
}
/**
* 重置开始日期
*/
resetSatrtDate(startDate) {
// 范围开始
this.startDate = startDate
}
/**
* 重置结束日期
*/
resetEndDate(endDate) {
// 范围结束
this.endDate = endDate
}
/**
* 重置是否每月仅显示当月数据
* @param {Boolean} show 是否仅显示当月数据
*/
resetMonthShowCurrentMonth(show) {
this.monthShowCurrentMonth = show
}
// 重置允许范围内重选结束日期
resetRangeEndRepick(val) {
this.rangeEndRepick = val
}
// 重置允许日期范围选择起始日期为同一天
resetRangeSameDay(val) {
this.rangeSameDay = val
}
// 重置范围内遇到打点禁用日期是否截断
resetRangeHaveDisableTruncation(val) {
this.rangeHaveDisableTruncation = val
}
// 重置日期选择模式
resetMode(val) {
this.mode = val
}
// 重置折叠状态
resetFoldStatus(val) {
this.foldStatus = val
}
// 重置日历以周几开始
resetStartWeek(val) {
this.startWeek = val
}
/**
* 创建本月某一天的信息
*/
_createCurrentDay(nowDate, full, date) {
// 是否今天
let isDay = this.date.fullDate === nowDate
// 获取打点信息
let info = this.selected && this.selected.find((item) => {
if (this.dateEqual(nowDate, item.date)) {
return item
}
})
// 日期禁用
let disableBefore = true
let disableAfter = true
if (this.startDate) {
disableBefore = this.dateCompare(this.startDate, nowDate)
}
if (this.endDate) {
disableAfter = this.dateCompare(nowDate, this.endDate)
}
// 范围选择模式
let ranges = this.rangeStatus.data
let checked = false
if (this.mode == 'range') {
checked = ranges.findIndex((item) => this.dateEqual(item, nowDate)) !== -1 ? true : false;
}
// 多日期选择模式
let multiples = this.multiple
let multiplesChecked = false
if (this.mode == 'multiple') {
multiplesChecked = multiples.findIndex(item => this.dateEqual(item, nowDate)) !== -1;
}
let data = {
fullDate: nowDate,
year: full.year,
date,
type: this.type,
mode: this.mode,
multiples: this.mode == 'multiple' ? multiplesChecked : false,
rangeMultiple: this.mode == 'range' ? checked : false,
beforeRange: this.dateEqual(this.rangeStatus.before, nowDate),
afterRange: this.dateEqual(this.rangeStatus.after, nowDate),
month: full.month,
lunar: this.getlunar(full.year, full.month, date),
disable: !(disableBefore && disableAfter),
isDay
}
if (info) {
data.extraInfo = info;
data.disable = info.disable || false;
}
return data
}
/**
* 获取任意时间
*/
getDate(date, AddDayCount = 0, str = 'day') {
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
break
case 'month':
if (dd.getDate() === 31 && AddDayCount > 0) {
dd.setDate(dd.getDate() + AddDayCount)
} else {
const preMonth = dd.getMonth()
dd.setMonth(preMonth + AddDayCount) // 获取AddDayCount天后的日期
const nextMonth = dd.getMonth()
// 处理 pre 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
if (AddDayCount < 0 && preMonth !== 0 && nextMonth - preMonth > AddDayCount) {
dd.setMonth(nextMonth + (nextMonth - preMonth + AddDayCount))
}
// 处理 next 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
if (AddDayCount > 0 && nextMonth - preMonth > AddDayCount) {
dd.setMonth(nextMonth - (nextMonth - preMonth - AddDayCount))
}
}
break
case 'week':
dd.setDate(dd.getDate() + (AddDayCount * 7))
break;
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
break
}
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
}
}
/**
* 获取上月剩余天数
*/
_getLastMonthDays(firstDay, full) {
let dateArr = []
for (let i = firstDay; i > 0; i--) {
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
dateArr.push({
date: beforeDate,
month: full.month - 1,
year: full.year,
lunar: this.getlunar(full.year, full.month - 1, beforeDate),
disable: true
})
}
return dateArr
}
/**
* 获取本月天数
*/
_currentMonthDays(dateData, full) {
let dateArr = []
let fullDate = this.date.fullDate
for (let i = 1; i <= dateData; i++) {
let nowDate = full.year + '-' + (full.month < 10 ?
full.month : full.month) + '-' + (i < 10 ?
'0' + i : i)
dateArr.push(this._createCurrentDay(nowDate, full, i))
}
return dateArr
}
/**
* 获取下月天数
*/
_getNextMonthDays(surplus, full) {
let dateArr = []
for (let i = 1; i < surplus + 1; i++) {
dateArr.push({
date: i,
month: Number(full.month) + 1,
lunar: this.getlunar(full.year, Number(full.month) + 1, i),
disable: true
})
}
return dateArr
}
/**
* 获取任意日期的一周
*/
_getWeekDays(dateData) {
let dateArr = [];
let oneDayTime = 1000 * 60 * 60 * 24
let today = new Date(dateData);
// 获取这个日期是星期几
let todayDay;
let startDate;
// 如果日历以周一开始
if(this.startWeek == 'mon') {
todayDay = today.getDay() || 7;
startDate = new Date(today.getTime() - oneDayTime * (todayDay - 1));
} else {
todayDay = today.getDay();
startDate = new Date(today.getTime() - oneDayTime * todayDay);
}
for (let i = 0; i < 7; i++) {
let temp = new Date(startDate.getTime() + i * oneDayTime)
let newDate = this.getDate(`${temp.getFullYear()}-${temp.getMonth() + 1}-${temp.getDate()}`)
dateArr.push(this._createCurrentDay(newDate.fullDate, newDate, Number(newDate.date)))
}
return dateArr;
}
/**
* 获取当前日期详情
* @param {Object} date
*/
getInfo(date) {
if (!date) {
date = new Date()
}
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
return dateInfo
}
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
}
/**
* 比较时间是否相等
*/
dateEqual(before = '', after = '') {
// 计算截止时间
before = new Date(before.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
after = new Date(after.replace('-', '/').replace('-', '/'))
if (before.getTime() - after.getTime() === 0) {
return true
} else {
return false
}
}
/**
* 获取日期范围内所有日期
* @param {Object} begin
* @param {Object} end
*/
getDateAll(begin, end) {
// 找出所有打点中已禁用的部分 不让其被添加在日期选择范围内
let disableList = this.selected.filter(item => item.date && item.disable).map(item => item.date)
var arr = []
var ab = begin.split('-')
var ae = end.split('-')
var db = new Date()
db.setFullYear(ab[0], ab[1] - 1, ab[2])
var de = new Date()
de.setFullYear(ae[0], ae[1] - 1, ae[2])
var wuxDb = db.getTime() - 24 * 60 * 60 * 1000
var wuxDe = de.getTime() - 24 * 60 * 60 * 1000
for (var k = wuxDb; k <= wuxDe;) {
k = k + 24 * 60 * 60 * 1000
let fullDate = this.getDate(new Date(parseInt(k))).fullDate
// 如果要在选择范围内截断日期
if(this.rangeHaveDisableTruncation) {
// 如果不在打点禁止列表中
if (disableList.includes(fullDate)) return arr;
arr.push(fullDate)
} else {
if (!disableList.includes(fullDate)) arr.push(fullDate);
}
}
return arr
}
/**
* 计算阴历日期显示
*/
getlunar(year, month, date) {
return CALENDAR.solar2lunar(year, month, date)
}
/**
* 设置打点
*/
setSelectInfo(data, value) {
this.selected = value
this._getWeek(data)
}
/**
* 设置范围
*/
setRange(fullDate) {
let {
before,
after
} = this.rangeStatus;
// 非范围选择不再执行
if (this.mode != 'range') return
// 判断目前的日期 是否 比before日期小或者等于before日期 如果为true就要重置
let reset = this.dateCompare(fullDate, before);
// 如果日期选择范围允许为同一天 且 目前是需要重置的
if (this.rangeSameDay && before && reset) {
// 判断是否需要相等 如果 不相等 则 重置 如果相等 则不重置
reset = !this.dateEqual(fullDate, before);
}
if ((before && after || reset) && (!this.rangeEndRepick || (this.rangeEndRepick && this.rangeStatus.data
.indexOf(fullDate) == -1))) {
this.rangeStatus.before = fullDate;
this.rangeStatus.after = '';
this.rangeStatus.data = [];
} else {
if (!before) {
this.rangeStatus.before = fullDate
} else {
if (this.dateCompare(this.rangeStatus.before, fullDate)) {
this.rangeStatus.data = this.getDateAll(this.rangeStatus.before, fullDate);
} else {
this.rangeStatus.data = this.getDateAll(fullDate, this.rangeStatus.before);
}
this.rangeStatus.after = this.rangeStatus.data[this.rangeStatus.data.length - 1]
}
}
this._getWeek(fullDate)
}
/**
* 设置多选
*/
setMultiple(fullDate) {
// 非多选不再执行
if (this.mode != 'multiple') return
// 检查是否已经多选
let index = this.multiple.findIndex((item) => {
if (this.dateEqual(fullDate, item)) {
return item
}
});
if (index === -1) {
this.multiple.push(fullDate)
this.setDate(fullDate)
} else {
this.multiple = this.multiple.filter((item, i) => i != index)
}
this._getWeek(fullDate)
}
/**
* 获取每周数据
* @param {Object} dateData
*/
_getWeek(dateData, useWeeks = true) {
const {
year,
month
} = this.getDate(dateData)
let weeks = {}
// 日历数据
let canlender = [];
if (this.foldStatus === 'open') {
// 默认以周末开始
let firstDay = new Date(year, month - 1, 1).getDay();
// 如果以周一开始
if(this.startWeek === 'mon') {
firstDay = firstDay === 0 ? 6 : firstDay - 1;
}
let currentDay = new Date(year, month, 0).getDate()
// 日期数据
let dates = {
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
currentMonthDys: this._currentMonthDays(currentDay, this.getDate(dateData)), // 本月天数
weeks: []
}
// 下月开始几天
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
// 如果仅显示当月
if (this.monthShowCurrentMonth) {
// 日历数据
canlender = canlender.concat(
dates.lastMonthDays.map(item => item = {
empty: true,
lunar: {},
}),
dates.currentMonthDys,
dates.nextMonthDays.map(item => item = {
empty: true,
lunar: {},
}),
);
} else {
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
}
} else {
canlender = this._getWeekDays(dateData)
}
for (let i = 0; i < canlender.length; i++) {
if (i % 7 === 0) {
weeks[parseInt(i / 7)] = new Array(7)
}
weeks[parseInt(i / 7)][i % 7] = canlender[i] || {};
}
if (useWeeks) {
this.canlender = canlender
this.weeks = weeks
}
return weeks
}
//静态方法
// static init(date) {
// if (!this.instance) {
// this.instance = new Calendar(date);
// }
// return this.instance;
// }
}
export default Calendar
\ No newline at end of file
<template>
<view class="wu-calendar" @click.stop>
<view v-if="!insert && show" class="wu-calendar__mask" :class="{'wu-calendar--mask-show': aniMaskShow}"
@click="clean"></view>
<view v-if="insert || show" class="wu-calendar__content"
:class="{'wu-calendar--fixed':!insert, 'wu-calendar--ani-show':aniMaskShow}">
<!-- 弹窗日历取消与确认按钮位置 -->
<slot name="operation" v-if="operationPosition == 'top'">
<view v-if="!insert" class="wu-calendar__header wu-calendar--fixed-top">
<view class="wu-calendar__header-btn-box" @click="cancel">
<text class="wu-calendar__header-text wu-calendar--fixed-width" :style="[{color: cancelColor}]">{{cancelText}}</text>
</view>
<view class="wu-calendar__header-btn-box" @click="confirm">
<text class="wu-calendar__header-text wu-calendar--fixed-width" :style="[{color: confirmColor}]">{{okText}}</text>
</view>
</view>
</slot>
<!-- 日历头部 -->
<slot name="header" :nowDate="nowDate">
<view class="wu-calendar__header">
<!-- 纵向滑动切换展示内容 -->
<template v-if="slideSwitchMode == 'vertical'">
<view class="wu-calendar__header-btn-box vertical">
<view class="wu-calendar__header-btn wu-calendar--top" @click.stop="pre"></view>
<picker mode="date" :value="nowDate.fullDate" fields="month" @change="bindDateChange">
<text
class="wu-calendar__header-text">{{ (nowDate.year||'') + YearText + ( nowDate.month||'') + MonthText }}</text>
</picker>
<view class="wu-calendar__header-btn wu-calendar--bottom" @click.stop="next"></view>
</view>
<text class="wu-calendar__backtoday vertical" @click="backToday">{{todayText}}</text>
</template>
<!-- 横向滑动与无滑动展示内容 -->
<template v-else>
<view class="wu-calendar__header-btn-box horizontal" @click.stop="pre">
<view class="wu-calendar__header-btn wu-calendar--left"></view>
</view>
<picker mode="date" :value="nowDate.fullDate" fields="month" @change="bindDateChange">
<text
class="wu-calendar__header-text">{{ (nowDate.year||'') + YearText + ( nowDate.month||'') + MonthText }}</text>
</picker>
<view class="wu-calendar__header-btn-box horizontal" @click.stop="next">
<view class="wu-calendar__header-btn wu-calendar--right"></view>
</view>
<text class="wu-calendar__backtoday" @click="backToday">{{todayText}}</text>
</template>
</view>
</slot>
<view class="wu-calendar__box">
<view class="wu-calendar__weeks">
<view class="wu-calendar__weeks-day" v-if="startWeek === 'sun'">
<text class="wu-calendar__weeks-day-text">{{SUNText}}</text>
</view>
<view class="wu-calendar__weeks-day">
<text class="wu-calendar__weeks-day-text">{{monText}}</text>
</view>
<view class="wu-calendar__weeks-day">
<text class="wu-calendar__weeks-day-text">{{TUEText}}</text>
</view>
<view class="wu-calendar__weeks-day">
<text class="wu-calendar__weeks-day-text">{{WEDText}}</text>
</view>
<view class="wu-calendar__weeks-day">
<text class="wu-calendar__weeks-day-text">{{THUText}}</text>
</view>
<view class="wu-calendar__weeks-day">
<text class="wu-calendar__weeks-day-text">{{FRIText}}</text>
</view>
<view class="wu-calendar__weeks-day">
<text class="wu-calendar__weeks-day-text">{{SATText}}</text>
</view>
<view class="wu-calendar__weeks-day" v-if="startWeek === 'mon'">
<text class="wu-calendar__weeks-day-text">{{SUNText}}</text>
</view>
</view>
<!-- 滑动切换 -->
<swiper v-if="slideSwitchMode !== 'none'" class="wu-calendar__weeks_container"
:style="[calendarContentStyle]" :duration="500" :vertical="slideSwitchMode == 'vertical'" circular
:current="swiperCurrent" @change="swiperChange" skip-hidden-item-layout>
<!-- 月或周日历 -->
<template v-if="type === 'month' || type === 'week'">
<swiper-item>
<wu-calendar-block :weeks="preWeeks" :calendar="calendar" :selected="selected"
:lunar="lunar" @change="choiceDate" :color="color" :actBadgeColor="actBadgeColor" :startText="startText"
:endText="endText" :month="preWeeksMonth" :FoldStatus="FoldStatus"
:monthShowCurrentMonth="monthShowCurrentMonth" :showMonth="showMonth"
:itemHeight="itemHeight"></wu-calendar-block>
</swiper-item>
<swiper-item>
<wu-calendar-block :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar"
@change="choiceDate" :color="color" :actBadgeColor="actBadgeColor" :startText="startText" :endText="endText"
:monthShowCurrentMonth="monthShowCurrentMonth" :month="weeksMonth"
:FoldStatus="FoldStatus" :showMonth="showMonth"
:itemHeight="itemHeight"></wu-calendar-block>
</swiper-item>
<swiper-item>
<wu-calendar-block :weeks="nextWeeks" :calendar="calendar" :selected="selected"
:lunar="lunar" @change="choiceDate" :color="color" :actBadgeColor="actBadgeColor" :startText="startText"
:endText="endText" :month="nextWeeksMonth" :FoldStatus="FoldStatus"
:monthShowCurrentMonth="monthShowCurrentMonth" :showMonth="showMonth"
:itemHeight="itemHeight"></wu-calendar-block>
</swiper-item>
</template>
</swiper>
<!-- 无滑动切换 -->
<template v-else>
<!-- 月或周日历 -->
<wu-calendar-block class="wu-calendar__weeks_container" :style="[calendarContentStyle]"
:weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"
:color="color" :actBadgeColor="actBadgeColor" :startText="startText" :endText="endText" :month="nowDate.month"
:FoldStatus="FoldStatus" :monthShowCurrentMonth="monthShowCurrentMonth" :showMonth="showMonth"
:itemHeight="itemHeight"></wu-calendar-block>
</template>
</view>
<view class="wu-calendar__fold" v-if="type !== 'year' && Fold" @click="FoldClick">
<wu-icon v-if="FoldStatus == 'open'" name="arrow-up" bold size="18"></wu-icon>
<wu-icon v-else-if="FoldStatus == 'close'" name="arrow-down" bold size="18"></wu-icon>
</view>
<!-- 弹窗日历取消与确认按钮位置 -->
<slot name="operation" v-if="operationPosition == 'bottom'">
<view v-if="!insert" class="wu-calendar__header wu-calendar--fixed-top">
<view class="wu-calendar__header-btn-box" @click="cancel">
<text class="wu-calendar__header-text wu-calendar--fixed-width" :style="[{color: cancelColor}]">{{cancelText}}</text>
</view>
<view class="wu-calendar__header-btn-box" @click="confirm">
<text class="wu-calendar__header-text wu-calendar--fixed-width" :style="[{color: confirmColor}]">{{okText}}</text>
</view>
</view>
</slot>
<wu-safe-bottom v-if="!insert && show"></wu-safe-bottom>
</view>
</view>
</template>
<script>
import Calendar from './util.js';
import mpMixin from '@/uni_modules/wu-ui-tools/libs/mixin/mpMixin.js';
import mixin from '@/uni_modules/wu-ui-tools/libs/mixin/mixin.js';
import props from './props.js';
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import i18nMessages from '../i18n/index.js'
const {
t
} = initVueI18n(i18nMessages)
/**
* Calendar 日历
* @description 日历组件,多模式选择(单日期、多日期、范围日期选择),多日历类型(周、月日历),动态计算滑动。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
* @tutorial https://wuui.cn/zh-CN/components/calendar.html
* @property {String} date 自定义当前时间,默认为今天
* @property {String} type 日历类型(默认为month)
* @value month 月日历
* @value week 周日历
* @property {Boolean} fold 是否支持折叠(默认值 month: true, week: false)
* @property {Boolean} useToday 是否使用默认日期(今天,默认为true)
* @property {String} mode = [single|multiple|range] 日期选择类型(默认single(单日期选择))
* @value single 单日期选择
* @value multiple 多日期选择
* @value range 范围选择
* @property {String} color 主题色(默认#3c9cff)
* @property {Number} itemHeight 日历中每一项日期的高度(默认60),单位px
* @property {String} cancelColor 取消文字的颜色(默认#333333)
* @property {String} confirmColor 确认文字的颜色(默认#333333)
* @property {String} startText mode=range时,第一个日期底部的提示文字
* @property {String} endText mode=range时,最后一个日期底部的提示文字
* @property {String} startWeek 日历以周几开始,默认为周日(sun),`type: month | week`时生效
* @value sun 周日
* @value mon 周一
* @property {Boolean} lunar 显示农历
* @property {String} startDate 日期选择范围-开始日期
* @property {String} endDate 日期选择范围-结束日期
* @property {Boolean} rangeEndRepick 允许范围内重选结束日期(默认false)
* @property {Boolean} rangeSameDay 允许日期选择范围起始日期为同一天(默认false)
* @property {Boolean} rangeHaveDisableTruncation 允许日期选择范围内遇到打点禁用日期进行截断
* @property {Boolean} monthShowCurrentMonth 当月是否仅展示当月数据
* @property {Boolean} insert = [true|false] 插入模式,默认为true
* @value true 插入模式
* @value false 弹窗模式
* @property {String} slideSwitchMode 滑动切换模式,默认为horizontal(横向滑动切换)
* @value horizontal 横向滑动切换
* @value vertical 纵向滑动切换
* @value none 不使用滑动切换
* @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
* @property {Array[Object]} selected 自定义打点,期待格式 [{date: '2023-11-18', info: '¥888', infoColor: '#6ac695', topInfo: '机票打折', topInfoColor: '#6ac695', badge: true, badgePosition: 'top-right', disable: false}, ...]
* @property {Boolean} showMonth 是否选择月份为背景(默认true)
* @property {Boolean} maskClick 是否点击遮罩层关闭(默认false)
* @property {Boolean} disabledChoice 是否禁止点击日历(默认false)
* @property {String} actBadgeColor 当通过 `selected` 属性设置某个日期 `badgeColor`后,如果该日期被选择且主题色与 `badgeColor` 一致时,徽标会显示本颜色
* @property {String} operationPosition 弹窗日历取消和确认按钮的显示位置
* @property {Boolean} confirmFullDate 弹窗日历点击确认时是否需要选择完整日期
* @event {Function} close 日历弹窗点击遮罩层关闭,`insert :false` 时生效
* @event {Function} change 日期改变,`insert :ture` 时生效
* @event {Function} confirm 确认选择,`insert :false` 时生效
* @event {Function} cancel 点击取消按钮,`insert :false` 时生效
* @event {Function} monthSwitch 切换月份时触发
* @event {Function} foldSwitch 切换折叠状态时触发,`type: month | week` & `fold: true` 时生效
* @example <wu-calendar :insert="true":lunar="true" start-date="2022-5-20" end-date="2023-5-20"@change="change" />
*/
export default {
mixins: [mpMixin, mixin, props],
emits: ['close', 'cancel', 'confirm', 'change', 'monthSwitch', 'foldSwitch'],
data() {
return {
show: false,
weeks: [],
preWeeks: [],
nextWeeks: [],
weeksMonth: null,
preWeeksMonth: null,
nextWeeksMonth: null,
calendar: {},
nowDate: '',
aniMaskShow: false,
swiperCurrent: 1,
swiperChangeDirection: '',
pickerDate: '',
Fold: null,
FoldStatus: null,
weekContentStyle: {},
}
},
computed: {
/**
* for i18n
*/
okText() {
return t("wu-calender.ok")
},
cancelText() {
return t("wu-calender.cancel")
},
YearText() {
return t("wu-calender.year")
},
MonthText() {
return t("wu-calender.month")
},
todayText() {
return t("wu-calender.today")
},
monText() {
return t("wu-calender.MON")
},
TUEText() {
return t("wu-calender.TUE")
},
WEDText() {
return t("wu-calender.WED")
},
THUText() {
return t("wu-calender.THU")
},
FRIText() {
return t("wu-calender.FRI")
},
SATText() {
return t("wu-calender.SAT")
},
SUNText() {
return t("wu-calender.SUN")
},
calendarContentStyle() {
return {
height: (this.FoldStatus === 'open' ? this.itemHeight * 6 : this.itemHeight) + 'px'
}
},
getDateType() {
if (this.type === 'year') return this.type
return this.FoldStatus === 'open' ? 'month' : 'week'
}
},
watch: {
date(newVal) {
this.cale.cleanRange();
this.init(newVal)
},
mode(newVal) {
this.cale.cleanRange();
this.cale.resetMode(newVal)
this.init(this.date)
},
startDate(val) {
this.cale.resetSatrtDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.assignmentWeeks();
},
endDate(val) {
this.cale.resetEndDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.assignmentWeeks();
},
monthShowCurrentMonth(val) {
this.cale.resetMonthShowCurrentMonth(val)
this.setDate(this.nowDate.fullDate)
},
rangeEndRepick(val) {
this.cale.resetRangeEndRepick(val)
},
rangeSameDay(val) {
this.cale.resetRangeSameDay(val)
},
rangeHaveDisableTruncation(val) {
this.cale.resetRangeHaveDisableTruncation(val)
this.cale.cleanRange()
this.init(this.date)
},
selected: {
handler(newVal) {
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.assignmentWeeks()
// 找出目前的信息weeks 并 将 this.calendar重新赋值
let nowDateInfo = this.cale.canlender.filter(item => item.fullDate && this.cale.dateEqual(item
.fullDate, this.calendar.fullDate))
if (nowDateInfo.length) this.calendar = nowDateInfo[0]
},
deep: true
},
fold(newVal) {
this.Fold = newVal
},
type(newVal) {
this.initFold();
this.cale.resetFoldStatus(this.FoldStatus);
this.init(this.date)
},
startWeek(newVal) {
this.cale.cleanRange();
this.cale.resetStartWeek(newVal)
this.init(this.date)
}
},
created() {
this.initFold();
this.cale = new Calendar({
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
mode: this.mode,
type: this.type,
startWeek: this.startWeek,
foldStatus: this.FoldStatus,
monthShowCurrentMonth: this.monthShowCurrentMonth,
rangeEndRepick: this.rangeEndRepick,
rangeSameDay: this.rangeSameDay,
rangeHaveDisableTruncation: this.rangeHaveDisableTruncation
})
this.init(this.date)
},
methods: {
// 取消穿透
clean() {
if (this.maskClick) {
this.$emit('close')
this.close()
}
},
bindDateChange(e) {
const value = e.detail.value + '-1'
this.setDate(value)
this.swiperCurrentChangeWeeks();
const {
year,
month
} = this.cale.getDate(value)
this.$emit('monthSwitch', {
year,
month: Number(month),
fullDate: `${year}-${`${month}`.padStart(2, '0')}`
})
},
/**
* 初始化日期显示
* @param {Object} date
*/
init(date) {
let firstDate = this.mode == 'single' ? date : date[0];
// 如果填写默认值
if (date) {
// 当前数据类型
let dateType = Object.prototype.toString.call(date);
// 验证类型
if (this.mode == 'single' && dateType != '[object String]') {
return console.error(`类型错误,mode=${this.mode}时,date=String`)
} else if (this.mode != 'single' && dateType != '[object Array]') {
return console.error(`类型错误,mode=${this.mode}时,date=Array`)
}
// 根据类型默认选中不同的值
if (this.mode == 'multiple') {
this.cale.multiple = date.map(item=>item);
this.cale._getWeek(this.cale.multiple[this.cale.multiple.length - 1]);
} else if (this.mode == 'range') {
date[0] ? this.cale.setRange(date[0]) : ''
date[1] ? this.cale.setRange(date[1]) : ''
}
}
// 如果不填写默认值 且 使用今日作为默认值 并且 还没有在打点中禁用今天的日期
else if (this.useToday && !this.selected.filter(item => item.disable && this.cale.dateEqual(item.date, this
.cale.date.fullDate)).length) {
if (this.mode == 'multiple') {
this.cale.multiple = [this.cale.date.fullDate];
this.cale._getWeek(this.cale.multiple[this.cale.multiple.length - 1]);
} else if (this.mode == 'range') {
this.cale.setRange(this.cale.date.fullDate)
}
}
// 设置日期
this.cale.setDate(firstDate);
// 现在的日期
this.nowDate = this.cale.getInfo(firstDate);
// 设置当前月份
this.weeksMonth = this.nowDate.month;
// 如果不填写默认值 且 使用今日作为默认值
if ((this.useToday && !this.date) || this.date) {
this.calendar = this.nowDate;
}
// 渲染
this.updateWeeks(false, true);
},
/**
* 打开日历弹窗
*/
open() {
this.show = true
// #ifdef H5
if (!this.insert) document.body.style = 'overflow: hidden'
// #endif
this.$nextTick(() => {
setTimeout(() => {
this.aniMaskShow = true
}, 50)
})
},
/**
* 关闭日历弹窗
*/
close() {
this.aniMaskShow = false
this.$nextTick(() => {
setTimeout(() => {
this.show = false
// #ifdef H5
if (!this.insert) document.body.style = 'overflow: visible'
// #endif
// 为弹窗模式且需要清理数据
if (this.clearDate && !this.insert) {
this.reset()
}
}, 300)
})
},
/**
* 重置
*/
reset() {
this.cale.cleanRange()
this.cale.cleanMultiple()
this.swiperCurrent = 1;
this.init(this.date);
},
/**
* 确认按钮
*/
confirm() {
if(this.confirmFullDate) {
if(this.mode == 'single' && !this.calendar.fullDate) {
return uni.showToast({
icon: 'none',
title: '请选择日期',
duration: 600
});
} else if(this.mode == 'multiple' && !this.cale.multiple.length) {
return uni.showToast({
icon: 'none',
title: '请至少选择一个日期',
duration: 600
});
} else if(this.mode == 'range') {
if(!this.cale.rangeStatus.before) {
return uni.showToast({
icon: 'none',
title: '请选择开始日期',
duration: 600
});
} else if(!this.cale.rangeStatus.after) {
return uni.showToast({
icon: 'none',
title: '请选择结束日期',
duration: 600
});
}
}
}
this.setEmit('confirm')
this.close()
},
/**
* 取消按钮
*/
cancel() {
this.$emit('cancel')
this.close()
},
/**
* 变化触发
*/
change() {
if (!this.insert) return
this.setEmit('change')
},
/**
* 选择月份触发
*/
monthSwitch() {
let {
year,
month
} = this.nowDate;
this.$emit('monthSwitch', {
year,
month: Number(month),
fullDate: `${year}-${`${month}`.padStart(2, '0')}`
})
},
/**
* 派发事件
* @param {Object} name
*/
setEmit(name) {
let {
year,
month,
date,
fullDate,
lunar,
extraInfo,
type,
mode
} = this.calendar;
let params = {
range: this.cale.rangeStatus,
multiple: this.cale.multiple,
mode,
type,
year,
month: Number(month),
date,
fulldate: fullDate,
lunar,
extraInfo: extraInfo || {}
}
if (this.type === 'month' || this.type === 'week') {
params.foldStatus = this.FoldStatus
}
this.$emit(name, params)
},
/**
* 选择天触发
* @param {Object} weeks
*/
choiceDate(weeks) {
// 如果为禁用 或者 空数据 或者 禁止点击日期
if (weeks.disable || weeks.empty || this.disabledChoice) return;
this.calendar = weeks;
// 保存操作的日历信息
this.nowDate = this.calendar;
// 设置选择范围
this.cale.setRange(this.calendar.fullDate);
// 设置多选
this.cale.setMultiple(this.calendar.fullDate);
// 如果启用滑动切换 且当前模式为范围选择时则重新计算上月与下月
if (this.slideSwitchMode !== 'none') {
let weekName = '';
switch (this.swiperCurrent) {
case 0:
weekName = 'preWeeks'
if (this.mode == 'range') {
this.weeks = this.cale._getWeek(this.weeks[0].find(item => item.fullDate).fullDate, false)
this.nextWeeks = this.cale._getWeek(this.nextWeeks[0].find(item => item.fullDate).fullDate,
false)
}
break;
case 1:
weekName = 'weeks'
if (this.mode == 'range') {
this.preWeeks = this.cale._getWeek(this.preWeeks[0].find(item => item.fullDate).fullDate,
false)
this.nextWeeks = this.cale._getWeek(this.nextWeeks[0].find(item => item.fullDate).fullDate,
false)
}
break;
case 2:
weekName = 'nextWeeks'
if (this.mode == 'range') {
this.weeks = this.cale._getWeek(this.weeks[0].find(item => item.fullDate).fullDate, false)
this.preWeeks = this.cale._getWeek(this.preWeeks[0].find(item => item.fullDate).fullDate,
false)
}
break;
}
this[weekName] = this.cale.weeks;
} else {
this.weeks = this.cale.weeks;
}
this.change();
},
/**
* 回到今天
*/
backToday() {
// 获取目前的年月
const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
if (this.cale.rangeStatus.before && !this.cale.rangeStatus.after) {
this.cale.rangeStatus.before = '';
}
// 设置日期
this.setDate(this.cale.date.fullDate);
let date = this.nowDate;
this.calendar = date;
// 设置选中的日期
this.cale.setRange(date.fullDate);
// 今天的日期
const todayYearMonth = `${date.year}-${date.month}`
// 如果当前日期 与 今天的日期不符
if (nowYearMonth !== todayYearMonth) {
// 触发月份切换事件
this.monthSwitch()
}
// 设置日期
this.setDate(this.cale.date.fullDate);
// swiperCurrent改变需要改动的weeks
this.swiperCurrentChangeWeeks();
// 改变事件
this.change()
},
/**
* 上个月
*/
pre() {
this.swiperChangeDirection = 'pre'
this.updateWeeks();
},
/**
* 下个月
*/
next() {
this.swiperChangeDirection = 'next'
this.updateWeeks();
},
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.cale.setDate(date)
this.nowDate = this.cale.getInfo(date)
this.assignmentWeeks()
},
/**
* 用来将cale.weeks 赋值到 weeks
*/
assignmentWeeks() {
let weekName = '';
let weekMonthName = '';
switch (this.swiperCurrent) {
case 0:
weekName = 'preWeeks'
weekMonthName = 'preWeeksMonth'
break;
case 1:
weekName = 'weeks'
weekMonthName = 'weeksMonth'
break;
case 2:
weekName = 'nextWeeks'
weekMonthName = 'nextWeeksMonth'
break;
}
this[weekName] = this.cale.weeks;
this[weekMonthName] = this.cale.selectDate.month;
},
/**
* 滑动切换日期
*/
swiperChange(e) {
// 非用户滑动不执行
if (e.detail.source !== 'touch') return;
let curr = e.detail.current;
if (curr - this.swiperCurrent == 1 || curr - this.swiperCurrent == -2) {
this.swiperChangeDirection = 'next'
} else {
this.swiperChangeDirection = 'pre'
}
this.swiperCurrent = curr;
this.updateWeeks();
},
/**
* 更新weeks
* @param {Boolean} isChange 是否使当前的weeks发生变化
*/
updateWeeks(isChange = true, isInt = false) {
let newFullDate = ''
// 是否变动日期信息
if (isChange) {
// 如果目前处于打开状态也就是月日历时,将记录月份改为一号(这样可以在用户切换到任意月份时并折叠自动选中1号)
let fullDate = this.FoldStatus === 'close' ? this.nowDate.fullDate : `${this.nowDate.year}-${this.nowDate.month}-${1}`
newFullDate = this.cale.getDate(fullDate, this.swiperChangeDirection === 'next' ? +1 : -1, this.getDateType).fullDate;
} else {
newFullDate = this.cale.getDate(this.nowDate.fullDate, 0, this.getDateType).fullDate;
}
this.setDate(newFullDate)
this.swiperCurrentChangeWeeks();
if(!isInt) {
this.monthSwitch();
}
},
/**
* swiperCurrent改变需要改动的weeks
*/
swiperCurrentChangeWeeks() {
if (this.slideSwitchMode === 'none') return;
// 防止一次渲染过多数据,造成对nvue的卡顿
this.$nextTick(() => {
let nextDate = this.cale.getDate(this.nowDate.fullDate, +1, this.getDateType);
let nextWeeks = this.cale._getWeek(nextDate.fullDate, false);
let nextWeeksMonth = nextDate.month
let preDate = this.cale.getDate(this.nowDate.fullDate, -1, this.getDateType);
let preWeeks = this.cale._getWeek(preDate.fullDate, false);
let preWeeksMonth = preDate.month
if (this.swiperCurrent == 0) {
this.weeks = nextWeeks;
this.weeksMonth = nextWeeksMonth;
this.nextWeeks = preWeeks;
this.nextWeeksMonth = preWeeksMonth;
} else if (this.swiperCurrent == 1) {
this.nextWeeks = nextWeeks;
this.nextWeeksMonth = nextWeeksMonth;
this.preWeeks = preWeeks;
this.preWeeksMonth = preWeeksMonth;
} else {
this.preWeeks = nextWeeks;
this.preWeeksMonth = nextWeeksMonth;
this.weeks = preWeeks;
this.weeksMonth = preWeeksMonth;
}
})
},
// 点击折叠
FoldClick() {
this.FoldStatus = this.FoldStatus === 'open' ? 'close' : 'open';
this.cale.resetFoldStatus(this.FoldStatus);
// 重置当前weeks
this.setDate(this.nowDate.fullDate);
this.$nextTick(() => {
// 重置左右切换的上下weeks
if (this.slideSwitchMode !== 'none') {
let nextDate = this.cale.getDate(this.nowDate.fullDate, +1, this.getDateType);
let nextWeeks = this.cale._getWeek(nextDate.fullDate, false);
let nextWeeksMonth = nextDate.month;
let preDate = this.cale.getDate(this.nowDate.fullDate, -1, this.getDateType);
let preWeeks = this.cale._getWeek(preDate.fullDate, false);
let preWeeksMonth = preDate.month;
if (this.swiperChangeDirection == 'next') {
if (this.swiperCurrent == 0) {
this.weeks = nextWeeks;
this.weeksMonth = nextWeeksMonth;
this.nextWeeks = preWeeks;
this.nextWeeksMonth = preWeeksMonth;
} else if (this.swiperCurrent == 1) {
this.nextWeeks = nextWeeks;
this.nextWeeksMonth = nextWeeksMonth;
this.preWeeks = preWeeks;
this.preWeeksMonth = preWeeksMonth;
} else {
this.preWeeks = nextWeeks;
this.preWeeksMonth = nextWeeksMonth;
this.weeks = preWeeks;
this.weeksMonth = preWeeksMonth;
}
} else {
if (this.swiperCurrent == 0) {
this.nextWeeks = preWeeks;
this.nextWeeksMonth = preWeeksMonth;
this.weeks = nextWeeks;
this.weeksMonth = nextWeeksMonth;
} else if (this.swiperCurrent == 1) {
this.preWeeks = preWeeks;
this.preWeeksMonth = preWeeksMonth;
this.nextWeeks = nextWeeks;
this.nextWeeksMonth = nextWeeksMonth;
} else {
this.weeks = preWeeks;
this.weeksMonth = preWeeksMonth;
this.preWeeks = nextWeeks;
this.preWeeksMonth = nextWeeksMonth;
}
}
}
})
// fold切换事件
this.$emit('foldSwitch', {
type: this.type,
status: this.FoldStatus
})
},
// 初始化折叠
initFold() {
if (this.type === 'month' || this.type === 'week') {
this.Fold = this.fold === null ? this.type !== 'month' : this.fold;
this.FoldStatus = this.type !== 'month' ? 'close' : 'open';
}
}
}
}
</script>
<style lang="scss" scoped>
$wu-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
$wu-border-color: #EDEDED;
$wu-text-color: #333;
$wu-bg-color-hover: #f1f1f1;
$wu-font-size-base: 32rpx;
$wu-text-color-placeholder: #808080;
$wu-color-subtitle: #555555;
$wu-text-color-grey: #999;
.wu-calendar {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.wu-calendar__mask {
position: fixed;
bottom: 0;
top: 0;
left: 0;
right: 0;
background-color: $wu-bg-color-mask;
transition-property: opacity;
transition-duration: 0.3s;
opacity: 0;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.wu-calendar--mask-show {
opacity: 1
}
.wu-calendar--fixed {
position: fixed;
/* #ifdef APP-NVUE */
bottom: 0;
/* #endif */
left: 0;
right: 0;
transition-property: transform;
transition-duration: 0.3s;
transform: translateY(1080rpx);
/* #ifndef APP-NVUE */
bottom: calc(var(--window-bottom));
z-index: 99;
/* #endif */
}
.wu-calendar--ani-show {
transform: translateY(0);
}
.wu-calendar__content {
background-color: #fff;
}
.wu-calendar__header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
border-bottom-color: $wu-border-color;
border-bottom-style: solid;
border-bottom-width: 2rpx;
}
.wu-calendar--fixed-top {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
height: 90rpx;
flex-direction: row;
justify-content: space-between;
border-top-color: $wu-border-color;
border-top-style: solid;
border-top-width: 2rpx;
}
.wu-calendar--fixed-width {
width: 100rpx;
}
.wu-calendar__backtoday {
position: absolute;
right: 0;
top: 25rpx;
padding: 0 10rpx;
padding-left: 20rpx;
height: 50rpx;
line-height: 50rpx;
font-size: 24rpx;
border-top-left-radius: 50rpx;
border-bottom-left-radius: 50rpx;
color: $wu-text-color;
background-color: $wu-bg-color-hover;
&.vertical {
top: 38rpx;
}
}
.wu-calendar__header-text {
text-align: center;
width: 200rpx;
font-size: $wu-font-size-base;
color: $wu-text-color;
}
.wu-calendar__header-btn-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
.wu-calendar__header-btn {
width: 20rpx;
height: 20rpx;
}
&.horizontal {
width: 100rpx;
height: 100rpx;
}
&.vertical {
flex-direction: column;
padding: 20rpx 0;
}
}
.wu-calendar__header-btn {
border-left-color: $wu-text-color-placeholder;
border-left-style: solid;
border-left-width: 4rpx;
border-top-color: $wu-color-subtitle;
border-top-style: solid;
border-top-width: 4rpx;
}
.wu-calendar--left {
transform: rotate(-45deg);
}
.wu-calendar--right {
transform: rotate(135deg);
}
.wu-calendar--top {
transform: rotate(45deg);
}
.wu-calendar--bottom {
transform: rotate(225deg);
}
.wu-calendar__weeks {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 0 8rpx;
}
.wu-calendar__weeks-day {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
height: 90rpx;
border-bottom-color: #F5F5F5;
border-bottom-style: solid;
border-bottom-width: 2rpx;
}
.wu-calendar__weeks-day-text {
font-size: 28rpx;
}
.wu-calendar__box {
position: relative;
}
.wu-calendar__weeks_container {
transition: height 0.2s linear;
}
.wu-calendar__fold {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
}
</style>
\ No newline at end of file
{
"id": "wu-calendar",
"displayName": "wu-calendar 最全日历,动态滑动切换、多滑动模式、多日历类型、多日期选择模式等,全端兼容,无论平台,一致体验。",
"version": "1.5.2",
"description": "唯一支持动态滑动计算的日历插件,多滑动切换模式(纵、横、不滑动)、多日历类型(周、月)、多日期选择模式(单选、多选、范围)、日历周几(周日、周一)、自定义主题、农历显示等,可以让您纵享丝滑的使用日历",
"keywords": [
"wu-calendar",
"日历",
"多日历类型",
"动态滑动计算",
"wu-ui"
],
"repository": "",
"engines": {
"HBuilderX": "^3.5.5"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"wu-ui-tools",
"wu-icon",
"wu-safe-bottom"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}
\ No newline at end of file
## wu-calendar 最全日历
> **组件名:wu-calendar**
目前插件市场上唯一可以动态滑动计算的日历插件,多滑动切换模式(纵、横向滑动,不滑动)、多日历类型(周、月日历)、多日历选择模式(日期单选、多选、范围选择)、多日历起始周几设置(周日、周一)、自定义主题颜色(副色自动生成)、自定义文案、农历显示等功能,可以让您纵享丝滑的使用日历。
## [查看文档](https://wuui.cn/zh-CN/components/calendar.html)
## [更多组件, 请查看 `wu-ui` 组件库](https://ext.dcloud.net.cn/plugin?name=wu--ui)
(请勿下载插件zip)
<a href="https://ext.dcloud.net.cn/plugin?name=wu--ui">
<img src="https://wuui.cn/intr.png">
</a>
**如使用过程中有任何问题,或者您对wu-ui有一些好的建议。<br>欢迎加入 [wu-ui 交流群](https://wuui.cn/zh-CN/components/qqFeedBack.html)**
\ No newline at end of file
## 1.0.4(2024-05-08)
更新域名
## 1.0.3(2023-08-08)
修复链接引入错误
## 1.0.2(2023-08-07)
修复引入错误
## 1.0.1(2023-08-07)
支持自定义(包括nvue)文字与图片图标
## 1.0.0(2023-08-03)
基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。可自定义颜色、大小。
export default {
'wuicon-level': 'e68f',
'wuicon-download': 'e670',
'wuicon-search': 'e632',
'wuicon-reload': 'e627',
'wuicon-scan': 'e631',
'wuicon-calendar': 'e65c',
'wuicon-bag': 'e647',
'wuicon-checkbox-mark': 'e659',
'wuicon-attach': 'e640',
'wuicon-wifi-off': 'e6cc',
'wuicon-woman': 'e626',
'wuicon-man': 'e675',
'wuicon-chat': 'e656',
'wuicon-chat-fill': 'e63f',
'wuicon-red-packet': 'e6c3',
'wuicon-folder': 'e694',
'wuicon-order': 'e695',
'wuicon-arrow-up-fill': 'e636',
'wuicon-arrow-down-fill': 'e638',
'wuicon-backspace': 'e64d',
'wuicon-photo': 'e60d',
'wuicon-photo-fill': 'e6b4',
'wuicon-lock': 'e69d',
'wuicon-lock-fill': 'e6a6',
'wuicon-lock-open': 'e68d',
'wuicon-lock-opened-fill': 'e6a1',
'wuicon-home': 'e67b',
'wuicon-home-fill': 'e68e',
'wuicon-star': 'e618',
'wuicon-star-fill': 'e61e',
'wuicon-share': 'e629',
'wuicon-share-fill': 'e6bb',
'wuicon-share-square': 'e6c4',
'wuicon-volume': 'e605',
'wuicon-volume-fill': 'e624',
'wuicon-volume-off': 'e6bd',
'wuicon-volume-off-fill': 'e6c8',
'wuicon-trash': 'e623',
'wuicon-trash-fill': 'e6ce',
'wuicon-shopping-cart': 'e6cb',
'wuicon-shopping-cart-fill': 'e630',
'wuicon-question-circle': 'e622',
'wuicon-question-circle-fill': 'e6bc',
'wuicon-plus': 'e625',
'wuicon-plus-circle': 'e603',
'wuicon-plus-circle-fill': 'e611',
'wuicon-tags': 'e621',
'wuicon-tags-fill': 'e613',
'wuicon-pause': 'e61c',
'wuicon-pause-circle': 'e696',
'wuicon-pause-circle-fill': 'e60c',
'wuicon-play-circle': 'e6af',
'wuicon-play-circle-fill': 'e62a',
'wuicon-map': 'e665',
'wuicon-map-fill': 'e6a8',
'wuicon-phone': 'e6ba',
'wuicon-phone-fill': 'e6ac',
'wuicon-list': 'e690',
'wuicon-list-dot': 'e6a9',
'wuicon-info-circle': 'e69f',
'wuicon-info-circle-fill': 'e6a7',
'wuicon-minus': 'e614',
'wuicon-minus-circle': 'e6a5',
'wuicon-mic': 'e66d',
'wuicon-mic-off': 'e691',
'wuicon-grid': 'e68c',
'wuicon-grid-fill': 'e698',
'wuicon-eye': 'e664',
'wuicon-eye-fill': 'e697',
'wuicon-eye-off': 'e69c',
'wuicon-eye-off-outline': 'e688',
'wuicon-file-text': 'e687',
'wuicon-file-text-fill': 'e67f',
'wuicon-edit-pen': 'e65d',
'wuicon-edit-pen-fill': 'e679',
'wuicon-email': 'e673',
'wuicon-email-fill': 'e683',
'wuicon-checkmark': 'e64a',
'wuicon-checkmark-circle': 'e643',
'wuicon-checkmark-circle-fill': 'e668',
'wuicon-clock': 'e66c',
'wuicon-clock-fill': 'e64b',
'wuicon-close': 'e65a',
'wuicon-close-circle': 'e64e',
'wuicon-close-circle-fill': 'e666',
'wuicon-car': 'e64f',
'wuicon-car-fill': 'e648',
'wuicon-bell': 'e651',
'wuicon-bell-fill': 'e604',
'wuicon-play-left': 'e6bf',
'wuicon-play-right': 'e6b3',
'wuicon-play-left-fill': 'e6ae',
'wuicon-play-right-fill': 'e6ad',
'wuicon-skip-back-left': 'e6c5',
'wuicon-skip-forward-right': 'e61f',
'wuicon-setting': 'e602',
'wuicon-setting-fill': 'e6d0',
'wuicon-more-dot-fill': 'e66f',
'wuicon-more-circle': 'e69e',
'wuicon-more-circle-fill': 'e684',
'wuicon-arrow-upward': 'e641',
'wuicon-arrow-downward': 'e634',
'wuicon-arrow-leftward': 'e63b',
'wuicon-arrow-rightward': 'e644',
'wuicon-arrow-up': 'e633',
'wuicon-arrow-down': 'e63e',
'wuicon-arrow-left': 'e646',
'wuicon-arrow-right': 'e63c',
'wuicon-thumb-up': 'e612',
'wuicon-thumb-up-fill': 'e62c',
'wuicon-thumb-down': 'e60a',
'wuicon-thumb-down-fill': 'e628',
'wuicon-coupon': 'e65f',
'wuicon-coupon-fill': 'e64c',
'wuicon-kefu-ermai': 'e660',
'wuicon-server-fill': 'e610',
'wuicon-server-man': 'e601',
'wuicon-warning': 'e6c1',
'wuicon-warning-fill': 'e6c7',
'wuicon-camera': 'e642',
'wuicon-camera-fill': 'e650',
'wuicon-pushpin': 'e6d1',
'wuicon-pushpin-fill': 'e6b6',
'wuicon-heart': 'e6a2',
'wuicon-heart-fill': 'e68b',
'wuicon-account': 'e63a',
'wuicon-account-fill': 'e653',
'wuicon-integral': 'e693',
'wuicon-integral-fill': 'e6b1',
'wuicon-gift': 'e680',
'wuicon-gift-fill': 'e6b0',
'wuicon-empty-data': 'e671',
'wuicon-empty-address': 'e68a',
'wuicon-empty-favor': 'e662',
'wuicon-empty-car': 'e656',
'wuicon-empty-order': 'e66b',
'wuicon-empty-list': 'e671',
'wuicon-empty-search': 'e677',
'wuicon-empty-permission': 'e67c',
'wuicon-empty-news': 'e67d',
'wuicon-empty-history': 'e684',
'wuicon-empty-coupon': 'e69b',
'wuicon-empty-page': 'e60e',
'wuicon-apple-fill': 'e635',
'wuicon-zhifubao-circle-fill': 'e617',
'wuicon-weixin-circle-fill': 'e6cd',
'wuicon-weixin-fill': 'e620',
'wuicon-qq-fill': 'e608',
'wuicon-qq-circle-fill': 'e6b9',
'wuicon-moments': 'e6a0',
'wuicon-moments-circel-fill': 'e6c2',
'wuicon-twitter': 'e607',
'wuicon-twitter-circle-fill': 'e6cf',
}
\ No newline at end of file
export default {
props: {
// 图标类名
name: {
type: String,
default: ''
},
// 图标颜色,可接受主题色
color: {
type: String,
default: '#606266'
},
// 字体大小,单位px
size: {
type: [String, Number],
default: '16px'
},
// 是否显示粗体
bold: {
type: Boolean,
default: false
},
// 点击图标的时候传递事件出去的index(用于区分点击了哪一个)
index: {
type: [String, Number],
default: null
},
// 触摸图标时的类名
hoverClass: {
type: String,
default: ''
},
// 自定义扩展前缀,方便用户扩展自己的图标库
customPrefix: {
type: String,
default: 'wuicon'
},
// 图标右边或者下面的文字
label: {
type: [String, Number],
default: ''
},
// label的位置,只能右边或者下边
labelPos: {
type: String,
default: 'right'
},
// label的大小
labelSize: {
type: [String, Number],
default: '15px'
},
// label的颜色
labelColor: {
type: String,
default: '#606266'
},
// label与图标的距离
space: {
type: [String, Number],
default: '3px'
},
// 图片的mode
imgMode: {
type: String,
default: ''
},
// 用于显示图片小图标时,图片的宽度
width: {
type: [String, Number],
default: ''
},
// 用于显示图片小图标时,图片的高度
height: {
type: [String, Number],
default: ''
},
// 用于解决某些情况下,让图标垂直居中的用途
top: {
type: [String, Number],
default: 0
},
// 是否阻止事件传播
stop: {
type: Boolean,
default: false
},
...uni.$w?.props?.icon
}
}
\ No newline at end of file
<template>
<view class="wu-icon" @tap="clickHandler" :class="['wu-icon--' + labelPos]">
<!-- 这里进行空字符串判断,如果仅仅是v-if="label",可能会出现传递0的时候,结果也无法显示 -->
<text v-if="label !== '' && (labelPos == 'left' || labelPos == 'top')" class="wu-icon__label" :style="labelStyle">{{ label }}</text>
<image class="wu-icon__img" v-if="isImg" :src="name" :mode="imgMode"
:style="[imgStyle, $w.addStyle(customStyle)]"></image>
<text v-else class="wu-icon__icon" :class="uClasses" :style="[iconStyle, $w.addStyle(customStyle)]"
:hover-class="hoverClass">{{icon}}</text>
<!-- 这里进行空字符串判断,如果仅仅是v-if="label",可能会出现传递0的时候,结果也无法显示 -->
<text v-if="label !== '' && (labelPos == 'right' || labelPos == 'bottom')" class="wu-icon__label" :style="labelStyle">{{ label }}</text>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/wu-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/wu-ui-tools/libs/mixin/mixin.js'
// #ifdef APP-NVUE
// nvue通过weex的dom模块引入字体,相关文档地址如下:
// https://weex.apache.org/zh/docs/modules/dom.html#addrule
import iconUrl from './wuicons.ttf';
const domModule = weex.requireModule('dom');
domModule.addRule('fontFace', {
'fontFamily': "wuicon-iconfont",
'src': "url('" + iconUrl + "')"
})
// #endif
// 引入图标名称,已经对应的unicode
import icons from './icons';
import props from './props.js';
/**
* icon 图标
* @description 基于字体的图标集,包含了大多数常见场景的图标。
* @tutorial https://wuui.cn/zh-CN/components/icon.html
* @property {String} name 图标名称,若带有 `/` 或遵循 `base64` 图片格式,会被认为是图片图标,则文字图标相关属性会失效。
* @property {String} color 图标颜色,可接受主题色 (默认 color: #606266 )
* @property {String | Number} size 图标字体大小,单位px/rpx (默认 '16px' )
* @property {Boolean} bold 是否显示粗体 (默认 false )
* @property {String | Number} index 点击图标的时候传递事件出去的index(用于区分点击了哪一个)
* @property {String} hoverClass 图标按下去的样式类,用法同uni的view组件的hoverClass参数,详情见官网
* @property {String} customPrefix 自定义扩展前缀,方便用户扩展自己的图标库 (默认 'wuicon' )
* @property {String | Number} label 图标右侧的label文字
* @property {String} labelPos label相对于图标的位置(默认 'right' )
* @value top 上方
* @value bottom 下方
* @value left 左侧
* @value right 右侧
* @property {String | Number} labelSize label字体大小,单位px (默认 '15px' )
* @property {String} labelColor 图标右侧的label文字颜色 ( 默认 color['wu-content-color'] )
* @property {String | Number} space label与图标的距离,单位px (默认 '3px' )
* @property {String} imgMode image组件的mode,详见:[image](https://uniapp.dcloud.net.cn/component/image.html#image)
* @property {String | Number} width 显示图片小图标时的宽度
* @property {String | Number} height 显示图片小图标时的高度
* @property {String | Number} top 图标在垂直方向上的定位 用于解决某些情况下,让图标垂直居中的用途 (默认 0 )
* @property {Boolean} stop 是否阻止事件传播 (默认 false )
* @property {Object} customStyle icon的样式,对象形式
* @event {Function} click 点击图标时触发
* @event {Function} touchstart 事件触摸时触发
* @example <wu-icon name="photo" color="#2979ff" size="28"></wu-icon>
*/
export default {
name: 'wu-icon',
emits: ['click'],
mixins: [mpMixin, mixin, props],
data() {
return {
colorType: [
'primary',
'success',
'info',
'error',
'warning'
]
}
},
computed: {
uClasses() {
let classes = []
classes.push(this.customPrefix)
classes.push(this.customPrefix + '-' + this.name)
// 主题色,通过类配置
if (this.color && this.colorType.includes(this.color)) classes.push('wu-icon__icon--' + this.color)
// 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别
// 故需将其拆成一个字符串的形式,通过空格隔开各个类名
//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
classes = classes.join(' ')
//#endif
return classes
},
iconStyle() {
let style = {}
style = {
fontSize: this.$w.addUnit(this.size),
lineHeight: this.$w.addUnit(this.size),
fontWeight: this.bold ? 'bold' : 'normal',
// 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中
top: this.$w.addUnit(this.top)
}
// 非主题色值时,才当作颜色值
if (this.color && !this.colorType.includes(this.color)) style.color = this.color
return style
},
// 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
isImg() {
const isBase64 = this.name.indexOf('data:') > -1 && this.name.indexOf('base64') > -1;
return this.name.indexOf('/') !== -1 || isBase64;
},
imgStyle() {
let style = {}
// 如果设置width和height属性,则优先使用,否则使用size属性
style.width = this.width ? this.$w.addUnit(this.width) : this.$w.addUnit(this.size)
style.height = this.height ? this.$w.addUnit(this.height) : this.$w.addUnit(this.size)
return style
},
// 通过图标名,查找对应的图标
icon() {
// 如果内置的图标中找不到对应的图标,就直接返回name值,因为用户可能传入的是unicode代码
const code = icons['wuicon-' + this.name];
if (['wuicon'].indexOf(this.customPrefix) > -1) {
return code ? unescape(`%u${code}`) : this.name;
} else {
// #ifndef APP-NVUE
return ''
// #endif
// #ifdef APP-NVUE
return unescape(`%u${this.name}`)
// #endif
}
},
// label样式
labelStyle() {
let style = {
color: this.labelColor,
fontSize: this.$w.addUnit(this.labelSize),
marginLeft: this.labelPos == 'right' ? this.$w.addUnit(this.space) : 0,
marginTop: this.labelPos == 'bottom' ? this.$w.addUnit(this.space) : 0,
marginRight: this.labelPos == 'left' ? this.$w.addUnit(this.space) : 0,
marginBottom: this.labelPos == 'top' ? this.$w.addUnit(this.space) : 0
};
return style
}
},
methods: {
clickHandler(e) {
this.$emit('click', this.index)
// 是否阻止事件冒泡
this.stop && this.preventEvent(e)
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/wu-ui-tools/libs/css/components.scss';
@import '@/uni_modules/wu-ui-tools/libs/css/color.scss';
// 变量定义
$wu-icon-primary: $wu-primary !default;
$wu-icon-success: $wu-success !default;
$wu-icon-info: $wu-info !default;
$wu-icon-warning: $wu-warning !default;
$wu-icon-error: $wu-error !default;
$wu-icon-label-line-height: 1 !default;
/* #ifndef APP-NVUE */
// nvue下加载字体
@font-face {
font-family: 'wuicon-iconfont';
src: url('./wuicons.ttf') format('truetype');
}
/* #endif */
.wu-icon {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
&--left, &--right {
flex-direction: row;
}
&--top, &--bottom {
flex-direction: column;
}
&__icon {
font-family: wuicon-iconfont;
position: relative;
@include flex;
align-items: center;
&--primary {
color: $wu-icon-primary;
}
&--success {
color: $wu-icon-success;
}
&--error {
color: $wu-icon-error;
}
&--warning {
color: $wu-icon-warning;
}
&--info {
color: $wu-icon-info;
}
}
&__img {
/* #ifndef APP-NVUE */
height: auto;
will-change: transform;
/* #endif */
}
&__label {
/* #ifndef APP-NVUE */
line-height: $wu-icon-label-line-height;
/* #endif */
}
}
</style>
\ No newline at end of file
{
"id": "wu-icon",
"displayName": "wu-icon 图标 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.4",
"description": "基于字体的图标集,包含了大多数常见场景的图标,支持自定义,支持自定义图片图标等。可自定义颜色、大小。",
"keywords": [
"wu-ui",
"图标",
"wu-icon",
"文字图标"
],
"repository": "",
"engines": {
"HBuilderX": "^3.4.15"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"wu-ui-tools"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}
\ No newline at end of file
## wu-icon 图标库
> **组件名:wu-icon**
基于字体的图标集,包含了大多数常见场景的图标,支持自定义(包括nvue)文字与图片图标等。
## <a href="https://wuui.cn/zh-CN/components/icon" target="_blank">查看文档</a>
## [更多插件,请关注wu-ui组件库](https://ext.dcloud.net.cn/plugin?name=wuui) <small>(请不要 下载插件ZIP)</small>
**如使用过程中有任何问题,或者您对wu-ui有一些好的建议。<br>欢迎加入 [wu-ui 交流群](https://wuui.cn/zh-CN/components/qqFeedBack.html)**
\ No newline at end of file
## 1.0.2(2024-05-08)
更新域名
## 1.0.1(2023-09-11)
优化底部安全距离计算方法
## 1.0.0(2023-09-01)
主要是针对IPhone X等一些底部带指示条的机型,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行底部安全区适配。
export default {
props: {
...uni.$w?.props?.safeBottom
}
}
<template>
<view
class="wu-safe-bottom"
:style="[style]"
>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/wu-ui-tools/libs/mixin/mpMixin.js';
import mixin from '@/uni_modules/wu-ui-tools/libs/mixin/mixin.js';
import props from './props.js';
/**
* SafeBottom 底部安全区
* @description 这个适配,主要是针对IPhone X等一些底部带指示条的机型,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行底部安全区适配。
* @tutorial https://wuui.cn/zh-CN/components/safeAreaInset.html
* @property {type} prop_name
* @property {Object} customStyle 定义需要用到的外部样式
*
* @event {Function()}
* @example <wu-status-bar></wu-status-bar>
*/
export default {
name: "wu-safe-bottom",
mixins: [mpMixin, mixin, props],
data() {
return {
safeAreaBottomHeight: 0,
isNvue: false,
};
},
computed: {
style() {
const {
windowWidth,
windowHeight,
windowTop,
safeArea,
screenHeight,
safeAreaInsets
} = this.$w.sys();
const style = {};
// #ifdef MP-WEIXIN
style.height = this.$w.addUnit(screenHeight - safeArea.bottom, 'px');
// #endif
// #ifndef MP-WEIXIN
style.height = this.$w.addUnit(safeAreaInsets.bottom, 'px');
// #endif
return this.$w.deepMerge(style, this.$w.addStyle(this.customStyle));
},
},
};
</script>
<style lang="scss" scoped>
.wu-safe-bottom {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
}
</style>
{
"id": "wu-safe-bottom",
"displayName": "wu-safe-bottom底部安全区 全端兼容 无论平台 一致体验",
"version": "1.0.2",
"description": "针对一些底部带指示条的机型,操作区域与页面底部重合,容易误操作,因此本插件对这些机型进行底部安全区适配",
"keywords": [
"wu-ui",
"wuui",
"wu-safe-bottom",
"safe-bottom",
"底部安全区"
],
"repository": "",
"engines": {
"HBuilderX": "^3.4.15"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"wu-ui-tools"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}
\ No newline at end of file
## wu-safe-bottom 底部安全区
> **组件名:wu-safe-bottom**
这个适配,主要是针对IPhone X等一些底部带指示条的机型,指示条的操作区域与页面底部存在重合,容易导致用户误操作,因此我们需要针对这些机型进行底部安全区适配。
## [查看文档](https://wuui.cn/zh-CN/components/safeAreaInset.html)
## [更多组件, 请查看 `wu-ui` 组件库](https://ext.dcloud.net.cn/plugin?name=wu--ui)
(请勿下载插件zip)
<a href="https://ext.dcloud.net.cn/plugin?name=wu--ui">
<img src="https://wuui.cn/intr.png">
</a>
**如使用过程中有任何问题,或者您对wu-ui有一些好的建议。<br>欢迎加入 [wu-ui 交流群](https://wuui.cn/zh-CN/components/qqFeedBack.html)**
\ No newline at end of file
## 1.1.0(2023-09-13)
更新版本
## 1.0.9(2023-09-08)
修复Color方法引入路径错误
## 1.0.8(2023-09-05)
Color不在使用npm包,改为本地方法
## 1.0.7(2023-09-03)
修复引入错误
## 1.0.6(2023-09-03)
发布1.0.6版本
## 1.0.5(2023-08-30)
修复api部分未导入
## 1.0.4(2023-08-21)
修复Color API引入错误
## 1.0.3(2023-08-18)
新增颜色API,支持任意颜色格式转换,颜色亮度调节、颜色饱和度调节、亮度获取、颜色是否深/亮等
## 1.0.2(2023-08-16)
mixin交互节点信息根边距设置
## 1.0.1(2023-08-16)
mixin更新节点交互信息查询
## 1.0.0(2023-08-01)
wu-ui-tools 工具库 全面兼容小程序、nvue、vue2、vue3等多端
<template>
</template>
<script>
</script>
<style>
</style>
// 全局挂载引入http相关请求拦截插件
import Request from './libs/luch-request'
// 引入全局mixin
import mixin from './libs/mixin/mixin.js'
// 小程序特有的mixin
import mpMixin from './libs/mixin/mpMixin.js'
// #ifdef MP
import mpShare from './libs/mixin/mpShare.js'
// #endif
// 路由封装
import route from './libs/util/route.js'
// 公共工具函数
import * as index from './libs/function/index.js'
// 防抖方法
import debounce from './libs/function/debounce.js'
// 节流方法
import throttle from './libs/function/throttle.js'
// 规则检验
import * as test from './libs/function/test.js'
// 配置信息
import config from './libs/config/config.js'
// 平台
import platform from './libs/function/platform'
import Color from './libs/function/color/index.js'
const $w = {
...index,
route,
config,
test,
throttle,
date: index.timeFormat, // 另名date
Color,
http: new Request(),
debounce,
throttle,
platform,
mixin,
mpMixin
}
uni.$w = $w;
const install = (Vue,options={}) => {
// #ifndef APP-NVUE
Vue.mixin(mixin);
// #ifdef MP
if(options.mpShare){
Vue.mixin(mpShare);
}
// #endif
// #endif
// #ifdef VUE2
// 时间格式化,同时两个名称,date和timeFormat
Vue.filter('timeFormat', (timestamp, format) => uni.$w.timeFormat(timestamp, format));
Vue.filter('date', (timestamp, format) => uni.$w.timeFormat(timestamp, format));
// 将多久以前的方法,注入到全局过滤器
Vue.filter('timeFrom', (timestamp, format) => uni.$w.timeFrom(timestamp, format));
// 同时挂载到uni和Vue.prototype中
// #ifndef APP-NVUE
// 只有vue,挂载到Vue.prototype才有意义,因为nvue中全局Vue.prototype和Vue.mixin是无效的
Vue.prototype.$w = $w;
// #endif
// #endif
// #ifdef VUE3
Vue.config.globalProperties.$w = $w;
// #endif
}
export default {
install
}
\ No newline at end of file
// 引入公共基础类
@import "./libs/css/common.scss";
// 非nvue的样式
/* #ifndef APP-NVUE */
@import "./libs/css/vue.scss";
/* #endif */
\ No newline at end of file
// 此版本发布于2023-09-13
const version = '1.0.9'
// 开发环境才提示,生产环境不会提示
if (process.env.NODE_ENV === 'development') {
console.log(`\n %c wuui V${version} https://wuui.geeks.ink/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0; border-radius: 5px;');
}
export default {
v: version,
version,
// 主题名称
type: [
'primary',
'success',
'info',
'error',
'warning'
],
// 颜色部分,本来可以通过scss的:export导出供js使用,但是奈何nvue不支持
color: {
'wu-primary': '#2979ff',
'wu-warning': '#ff9900',
'wu-success': '#19be6b',
'wu-error': '#fa3534',
'wu-info': '#909399',
'wu-main-color': '#303133',
'wu-content-color': '#606266',
'wu-tips-color': '#909399',
'wu-light-color': '#c0c4cc'
},
// 默认单位,可以通过配置为rpx,那么在用于传入组件大小参数为数值时,就默认为rpx
unit: 'px'
}
$wu-main-color: #303133 !default;
$wu-content-color: #606266 !default;
$wu-tips-color: #909193 !default;
$wu-light-color: #c0c4cc !default;
$wu-border-color: #dadbde !default;
$wu-bg-color: #f3f4f6 !default;
$wu-disabled-color: #c8c9cc !default;
$wu-primary: #3c9cff !default;
$wu-primary-dark: #398ade !default;
$wu-primary-disabled: #9acafc !default;
$wu-primary-light: #ecf5ff !default;
$wu-warning: #f9ae3d !default;
$wu-warning-dark: #f1a532 !default;
$wu-warning-disabled: #f9d39b !default;
$wu-warning-light: #fdf6ec !default;
$wu-success: #5ac725 !default;
$wu-success-dark: #53c21d !default;
$wu-success-disabled: #a9e08f !default;
$wu-success-light: #f5fff0;
$wu-error: #f56c6c !default;
$wu-error-dark: #e45656 !default;
$wu-error-disabled: #f7b2b2 !default;
$wu-error-light: #fef0f0 !default;
$wu-info: #909399 !default;
$wu-info-dark: #767a82 !default;
$wu-info-disabled: #c4c6c9 !default;
$wu-info-light: #f4f4f5 !default;
// 超出行数,自动显示行尾省略号,最多5行
// 来自wuui的温馨提示:当您在控制台看到此报错,说明需要在App.vue的style标签加上【lang="scss"】
@for $i from 1 through 5 {
.wu-line-#{$i} {
/* #ifdef APP-NVUE */
// nvue下,可以直接使用lines属性,这是weex特有样式
lines: $i;
text-overflow: ellipsis;
overflow: hidden;
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
// vue下,单行和多行显示省略号需要单独处理
@if $i == '1' {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} @else {
display: -webkit-box!important;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: $i;
-webkit-box-orient: vertical!important;
}
/* #endif */
}
}
$wu-bordercolor: #dadbde;
@if variable-exists(wu-border-color) {
$wu-bordercolor: $wu-border-color;
}
// 此处加上!important并非随意乱用,而是因为目前*.nvue页面编译到H5时,
// App.vue的样式会被uni-app的view元素的自带border属性覆盖,导致无效
// 综上,这是uni-app的缺陷导致我们为了多端兼容,而必须要加上!important
// 移动端兼容性较好,直接使用0.5px去实现细边框,不使用伪元素形式实现
.wu-border {
border-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-style: solid;
}
.wu-border-top {
border-top-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-top-style: solid;
}
.wu-border-left {
border-left-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-left-style: solid;
}
.wu-border-right {
border-right-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-right-style: solid;
}
.wu-border-bottom {
border-bottom-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-bottom-style: solid;
}
.wu-border-top-bottom {
border-top-width: 0.5px!important;
border-bottom-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-top-style: solid;
border-bottom-style: solid;
}
// 去除button的所有默认样式,让其表现跟普通的view、text元素一样
.wu-reset-button {
padding: 0;
background-color: transparent;
/* #ifndef APP-PLUS */
font-size: inherit;
line-height: inherit;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
/* #ifndef APP-NVUE */
.wu-reset-button::after {
border: none;
}
/* #endif */
.wu-hover-class {
opacity: 0.7;
}
@mixin flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: $direction;
}
/* #ifndef APP-NVUE */
// 由于wuui是基于nvue环境进行开发的,此环境中普通元素默认为flex-direction: column;
// 所以在非nvue中,需要对元素进行重置为flex-direction: column; 否则可能会表现异常
$wuui-nvue-style: true !default;
@if $wuui-nvue-style == true {
view, scroll-view, swiper-item {
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
align-items: stretch;
align-content: flex-start;
}
}
/* #endif */
// 超出行数,自动显示行尾省略号,最多5行
// 来自uvui的温馨提示:当您在控制台看到此报错,说明需要在App.vue的style标签加上【lang="scss"】
@if variable-exists(show-lines) {
@for $i from 1 through 5 {
.wu-line-#{$i} {
/* #ifdef APP-NVUE */
// nvue下,可以直接使用lines属性,这是weex特有样式
lines: $i;
text-overflow: ellipsis;
overflow: hidden;
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
// vue下,单行和多行显示省略号需要单独处理
@if $i == '1' {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} @else {
display: -webkit-box!important;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: $i;
-webkit-box-orient: vertical!important;
}
/* #endif */
}
}
}
@if variable-exists(show-border) {
$wu-bordercolor: #dadbde;
@if variable-exists(wu-border-color) {
$wu-bordercolor: $wu-border-color;
}
// 此处加上!important并非随意乱用,而是因为目前*.nvue页面编译到H5时,
// App.vue的样式会被uni-app的view元素的自带border属性覆盖,导致无效
// 综上,这是uni-app的缺陷导致我们为了多端兼容,而必须要加上!important
// 移动端兼容性较好,直接使用0.5px去实现细边框,不使用伪元素形式实现
@if variable-exists(show-border-surround) {
.wu-border {
border-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-style: solid;
}
}
@if variable-exists(show-border-top) {
.wu-border-top {
border-top-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-top-style: solid;
}
}
@if variable-exists(show-border-left) {
.wu-border-left {
border-left-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-left-style: solid;
}
}
@if variable-exists(show-border-right) {
.wu-border-right {
border-right-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-right-style: solid;
}
}
@if variable-exists(show-border-bottom) {
.wu-border-bottom {
border-bottom-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-bottom-style: solid;
}
}
@if variable-exists(show-border-top-bottom) {
.wu-border-top-bottom {
border-top-width: 0.5px!important;
border-bottom-width: 0.5px!important;
border-color: $wu-bordercolor!important;
border-top-style: solid;
border-bottom-style: solid;
}
}
}
@if variable-exists(show-reset-button) {
// 去除button的所有默认样式,让其表现跟普通的view、text元素一样
.wu-reset-button {
padding: 0;
background-color: transparent;
/* #ifndef APP-PLUS */
font-size: inherit;
line-height: inherit;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
/* #ifndef APP-NVUE */
.wu-reset-button::after {
border: none;
}
/* #endif */
}
@if variable-exists(show-hover) {
.wu-hover-class {
opacity: 0.7;
}
}
// 历遍生成4个方向的底部安全区
@each $d in top, right, bottom, left {
.wu-safe-area-inset-#{$d} {
padding-#{$d}: 0;
padding-#{$d}: constant(safe-area-inset-#{$d});
padding-#{$d}: env(safe-area-inset-#{$d});
}
}
//提升H5端uni.toast()的层级,避免被wuui的modal等遮盖
/* #ifdef H5 */
uni-toast {
z-index: 10090;
}
uni-toast .uni-toast {
z-index: 10090;
}
/* #endif */
// 隐藏scroll-view的滚动条
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
$wuui-nvue-style: true !default;
@if $wuui-nvue-style == false {
view, scroll-view, swiper-item {
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
align-items: stretch;
align-content: flex-start;
}
}
# 1.0.0 - 2016-01-07
- Removed: unused speed test
- Added: Automatic routing between previously unsupported conversions
([#27](https://github.com/Qix-/color-convert/pull/27))
- Removed: `xxx2xxx()` and `xxx2xxxRaw()` functions
([#27](https://github.com/Qix-/color-convert/pull/27))
- Removed: `convert()` class
([#27](https://github.com/Qix-/color-convert/pull/27))
- Changed: all functions to lookup dictionary
([#27](https://github.com/Qix-/color-convert/pull/27))
- Changed: `ansi` to `ansi256`
([#27](https://github.com/Qix-/color-convert/pull/27))
- Fixed: argument grouping for functions requiring only one argument
([#27](https://github.com/Qix-/color-convert/pull/27))
# 0.6.0 - 2015-07-23
- Added: methods to handle
[ANSI](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) 16/256 colors:
- rgb2ansi16
- rgb2ansi
- hsl2ansi16
- hsl2ansi
- hsv2ansi16
- hsv2ansi
- hwb2ansi16
- hwb2ansi
- cmyk2ansi16
- cmyk2ansi
- keyword2ansi16
- keyword2ansi
- ansi162rgb
- ansi162hsl
- ansi162hsv
- ansi162hwb
- ansi162cmyk
- ansi162keyword
- ansi2rgb
- ansi2hsl
- ansi2hsv
- ansi2hwb
- ansi2cmyk
- ansi2keyword
([#18](https://github.com/harthur/color-convert/pull/18))
# 0.5.3 - 2015-06-02
- Fixed: hsl2hsv does not return `NaN` anymore when using `[0,0,0]`
([#15](https://github.com/harthur/color-convert/issues/15))
---
Check out commit logs for older releases
Copyright (c) 2011-2016 Heather Arthur <fayearthur@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# color-convert
[![Build Status](https://travis-ci.org/Qix-/color-convert.svg?branch=master)](https://travis-ci.org/Qix-/color-convert)
Color-convert is a color conversion library for JavaScript and node.
It converts all ways between `rgb`, `hsl`, `hsv`, `hwb`, `cmyk`, `ansi`, `ansi16`, `hex` strings, and CSS `keyword`s (will round to closest):
```js
var convert = require('color-convert');
convert.rgb.hsl(140, 200, 100); // [96, 48, 59]
convert.keyword.rgb('blue'); // [0, 0, 255]
var rgbChannels = convert.rgb.channels; // 3
var cmykChannels = convert.cmyk.channels; // 4
var ansiChannels = convert.ansi16.channels; // 1
```
# Install
```console
$ npm install color-convert
```
# API
Simply get the property of the _from_ and _to_ conversion that you're looking for.
All functions have a rounded and unrounded variant. By default, return values are rounded. To get the unrounded (raw) results, simply tack on `.raw` to the function.
All 'from' functions have a hidden property called `.channels` that indicates the number of channels the function expects (not including alpha).
```js
var convert = require('color-convert');
// Hex to LAB
convert.hex.lab('DEADBF'); // [ 76, 21, -2 ]
convert.hex.lab.raw('DEADBF'); // [ 75.56213190997677, 20.653827952644754, -2.290532499330533 ]
// RGB to CMYK
convert.rgb.cmyk(167, 255, 4); // [ 35, 0, 98, 0 ]
convert.rgb.cmyk.raw(167, 255, 4); // [ 34.509803921568626, 0, 98.43137254901961, 0 ]
```
### Arrays
All functions that accept multiple arguments also support passing an array.
Note that this does **not** apply to functions that convert from a color that only requires one value (e.g. `keyword`, `ansi256`, `hex`, etc.)
```js
var convert = require('color-convert');
convert.rgb.hex(123, 45, 67); // '7B2D43'
convert.rgb.hex([123, 45, 67]); // '7B2D43'
```
## Routing
Conversions that don't have an _explicitly_ defined conversion (in [conversions.js](conversions.js)), but can be converted by means of sub-conversions (e.g. XYZ -> **RGB** -> CMYK), are automatically routed together. This allows just about any color model supported by `color-convert` to be converted to any other model, so long as a sub-conversion path exists. This is also true for conversions requiring more than one step in between (e.g. LCH -> **LAB** -> **XYZ** -> **RGB** -> Hex).
Keep in mind that extensive conversions _may_ result in a loss of precision, and exist only to be complete. For a list of "direct" (single-step) conversions, see [conversions.js](conversions.js).
# Contribute
If there is a new model you would like to support, or want to add a direct conversion between two existing models, please send us a pull request.
# License
Copyright &copy; 2011-2016, Heather Arthur and Josh Junon. Licensed under the [MIT License](LICENSE).
/* MIT license */
/* eslint-disable no-mixed-operators */
import cssKeywords from '../color-name';
// NOTE: conversions should only return primitive values (i.e. arrays, or
// values that give correct `typeof` results).
// do not use box values types (i.e. Number(), String(), etc.)
const reverseKeywords = {};
for (const key of Object.keys(cssKeywords)) {
reverseKeywords[cssKeywords[key]] = key;
}
const convert = {
rgb: {channels: 3, labels: 'rgb'},
hsl: {channels: 3, labels: 'hsl'},
hsv: {channels: 3, labels: 'hsv'},
hwb: {channels: 3, labels: 'hwb'},
cmyk: {channels: 4, labels: 'cmyk'},
xyz: {channels: 3, labels: 'xyz'},
lab: {channels: 3, labels: 'lab'},
lch: {channels: 3, labels: 'lch'},
hex: {channels: 1, labels: ['hex']},
keyword: {channels: 1, labels: ['keyword']},
ansi16: {channels: 1, labels: ['ansi16']},
ansi256: {channels: 1, labels: ['ansi256']},
hcg: {channels: 3, labels: ['h', 'c', 'g']},
apple: {channels: 3, labels: ['r16', 'g16', 'b16']},
gray: {channels: 1, labels: ['gray']}
};
export default convert;
// Hide .channels and .labels properties
for (const model of Object.keys(convert)) {
if (!('channels' in convert[model])) {
throw new Error('missing channels property: ' + model);
}
if (!('labels' in convert[model])) {
throw new Error('missing channel labels property: ' + model);
}
if (convert[model].labels.length !== convert[model].channels) {
throw new Error('channel and label counts mismatch: ' + model);
}
const {channels, labels} = convert[model];
delete convert[model].channels;
delete convert[model].labels;
Object.defineProperty(convert[model], 'channels', {value: channels});
Object.defineProperty(convert[model], 'labels', {value: labels});
}
convert.rgb.hsl = function (rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const delta = max - min;
let h;
let s;
if (max === min) {
h = 0;
} else if (r === max) {
h = (g - b) / delta;
} else if (g === max) {
h = 2 + (b - r) / delta;
} else if (b === max) {
h = 4 + (r - g) / delta;
}
h = Math.min(h * 60, 360);
if (h < 0) {
h += 360;
}
const l = (min + max) / 2;
if (max === min) {
s = 0;
} else if (l <= 0.5) {
s = delta / (max + min);
} else {
s = delta / (2 - max - min);
}
return [h, s * 100, l * 100];
};
convert.rgb.hsv = function (rgb) {
let rdif;
let gdif;
let bdif;
let h;
let s;
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const v = Math.max(r, g, b);
const diff = v - Math.min(r, g, b);
const diffc = function (c) {
return (v - c) / 6 / diff + 1 / 2;
};
if (diff === 0) {
h = 0;
s = 0;
} else {
s = diff / v;
rdif = diffc(r);
gdif = diffc(g);
bdif = diffc(b);
if (r === v) {
h = bdif - gdif;
} else if (g === v) {
h = (1 / 3) + rdif - bdif;
} else if (b === v) {
h = (2 / 3) + gdif - rdif;
}
if (h < 0) {
h += 1;
} else if (h > 1) {
h -= 1;
}
}
return [
h * 360,
s * 100,
v * 100
];
};
convert.rgb.hwb = function (rgb) {
const r = rgb[0];
const g = rgb[1];
let b = rgb[2];
const h = convert.rgb.hsl(rgb)[0];
const w = 1 / 255 * Math.min(r, Math.min(g, b));
b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
return [h, w * 100, b * 100];
};
convert.rgb.cmyk = function (rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const k = Math.min(1 - r, 1 - g, 1 - b);
const c = (1 - r - k) / (1 - k) || 0;
const m = (1 - g - k) / (1 - k) || 0;
const y = (1 - b - k) / (1 - k) || 0;
return [c * 100, m * 100, y * 100, k * 100];
};
function comparativeDistance(x, y) {
/*
See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance
*/
return (
((x[0] - y[0]) ** 2) +
((x[1] - y[1]) ** 2) +
((x[2] - y[2]) ** 2)
);
}
convert.rgb.keyword = function (rgb) {
const reversed = reverseKeywords[rgb];
if (reversed) {
return reversed;
}
let currentClosestDistance = Infinity;
let currentClosestKeyword;
for (const keyword of Object.keys(cssKeywords)) {
const value = cssKeywords[keyword];
// Compute comparative distance
const distance = comparativeDistance(rgb, value);
// Check if its less, if so set as closest
if (distance < currentClosestDistance) {
currentClosestDistance = distance;
currentClosestKeyword = keyword;
}
}
return currentClosestKeyword;
};
convert.keyword.rgb = function (keyword) {
return cssKeywords[keyword];
};
convert.rgb.xyz = function (rgb) {
let r = rgb[0] / 255;
let g = rgb[1] / 255;
let b = rgb[2] / 255;
// Assume sRGB
r = r > 0.04045 ? (((r + 0.055) / 1.055) ** 2.4) : (r / 12.92);
g = g > 0.04045 ? (((g + 0.055) / 1.055) ** 2.4) : (g / 12.92);
b = b > 0.04045 ? (((b + 0.055) / 1.055) ** 2.4) : (b / 12.92);
const x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
const y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
const z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
return [x * 100, y * 100, z * 100];
};
convert.rgb.lab = function (rgb) {
const xyz = convert.rgb.xyz(rgb);
let x = xyz[0];
let y = xyz[1];
let z = xyz[2];
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116);
y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116);
z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116);
const l = (116 * y) - 16;
const a = 500 * (x - y);
const b = 200 * (y - z);
return [l, a, b];
};
convert.hsl.rgb = function (hsl) {
const h = hsl[0] / 360;
const s = hsl[1] / 100;
const l = hsl[2] / 100;
let t2;
let t3;
let val;
if (s === 0) {
val = l * 255;
return [val, val, val];
}
if (l < 0.5) {
t2 = l * (1 + s);
} else {
t2 = l + s - l * s;
}
const t1 = 2 * l - t2;
const rgb = [0, 0, 0];
for (let i = 0; i < 3; i++) {
t3 = h + 1 / 3 * -(i - 1);
if (t3 < 0) {
t3++;
}
if (t3 > 1) {
t3--;
}
if (6 * t3 < 1) {
val = t1 + (t2 - t1) * 6 * t3;
} else if (2 * t3 < 1) {
val = t2;
} else if (3 * t3 < 2) {
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
} else {
val = t1;
}
rgb[i] = val * 255;
}
return rgb;
};
convert.hsl.hsv = function (hsl) {
const h = hsl[0];
let s = hsl[1] / 100;
let l = hsl[2] / 100;
let smin = s;
const lmin = Math.max(l, 0.01);
l *= 2;
s *= (l <= 1) ? l : 2 - l;
smin *= lmin <= 1 ? lmin : 2 - lmin;
const v = (l + s) / 2;
const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);
return [h, sv * 100, v * 100];
};
convert.hsv.rgb = function (hsv) {
const h = hsv[0] / 60;
const s = hsv[1] / 100;
let v = hsv[2] / 100;
const hi = Math.floor(h) % 6;
const f = h - Math.floor(h);
const p = 255 * v * (1 - s);
const q = 255 * v * (1 - (s * f));
const t = 255 * v * (1 - (s * (1 - f)));
v *= 255;
switch (hi) {
case 0:
return [v, t, p];
case 1:
return [q, v, p];
case 2:
return [p, v, t];
case 3:
return [p, q, v];
case 4:
return [t, p, v];
case 5:
return [v, p, q];
}
};
convert.hsv.hsl = function (hsv) {
const h = hsv[0];
const s = hsv[1] / 100;
const v = hsv[2] / 100;
const vmin = Math.max(v, 0.01);
let sl;
let l;
l = (2 - s) * v;
const lmin = (2 - s) * vmin;
sl = s * vmin;
sl /= (lmin <= 1) ? lmin : 2 - lmin;
sl = sl || 0;
l /= 2;
return [h, sl * 100, l * 100];
};
// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
convert.hwb.rgb = function (hwb) {
const h = hwb[0] / 360;
let wh = hwb[1] / 100;
let bl = hwb[2] / 100;
const ratio = wh + bl;
let f;
// Wh + bl cant be > 1
if (ratio > 1) {
wh /= ratio;
bl /= ratio;
}
const i = Math.floor(6 * h);
const v = 1 - bl;
f = 6 * h - i;
if ((i & 0x01) !== 0) {
f = 1 - f;
}
const n = wh + f * (v - wh); // Linear interpolation
let r;
let g;
let b;
/* eslint-disable max-statements-per-line,no-multi-spaces */
switch (i) {
default:
case 6:
case 0: r = v; g = n; b = wh; break;
case 1: r = n; g = v; b = wh; break;
case 2: r = wh; g = v; b = n; break;
case 3: r = wh; g = n; b = v; break;
case 4: r = n; g = wh; b = v; break;
case 5: r = v; g = wh; b = n; break;
}
/* eslint-enable max-statements-per-line,no-multi-spaces */
return [r * 255, g * 255, b * 255];
};
convert.cmyk.rgb = function (cmyk) {
const c = cmyk[0] / 100;
const m = cmyk[1] / 100;
const y = cmyk[2] / 100;
const k = cmyk[3] / 100;
const r = 1 - Math.min(1, c * (1 - k) + k);
const g = 1 - Math.min(1, m * (1 - k) + k);
const b = 1 - Math.min(1, y * (1 - k) + k);
return [r * 255, g * 255, b * 255];
};
convert.xyz.rgb = function (xyz) {
const x = xyz[0] / 100;
const y = xyz[1] / 100;
const z = xyz[2] / 100;
let r;
let g;
let b;
r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
// Assume sRGB
r = r > 0.0031308
? ((1.055 * (r ** (1.0 / 2.4))) - 0.055)
: r * 12.92;
g = g > 0.0031308
? ((1.055 * (g ** (1.0 / 2.4))) - 0.055)
: g * 12.92;
b = b > 0.0031308
? ((1.055 * (b ** (1.0 / 2.4))) - 0.055)
: b * 12.92;
r = Math.min(Math.max(0, r), 1);
g = Math.min(Math.max(0, g), 1);
b = Math.min(Math.max(0, b), 1);
return [r * 255, g * 255, b * 255];
};
convert.xyz.lab = function (xyz) {
let x = xyz[0];
let y = xyz[1];
let z = xyz[2];
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116);
y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116);
z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116);
const l = (116 * y) - 16;
const a = 500 * (x - y);
const b = 200 * (y - z);
return [l, a, b];
};
convert.lab.xyz = function (lab) {
const l = lab[0];
const a = lab[1];
const b = lab[2];
let x;
let y;
let z;
y = (l + 16) / 116;
x = a / 500 + y;
z = y - b / 200;
const y2 = y ** 3;
const x2 = x ** 3;
const z2 = z ** 3;
y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;
x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;
z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;
x *= 95.047;
y *= 100;
z *= 108.883;
return [x, y, z];
};
convert.lab.lch = function (lab) {
const l = lab[0];
const a = lab[1];
const b = lab[2];
let h;
const hr = Math.atan2(b, a);
h = hr * 360 / 2 / Math.PI;
if (h < 0) {
h += 360;
}
const c = Math.sqrt(a * a + b * b);
return [l, c, h];
};
convert.lch.lab = function (lch) {
const l = lch[0];
const c = lch[1];
const h = lch[2];
const hr = h / 360 * 2 * Math.PI;
const a = c * Math.cos(hr);
const b = c * Math.sin(hr);
return [l, a, b];
};
convert.rgb.ansi16 = function (args, saturation = null) {
const [r, g, b] = args;
let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization
value = Math.round(value / 50);
if (value === 0) {
return 30;
}
let ansi = 30
+ ((Math.round(b / 255) << 2)
| (Math.round(g / 255) << 1)
| Math.round(r / 255));
if (value === 2) {
ansi += 60;
}
return ansi;
};
convert.hsv.ansi16 = function (args) {
// Optimization here; we already know the value and don't need to get
// it converted for us.
return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
};
convert.rgb.ansi256 = function (args) {
const r = args[0];
const g = args[1];
const b = args[2];
// We use the extended greyscale palette here, with the exception of
// black and white. normal palette only has 4 greyscale shades.
if (r === g && g === b) {
if (r < 8) {
return 16;
}
if (r > 248) {
return 231;
}
return Math.round(((r - 8) / 247) * 24) + 232;
}
const ansi = 16
+ (36 * Math.round(r / 255 * 5))
+ (6 * Math.round(g / 255 * 5))
+ Math.round(b / 255 * 5);
return ansi;
};
convert.ansi16.rgb = function (args) {
let color = args % 10;
// Handle greyscale
if (color === 0 || color === 7) {
if (args > 50) {
color += 3.5;
}
color = color / 10.5 * 255;
return [color, color, color];
}
const mult = (~~(args > 50) + 1) * 0.5;
const r = ((color & 1) * mult) * 255;
const g = (((color >> 1) & 1) * mult) * 255;
const b = (((color >> 2) & 1) * mult) * 255;
return [r, g, b];
};
convert.ansi256.rgb = function (args) {
// Handle greyscale
if (args >= 232) {
const c = (args - 232) * 10 + 8;
return [c, c, c];
}
args -= 16;
let rem;
const r = Math.floor(args / 36) / 5 * 255;
const g = Math.floor((rem = args % 36) / 6) / 5 * 255;
const b = (rem % 6) / 5 * 255;
return [r, g, b];
};
convert.rgb.hex = function (args) {
const integer = ((Math.round(args[0]) & 0xFF) << 16)
+ ((Math.round(args[1]) & 0xFF) << 8)
+ (Math.round(args[2]) & 0xFF);
const string = integer.toString(16).toUpperCase();
return '000000'.substring(string.length) + string;
};
convert.hex.rgb = function (args) {
const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
if (!match) {
return [0, 0, 0];
}
let colorString = match[0];
if (match[0].length === 3) {
colorString = colorString.split('').map(char => {
return char + char;
}).join('');
}
const integer = parseInt(colorString, 16);
const r = (integer >> 16) & 0xFF;
const g = (integer >> 8) & 0xFF;
const b = integer & 0xFF;
return [r, g, b];
};
convert.rgb.hcg = function (rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const max = Math.max(Math.max(r, g), b);
const min = Math.min(Math.min(r, g), b);
const chroma = (max - min);
let grayscale;
let hue;
if (chroma < 1) {
grayscale = min / (1 - chroma);
} else {
grayscale = 0;
}
if (chroma <= 0) {
hue = 0;
} else
if (max === r) {
hue = ((g - b) / chroma) % 6;
} else
if (max === g) {
hue = 2 + (b - r) / chroma;
} else {
hue = 4 + (r - g) / chroma;
}
hue /= 6;
hue %= 1;
return [hue * 360, chroma * 100, grayscale * 100];
};
convert.hsl.hcg = function (hsl) {
const s = hsl[1] / 100;
const l = hsl[2] / 100;
const c = l < 0.5 ? (2.0 * s * l) : (2.0 * s * (1.0 - l));
let f = 0;
if (c < 1.0) {
f = (l - 0.5 * c) / (1.0 - c);
}
return [hsl[0], c * 100, f * 100];
};
convert.hsv.hcg = function (hsv) {
const s = hsv[1] / 100;
const v = hsv[2] / 100;
const c = s * v;
let f = 0;
if (c < 1.0) {
f = (v - c) / (1 - c);
}
return [hsv[0], c * 100, f * 100];
};
convert.hcg.rgb = function (hcg) {
const h = hcg[0] / 360;
const c = hcg[1] / 100;
const g = hcg[2] / 100;
if (c === 0.0) {
return [g * 255, g * 255, g * 255];
}
const pure = [0, 0, 0];
const hi = (h % 1) * 6;
const v = hi % 1;
const w = 1 - v;
let mg = 0;
/* eslint-disable max-statements-per-line */
switch (Math.floor(hi)) {
case 0:
pure[0] = 1; pure[1] = v; pure[2] = 0; break;
case 1:
pure[0] = w; pure[1] = 1; pure[2] = 0; break;
case 2:
pure[0] = 0; pure[1] = 1; pure[2] = v; break;
case 3:
pure[0] = 0; pure[1] = w; pure[2] = 1; break;
case 4:
pure[0] = v; pure[1] = 0; pure[2] = 1; break;
default:
pure[0] = 1; pure[1] = 0; pure[2] = w;
}
/* eslint-enable max-statements-per-line */
mg = (1.0 - c) * g;
return [
(c * pure[0] + mg) * 255,
(c * pure[1] + mg) * 255,
(c * pure[2] + mg) * 255
];
};
convert.hcg.hsv = function (hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const v = c + g * (1.0 - c);
let f = 0;
if (v > 0.0) {
f = c / v;
}
return [hcg[0], f * 100, v * 100];
};
convert.hcg.hsl = function (hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const l = g * (1.0 - c) + 0.5 * c;
let s = 0;
if (l > 0.0 && l < 0.5) {
s = c / (2 * l);
} else
if (l >= 0.5 && l < 1.0) {
s = c / (2 * (1 - l));
}
return [hcg[0], s * 100, l * 100];
};
convert.hcg.hwb = function (hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const v = c + g * (1.0 - c);
return [hcg[0], (v - c) * 100, (1 - v) * 100];
};
convert.hwb.hcg = function (hwb) {
const w = hwb[1] / 100;
const b = hwb[2] / 100;
const v = 1 - b;
const c = v - w;
let g = 0;
if (c < 1) {
g = (v - c) / (1 - c);
}
return [hwb[0], c * 100, g * 100];
};
convert.apple.rgb = function (apple) {
return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];
};
convert.rgb.apple = function (rgb) {
return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];
};
convert.gray.rgb = function (args) {
return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
};
convert.gray.hsl = function (args) {
return [0, 0, args[0]];
};
convert.gray.hsv = convert.gray.hsl;
convert.gray.hwb = function (gray) {
return [0, 100, gray[0]];
};
convert.gray.cmyk = function (gray) {
return [0, 0, 0, gray[0]];
};
convert.gray.lab = function (gray) {
return [gray[0], 0, 0];
};
convert.gray.hex = function (gray) {
const val = Math.round(gray[0] / 100 * 255) & 0xFF;
const integer = (val << 16) + (val << 8) + val;
const string = integer.toString(16).toUpperCase();
return '000000'.substring(string.length) + string;
};
convert.rgb.gray = function (rgb) {
const val = (rgb[0] + rgb[1] + rgb[2]) / 3;
return [val / 255 * 100];
};
import route from './route'
import conversions from './conversions'
const convert = {};
const models = Object.keys(conversions);
function wrapRaw(fn) {
const wrappedFn = function (...args) {
const arg0 = args[0];
if (arg0 === undefined || arg0 === null) {
return arg0;
}
if (arg0.length > 1) {
args = arg0;
}
return fn(args);
};
// Preserve .conversion property if there is one
if ('conversion' in fn) {
wrappedFn.conversion = fn.conversion;
}
return wrappedFn;
}
function wrapRounded(fn) {
const wrappedFn = function (...args) {
const arg0 = args[0];
if (arg0 === undefined || arg0 === null) {
return arg0;
}
if (arg0.length > 1) {
args = arg0;
}
const result = fn(args);
// We're assuming the result is an array here.
// see notice in conversions.js; don't use box types
// in conversion functions.
if (typeof result === 'object') {
for (let len = result.length, i = 0; i < len; i++) {
result[i] = Math.round(result[i]);
}
}
return result;
};
// Preserve .conversion property if there is one
if ('conversion' in fn) {
wrappedFn.conversion = fn.conversion;
}
return wrappedFn;
}
models.forEach(fromModel => {
convert[fromModel] = {};
Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});
Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});
const routes = route(fromModel);
const routeModels = Object.keys(routes);
routeModels.forEach(toModel => {
const fn = routes[toModel];
convert[fromModel][toModel] = wrapRounded(fn);
convert[fromModel][toModel].raw = wrapRaw(fn);
});
});
export default convert;
{
"name": "color-convert",
"description": "Plain color conversion functions",
"version": "2.0.1",
"author": "Heather Arthur <fayearthur@gmail.com>",
"license": "MIT",
"repository": "Qix-/color-convert",
"scripts": {
"pretest": "xo",
"test": "node test/basic.js"
},
"engines": {
"node": ">=7.0.0"
},
"keywords": [
"color",
"colour",
"convert",
"converter",
"conversion",
"rgb",
"hsl",
"hsv",
"hwb",
"cmyk",
"ansi",
"ansi16"
],
"files": [
"index.js",
"conversions.js",
"route.js"
],
"xo": {
"rules": {
"default-case": 0,
"no-inline-comments": 0,
"operator-linebreak": 0
}
},
"devDependencies": {
"chalk": "^2.4.2",
"xo": "^0.24.0"
},
"dependencies": {
"color-name": "~1.1.4"
}
}
import conversions from './conversions'
/*
This function routes a model to all other models.
all functions that are routed have a property `.conversion` attached
to the returned synthetic function. This property is an array
of strings, each with the steps in between the 'from' and 'to'
color models (inclusive).
conversions that are not possible simply are not included.
*/
function buildGraph() {
const graph = {};
// https://jsperf.com/object-keys-vs-for-in-with-closure/3
const models = Object.keys(conversions);
for (let len = models.length, i = 0; i < len; i++) {
graph[models[i]] = {
// http://jsperf.com/1-vs-infinity
// micro-opt, but this is simple.
distance: -1,
parent: null
};
}
return graph;
}
// https://en.wikipedia.org/wiki/Breadth-first_search
function deriveBFS(fromModel) {
const graph = buildGraph();
const queue = [fromModel]; // Unshift -> queue -> pop
graph[fromModel].distance = 0;
while (queue.length) {
const current = queue.pop();
const adjacents = Object.keys(conversions[current]);
for (let len = adjacents.length, i = 0; i < len; i++) {
const adjacent = adjacents[i];
const node = graph[adjacent];
if (node.distance === -1) {
node.distance = graph[current].distance + 1;
node.parent = current;
queue.unshift(adjacent);
}
}
}
return graph;
}
function link(from, to) {
return function (args) {
return to(from(args));
};
}
function wrapConversion(toModel, graph) {
const path = [graph[toModel].parent, toModel];
let fn = conversions[graph[toModel].parent][toModel];
let cur = graph[toModel].parent;
while (graph[cur].parent) {
path.unshift(graph[cur].parent);
fn = link(conversions[graph[cur].parent][cur], fn);
cur = graph[cur].parent;
}
fn.conversion = path;
return fn;
}
export default function (fromModel) {
const graph = deriveBFS(fromModel);
const conversion = {};
const models = Object.keys(graph);
for (let len = models.length, i = 0; i < len; i++) {
const toModel = models[i];
const node = graph[toModel];
if (node.parent === null) {
// No possible conversion, or this node is the source model.
continue;
}
conversion[toModel] = wrapConversion(toModel, graph);
}
return conversion;
};
The MIT License (MIT)
Copyright (c) 2015 Dmitry Ivanov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
A JSON with color names and its values. Based on http://dev.w3.org/csswg/css-color/#named-colors.
[![NPM](https://nodei.co/npm/color-name.png?mini=true)](https://nodei.co/npm/color-name/)
```js
var colors = require('color-name');
colors.red //[255,0,0]
```
<a href="LICENSE"><img src="https://upload.wikimedia.org/wikipedia/commons/0/0c/MIT_logo.svg" width="120"/></a>
'use strict'
export default {
"aliceblue": [240, 248, 255],
"antiquewhite": [250, 235, 215],
"aqua": [0, 255, 255],
"aquamarine": [127, 255, 212],
"azure": [240, 255, 255],
"beige": [245, 245, 220],
"bisque": [255, 228, 196],
"black": [0, 0, 0],
"blanchedalmond": [255, 235, 205],
"blue": [0, 0, 255],
"blueviolet": [138, 43, 226],
"brown": [165, 42, 42],
"burlywood": [222, 184, 135],
"cadetblue": [95, 158, 160],
"chartreuse": [127, 255, 0],
"chocolate": [210, 105, 30],
"coral": [255, 127, 80],
"cornflowerblue": [100, 149, 237],
"cornsilk": [255, 248, 220],
"crimson": [220, 20, 60],
"cyan": [0, 255, 255],
"darkblue": [0, 0, 139],
"darkcyan": [0, 139, 139],
"darkgoldenrod": [184, 134, 11],
"darkgray": [169, 169, 169],
"darkgreen": [0, 100, 0],
"darkgrey": [169, 169, 169],
"darkkhaki": [189, 183, 107],
"darkmagenta": [139, 0, 139],
"darkolivegreen": [85, 107, 47],
"darkorange": [255, 140, 0],
"darkorchid": [153, 50, 204],
"darkred": [139, 0, 0],
"darksalmon": [233, 150, 122],
"darkseagreen": [143, 188, 143],
"darkslateblue": [72, 61, 139],
"darkslategray": [47, 79, 79],
"darkslategrey": [47, 79, 79],
"darkturquoise": [0, 206, 209],
"darkviolet": [148, 0, 211],
"deeppink": [255, 20, 147],
"deepskyblue": [0, 191, 255],
"dimgray": [105, 105, 105],
"dimgrey": [105, 105, 105],
"dodgerblue": [30, 144, 255],
"firebrick": [178, 34, 34],
"floralwhite": [255, 250, 240],
"forestgreen": [34, 139, 34],
"fuchsia": [255, 0, 255],
"gainsboro": [220, 220, 220],
"ghostwhite": [248, 248, 255],
"gold": [255, 215, 0],
"goldenrod": [218, 165, 32],
"gray": [128, 128, 128],
"green": [0, 128, 0],
"greenyellow": [173, 255, 47],
"grey": [128, 128, 128],
"honeydew": [240, 255, 240],
"hotpink": [255, 105, 180],
"indianred": [205, 92, 92],
"indigo": [75, 0, 130],
"ivory": [255, 255, 240],
"khaki": [240, 230, 140],
"lavender": [230, 230, 250],
"lavenderblush": [255, 240, 245],
"lawngreen": [124, 252, 0],
"lemonchiffon": [255, 250, 205],
"lightblue": [173, 216, 230],
"lightcoral": [240, 128, 128],
"lightcyan": [224, 255, 255],
"lightgoldenrodyellow": [250, 250, 210],
"lightgray": [211, 211, 211],
"lightgreen": [144, 238, 144],
"lightgrey": [211, 211, 211],
"lightpink": [255, 182, 193],
"lightsalmon": [255, 160, 122],
"lightseagreen": [32, 178, 170],
"lightskyblue": [135, 206, 250],
"lightslategray": [119, 136, 153],
"lightslategrey": [119, 136, 153],
"lightsteelblue": [176, 196, 222],
"lightyellow": [255, 255, 224],
"lime": [0, 255, 0],
"limegreen": [50, 205, 50],
"linen": [250, 240, 230],
"magenta": [255, 0, 255],
"maroon": [128, 0, 0],
"mediumaquamarine": [102, 205, 170],
"mediumblue": [0, 0, 205],
"mediumorchid": [186, 85, 211],
"mediumpurple": [147, 112, 219],
"mediumseagreen": [60, 179, 113],
"mediumslateblue": [123, 104, 238],
"mediumspringgreen": [0, 250, 154],
"mediumturquoise": [72, 209, 204],
"mediumvioletred": [199, 21, 133],
"midnightblue": [25, 25, 112],
"mintcream": [245, 255, 250],
"mistyrose": [255, 228, 225],
"moccasin": [255, 228, 181],
"navajowhite": [255, 222, 173],
"navy": [0, 0, 128],
"oldlace": [253, 245, 230],
"olive": [128, 128, 0],
"olivedrab": [107, 142, 35],
"orange": [255, 165, 0],
"orangered": [255, 69, 0],
"orchid": [218, 112, 214],
"palegoldenrod": [238, 232, 170],
"palegreen": [152, 251, 152],
"paleturquoise": [175, 238, 238],
"palevioletred": [219, 112, 147],
"papayawhip": [255, 239, 213],
"peachpuff": [255, 218, 185],
"peru": [205, 133, 63],
"pink": [255, 192, 203],
"plum": [221, 160, 221],
"powderblue": [176, 224, 230],
"purple": [128, 0, 128],
"rebeccapurple": [102, 51, 153],
"red": [255, 0, 0],
"rosybrown": [188, 143, 143],
"royalblue": [65, 105, 225],
"saddlebrown": [139, 69, 19],
"salmon": [250, 128, 114],
"sandybrown": [244, 164, 96],
"seagreen": [46, 139, 87],
"seashell": [255, 245, 238],
"sienna": [160, 82, 45],
"silver": [192, 192, 192],
"skyblue": [135, 206, 235],
"slateblue": [106, 90, 205],
"slategray": [112, 128, 144],
"slategrey": [112, 128, 144],
"snow": [255, 250, 250],
"springgreen": [0, 255, 127],
"steelblue": [70, 130, 180],
"tan": [210, 180, 140],
"teal": [0, 128, 128],
"thistle": [216, 191, 216],
"tomato": [255, 99, 71],
"turquoise": [64, 224, 208],
"violet": [238, 130, 238],
"wheat": [245, 222, 179],
"white": [255, 255, 255],
"whitesmoke": [245, 245, 245],
"yellow": [255, 255, 0],
"yellowgreen": [154, 205, 50]
};
{
"name": "color-name",
"version": "1.1.4",
"description": "A list of color names and its values",
"main": "index.js",
"files": [
"index.js"
],
"scripts": {
"test": "node test.js"
},
"repository": {
"type": "git",
"url": "git@github.com:colorjs/color-name.git"
},
"keywords": [
"color-name",
"color",
"color-keyword",
"keyword"
],
"author": "DY <dfcreative@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/colorjs/color-name/issues"
},
"homepage": "https://github.com/colorjs/color-name"
}
Copyright (c) 2011 Heather Arthur <fayearthur@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# color-string
> library for parsing and generating CSS color strings.
## Install
With [npm](http://npmjs.org/):
```console
$ npm install color-string
```
## Usage
### Parsing
```js
colorString.get('#FFF') // {model: 'rgb', value: [255, 255, 255, 1]}
colorString.get('#FFFA') // {model: 'rgb', value: [255, 255, 255, 0.67]}
colorString.get('#FFFFFFAA') // {model: 'rgb', value: [255, 255, 255, 0.67]}
colorString.get('hsl(360, 100%, 50%)') // {model: 'hsl', value: [0, 100, 50, 1]}
colorString.get('hsl(360 100% 50%)') // {model: 'hsl', value: [0, 100, 50, 1]}
colorString.get('hwb(60, 3%, 60%)') // {model: 'hwb', value: [60, 3, 60, 1]}
colorString.get.rgb('#FFF') // [255, 255, 255, 1]
colorString.get.rgb('blue') // [0, 0, 255, 1]
colorString.get.rgb('rgba(200, 60, 60, 0.3)') // [200, 60, 60, 0.3]
colorString.get.rgb('rgba(200 60 60 / 0.3)') // [200, 60, 60, 0.3]
colorString.get.rgb('rgba(200 60 60 / 30%)') // [200, 60, 60, 0.3]
colorString.get.rgb('rgb(200, 200, 200)') // [200, 200, 200, 1]
colorString.get.rgb('rgb(200 200 200)') // [200, 200, 200, 1]
colorString.get.hsl('hsl(360, 100%, 50%)') // [0, 100, 50, 1]
colorString.get.hsl('hsl(360 100% 50%)') // [0, 100, 50, 1]
colorString.get.hsl('hsla(360, 60%, 50%, 0.4)') // [0, 60, 50, 0.4]
colorString.get.hsl('hsl(360 60% 50% / 0.4)') // [0, 60, 50, 0.4]
colorString.get.hwb('hwb(60, 3%, 60%)') // [60, 3, 60, 1]
colorString.get.hwb('hwb(60, 3%, 60%, 0.6)') // [60, 3, 60, 0.6]
colorString.get.rgb('invalid color string') // null
```
### Generation
```js
colorString.to.hex([255, 255, 255]) // "#FFFFFF"
colorString.to.hex([0, 0, 255, 0.4]) // "#0000FF66"
colorString.to.hex([0, 0, 255], 0.4) // "#0000FF66"
colorString.to.rgb([255, 255, 255]) // "rgb(255, 255, 255)"
colorString.to.rgb([0, 0, 255, 0.4]) // "rgba(0, 0, 255, 0.4)"
colorString.to.rgb([0, 0, 255], 0.4) // "rgba(0, 0, 255, 0.4)"
colorString.to.rgb.percent([0, 0, 255]) // "rgb(0%, 0%, 100%)"
colorString.to.keyword([255, 255, 0]) // "yellow"
colorString.to.hsl([360, 100, 100]) // "hsl(360, 100%, 100%)"
colorString.to.hwb([50, 3, 15]) // "hwb(50, 3%, 15%)"
// all functions also support swizzling
colorString.to.rgb(0, [0, 255], 0.4) // "rgba(0, 0, 255, 0.4)"
colorString.to.rgb([0, 0], [255], 0.4) // "rgba(0, 0, 255, 0.4)"
colorString.to.rgb([0], 0, [255, 0.4]) // "rgba(0, 0, 255, 0.4)"
```
/* MIT license */
import colorNames from '../color-name'
import swizzle from '../simple-swizzle'
var hasOwnProperty = Object.hasOwnProperty;
var reverseNames = Object.create(null);
// create a list of reverse color names
for (var name in colorNames) {
if (hasOwnProperty.call(colorNames, name)) {
reverseNames[colorNames[name]] = name;
}
}
var cs = {
to: {},
get: {}
};
cs.get = function (string) {
var prefix = string.substring(0, 3).toLowerCase();
var val;
var model;
switch (prefix) {
case 'hsl':
val = cs.get.hsl(string);
model = 'hsl';
break;
case 'hwb':
val = cs.get.hwb(string);
model = 'hwb';
break;
default:
val = cs.get.rgb(string);
model = 'rgb';
break;
}
if (!val) {
return null;
}
return {model: model, value: val};
};
cs.get.rgb = function (string) {
if (!string) {
return null;
}
var abbr = /^#([a-f0-9]{3,4})$/i;
var hex = /^#([a-f0-9]{6})([a-f0-9]{2})?$/i;
var rgba = /^rgba?\(\s*([+-]?\d+)(?=[\s,])\s*(?:,\s*)?([+-]?\d+)(?=[\s,])\s*(?:,\s*)?([+-]?\d+)\s*(?:[,|\/]\s*([+-]?[\d\.]+)(%?)\s*)?\)$/;
var per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,?\s*([+-]?[\d\.]+)\%\s*,?\s*([+-]?[\d\.]+)\%\s*(?:[,|\/]\s*([+-]?[\d\.]+)(%?)\s*)?\)$/;
var keyword = /^(\w+)$/;
var rgb = [0, 0, 0, 1];
var match;
var i;
var hexAlpha;
if (match = string.match(hex)) {
hexAlpha = match[2];
match = match[1];
for (i = 0; i < 3; i++) {
// https://jsperf.com/slice-vs-substr-vs-substring-methods-long-string/19
var i2 = i * 2;
rgb[i] = parseInt(match.slice(i2, i2 + 2), 16);
}
if (hexAlpha) {
rgb[3] = parseInt(hexAlpha, 16) / 255;
}
} else if (match = string.match(abbr)) {
match = match[1];
hexAlpha = match[3];
for (i = 0; i < 3; i++) {
rgb[i] = parseInt(match[i] + match[i], 16);
}
if (hexAlpha) {
rgb[3] = parseInt(hexAlpha + hexAlpha, 16) / 255;
}
} else if (match = string.match(rgba)) {
for (i = 0; i < 3; i++) {
rgb[i] = parseInt(match[i + 1], 0);
}
if (match[4]) {
if (match[5]) {
rgb[3] = parseFloat(match[4]) * 0.01;
} else {
rgb[3] = parseFloat(match[4]);
}
}
} else if (match = string.match(per)) {
for (i = 0; i < 3; i++) {
rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
}
if (match[4]) {
if (match[5]) {
rgb[3] = parseFloat(match[4]) * 0.01;
} else {
rgb[3] = parseFloat(match[4]);
}
}
} else if (match = string.match(keyword)) {
if (match[1] === 'transparent') {
return [0, 0, 0, 0];
}
if (!hasOwnProperty.call(colorNames, match[1])) {
return null;
}
rgb = colorNames[match[1]];
rgb[3] = 1;
return rgb;
} else {
return null;
}
for (i = 0; i < 3; i++) {
rgb[i] = clamp(rgb[i], 0, 255);
}
rgb[3] = clamp(rgb[3], 0, 1);
return rgb;
};
cs.get.hsl = function (string) {
if (!string) {
return null;
}
var hsl = /^hsla?\(\s*([+-]?(?:\d{0,3}\.)?\d+)(?:deg)?\s*,?\s*([+-]?[\d\.]+)%\s*,?\s*([+-]?[\d\.]+)%\s*(?:[,|\/]\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/;
var match = string.match(hsl);
if (match) {
var alpha = parseFloat(match[4]);
var h = ((parseFloat(match[1]) % 360) + 360) % 360;
var s = clamp(parseFloat(match[2]), 0, 100);
var l = clamp(parseFloat(match[3]), 0, 100);
var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
return [h, s, l, a];
}
return null;
};
cs.get.hwb = function (string) {
if (!string) {
return null;
}
var hwb = /^hwb\(\s*([+-]?\d{0,3}(?:\.\d+)?)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/;
var match = string.match(hwb);
if (match) {
var alpha = parseFloat(match[4]);
var h = ((parseFloat(match[1]) % 360) + 360) % 360;
var w = clamp(parseFloat(match[2]), 0, 100);
var b = clamp(parseFloat(match[3]), 0, 100);
var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
return [h, w, b, a];
}
return null;
};
cs.to.hex = function () {
var rgba = swizzle(arguments);
return (
'#' +
hexDouble(rgba[0]) +
hexDouble(rgba[1]) +
hexDouble(rgba[2]) +
(rgba[3] < 1
? (hexDouble(Math.round(rgba[3] * 255)))
: '')
);
};
cs.to.rgb = function () {
var rgba = swizzle(arguments);
return rgba.length < 4 || rgba[3] === 1
? 'rgb(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ')'
: 'rgba(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ', ' + rgba[3] + ')';
};
cs.to.rgb.percent = function () {
var rgba = swizzle(arguments);
var r = Math.round(rgba[0] / 255 * 100);
var g = Math.round(rgba[1] / 255 * 100);
var b = Math.round(rgba[2] / 255 * 100);
return rgba.length < 4 || rgba[3] === 1
? 'rgb(' + r + '%, ' + g + '%, ' + b + '%)'
: 'rgba(' + r + '%, ' + g + '%, ' + b + '%, ' + rgba[3] + ')';
};
cs.to.hsl = function () {
var hsla = swizzle(arguments);
return hsla.length < 4 || hsla[3] === 1
? 'hsl(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%)'
: 'hsla(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%, ' + hsla[3] + ')';
};
// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
// (hwb have alpha optional & 1 is default value)
cs.to.hwb = function () {
var hwba = swizzle(arguments);
var a = '';
if (hwba.length >= 4 && hwba[3] !== 1) {
a = ', ' + hwba[3];
}
return 'hwb(' + hwba[0] + ', ' + hwba[1] + '%, ' + hwba[2] + '%' + a + ')';
};
cs.to.keyword = function (rgb) {
return reverseNames[rgb.slice(0, 3)];
};
// helpers
function clamp(num, min, max) {
return Math.min(Math.max(min, num), max);
}
function hexDouble(num) {
var str = Math.round(num).toString(16).toUpperCase();
return (str.length < 2) ? '0' + str : str;
}
export default cs;
\ No newline at end of file
{
"name": "color-string",
"description": "Parser and generator for CSS color strings",
"version": "1.9.1",
"author": "Heather Arthur <fayearthur@gmail.com>",
"contributors": [
"Maxime Thirouin",
"Dyma Ywanov <dfcreative@gmail.com>",
"Josh Junon"
],
"repository": "Qix-/color-string",
"scripts": {
"pretest": "xo",
"test": "node test/basic.js"
},
"license": "MIT",
"files": [
"index.js"
],
"xo": {
"rules": {
"no-cond-assign": 0,
"operator-linebreak": 0
}
},
"dependencies": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
},
"devDependencies": {
"xo": "^0.12.1"
},
"keywords": [
"color",
"colour",
"rgb",
"css"
]
}
import colorString from './color-string'
import convert from './color-convert'
const skippedModels = [
// To be honest, I don't really feel like keyword belongs in color convert, but eh.
'keyword',
// Gray conflicts with some method names, and has its own method defined.
'gray',
// Shouldn't really be in color-convert either...
'hex',
];
const hashedModelKeys = {};
for (const model of Object.keys(convert)) {
hashedModelKeys[[...convert[model].labels].sort().join('')] = model;
}
const limiters = {};
function Color(object, model) {
if (!(this instanceof Color)) {
return new Color(object, model);
}
if (model && model in skippedModels) {
model = null;
}
if (model && !(model in convert)) {
throw new Error('Unknown model: ' + model);
}
let i;
let channels;
if (object == null) { // eslint-disable-line no-eq-null,eqeqeq
this.model = 'rgb';
this.color = [0, 0, 0];
this.valpha = 1;
} else if (object instanceof Color) {
this.model = object.model;
this.color = [...object.color];
this.valpha = object.valpha;
} else if (typeof object === 'string') {
const result = colorString.get(object);
if (result === null) {
throw new Error('Unable to parse color from string: ' + object);
}
this.model = result.model;
channels = convert[this.model].channels;
this.color = result.value.slice(0, channels);
this.valpha = typeof result.value[channels] === 'number' ? result.value[channels] : 1;
} else if (object.length > 0) {
this.model = model || 'rgb';
channels = convert[this.model].channels;
const newArray = Array.prototype.slice.call(object, 0, channels);
this.color = zeroArray(newArray, channels);
this.valpha = typeof object[channels] === 'number' ? object[channels] : 1;
} else if (typeof object === 'number') {
// This is always RGB - can be converted later on.
this.model = 'rgb';
this.color = [
(object >> 16) & 0xFF,
(object >> 8) & 0xFF,
object & 0xFF,
];
this.valpha = 1;
} else {
this.valpha = 1;
const keys = Object.keys(object);
if ('alpha' in object) {
keys.splice(keys.indexOf('alpha'), 1);
this.valpha = typeof object.alpha === 'number' ? object.alpha : 0;
}
const hashedKeys = keys.sort().join('');
if (!(hashedKeys in hashedModelKeys)) {
throw new Error('Unable to parse color from object: ' + JSON.stringify(object));
}
this.model = hashedModelKeys[hashedKeys];
const {labels} = convert[this.model];
const color = [];
for (i = 0; i < labels.length; i++) {
color.push(object[labels[i]]);
}
this.color = zeroArray(color);
}
// Perform limitations (clamping, etc.)
if (limiters[this.model]) {
channels = convert[this.model].channels;
for (i = 0; i < channels; i++) {
const limit = limiters[this.model][i];
if (limit) {
this.color[i] = limit(this.color[i]);
}
}
}
this.valpha = Math.max(0, Math.min(1, this.valpha));
if (Object.freeze) {
Object.freeze(this);
}
}
Color.prototype = {
toString() {
return this.string();
},
toJSON() {
return this[this.model]();
},
string(places) {
let self = this.model in colorString.to ? this : this.rgb();
self = self.round(typeof places === 'number' ? places : 1);
const args = self.valpha === 1 ? self.color : [...self.color, this.valpha];
return colorString.to[self.model](args);
},
percentString(places) {
const self = this.rgb().round(typeof places === 'number' ? places : 1);
const args = self.valpha === 1 ? self.color : [...self.color, this.valpha];
return colorString.to.rgb.percent(args);
},
array() {
return this.valpha === 1 ? [...this.color] : [...this.color, this.valpha];
},
object() {
const result = {};
const {channels} = convert[this.model];
const {labels} = convert[this.model];
for (let i = 0; i < channels; i++) {
result[labels[i]] = this.color[i];
}
if (this.valpha !== 1) {
result.alpha = this.valpha;
}
return result;
},
unitArray() {
const rgb = this.rgb().color;
rgb[0] /= 255;
rgb[1] /= 255;
rgb[2] /= 255;
if (this.valpha !== 1) {
rgb.push(this.valpha);
}
return rgb;
},
unitObject() {
const rgb = this.rgb().object();
rgb.r /= 255;
rgb.g /= 255;
rgb.b /= 255;
if (this.valpha !== 1) {
rgb.alpha = this.valpha;
}
return rgb;
},
round(places) {
places = Math.max(places || 0, 0);
return new Color([...this.color.map(roundToPlace(places)), this.valpha], this.model);
},
alpha(value) {
if (value !== undefined) {
return new Color([...this.color, Math.max(0, Math.min(1, value))], this.model);
}
return this.valpha;
},
// Rgb
red: getset('rgb', 0, maxfn(255)),
green: getset('rgb', 1, maxfn(255)),
blue: getset('rgb', 2, maxfn(255)),
hue: getset(['hsl', 'hsv', 'hsl', 'hwb', 'hcg'], 0, value => ((value % 360) + 360) % 360),
saturationl: getset('hsl', 1, maxfn(100)),
lightness: getset('hsl', 2, maxfn(100)),
saturationv: getset('hsv', 1, maxfn(100)),
value: getset('hsv', 2, maxfn(100)),
chroma: getset('hcg', 1, maxfn(100)),
gray: getset('hcg', 2, maxfn(100)),
white: getset('hwb', 1, maxfn(100)),
wblack: getset('hwb', 2, maxfn(100)),
cyan: getset('cmyk', 0, maxfn(100)),
magenta: getset('cmyk', 1, maxfn(100)),
yellow: getset('cmyk', 2, maxfn(100)),
black: getset('cmyk', 3, maxfn(100)),
x: getset('xyz', 0, maxfn(95.047)),
y: getset('xyz', 1, maxfn(100)),
z: getset('xyz', 2, maxfn(108.833)),
l: getset('lab', 0, maxfn(100)),
a: getset('lab', 1),
b: getset('lab', 2),
keyword(value) {
if (value !== undefined) {
return new Color(value);
}
return convert[this.model].keyword(this.color);
},
hex(value) {
if (value !== undefined) {
return new Color(value);
}
return colorString.to.hex(this.rgb().round().color);
},
hexa(value) {
if (value !== undefined) {
return new Color(value);
}
const rgbArray = this.rgb().round().color;
let alphaHex = Math.round(this.valpha * 255).toString(16).toUpperCase();
if (alphaHex.length === 1) {
alphaHex = '0' + alphaHex;
}
return colorString.to.hex(rgbArray) + alphaHex;
},
rgbNumber() {
const rgb = this.rgb().color;
return ((rgb[0] & 0xFF) << 16) | ((rgb[1] & 0xFF) << 8) | (rgb[2] & 0xFF);
},
luminosity() {
// http://www.w3.org/TR/WCAG20/#relativeluminancedef
const rgb = this.rgb().color;
const lum = [];
for (const [i, element] of rgb.entries()) {
const chan = element / 255;
lum[i] = (chan <= 0.04045) ? chan / 12.92 : ((chan + 0.055) / 1.055) ** 2.4;
}
return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
},
contrast(color2) {
// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
const lum1 = this.luminosity();
const lum2 = color2.luminosity();
if (lum1 > lum2) {
return (lum1 + 0.05) / (lum2 + 0.05);
}
return (lum2 + 0.05) / (lum1 + 0.05);
},
level(color2) {
// https://www.w3.org/TR/WCAG/#contrast-enhanced
const contrastRatio = this.contrast(color2);
if (contrastRatio >= 7) {
return 'AAA';
}
return (contrastRatio >= 4.5) ? 'AA' : '';
},
isDark() {
// YIQ equation from http://24ways.org/2010/calculating-color-contrast
const rgb = this.rgb().color;
const yiq = (rgb[0] * 2126 + rgb[1] * 7152 + rgb[2] * 722) / 10000;
return yiq < 128;
},
isLight() {
return !this.isDark();
},
negate() {
const rgb = this.rgb();
for (let i = 0; i < 3; i++) {
rgb.color[i] = 255 - rgb.color[i];
}
return rgb;
},
lighten(ratio) {
const hsl = this.hsl();
hsl.color[2] += hsl.color[2] * ratio;
return hsl;
},
darken(ratio) {
const hsl = this.hsl();
hsl.color[2] -= hsl.color[2] * ratio;
return hsl;
},
saturate(ratio) {
const hsl = this.hsl();
hsl.color[1] += hsl.color[1] * ratio;
return hsl;
},
desaturate(ratio) {
const hsl = this.hsl();
hsl.color[1] -= hsl.color[1] * ratio;
return hsl;
},
whiten(ratio) {
const hwb = this.hwb();
hwb.color[1] += hwb.color[1] * ratio;
return hwb;
},
blacken(ratio) {
const hwb = this.hwb();
hwb.color[2] += hwb.color[2] * ratio;
return hwb;
},
grayscale() {
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
const rgb = this.rgb().color;
const value = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
return Color.rgb(value, value, value);
},
fade(ratio) {
return this.alpha(this.valpha - (this.valpha * ratio));
},
opaquer(ratio) {
return this.alpha(this.valpha + (this.valpha * ratio));
},
rotate(degrees) {
const hsl = this.hsl();
let hue = hsl.color[0];
hue = (hue + degrees) % 360;
hue = hue < 0 ? 360 + hue : hue;
hsl.color[0] = hue;
return hsl;
},
mix(mixinColor, weight) {
// Ported from sass implementation in C
// https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
if (!mixinColor || !mixinColor.rgb) {
throw new Error('Argument to "mix" was not a Color instance, but rather an instance of ' + typeof mixinColor);
}
const color1 = mixinColor.rgb();
const color2 = this.rgb();
const p = weight === undefined ? 0.5 : weight;
const w = 2 * p - 1;
const a = color1.alpha() - color2.alpha();
const w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2;
const w2 = 1 - w1;
return Color.rgb(
w1 * color1.red() + w2 * color2.red(),
w1 * color1.green() + w2 * color2.green(),
w1 * color1.blue() + w2 * color2.blue(),
color1.alpha() * p + color2.alpha() * (1 - p));
},
};
// Model conversion methods and static constructors
for (const model of Object.keys(convert)) {
if (skippedModels.includes(model)) {
continue;
}
const {channels} = convert[model];
// Conversion methods
Color.prototype[model] = function (...args) {
if (this.model === model) {
return new Color(this);
}
if (args.length > 0) {
return new Color(args, model);
}
return new Color([...assertArray(convert[this.model][model].raw(this.color)), this.valpha], model);
};
// 'static' construction methods
Color[model] = function (...args) {
let color = args[0];
if (typeof color === 'number') {
color = zeroArray(args, channels);
}
return new Color(color, model);
};
}
function roundTo(number, places) {
return Number(number.toFixed(places));
}
function roundToPlace(places) {
return function (number) {
return roundTo(number, places);
};
}
function getset(model, channel, modifier) {
model = Array.isArray(model) ? model : [model];
for (const m of model) {
(limiters[m] || (limiters[m] = []))[channel] = modifier;
}
model = model[0];
return function (value) {
let result;
if (value !== undefined) {
if (modifier) {
value = modifier(value);
}
result = this[model]();
result.color[channel] = value;
return result;
}
result = this[model]().color[channel];
if (modifier) {
result = modifier(result);
}
return result;
};
}
function maxfn(max) {
return function (v) {
return Math.max(0, Math.min(max, v));
};
}
function assertArray(value) {
return Array.isArray(value) ? value : [value];
}
function zeroArray(array, length) {
for (let i = 0; i < length; i++) {
if (typeof array[i] !== 'number') {
array[i] = 0;
}
}
return array;
}
export default Color;
import Color from './color';
/**
* 转换颜色格式。
* @param {Object} params - 参数对象。
* @param {string} color - 输入的颜色,默认为 '#fff'。
* @param {string} format - 需要转换的格式(支持 'rgb', 'hex', 'hsl', 'hsv', 'hwb')。
* @param {string} type - 转换后的类型(支持 'string', 'object', 'array', 'round')。
* @returns {string|Object|Array} 转换后的颜色表示。
*/
function convertFormat(color = '#fff', format = 'rgb', type = 'string') {
let colorObj = Color(color);
// 如果格式存在
if (colorObj[format]) {
// hex 无法直接转换为 除string类型外的任何类型
// 所以转为rgb 后 获取其他类型
if(format == 'hex' && type != 'string') format = 'rgb';
// 类型名称
let typeName = '';
switch (type) {
case 'string':
typeName = 'toString';
break;
case 'object':
typeName = 'object';
break;
case 'array':
typeName = 'array';
break;
case 'round':
typeName = 'round';
break;
default:
throw Error('Unsupported target type:' + type)
}
return colorObj[format]()[typeName]();
} else {
throw Error('Unsupported target format: ' + format);
}
}
/**
* 计算两个颜色之间的渐变值。
* @param {string} startColor - 开始的颜色,默认为黑色。
* @param {string} endColor - 结束的颜色,默认为白色。
* @param {number} step - 渐变的步数,默认为10。
* @returns {Array<string>} 两个颜色之间的渐变颜色数组。
*/
function gradient(startColor = 'rgb(0, 0, 0)', endColor = 'rgb(255, 255, 255)', step = 10) {
const startRGB = convertFormat(startColor, 'rgb', 'array') // 转换为rgb数组模式
const startR = startRGB[0]
const startG = startRGB[1]
const startB = startRGB[2]
const endRGB = convertFormat(endColor, 'rgb', 'array')
const endR = endRGB[0]
const endG = endRGB[1]
const endB = endRGB[2]
const sR = (endR - startR) / step // 总差值
const sG = (endG - startG) / step
const sB = (endB - startB) / step
const colorArr = []
for (let i = 0; i < step; i++) {
// 计算每一步的hex值
let hex = convertFormat(`rgb(${Math.round((sR * i + startR))},${Math.round((sG * i + startG))},${Math.round((sB
* i + startB))})`, 'hex')
// 确保第一个颜色值为startColor的值
if (i === 0) hex = convertFormat(startColor, 'hex')
// 确保最后一个颜色值为endColor的值
if (i === step - 1) hex = convertFormat(endColor, 'hex')
colorArr.push(hex)
}
return colorArr
}
export default {
/**
* 格式转换。
*/
convertFormat,
/**
* 计算两个颜色之间的渐变值。
*/
gradient,
/**
* 增加颜色的亮度。
* @param {string} color - 输入的颜色。
* @param {number} value - 增加的亮度值(0-1)。
* @returns {string} 调整后的颜色。
*/
lighten: (color, value, format = 'rgb', type = 'string') => convertFormat(Color(color).lighten(value), format, type),
/**
* 减少颜色的亮度。
* @param {string} color - 输入的颜色。
* @param {number} value - 减少的亮度值(0-1)。
* @returns {string} 调整后的颜色。
*/
darken: (color, value, format = 'rgb', type = 'string') => convertFormat(Color(color).darken(value), format, type),
/**
* 增加颜色的饱和度。
* @param {string} color - 输入的颜色。
* @param {number} value - 增加的饱和度值(0-1)。
* @returns {string} 调整后的颜色。
*/
saturate: (color, value, format = 'rgb', type = 'string') => convertFormat(Color(color).saturate(value), format, type),
/**
* 减少颜色的饱和度。
* @param {string} color - 输入的颜色。
* @param {number} value - 减少的饱和度值(0-1)。
* @returns {string} 调整后的颜色。
*/
desaturate: (color, value, format = 'rgb', type = 'string') => convertFormat(Color(color).desaturate(value), format, type),
/**
* 旋转颜色的色相。
* @param {string} color - 输入的颜色。
* @param {number} degrees - 旋转的度数。
* @returns {string} 调整后的颜色。
*/
rotate: (color, degrees, format = 'rgb', type = 'string') => convertFormat(Color(color).rotate(degrees), format, type),
/**
* 调整颜色的透明度。
* @param {string} color - 输入的颜色。
* @param {number} value - 透明度值(0-1,其中 1 是不透明)。
* @returns {string} 调整后的颜色。
*/
adjustAlpha: (color, value, format = 'rgb', type = 'string') => convertFormat(Color(color).alpha(value), format, type),
/**
* 获取颜色的亮度。
* @param {string} color - 输入的颜色。
* @returns {number} 颜色的亮度值(0-1)。
*/
luminosity: (color, format) => Color(color).luminosity(),
/**
* 判断颜色是否为暗色。
* @param {string} color - 输入的颜色。
* @returns {boolean} 如果是暗色则返回 true,否则返回 false。
*/
isDark: (color, format) => Color(color).isDark(),
/**
* 判断颜色是否为亮色。
* @param {string} color - 输入的颜色。
* @returns {boolean} 如果是亮色则返回 true,否则返回 false。
*/
isLight: (color, format) => Color(color).isLight()
};
\ No newline at end of file
The MIT License (MIT)
Copyright (c) 2015 JD Ballard
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# node-is-arrayish [![Travis-CI.org Build Status](https://img.shields.io/travis/Qix-/node-is-arrayish.svg?style=flat-square)](https://travis-ci.org/Qix-/node-is-arrayish) [![Coveralls.io Coverage Rating](https://img.shields.io/coveralls/Qix-/node-is-arrayish.svg?style=flat-square)](https://coveralls.io/r/Qix-/node-is-arrayish)
> Determines if an object can be used like an Array
## Example
```javascript
var isArrayish = require('is-arrayish');
isArrayish([]); // true
isArrayish({__proto__: []}); // true
isArrayish({}); // false
isArrayish({length:10}); // false
```
## License
Licensed under the [MIT License](http://opensource.org/licenses/MIT).
You can find a copy of it in [LICENSE](LICENSE).
export default function isArrayish(obj) {
if (!obj || typeof obj === 'string') {
return false;
}
return obj instanceof Array || Array.isArray(obj) ||
(obj.length >= 0 && (obj.splice instanceof Function ||
(Object.getOwnPropertyDescriptor(obj, (obj.length - 1)) && obj.constructor.name !== 'String')));
};
{
"name": "is-arrayish",
"description": "Determines if an object can be used as an array",
"version": "0.3.2",
"author": "Qix (http://github.com/qix-)",
"keywords": [
"is",
"array",
"duck",
"type",
"arrayish",
"similar",
"proto",
"prototype",
"type"
],
"license": "MIT",
"scripts": {
"test": "mocha --require coffeescript/register ./test/**/*.coffee",
"lint": "zeit-eslint --ext .jsx,.js .",
"lint-staged": "git diff --diff-filter=ACMRT --cached --name-only '*.js' '*.jsx' | xargs zeit-eslint"
},
"repository": {
"type": "git",
"url": "https://github.com/qix-/node-is-arrayish.git"
},
"devDependencies": {
"@zeit/eslint-config-node": "^0.3.0",
"@zeit/git-hooks": "^0.1.4",
"coffeescript": "^2.3.1",
"coveralls": "^3.0.1",
"eslint": "^4.19.1",
"istanbul": "^0.4.5",
"mocha": "^5.2.0",
"should": "^13.2.1"
},
"eslintConfig": {
"extends": [
"@zeit/eslint-config-node"
]
},
"git": {
"pre-commit": "lint-staged"
}
}
The MIT License (MIT)
Copyright (c) 2015 Josh Junon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# simple-swizzle [![Travis-CI.org Build Status](https://img.shields.io/travis/Qix-/node-simple-swizzle.svg?style=flat-square)](https://travis-ci.org/Qix-/node-simple-swizzle) [![Coveralls.io Coverage Rating](https://img.shields.io/coveralls/Qix-/node-simple-swizzle.svg?style=flat-square)](https://coveralls.io/r/Qix-/node-simple-swizzle)
> [Swizzle](https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)) your function arguments; pass in mixed arrays/values and get a clean array
## Usage
```js
var swizzle = require('simple-swizzle');
function myFunc() {
var args = swizzle(arguments);
// ...
return args;
}
myFunc(1, [2, 3], 4); // [1, 2, 3, 4]
myFunc(1, 2, 3, 4); // [1, 2, 3, 4]
myFunc([1, 2, 3, 4]); // [1, 2, 3, 4]
```
Functions can also be wrapped to automatically swizzle arguments and be passed
the resulting array.
```js
var swizzle = require('simple-swizzle');
var swizzledFn = swizzle.wrap(function (args) {
// ...
return args;
});
swizzledFn(1, [2, 3], 4); // [1, 2, 3, 4]
swizzledFn(1, 2, 3, 4); // [1, 2, 3, 4]
swizzledFn([1, 2, 3, 4]); // [1, 2, 3, 4]
```
## License
Licensed under the [MIT License](http://opensource.org/licenses/MIT).
You can find a copy of it in [LICENSE](LICENSE).
'use strict';
import isArrayish from '../is-arrayish';
var concat = Array.prototype.concat;
var slice = Array.prototype.slice;
export default function swizzle(args) {
var results = [];
for (var i = 0, len = args.length; i < len; i++) {
var arg = args[i];
if (isArrayish(arg)) {
// http://jsperf.com/javascript-array-concat-vs-push/98
results = concat.call(results, slice.call(arg));
} else {
results.push(arg);
}
}
return results;
};
swizzle.wrap = function (fn) {
return function () {
return fn(swizzle(arguments));
};
};
{
"name": "simple-swizzle",
"description": "Simply swizzle your arguments",
"version": "0.2.2",
"author": "Qix (http://github.com/qix-)",
"keywords": [
"argument",
"arguments",
"swizzle",
"swizzling",
"parameter",
"parameters",
"mixed",
"array"
],
"license": "MIT",
"scripts": {
"pretest": "xo",
"test": "mocha --compilers coffee:coffee-script/register"
},
"files": [
"index.js"
],
"repository": "qix-/node-simple-swizzle",
"devDependencies": {
"coffee-script": "^1.9.3",
"coveralls": "^2.11.2",
"istanbul": "^0.3.17",
"mocha": "^2.2.5",
"should": "^7.0.1",
"xo": "^0.7.1"
},
"dependencies": {
"is-arrayish": "^0.3.1"
}
}
let timeout = null
/**
* 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
function debounce(func, wait = 500, immediate = false) {
// 清除定时器
if (timeout !== null) clearTimeout(timeout)
// 立即执行,此类情况一般用不到
if (immediate) {
const callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) typeof func === 'function' && func()
} else {
// 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
timeout = setTimeout(() => {
typeof func === 'function' && func()
}, wait)
}
}
export default debounce
let _boundaryCheckingState = true; // 是否进行越界检查的全局开关
/**
* 把错误的数据转正
* @private
* @example strip(0.09999999999999998)=0.1
*/
function strip(num, precision = 15) {
return +parseFloat(Number(num).toPrecision(precision));
}
/**
* Return digits length of a number
* @private
* @param {*number} num Input number
*/
function digitLength(num) {
// Get digit length of e
const eSplit = num.toString().split(/[eE]/);
const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);
return len > 0 ? len : 0;
}
/**
* 把小数转成整数,如果是小数则放大成整数
* @private
* @param {*number} num 输入数
*/
function float2Fixed(num) {
if (num.toString().indexOf('e') === -1) {
return Number(num.toString().replace('.', ''));
}
const dLen = digitLength(num);
return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num);
}
/**
* 检测数字是否越界,如果越界给出提示
* @private
* @param {*number} num 输入数
*/
function checkBoundary(num) {
if (_boundaryCheckingState) {
if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
console.warn(`${num} 超出了精度限制,结果可能不正确`);
}
}
}
/**
* 把递归操作扁平迭代化
* @param {number[]} arr 要操作的数字数组
* @param {function} operation 迭代操作
* @private
*/
function iteratorOperation(arr, operation) {
const [num1, num2, ...others] = arr;
let res = operation(num1, num2);
others.forEach((num) => {
res = operation(res, num);
});
return res;
}
/**
* 高精度乘法
* @export
*/
export function times(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, times);
}
const [num1, num2] = nums;
const num1Changed = float2Fixed(num1);
const num2Changed = float2Fixed(num2);
const baseNum = digitLength(num1) + digitLength(num2);
const leftValue = num1Changed * num2Changed;
checkBoundary(leftValue);
return leftValue / Math.pow(10, baseNum);
}
/**
* 高精度加法
* @export
*/
export function plus(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, plus);
}
const [num1, num2] = nums;
// 取最大的小数位
const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
// 把小数都转为整数然后再计算
return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
}
/**
* 高精度减法
* @export
*/
export function minus(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, minus);
}
const [num1, num2] = nums;
const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
}
/**
* 高精度除法
* @export
*/
export function divide(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, divide);
}
const [num1, num2] = nums;
const num1Changed = float2Fixed(num1);
const num2Changed = float2Fixed(num2);
checkBoundary(num1Changed);
checkBoundary(num2Changed);
// 重要,这里必须用strip进行修正
return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));
}
/**
* 四舍五入
* @export
*/
export function round(num, ratio) {
const base = Math.pow(10, ratio);
let result = divide(Math.round(Math.abs(times(num, base))), base);
if (num < 0 && result !== 0) {
result = times(result, -1);
}
// 位数不足则补0
return result;
}
/**
* 是否进行边界检查,默认开启
* @param flag 标记开关,true 为开启,false 为关闭,默认为 true
* @export
*/
export function enableBoundaryChecking(flag = true) {
_boundaryCheckingState = flag;
}
export default {
times,
plus,
minus,
divide,
round,
enableBoundaryChecking,
};
import { number, empty } from './test.js'
import { round } from './digit.js'
// 颜色操作方法
import Color from './color'
/**
* @description 如果value小于min,取min;如果value大于max,取max
* @param {number} min
* @param {number} max
* @param {number} value
*/
function range(min = 0, max = 0, value = 0) {
return Math.max(min, Math.min(max, Number(value)))
}
/**
* @description 用于获取用户传递值的px值 如果用户传递了"xxpx"或者"xxrpx",取出其数值部分,如果是"xxxrpx"还需要用过uni.upx2px进行转换
* @param {number|string} value 用户传递值的px值
* @param {boolean} unit
* @returns {number|string}
*/
function getPx(value, unit = false) {
if (number(value)) {
return unit ? `${value}px` : Number(value)
}
// 如果带有rpx,先取出其数值部分,再转为px值
if (/(rpx|upx)$/.test(value)) {
return unit ? `${uni.upx2px(parseInt(value))}px` : Number(uni.upx2px(parseInt(value)))
}
return unit ? `${parseInt(value)}px` : parseInt(value)
}
/**
* @description 进行延时,以达到可以简写代码的目的 比如: await uni.$w.sleep(20)将会阻塞20ms
* @param {number} value 堵塞时间 单位ms 毫秒
* @returns {Promise} 返回promise
*/
function sleep(value = 30) {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, value)
})
}
/**
* @description 运行期判断平台
* @returns {string} 返回所在平台(小写)
* @link 运行期判断平台 https://uniapp.dcloud.io/frame?id=判断平台
*/
function os() {
return uni.getSystemInfoSync().platform.toLowerCase()
}
/**
* @description 获取系统信息同步接口
* @link 获取系统信息同步接口 https://uniapp.dcloud.io/api/system/info?id=getsysteminfosync
*/
function sys() {
return uni.getSystemInfoSync()
}
/**
* @description 取一个区间数
* @param {Number} min 最小值
* @param {Number} max 最大值
*/
function random(min, max) {
if (min >= 0 && max > 0 && max >= min) {
const gab = max - min + 1
return Math.floor(Math.random() * gab + min)
}
return 0
}
/**
* @param {Number} len uuid的长度
* @param {Boolean} firstU 将返回的首字母置为"u"
* @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制
*/
function guid(len = 32, firstU = true, radix = null) {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
const uuid = []
radix = radix || chars.length
if (len) {
// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]
} else {
let r
// rfc4122标准要求返回的uuid中,某些位为固定的字符
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
uuid[14] = '4'
for (let i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]
}
}
}
// 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
if (firstU) {
uuid.shift()
return `u${uuid.join('')}`
}
return uuid.join('')
}
/**
* @description 获取父组件的参数,因为支付宝小程序不支持provide/inject的写法
this.$parent在非H5中,可以准确获取到父组件,但是在H5中,需要多次this.$parent.$parent.xxx
这里默认值等于undefined有它的含义,因为最顶层元素(组件)的$parent就是undefined,意味着不传name
值(默认为undefined),就是查找最顶层的$parent
* @param {string|undefined} name 父组件的参数名
*/
function $parent(name = undefined) {
let parent = this.$parent
// 通过while历遍,这里主要是为了H5需要多层解析的问题
while (parent) {
// 父组件
if (parent.$options && parent.$options.name !== name) {
// 如果组件的name不相等,继续上一级寻找
parent = parent.$parent
} else {
return parent
}
}
return false
}
/**
* @description 样式转换
* 对象转字符串,或者字符串转对象
* @param {object | string} customStyle 需要转换的目标
* @param {String} target 转换的目的,object-转为对象,string-转为字符串
* @returns {object|string}
*/
function addStyle(customStyle, target = 'object') {
// 字符串转字符串,对象转对象情形,直接返回
if (empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target === 'string' &&
typeof(customStyle) === 'string') {
return customStyle
}
// 字符串转对象
if (target === 'object') {
// 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的
customStyle = trim(customStyle)
// 根据";"将字符串转为数组形式
const styleArray = customStyle.split(';')
const style = {}
// 历遍数组,拼接成对象
for (let i = 0; i < styleArray.length; i++) {
// 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤
if (styleArray[i]) {
const item = styleArray[i].split(':')
style[trim(item[0])] = trim(item[1])
}
}
return style
}
// 这里为对象转字符串形式
let string = ''
for (const i in customStyle) {
// 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名
const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
string += `${key}:${customStyle[i]};`
}
// 去除两端空格
return trim(string)
}
/**
* @description 添加单位,如果有rpx,upx,%,px等单位结尾或者值为auto,直接返回,否则加上px单位结尾
* @param {string|number} value 需要添加单位的值
* @param {string} unit 添加的单位名 比如px
*/
function addUnit(value = 'auto', unit = uni?.$w?.config?.unit ? uni?.$w?.config?.unit : 'px') {
value = String(value)
// 用wuui内置验证规则中的number判断是否为数值
return number(value) ? `${value}${unit}` : value
}
/**
* @description 深度克隆
* @param {object} obj 需要深度克隆的对象
* @param cache 缓存
* @returns {*} 克隆后的对象或者原值(不是对象)
*/
function deepClone(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (cache.has(obj)) return cache.get(obj);
let clone;
if (obj instanceof Date) {
clone = new Date(obj.getTime());
} else if (obj instanceof RegExp) {
clone = new RegExp(obj);
} else if (obj instanceof Map) {
clone = new Map(Array.from(obj, ([key, value]) => [key, deepClone(value, cache)]));
} else if (obj instanceof Set) {
clone = new Set(Array.from(obj, value => deepClone(value, cache)));
} else if (Array.isArray(obj)) {
clone = obj.map(value => deepClone(value, cache));
} else if (Object.prototype.toString.call(obj) === '[object Object]') {
clone = Object.create(Object.getPrototypeOf(obj));
cache.set(obj, clone);
for (const [key, value] of Object.entries(obj)) {
clone[key] = deepClone(value, cache);
}
} else {
clone = Object.assign({}, obj);
}
cache.set(obj, clone);
return clone;
}
/**
* @description JS对象深度合并
* @param {object} target 需要拷贝的对象
* @param {object} source 拷贝的来源对象
* @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象)
*/
function deepMerge(target = {}, source = {}) {
target = deepClone(target)
if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) return target;
const merged = Array.isArray(target) ? target.slice() : Object.assign({}, target);
for (const prop in source) {
if (!source.hasOwnProperty(prop)) continue;
const sourceValue = source[prop];
const targetValue = merged[prop];
if (sourceValue instanceof Date) {
merged[prop] = new Date(sourceValue);
} else if (sourceValue instanceof RegExp) {
merged[prop] = new RegExp(sourceValue);
} else if (sourceValue instanceof Map) {
merged[prop] = new Map(sourceValue);
} else if (sourceValue instanceof Set) {
merged[prop] = new Set(sourceValue);
} else if (typeof sourceValue === 'object' && sourceValue !== null) {
merged[prop] = deepMerge(targetValue, sourceValue);
} else {
merged[prop] = sourceValue;
}
}
return merged;
}
/**
* @description error提示
* @param {*} err 错误内容
*/
function error(err) {
// 开发环境才提示,生产环境不会提示
if (process.env.NODE_ENV === 'development') {
console.error(`wuui提示:${err}`)
}
}
/**
* @description 打乱数组
* @param {array} array 需要打乱的数组
* @returns {array} 打乱后的数组
*/
function randomArray(array = []) {
// 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0
return array.sort(() => Math.random() - 0.5)
}
// padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序
// 所以这里做一个兼容polyfill的兼容处理
if (!String.prototype.padStart) {
// 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解
String.prototype.padStart = function(maxLength, fillString = ' ') {
if (Object.prototype.toString.call(fillString) !== '[object String]') {
throw new TypeError(
'fillString must be String'
)
}
const str = this
// 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉
if (str.length >= maxLength) return String(str)
const fillLength = maxLength - str.length
let times = Math.ceil(fillLength / fillString.length)
while (times >>= 1) {
fillString += fillString
if (times === 1) {
fillString += fillString
}
}
return fillString.slice(0, fillLength) + str
}
}
/**
* @description 格式化时间
* @param {String|Number} dateTime 需要格式化的时间戳
* @param {String} fmt 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd
* @returns {string} 返回格式化后的字符串
*/
function timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') {
let date
// 若传入时间为假值,则取当前时间
if (!dateTime) {
date = new Date()
}
// 若为unix秒时间戳,则转为毫秒时间戳(逻辑有点奇怪,但不敢改,以保证历史兼容)
else if (/^\d{10}$/.test(dateTime?.toString().trim())) {
date = new Date(dateTime * 1000)
}
// 若用户传入字符串格式时间戳,new Date无法解析,需做兼容
else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) {
date = new Date(Number(dateTime))
}
// 处理平台性差异,在Safari/Webkit中,new Date仅支持/作为分割符的字符串时间
// 处理 '2022-07-10 01:02:03',跳过 '2022-07-10T01:02:03'
else if (typeof dateTime === 'string' && dateTime.includes('-') && !dateTime.includes('T')) {
date = new Date(dateTime.replace(/-/g, '/'))
}
// 其他都认为符合 RFC 2822 规范
else {
date = new Date(dateTime)
}
const timeSource = {
'y': date.getFullYear().toString(), // 年
'm': (date.getMonth() + 1).toString().padStart(2, '0'), // 月
'd': date.getDate().toString().padStart(2, '0'), // 日
'h': date.getHours().toString().padStart(2, '0'), // 时
'M': date.getMinutes().toString().padStart(2, '0'), // 分
's': date.getSeconds().toString().padStart(2, '0') // 秒
// 有其他格式化字符需求可以继续添加,必须转化成字符串
}
for (const key in timeSource) {
const [ret] = new RegExp(`${key}+`).exec(formatStr) || []
if (ret) {
// 年可能只需展示两位
const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0
formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex))
}
}
return formatStr
}
/**
* @description 时间戳转为多久之前
* @param {String|Number} timestamp 时间戳
* @param {String|Boolean} format
* 格式化规则如果为时间格式字符串,超出一定时间范围,返回固定的时间格式;
* 如果为布尔值false,无论什么时间,都返回多久以前的格式
* @returns {string} 转化后的内容
*/
function timeFrom(timestamp = null, format = 'yyyy-mm-dd') {
if (timestamp == null) timestamp = Number(new Date())
timestamp = parseInt(timestamp)
// 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位)
if (timestamp.toString().length == 10) timestamp *= 1000
let timer = (new Date()).getTime() - timestamp
timer = parseInt(timer / 1000)
// 如果小于5分钟,则返回"刚刚",其他以此类推
let tips = ''
switch (true) {
case timer < 300:
tips = '刚刚'
break
case timer >= 300 && timer < 3600:
tips = `${parseInt(timer / 60)}分钟前`
break
case timer >= 3600 && timer < 86400:
tips = `${parseInt(timer / 3600)}小时前`
break
case timer >= 86400 && timer < 2592000:
tips = `${parseInt(timer / 86400)}天前`
break
default:
// 如果format为false,则无论什么时间戳,都显示xx之前
if (format === false) {
if (timer >= 2592000 && timer < 365 * 86400) {
tips = `${parseInt(timer / (86400 * 30))}个月前`
} else {
tips = `${parseInt(timer / (86400 * 365))}年前`
}
} else {
tips = timeFormat(timestamp, format)
}
}
return tips
}
/**
* @description 去除空格
* @param String str 需要去除空格的字符串
* @param String pos both(左右)|left|right|all 默认both
*/
function trim(str, pos = 'both') {
str = String(str)
if (pos == 'both') {
return str.replace(/^\s+|\s+$/g, '')
}
if (pos == 'left') {
return str.replace(/^\s*/, '')
}
if (pos == 'right') {
return str.replace(/(\s*$)/g, '')
}
if (pos == 'all') {
return str.replace(/\s+/g, '')
}
return str
}
/**
* @description 对象转url参数
* @param {object} data,对象
* @param {Boolean} isPrefix,是否自动加上"?"
* @param {string} arrayFormat 规则 indices|brackets|repeat|comma
*/
function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') {
const prefix = isPrefix ? '?' : ''
const _result = []
if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets'
for (const key in data) {
const value = data[key]
// 去掉为空的参数
if (['', undefined, null].indexOf(value) >= 0) {
continue
}
// 如果值为数组,另行处理
if (value.constructor === Array) {
// e.g. {ids: [1, 2, 3]}
switch (arrayFormat) {
case 'indices':
// 结果: ids[0]=1&ids[1]=2&ids[2]=3
for (let i = 0; i < value.length; i++) {
_result.push(`${key}[${i}]=${value[i]}`)
}
break
case 'brackets':
// 结果: ids[]=1&ids[]=2&ids[]=3
value.forEach((_value) => {
_result.push(`${key}[]=${_value}`)
})
break
case 'repeat':
// 结果: ids=1&ids=2&ids=3
value.forEach((_value) => {
_result.push(`${key}=${_value}`)
})
break
case 'comma':
// 结果: ids=1,2,3
let commaStr = ''
value.forEach((_value) => {
commaStr += (commaStr ? ',' : '') + _value
})
_result.push(`${key}=${commaStr}`)
break
default:
value.forEach((_value) => {
_result.push(`${key}[]=${_value}`)
})
}
} else {
_result.push(`${key}=${value}`)
}
}
return _result.length ? prefix + _result.join('&') : ''
}
/**
* 显示消息提示框
* @param {String} title 提示的内容,长度与 icon 取值有关。
* @param {Number} duration 提示的延迟时间,单位毫秒,默认:2000
*/
function toast(title, duration = 2000) {
uni.showToast({
title: String(title),
icon: 'none',
duration
})
}
/**
* @description 根据主题type值,获取对应的图标
* @param {String} type 主题名称,primary|info|error|warning|success
* @param {boolean} fill 是否使用fill填充实体的图标
*/
function type2icon(type = 'success', fill = false) {
// 如果非预置值,默认为success
if (['primary', 'info', 'error', 'warning', 'success'].indexOf(type) == -1) type = 'success'
let iconName = ''
// 目前(2019-12-12),info和primary使用同一个图标
switch (type) {
case 'primary':
iconName = 'info-circle'
break
case 'info':
iconName = 'info-circle'
break
case 'error':
iconName = 'close-circle'
break
case 'warning':
iconName = 'error-circle'
break
case 'success':
iconName = 'checkmark-circle'
break
default:
iconName = 'checkmark-circle'
}
// 是否是实体类型,加上-fill,在icon组件库中,实体的类名是后面加-fill的
if (fill) iconName += '-fill'
return iconName
}
/**
* @description 数字格式化
* @param {number|string} number 要格式化的数字
* @param {number} decimals 保留几位小数
* @param {string} decimalPoint 小数点符号
* @param {string} thousandsSeparator 千分位符号
* @returns {string} 格式化后的数字
*/
function priceFormat(number, decimals = 0, decimalPoint = '.', thousandsSeparator = ',') {
number = (`${number}`).replace(/[^0-9+-Ee.]/g, '')
const n = !isFinite(+number) ? 0 : +number
const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals)
const sep = (typeof thousandsSeparator === 'undefined') ? ',' : thousandsSeparator
const dec = (typeof decimalPoint === 'undefined') ? '.' : decimalPoint
let s = ''
s = (prec ? round(n, prec) + '' : `${Math.round(n)}`).split('.')
const re = /(-?\d+)(\d{3})/
while (re.test(s[0])) {
s[0] = s[0].replace(re, `$1${sep}$2`)
}
if ((s[1] || '').length < prec) {
s[1] = s[1] || ''
s[1] += new Array(prec - s[1].length + 1).join('0')
}
return s.join(dec)
}
/**
* @description 获取duration值
* 如果带有ms或者s直接返回,如果大于一定值,认为是ms单位,小于一定值,认为是s单位
* 比如以30位阈值,那么300大于30,可以理解为用户想要的是300ms,而不是想花300s去执行一个动画
* @param {String|number} value 比如: "1s"|"100ms"|1|100
* @param {boolean} unit 提示: 如果是false 默认返回number
* @return {string|number}
*/
function getDuration(value, unit = true) {
const valueNum = parseInt(value)
if (unit) {
if (/s$/.test(value)) return value
return value > 30 ? `${value}ms` : `${value}s`
}
if (/ms$/.test(value)) return valueNum
if (/s$/.test(value)) return valueNum > 30 ? valueNum : valueNum * 1000
return valueNum
}
/**
* @description 日期的月或日补零操作
* @param {String} value 需要补零的值
*/
function padZero(value) {
return `00${value}`.slice(-2)
}
/**
* @description 在wu-form的子组件内容发生变化,或者失去焦点时,尝试通知wu-form执行校验方法
* @param {*} instance
* @param {*} event
*/
function formValidate(instance, event) {
const formItem = $parent.call(instance, 'wu-form-item')
const form = $parent.call(instance, 'wu-form')
// 如果发生变化的input或者textarea等,其父组件中有wu-form-item或者wu-form等,就执行form的validate方法
// 同时将form-item的pros传递给form,让其进行精确对象验证
if (formItem && form) {
form.validateField(formItem.prop, () => {}, event)
}
}
/**
* @description 获取某个对象下的属性,用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式
* @param {object} obj 对象
* @param {string} key 需要获取的属性字段
* @returns {*}
*/
function getProperty(obj, key) {
if (!obj) {
return
}
if (typeof key !== 'string' || key === '') {
return ''
}
if (key.indexOf('.') !== -1) {
const keys = key.split('.')
let firstObj = obj[keys[0]] || {}
for (let i = 1; i < keys.length; i++) {
if (firstObj) {
firstObj = firstObj[keys[i]]
}
}
return firstObj
}
return obj[key]
}
/**
* @description 设置对象的属性值,如果'a.b.c'的形式进行设置
* @param {object} obj 对象
* @param {string} key 需要设置的属性
* @param {string} value 设置的值
*/
function setProperty(obj, key, value) {
if (!obj) {
return
}
// 递归赋值
const inFn = function(_obj, keys, v) {
// 最后一个属性key
if (keys.length === 1) {
_obj[keys[0]] = v
return
}
// 0~length-1个key
while (keys.length > 1) {
const k = keys[0]
if (!_obj[k] || (typeof _obj[k] !== 'object')) {
_obj[k] = {}
}
const key = keys.shift()
// 自调用判断是否存在属性,不存在则自动创建对象
inFn(_obj[k], keys, v)
}
}
if (typeof key !== 'string' || key === '') {
} else if (key.indexOf('.') !== -1) { // 支持多层级赋值操作
const keys = key.split('.')
inFn(obj, keys, value)
} else {
obj[key] = value
}
}
/**
* @description 获取当前页面路径
*/
function page() {
const pages = getCurrentPages();
const route = pages[pages.length - 1]?.route;
// 某些特殊情况下(比如页面进行redirectTo时的一些时机),pages可能为空数组
return `/${route ? route : ''}`
}
/**
* @description 获取当前路由栈实例数组
*/
function pages() {
const pages = getCurrentPages()
return pages
}
/**
* 获取页面历史栈指定层实例
* @param back {number} [0] - 0或者负数,表示获取历史栈的哪一层,0表示获取当前页面实例,-1 表示获取上一个页面实例。默认0。
*/
function getHistoryPage(back = 0) {
const pages = getCurrentPages()
const len = pages.length
return pages[len - 1 + back]
}
/**
* @description 修改wuui内置属性值
* @param {object} props 修改内置props属性
* @param {object} config 修改内置config属性
* @param {object} color 修改内置color属性
* @param {object} zIndex 修改内置zIndex属性
*/
function setConfig({
props = {},
config = {},
color = {},
zIndex = {}
}) {
const {
deepMerge,
} = uni.$w
uni.$w.config = deepMerge(uni.$w.config, config)
uni.$w.props = deepMerge(uni.$w.props, props)
uni.$w.color = deepMerge(uni.$w.color, color)
uni.$w.zIndex = deepMerge(uni.$w.zIndex, zIndex)
}
export {
range,
getPx,
sleep,
os,
sys,
random,
guid,
$parent,
addStyle,
addUnit,
deepClone,
deepMerge,
error,
randomArray,
timeFormat,
timeFrom,
trim,
queryParams,
toast,
type2icon,
priceFormat,
getDuration,
padZero,
formValidate,
getProperty,
setProperty,
page,
pages,
getHistoryPage,
setConfig,
Color
}
\ No newline at end of file
/**
* 注意:
* 此部分内容,在vue-cli模式下,需要在vue.config.js加入如下内容才有效:
* module.exports = {
* transpileDependencies: ['uview-v2']
* }
*/
let platform = 'none'
// #ifdef VUE3
platform = 'vue3'
// #endif
// #ifdef VUE2
platform = 'vue2'
// #endif
// #ifdef APP-PLUS
platform = 'plus'
// #endif
// #ifdef APP-NVUE
platform = 'nvue'
// #endif
// #ifdef H5
platform = 'h5'
// #endif
// #ifdef MP-WEIXIN
platform = 'weixin'
// #endif
// #ifdef MP-ALIPAY
platform = 'alipay'
// #endif
// #ifdef MP-BAIDU
platform = 'baidu'
// #endif
// #ifdef MP-TOUTIAO
platform = 'toutiao'
// #endif
// #ifdef MP-QQ
platform = 'qq'
// #endif
// #ifdef MP-KUAISHOU
platform = 'kuaishou'
// #endif
// #ifdef MP-360
platform = '360'
// #endif
// #ifdef MP
platform = 'mp'
// #endif
// #ifdef QUICKAPP-WEBVIEW
platform = 'quickapp-webview'
// #endif
// #ifdef QUICKAPP-WEBVIEW-HUAWEI
platform = 'quickapp-webview-huawei'
// #endif
// #ifdef QUICKAPP-WEBVIEW-UNION
platform = 'quckapp-webview-union'
// #endif
export default platform
/**
* 验证电子邮箱格式
*/
function email(value) {
return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value)
}
/**
* 验证手机格式
*/
function mobile(value) {
return /^1([3589]\d|4[5-9]|6[1-2,4-7]|7[0-8])\d{8}$/.test(value)
}
/**
* 验证URL格式
*/
function url(value) {
return /^((https|http|ftp|rtsp|mms):\/\/)(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z].[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/
.test(value)
}
/**
* 验证日期格式
*/
function date(value) {
if (!value) return false
// 判断是否数值或者字符串数值(意味着为时间戳),转为数值,否则new Date无法识别字符串时间戳
if (number(value)) value = +value
return !/Invalid|NaN/.test(new Date(value).toString())
}
/**
* 验证ISO类型的日期格式
*/
function dateISO(value) {
return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value)
}
/**
* 验证十进制数字
*/
function number(value) {
return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value)
}
/**
* 验证字符串
*/
function string(value) {
return typeof value === 'string'
}
/**
* 验证整数
*/
function digits(value) {
return /^\d+$/.test(value)
}
/**
* 验证身份证号码
*/
function idCard(value) {
return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
}
/**
* 是否车牌号
*/
function carNo(value) {
// 新能源车牌
const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/
// 旧车牌
const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/
if (value.length === 7) {
return creg.test(value)
} if (value.length === 8) {
return xreg.test(value)
}
return false
}
/**
* 金额,只允许2位小数
*/
function amount(value) {
// 金额,只允许保留两位小数
return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value)
}
/**
* 中文
*/
function chinese(value) {
const reg = /^[\u4e00-\u9fa5]+$/gi
return reg.test(value)
}
/**
* 只能输入字母
*/
function letter(value) {
return /^[a-zA-Z]*$/.test(value)
}
/**
* 只能是字母或者数字
*/
function enOrNum(value) {
// 英文或者数字
const reg = /^[0-9a-zA-Z]*$/g
return reg.test(value)
}
/**
* 验证是否包含某个值
*/
function contains(value, param) {
return value.indexOf(param) >= 0
}
/**
* 验证一个值范围[min, max]
*/
function range(value, param) {
return value >= param[0] && value <= param[1]
}
/**
* 验证一个长度范围[min, max]
*/
function rangeLength(value, param) {
return value.length >= param[0] && value.length <= param[1]
}
/**
* 是否固定电话
*/
function landline(value) {
const reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/
return reg.test(value)
}
/**
* 判断是否为空
*/
function empty(value) {
switch (typeof value) {
case 'undefined':
return true
case 'string':
if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true
break
case 'boolean':
if (!value) return true
break
case 'number':
if (value === 0 || isNaN(value)) return true
break
case 'object':
if (value === null || value.length === 0) return true
for (const i in value) {
return false
}
return true
}
return false
}
/**
* 是否json字符串
*/
function jsonString(value) {
if (typeof value === 'string') {
try {
const obj = JSON.parse(value)
if (typeof obj === 'object' && obj) {
return true
}
return false
} catch (e) {
return false
}
}
return false
}
/**
* 是否数组
*/
function array(value) {
if (typeof Array.isArray === 'function') {
return Array.isArray(value)
}
return Object.prototype.toString.call(value) === '[object Array]'
}
/**
* 是否对象
*/
function object(value) {
return Object.prototype.toString.call(value) === '[object Object]'
}
/**
* 是否短信验证码
*/
function code(value, len = 6) {
return new RegExp(`^\\d{${len}}$`).test(value)
}
/**
* 是否函数方法
* @param {Object} value
*/
function func(value) {
return typeof value === 'function'
}
/**
* 是否promise对象
* @param {Object} value
*/
function promise(value) {
return object(value) && func(value.then) && func(value.catch)
}
/** 是否图片格式
* @param {Object} value
*/
function image(value) {
const newValue = value.split('?')[0]
const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i
return IMAGE_REGEXP.test(newValue)
}
/**
* 是否视频格式
* @param {Object} value
*/
function video(value) {
const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8)/i
return VIDEO_REGEXP.test(value)
}
/**
* 是否为正则对象
* @param {Object}
* @return {Boolean}
*/
function regExp(o) {
return o && Object.prototype.toString.call(o) === '[object RegExp]'
}
export {
email,
mobile,
url,
date,
dateISO,
number,
digits,
idCard,
carNo,
amount,
chinese,
letter,
enOrNum,
contains,
range,
rangeLength,
empty,
jsonString,
landline,
object,
array,
code,
func,
promise,
video,
image,
regExp,
string
}
let timer; let
flag
/**
* 节流原理:在一定时间内,只能触发一次
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
function throttle(func, wait = 500, immediate = true) {
if (immediate) {
if (!flag) {
flag = true
// 如果是立即执行,则在wait毫秒内开始时执行
typeof func === 'function' && func()
timer = setTimeout(() => {
flag = false
}, wait)
}
} else if (!flag) {
flag = true
// 如果是非立即执行,则在wait毫秒内的结束处执行
timer = setTimeout(() => {
flag = false
typeof func === 'function' && func()
}, wait)
}
}
export default throttle
import buildURL from '../helpers/buildURL'
import buildFullPath from '../core/buildFullPath'
import settle from '../core/settle'
import { isUndefined } from '../utils'
/**
* 返回可选值存在的配置
* @param {Array} keys - 可选值数组
* @param {Object} config2 - 配置
* @return {{}} - 存在的配置项
*/
const mergeKeys = (keys, config2) => {
const config = {}
keys.forEach((prop) => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
return config
}
export default (config) => new Promise((resolve, reject) => {
const fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params)
const _config = {
url: fullPath,
header: config.header,
complete: (response) => {
config.fullPath = fullPath
response.config = config
try {
// 对可能字符串不是json 的情况容错
if (typeof response.data === 'string') {
response.data = JSON.parse(response.data)
}
// eslint-disable-next-line no-empty
} catch (e) {
}
settle(resolve, reject, response)
}
}
let requestTask
if (config.method === 'UPLOAD') {
delete _config.header['content-type']
delete _config.header['Content-Type']
const otherConfig = {
// #ifdef MP-ALIPAY
fileType: config.fileType,
// #endif
filePath: config.filePath,
name: config.name
}
const optionalKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef H5
'file',
// #endif
// #ifdef H5 || APP-PLUS
'timeout',
// #endif
'formData'
]
requestTask = uni.uploadFile({ ..._config, ...otherConfig, ...mergeKeys(optionalKeys, config) })
} else if (config.method === 'DOWNLOAD') {
// #ifdef H5 || APP-PLUS
if (!isUndefined(config.timeout)) {
_config.timeout = config.timeout
}
// #endif
requestTask = uni.downloadFile(_config)
} else {
const optionalKeys = [
'data',
'method',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4'
// #endif
]
requestTask = uni.request({ ..._config, ...mergeKeys(optionalKeys, config) })
}
if (config.getTask) {
config.getTask(requestTask, config)
}
})
'use strict'
function InterceptorManager() {
this.handlers = []
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled,
rejected
})
return this.handlers.length - 1
}
/**
* Remove an interceptor from the stack
*
* @param {Number} id The ID that was returned by `use`
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null
}
}
/**
* Iterate over all the registered interceptors
*
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
this.handlers.forEach((h) => {
if (h !== null) {
fn(h)
}
})
}
export default InterceptorManager
/**
* @Class Request
* @description luch-request http请求插件
* @version 3.0.7
* @Author lu-ch
* @Date 2021-09-04
* @Email webwork.s@qq.com
* 文档: https://www.quanzhan.co/luch-request/
* github: https://github.com/lei-mu/luch-request
* DCloud: http://ext.dcloud.net.cn/plugin?id=392
* HBuilderX: beat-3.0.4 alpha-3.0.4
*/
import dispatchRequest from './dispatchRequest'
import InterceptorManager from './InterceptorManager'
import mergeConfig from './mergeConfig'
import defaults from './defaults'
import { isPlainObject } from '../utils'
import clone from '../utils/clone'
export default class Request {
/**
* @param {Object} arg - 全局配置
* @param {String} arg.baseURL - 全局根路径
* @param {Object} arg.header - 全局header
* @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
* @param {String} arg.dataType = [json] - 全局默认的dataType
* @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType。支付宝小程序不支持
* @param {Object} arg.custom - 全局默认的自定义参数
* @param {Number} arg.timeout - 全局默认的超时时间,单位 ms。默认60000。H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序
* @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书。默认true.仅App安卓端支持(HBuilderX 2.3.3+)
* @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证(cookies)。默认false。仅H5支持(HBuilderX 2.6.15+)
* @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4。默认false。仅 App-Android 支持 (HBuilderX 2.8.0+)
* @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器。默认statusCode >= 200 && statusCode < 300
*/
constructor(arg = {}) {
if (!isPlainObject(arg)) {
arg = {}
console.warn('设置全局参数必须接收一个Object')
}
this.config = clone({ ...defaults, ...arg })
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}
}
/**
* @Function
* @param {Request~setConfigCallback} f - 设置全局默认配置
*/
setConfig(f) {
this.config = f(this.config)
}
middleware(config) {
config = mergeConfig(this.config, config)
const chain = [dispatchRequest, undefined]
let promise = Promise.resolve(config)
this.interceptors.request.forEach((interceptor) => {
chain.unshift(interceptor.fulfilled, interceptor.rejected)
})
this.interceptors.response.forEach((interceptor) => {
chain.push(interceptor.fulfilled, interceptor.rejected)
})
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift())
}
return promise
}
/**
* @Function
* @param {Object} config - 请求配置项
* @prop {String} options.url - 请求路径
* @prop {Object} options.data - 请求参数
* @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
* @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
* @prop {Object} [options.header = config.header] - 请求header
* @prop {Object} [options.method = config.method] - 请求方法
* @returns {Promise<unknown>}
*/
request(config = {}) {
return this.middleware(config)
}
get(url, options = {}) {
return this.middleware({
url,
method: 'GET',
...options
})
}
post(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'POST',
...options
})
}
// #ifndef MP-ALIPAY
put(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'PUT',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
delete(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'DELETE',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
connect(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'CONNECT',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN || MP-BAIDU
head(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'HEAD',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
options(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'OPTIONS',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
trace(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'TRACE',
...options
})
}
// #endif
upload(url, config = {}) {
config.url = url
config.method = 'UPLOAD'
return this.middleware(config)
}
download(url, config = {}) {
config.url = url
config.method = 'DOWNLOAD'
return this.middleware(config)
}
}
/**
* setConfig回调
* @return {Object} - 返回操作后的config
* @callback Request~setConfigCallback
* @param {Object} config - 全局默认config
*/
'use strict'
import isAbsoluteURL from '../helpers/isAbsoluteURL'
import combineURLs from '../helpers/combineURLs'
/**
* Creates a new URL by combining the baseURL with the requestedURL,
* only when the requestedURL is not already an absolute URL.
* If the requestURL is absolute, this function returns the requestedURL untouched.
*
* @param {string} baseURL The base URL
* @param {string} requestedURL Absolute or relative URL to combine
* @returns {string} The combined full path
*/
export default function buildFullPath(baseURL, requestedURL) {
if (baseURL && !isAbsoluteURL(requestedURL)) {
return combineURLs(baseURL, requestedURL)
}
return requestedURL
}
/**
* 默认的全局配置
*/
export default {
baseURL: '',
header: {},
method: 'GET',
dataType: 'json',
// #ifndef MP-ALIPAY
responseType: 'text',
// #endif
custom: {},
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
timeout: 60000,
// #endif
// #ifdef APP-PLUS
sslVerify: true,
// #endif
// #ifdef H5
withCredentials: false,
// #endif
// #ifdef APP-PLUS
firstIpv4: false,
// #endif
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300
}
}
import adapter from '../adapters/index'
export default (config) => adapter(config)
import { deepMerge, isUndefined } from '../utils'
/**
* 合并局部配置优先的配置,如果局部有该配置项则用局部,如果全局有该配置项则用全局
* @param {Array} keys - 配置项
* @param {Object} globalsConfig - 当前的全局配置
* @param {Object} config2 - 局部配置
* @return {{}}
*/
const mergeKeys = (keys, globalsConfig, config2) => {
const config = {}
keys.forEach((prop) => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
} else if (!isUndefined(globalsConfig[prop])) {
config[prop] = globalsConfig[prop]
}
})
return config
}
/**
*
* @param globalsConfig - 当前实例的全局配置
* @param config2 - 当前的局部配置
* @return - 合并后的配置
*/
export default (globalsConfig, config2 = {}) => {
const method = config2.method || globalsConfig.method || 'GET'
let config = {
baseURL: globalsConfig.baseURL || '',
method,
url: config2.url || '',
params: config2.params || {},
custom: { ...(globalsConfig.custom || {}), ...(config2.custom || {}) },
header: deepMerge(globalsConfig.header || {}, config2.header || {})
}
const defaultToConfig2Keys = ['getTask', 'validateStatus']
config = { ...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2) }
// eslint-disable-next-line no-empty
if (method === 'DOWNLOAD') {
// #ifdef H5 || APP-PLUS
if (!isUndefined(config2.timeout)) {
config.timeout = config2.timeout
} else if (!isUndefined(globalsConfig.timeout)) {
config.timeout = globalsConfig.timeout
}
// #endif
} else if (method === 'UPLOAD') {
delete config.header['content-type']
delete config.header['Content-Type']
const uploadKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef MP-ALIPAY
'fileType',
// #endif
// #ifdef H5
'file',
// #endif
'filePath',
'name',
// #ifdef H5 || APP-PLUS
'timeout',
// #endif
'formData'
]
uploadKeys.forEach((prop) => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
// #ifdef H5 || APP-PLUS
if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
config.timeout = globalsConfig.timeout
}
// #endif
} else {
const defaultsKeys = [
'data',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4'
// #endif
]
config = { ...config, ...mergeKeys(defaultsKeys, globalsConfig, config2) }
}
return config
}
/**
* Resolve or reject a Promise based on response status.
*
* @param {Function} resolve A function that resolves the promise.
* @param {Function} reject A function that rejects the promise.
* @param {object} response The response.
*/
export default function settle(resolve, reject, response) {
const { validateStatus } = response.config
const status = response.statusCode
if (status && (!validateStatus || validateStatus(status))) {
resolve(response)
} else {
reject(response)
}
}
'use strict'
import * as utils from '../utils'
function encode(val) {
return encodeURIComponent(val)
.replace(/%40/gi, '@')
.replace(/%3A/gi, ':')
.replace(/%24/g, '$')
.replace(/%2C/gi, ',')
.replace(/%20/g, '+')
.replace(/%5B/gi, '[')
.replace(/%5D/gi, ']')
}
/**
* Build a URL by appending params to the end
*
* @param {string} url The base of the url (e.g., http://www.google.com)
* @param {object} [params] The params to be appended
* @returns {string} The formatted url
*/
export default function buildURL(url, params) {
/* eslint no-param-reassign:0 */
if (!params) {
return url
}
let serializedParams
if (utils.isURLSearchParams(params)) {
serializedParams = params.toString()
} else {
const parts = []
utils.forEach(params, (val, key) => {
if (val === null || typeof val === 'undefined') {
return
}
if (utils.isArray(val)) {
key = `${key}[]`
} else {
val = [val]
}
utils.forEach(val, (v) => {
if (utils.isDate(v)) {
v = v.toISOString()
} else if (utils.isObject(v)) {
v = JSON.stringify(v)
}
parts.push(`${encode(key)}=${encode(v)}`)
})
})
serializedParams = parts.join('&')
}
if (serializedParams) {
const hashmarkIndex = url.indexOf('#')
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex)
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
}
return url
}
'use strict'
/**
* Creates a new URL by combining the specified URLs
*
* @param {string} baseURL The base URL
* @param {string} relativeURL The relative URL
* @returns {string} The combined URL
*/
export default function combineURLs(baseURL, relativeURL) {
return relativeURL
? `${baseURL.replace(/\/+$/, '')}/${relativeURL.replace(/^\/+/, '')}`
: baseURL
}
'use strict'
/**
* Determines whether the specified URL is absolute
*
* @param {string} url The URL to test
* @returns {boolean} True if the specified URL is absolute, otherwise false
*/
export default function isAbsoluteURL(url) {
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
// by any combination of letters, digits, plus, period, or hyphen.
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
}
type AnyObject = Record<string | number | symbol, any>
type HttpPromise<T> = Promise<HttpResponse<T>>;
type Tasks = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask
export interface RequestTask {
abort: () => void;
offHeadersReceived: () => void;
onHeadersReceived: () => void;
}
export interface HttpRequestConfig<T = Tasks> {
/** 请求基地址 */
baseURL?: string;
/** 请求服务器接口地址 */
url?: string;
/** 请求查询参数,自动拼接为查询字符串 */
params?: AnyObject;
/** 请求体参数 */
data?: AnyObject;
/** 文件对应的 key */
name?: string;
/** HTTP 请求中其他额外的 form data */
formData?: AnyObject;
/** 要上传文件资源的路径。 */
filePath?: string;
/** 需要上传的文件列表。使用 files 时,filePath 和 name 不生效,App、H5( 2.6.15+) */
files?: Array<{
name?: string;
file?: File;
uri: string;
}>;
/** 要上传的文件对象,仅H5(2.6.15+)支持 */
file?: File;
/** 请求头信息 */
header?: AnyObject;
/** 请求方式 */
method?: "GET" | "POST" | "PUT" | "DELETE" | "CONNECT" | "HEAD" | "OPTIONS" | "TRACE" | "UPLOAD" | "DOWNLOAD";
/** 如果设为 json,会尝试对返回的数据做一次 JSON.parse */
dataType?: string;
/** 设置响应的数据类型,支付宝小程序不支持 */
responseType?: "text" | "arraybuffer";
/** 自定义参数 */
custom?: AnyObject;
/** 超时时间,仅微信小程序(2.10.0)、支付宝小程序支持 */
timeout?: number;
/** DNS解析时优先使用ipv4,仅 App-Android 支持 (HBuilderX 2.8.0+) */
firstIpv4?: boolean;
/** 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+) */
sslVerify?: boolean;
/** 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+) */
withCredentials?: boolean;
/** 返回当前请求的task, options。请勿在此处修改options。 */
getTask?: (task: T, options: HttpRequestConfig<T>) => void;
/** 全局自定义验证器 */
validateStatus?: (statusCode: number) => boolean | void;
}
export interface HttpResponse<T = any> {
config: HttpRequestConfig;
statusCode: number;
cookies: Array<string>;
data: T;
errMsg: string;
header: AnyObject;
}
export interface HttpUploadResponse<T = any> {
config: HttpRequestConfig;
statusCode: number;
data: T;
errMsg: string;
}
export interface HttpDownloadResponse extends HttpResponse {
tempFilePath: string;
}
export interface HttpError {
config: HttpRequestConfig;
statusCode?: number;
cookies?: Array<string>;
data?: any;
errMsg: string;
header?: AnyObject;
}
export interface HttpInterceptorManager<V, E = V> {
use(
onFulfilled?: (config: V) => Promise<V> | V,
onRejected?: (config: E) => Promise<E> | E
): void;
eject(id: number): void;
}
export abstract class HttpRequestAbstract {
constructor(config?: HttpRequestConfig);
config: HttpRequestConfig;
interceptors: {
request: HttpInterceptorManager<HttpRequestConfig, HttpRequestConfig>;
response: HttpInterceptorManager<HttpResponse, HttpError>;
}
middleware<T = any>(config: HttpRequestConfig): HttpPromise<T>;
request<T = any>(config: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
get<T = any>(url: string, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
upload<T = any>(url: string, config?: HttpRequestConfig<UniApp.UploadTask>): HttpPromise<T>;
delete<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
head<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
post<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
put<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
connect<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
options<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
trace<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
download(url: string, config?: HttpRequestConfig<UniApp.DownloadTask>): Promise<HttpDownloadResponse>;
setConfig(onSend: (config: HttpRequestConfig) => HttpRequestConfig): void;
}
declare class HttpRequest extends HttpRequestAbstract { }
export default HttpRequest;
import Request from './core/Request'
export default Request
'use strict'
// utils is a library of generic helper functions non-specific to axios
const { toString } = Object.prototype
/**
* Determine if a value is an Array
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Array, otherwise false
*/
export function isArray(val) {
return toString.call(val) === '[object Array]'
}
/**
* Determine if a value is an Object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Object, otherwise false
*/
export function isObject(val) {
return val !== null && typeof val === 'object'
}
/**
* Determine if a value is a Date
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Date, otherwise false
*/
export function isDate(val) {
return toString.call(val) === '[object Date]'
}
/**
* Determine if a value is a URLSearchParams object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
*/
export function isURLSearchParams(val) {
return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
}
/**
* Iterate over an Array or an Object invoking a function for each item.
*
* If `obj` is an Array callback will be called passing
* the value, index, and complete array for each item.
*
* If 'obj' is an Object callback will be called passing
* the value, key, and complete object for each property.
*
* @param {Object|Array} obj The object to iterate
* @param {Function} fn The callback to invoke for each item
*/
export function forEach(obj, fn) {
// Don't bother if no value provided
if (obj === null || typeof obj === 'undefined') {
return
}
// Force an array if not already something iterable
if (typeof obj !== 'object') {
/* eslint no-param-reassign:0 */
obj = [obj]
}
if (isArray(obj)) {
// Iterate over array values
for (let i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj)
}
} else {
// Iterate over object keys
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn.call(null, obj[key], key, obj)
}
}
}
}
/**
* 是否为boolean 值
* @param val
* @returns {boolean}
*/
export function isBoolean(val) {
return typeof val === 'boolean'
}
/**
* 是否为真正的对象{} new Object
* @param {any} obj - 检测的对象
* @returns {boolean}
*/
export function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
}
/**
* Function equal to merge with the difference being that no reference
* to original objects is kept.
*
* @see merge
* @param {Object} obj1 Object to merge
* @returns {Object} Result of all merge properties
*/
export function deepMerge(/* obj1, obj2, obj3, ... */) {
const result = {}
function assignValue(val, key) {
if (typeof result[key] === 'object' && typeof val === 'object') {
result[key] = deepMerge(result[key], val)
} else if (typeof val === 'object') {
result[key] = deepMerge({}, val)
} else {
result[key] = val
}
}
for (let i = 0, l = arguments.length; i < l; i++) {
forEach(arguments[i], assignValue)
}
return result
}
export function isUndefined(val) {
return typeof val === 'undefined'
}
/* eslint-disable */
var clone = (function() {
'use strict';
function _instanceof(obj, type) {
return type != null && obj instanceof type;
}
var nativeMap;
try {
nativeMap = Map;
} catch(_) {
// maybe a reference error because no `Map`. Give it a dummy value that no
// value will ever be an instanceof.
nativeMap = function() {};
}
var nativeSet;
try {
nativeSet = Set;
} catch(_) {
nativeSet = function() {};
}
var nativePromise;
try {
nativePromise = Promise;
} catch(_) {
nativePromise = function() {};
}
/**
* Clones (copies) an Object using deep copying.
*
* This function supports circular references by default, but if you are certain
* there are no circular references in your object, you can save some CPU time
* by calling clone(obj, false).
*
* Caution: if `circular` is false and `parent` contains circular references,
* your program may enter an infinite loop and crash.
*
* @param `parent` - the object to be cloned
* @param `circular` - set to true if the object to be cloned may contain
* circular references. (optional - true by default)
* @param `depth` - set to a number if the object is only to be cloned to
* a particular depth. (optional - defaults to Infinity)
* @param `prototype` - sets the prototype to be used when cloning an object.
* (optional - defaults to parent prototype).
* @param `includeNonEnumerable` - set to true if the non-enumerable properties
* should be cloned as well. Non-enumerable properties on the prototype
* chain will be ignored. (optional - false by default)
*/
function clone(parent, circular, depth, prototype, includeNonEnumerable) {
if (typeof circular === 'object') {
depth = circular.depth;
prototype = circular.prototype;
includeNonEnumerable = circular.includeNonEnumerable;
circular = circular.circular;
}
// maintain two arrays for circular references, where corresponding parents
// and children have the same index
var allParents = [];
var allChildren = [];
var useBuffer = typeof Buffer != 'undefined';
if (typeof circular == 'undefined')
circular = true;
if (typeof depth == 'undefined')
depth = Infinity;
// recurse this function so we don't reset allParents and allChildren
function _clone(parent, depth) {
// cloning null always returns null
if (parent === null)
return null;
if (depth === 0)
return parent;
var child;
var proto;
if (typeof parent != 'object') {
return parent;
}
if (_instanceof(parent, nativeMap)) {
child = new nativeMap();
} else if (_instanceof(parent, nativeSet)) {
child = new nativeSet();
} else if (_instanceof(parent, nativePromise)) {
child = new nativePromise(function (resolve, reject) {
parent.then(function(value) {
resolve(_clone(value, depth - 1));
}, function(err) {
reject(_clone(err, depth - 1));
});
});
} else if (clone.__isArray(parent)) {
child = [];
} else if (clone.__isRegExp(parent)) {
child = new RegExp(parent.source, __getRegExpFlags(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (clone.__isDate(parent)) {
child = new Date(parent.getTime());
} else if (useBuffer && Buffer.isBuffer(parent)) {
if (Buffer.from) {
// Node.js >= 5.10.0
child = Buffer.from(parent);
} else {
// Older Node.js versions
child = new Buffer(parent.length);
parent.copy(child);
}
return child;
} else if (_instanceof(parent, Error)) {
child = Object.create(parent);
} else {
if (typeof prototype == 'undefined') {
proto = Object.getPrototypeOf(parent);
child = Object.create(proto);
}
else {
child = Object.create(prototype);
proto = prototype;
}
}
if (circular) {
var index = allParents.indexOf(parent);
if (index != -1) {
return allChildren[index];
}
allParents.push(parent);
allChildren.push(child);
}
if (_instanceof(parent, nativeMap)) {
parent.forEach(function(value, key) {
var keyChild = _clone(key, depth - 1);
var valueChild = _clone(value, depth - 1);
child.set(keyChild, valueChild);
});
}
if (_instanceof(parent, nativeSet)) {
parent.forEach(function(value) {
var entryChild = _clone(value, depth - 1);
child.add(entryChild);
});
}
for (var i in parent) {
var attrs = Object.getOwnPropertyDescriptor(parent, i);
if (attrs) {
child[i] = _clone(parent[i], depth - 1);
}
try {
var objProperty = Object.getOwnPropertyDescriptor(parent, i);
if (objProperty.set === 'undefined') {
// no setter defined. Skip cloning this property
continue;
}
child[i] = _clone(parent[i], depth - 1);
} catch(e){
if (e instanceof TypeError) {
// when in strict mode, TypeError will be thrown if child[i] property only has a getter
// we can't do anything about this, other than inform the user that this property cannot be set.
continue
} else if (e instanceof ReferenceError) {
//this may happen in non strict mode
continue
}
}
}
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(parent);
for (var i = 0; i < symbols.length; i++) {
// Don't need to worry about cloning a symbol because it is a primitive,
// like a number or string.
var symbol = symbols[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
continue;
}
child[symbol] = _clone(parent[symbol], depth - 1);
Object.defineProperty(child, symbol, descriptor);
}
}
if (includeNonEnumerable) {
var allPropertyNames = Object.getOwnPropertyNames(parent);
for (var i = 0; i < allPropertyNames.length; i++) {
var propertyName = allPropertyNames[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
if (descriptor && descriptor.enumerable) {
continue;
}
child[propertyName] = _clone(parent[propertyName], depth - 1);
Object.defineProperty(child, propertyName, descriptor);
}
}
return child;
}
return _clone(parent, depth);
}
/**
* Simple flat clone using prototype, accepts only objects, usefull for property
* override on FLAT configuration object (no nested props).
*
* USE WITH CAUTION! This may not behave as you wish if you do not know how this
* works.
*/
clone.clonePrototype = function clonePrototype(parent) {
if (parent === null)
return null;
var c = function () {};
c.prototype = parent;
return new c();
};
// private utility functions
function __objToStr(o) {
return Object.prototype.toString.call(o);
}
clone.__objToStr = __objToStr;
function __isDate(o) {
return typeof o === 'object' && __objToStr(o) === '[object Date]';
}
clone.__isDate = __isDate;
function __isArray(o) {
return typeof o === 'object' && __objToStr(o) === '[object Array]';
}
clone.__isArray = __isArray;
function __isRegExp(o) {
return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
}
clone.__isRegExp = __isRegExp;
function __getRegExpFlags(re) {
var flags = '';
if (re.global) flags += 'g';
if (re.ignoreCase) flags += 'i';
if (re.multiline) flags += 'm';
return flags;
}
clone.__getRegExpFlags = __getRegExpFlags;
return clone;
})();
export default clone
export default {
props: {
lang: String,
sessionFrom: String,
sendMessageTitle: String,
sendMessagePath: String,
sendMessageImg: String,
showMessageCard: Boolean,
appParameter: String,
formType: String,
openType: String
}
}
import * as index from '../function/index.js';
import * as test from '../function/test.js';
export default {
// 定义每个组件都可能需要用到的外部样式以及类名
props: {
// 每个组件都有的父组件传递的样式,可以为字符串或者对象形式
customStyle: {
type: [Object, String],
default: () => ({})
},
customClass: {
type: String,
default: ''
},
// 跳转的页面路径
url: {
type: String,
default: ''
},
// 页面跳转的类型
linkType: {
type: String,
default: 'navigateTo'
}
},
data() {
return {}
},
onLoad() {
// getRect挂载到$w上,因为这方法需要使用in(this),所以无法把它独立成一个单独的文件导出
this.$w.getRect = this.$wuGetRect;
},
created() {
// 组件当中,只有created声明周期,为了能在组件使用,故也在created中将方法挂载到$w
this.$w.getRect = this.$wuGetRect;
},
computed: {
$w() {
return {
...index,
test
}
},
/**
* 生成bem规则类名
* 由于微信小程序,H5,nvue之间绑定class的差异,无法通过:class="[bem()]"的形式进行同用
* 故采用如下折中做法,最后返回的是数组(一般平台)或字符串(支付宝和字节跳动平台),类似['a', 'b', 'c']或'a b c'的形式
* @param {String} name 组件名称
* @param {Array} fixed 一直会存在的类名
* @param {Array} change 会根据变量值为true或者false而出现或者隐藏的类名
* @returns {Array|string}
*/
bem() {
return function(name, fixed, change) {
// 类名前缀
const prefix = `wu-${name}--`
const classes = {}
if (fixed) {
fixed.map((item) => {
// 这里的类名,会一直存在
classes[prefix + this[item]] = true
})
}
if (change) {
change.map((item) => {
// 这里的类名,会根据this[item]的值为true或者false,而进行添加或者移除某一个类
this[item] ? (classes[prefix + item] = this[item]) : (delete classes[prefix + item])
})
}
return Object.keys(classes)
// 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
// #ifdef MP-ALIPAY || MP-TOUTIAO || MP-LARK || MP-BAIDU
.join(' ')
// #endif
}
}
},
methods: {
// 跳转某一个页面
openPage(urlKey = 'url') {
const url = this[urlKey]
if (url) {
// 执行类似uni.navigateTo的方法
uni[this.linkType]({
url
})
}
},
// 查询节点信息
// 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸,为支付宝的bug(2020-07-21)
// 解决办法为在组件根部再套一个没有任何作用的view元素
$wuGetRect(selector, all) {
return new Promise((resolve) => {
uni.createSelectorQuery()
.in(this)[all ? 'selectAll' : 'select'](selector)
.boundingClientRect((rect) => {
if (all && Array.isArray(rect) && rect.length) {
resolve(rect)
}
if (!all && rect) {
resolve(rect)
}
})
.exec()
})
},
// 查询节点布局是否相交
IntersectionObserver(_this, nodeName, callback) {
this.$nextTick(() => {
// #ifndef APP-NVUE || H5
let intersectionObserver = uni.createIntersectionObserver(_this)
intersectionObserver.relativeToViewport({
bottom: Number(this.lazyLoadRootMargin)
}).observe(nodeName, res => {
callback(res, ()=>intersectionObserver.disconnect());
})
// #endif
// #ifdef H5
if (!window.__wu_observedComponents) window.__wu_observedComponents = new Map(); // 用于存储元素及其对应的回调函数
if (!window.__wu_IntersectionObserver) {
window.__wu_IntersectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const callback = window.__wu_observedComponents.get(entry.target);
if (callback) {
callback(entry, ()=>{
window.__wu_IntersectionObserver.unobserve(entry.target);
window.__wu_observedComponents.delete(entry.target);
});
}
});
}, {
root: null,
rootMargin: Number(this.lazyLoadRootMargin) + 'px',
threshold: 0
});
}
window.__wu_observedComponents.set(_this.$el, callback);
window.__wu_IntersectionObserver.observe(_this.$el)
// #endif
})
},
getParentData(parentName = '') {
// 避免在created中去定义parent变量
if (!this.parent) this.parent = {}
// 这里的本质原理是,通过获取父组件实例(也即类似wu-radio的父组件wu-radio-group的this)
// 将父组件this中对应的参数,赋值给本组件(wu-radio的this)的parentData对象中对应的属性
// 之所以需要这么做,是因为所有端中,头条小程序不支持通过this.parent.xxx去监听父组件参数的变化
// 此处并不会自动更新子组件的数据,而是依赖父组件wu-radio-group去监听data的变化,手动调用更新子组件的方法去重新获取
this.parent = this.$w.$parent.call(this, parentName)
if (this.parent.children) {
// 如果父组件的children不存在本组件的实例,才将本实例添加到父组件的children中
this.parent.children.indexOf(this) === -1 && this.parent.children.push(this)
}
if (this.parent && this.parentData) {
// 历遍parentData中的属性,将parent中的同名属性赋值给parentData
Object.keys(this.parentData).map((key) => {
this.parentData[key] = this.parent[key]
})
}
},
// 阻止事件冒泡
preventEvent(e) {
e && typeof(e.stopPropagation) === 'function' && e.stopPropagation()
},
// 空操作
noop(e) {
this.preventEvent(e)
}
},
onReachBottom() {
uni.$emit('wuOnReachBottom')
},
beforeDestroy() {
// 判断当前页面是否存在parent和chldren,一般在checkbox和checkbox-group父子联动的场景会有此情况
// 组件销毁时,移除子组件在父组件children数组中的实例,释放资源,避免数据混乱
if (this.parent && test.array(this.parent.children)) {
// 组件销毁时,移除父组件中的children数组中对应的实例
const childrenList = this.parent.children
childrenList.map((child, index) => {
// 如果相等,则移除
if (child === this) {
childrenList.splice(index, 1)
}
})
}
}
}
\ No newline at end of file
export default {
// #ifdef MP-WEIXIN
// 将自定义节点设置成虚拟的(去掉自定义组件包裹层),更加接近Vue组件的表现,能更好的使用flex属性
options: {
virtualHost: true
}
// #endif
}
\ No newline at end of file
export default {
onLoad() {
// 设置默认的转发参数
uni.$wu.mpShare = {
title: '', // 默认为小程序名称
path: '', // 默认为当前页面路径
imageUrl: '' // 默认为当前页面的截图
}
},
onShareAppMessage() {
return uni.$wu.mpShare
}
}
\ No newline at end of file
export default {
props: {
openType: String
},
emits: ['getphonenumber','getuserinfo','error','opensetting','launchapp','contact','chooseavatar','addgroupapp','chooseaddress','subscribe','login','im'],
methods: {
onGetPhoneNumber(event) {
this.$emit('getphonenumber', event.detail)
},
onGetUserInfo(event) {
this.$emit('getuserinfo', event.detail)
},
onError(event) {
this.$emit('error', event.detail)
},
onOpenSetting(event) {
this.$emit('opensetting', event.detail)
},
onLaunchApp(event) {
this.$emit('launchapp', event.detail)
},
onContact(event) {
this.$emit('contact', event.detail)
},
onChooseavatar(event) {
this.$emit('chooseavatar', event.detail)
},
onAddgroupapp(event) {
this.$emit('addgroupapp', event.detail)
},
onChooseaddress(event) {
this.$emit('chooseaddress', event.detail)
},
onSubscribe(event) {
this.$emit('subscribe', event.detail)
},
onLogin(event) {
this.$emit('login', event.detail)
},
onIm(event) {
this.$emit('im', event.detail)
}
}
}
const MIN_DISTANCE = 10
function getDirection(x, y) {
if (x > y && x > MIN_DISTANCE) {
return 'horizontal'
}
if (y > x && y > MIN_DISTANCE) {
return 'vertical'
}
return ''
}
export default {
methods: {
getTouchPoint(e) {
if (!e) {
return {
x: 0,
y: 0
}
} if (e.touches && e.touches[0]) {
return {
x: e.touches[0].pageX,
y: e.touches[0].pageY
}
} if (e.changedTouches && e.changedTouches[0]) {
return {
x: e.changedTouches[0].pageX,
y: e.changedTouches[0].pageY
}
}
return {
x: e.clientX || 0,
y: e.clientY || 0
}
},
resetTouchStatus() {
this.direction = ''
this.deltaX = 0
this.deltaY = 0
this.offsetX = 0
this.offsetY = 0
},
touchStart(event) {
this.resetTouchStatus()
const touch = this.getTouchPoint(event)
this.startX = touch.x
this.startY = touch.y
},
touchMove(event) {
const touch = this.getTouchPoint(event)
this.deltaX = touch.x - this.startX
this.deltaY = touch.y - this.startY
this.offsetX = Math.abs(this.deltaX)
this.offsetY = Math.abs(this.deltaY)
this.direction = this.direction || getDirection(this.offsetX, this.offsetY)
}
}
}
function _extends() {
_extends = Object.assign || function (target) {
for (let i = 1; i < arguments.length; i++) {
const source = arguments[i]
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key]
}
}
}
return target
}
return _extends.apply(this, arguments)
}
/* eslint no-console:0 */
const formatRegExp = /%[sdj%]/g
let warning = function warning() {} // don't print warning message when in production env or node runtime
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production' && typeof window
!== 'undefined' && typeof document !== 'undefined') {
warning = function warning(type, errors) {
if (typeof console !== 'undefined' && console.warn) {
if (errors.every((e) => typeof e === 'string')) {
console.warn(type, errors)
}
}
}
}
function convertFieldsError(errors) {
if (!errors || !errors.length) return null
const fields = {}
errors.forEach((error) => {
const { field } = error
fields[field] = fields[field] || []
fields[field].push(error)
})
return fields
}
function format() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key]
}
let i = 1
const f = args[0]
const len = args.length
if (typeof f === 'function') {
return f.apply(null, args.slice(1))
}
if (typeof f === 'string') {
let str = String(f).replace(formatRegExp, (x) => {
if (x === '%%') {
return '%'
}
if (i >= len) {
return x
}
switch (x) {
case '%s':
return String(args[i++])
case '%d':
return Number(args[i++])
case '%j':
try {
return JSON.stringify(args[i++])
} catch (_) {
return '[Circular]'
}
break
default:
return x
}
})
for (let arg = args[i]; i < len; arg = args[++i]) {
str += ` ${arg}`
}
return str
}
return f
}
function isNativeStringType(type) {
return type === 'string' || type === 'url' || type === 'hex' || type === 'email' || type === 'pattern'
}
function isEmptyValue(value, type) {
if (value === undefined || value === null) {
return true
}
if (type === 'array' && Array.isArray(value) && !value.length) {
return true
}
if (isNativeStringType(type) && typeof value === 'string' && !value) {
return true
}
return false
}
function asyncParallelArray(arr, func, callback) {
const results = []
let total = 0
const arrLength = arr.length
function count(errors) {
results.push.apply(results, errors)
total++
if (total === arrLength) {
callback(results)
}
}
arr.forEach((a) => {
func(a, count)
})
}
function asyncSerialArray(arr, func, callback) {
let index = 0
const arrLength = arr.length
function next(errors) {
if (errors && errors.length) {
callback(errors)
return
}
const original = index
index += 1
if (original < arrLength) {
func(arr[original], next)
} else {
callback([])
}
}
next([])
}
function flattenObjArr(objArr) {
const ret = []
Object.keys(objArr).forEach((k) => {
ret.push.apply(ret, objArr[k])
})
return ret
}
function asyncMap(objArr, option, func, callback) {
if (option.first) {
const _pending = new Promise((resolve, reject) => {
const next = function next(errors) {
callback(errors)
return errors.length ? reject({
errors,
fields: convertFieldsError(errors)
}) : resolve()
}
const flattenArr = flattenObjArr(objArr)
asyncSerialArray(flattenArr, func, next)
})
_pending.catch((e) => e)
return _pending
}
let firstFields = option.firstFields || []
if (firstFields === true) {
firstFields = Object.keys(objArr)
}
const objArrKeys = Object.keys(objArr)
const objArrLength = objArrKeys.length
let total = 0
const results = []
const pending = new Promise((resolve, reject) => {
const next = function next(errors) {
results.push.apply(results, errors)
total++
if (total === objArrLength) {
callback(results)
return results.length ? reject({
errors: results,
fields: convertFieldsError(results)
}) : resolve()
}
}
if (!objArrKeys.length) {
callback(results)
resolve()
}
objArrKeys.forEach((key) => {
const arr = objArr[key]
if (firstFields.indexOf(key) !== -1) {
asyncSerialArray(arr, func, next)
} else {
asyncParallelArray(arr, func, next)
}
})
})
pending.catch((e) => e)
return pending
}
function complementError(rule) {
return function (oe) {
if (oe && oe.message) {
oe.field = oe.field || rule.fullField
return oe
}
return {
message: typeof oe === 'function' ? oe() : oe,
field: oe.field || rule.fullField
}
}
}
function deepMerge(target, source) {
if (source) {
for (const s in source) {
if (source.hasOwnProperty(s)) {
const value = source[s]
if (typeof value === 'object' && typeof target[s] === 'object') {
target[s] = { ...target[s], ...value }
} else {
target[s] = value
}
}
}
}
return target
}
/**
* Rule for validating required fields.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function required(rule, value, source, errors, options, type) {
if (rule.required && (!source.hasOwnProperty(rule.field) || isEmptyValue(value, type || rule.type))) {
errors.push(format(options.messages.required, rule.fullField))
}
}
/**
* Rule for validating whitespace.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function whitespace(rule, value, source, errors, options) {
if (/^\s+$/.test(value) || value === '') {
errors.push(format(options.messages.whitespace, rule.fullField))
}
}
/* eslint max-len:0 */
const pattern = {
// http://emailregex.com/
email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
url: new RegExp(
'^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$',
'i'
),
hex: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i
}
var types = {
integer: function integer(value) {
return /^(-)?\d+$/.test(value);
},
float: function float(value) {
return /^(-)?\d+(\.\d+)?$/.test(value);
},
array: function array(value) {
return Array.isArray(value)
},
regexp: function regexp(value) {
if (value instanceof RegExp) {
return true
}
try {
return !!new RegExp(value)
} catch (e) {
return false
}
},
date: function date(value) {
return typeof value.getTime === 'function' && typeof value.getMonth === 'function' && typeof value.getYear
=== 'function'
},
number: function number(value) {
if (isNaN(value)) {
return false
}
// 修改源码,将字符串数值先转为数值
return typeof +value === 'number'
},
object: function object(value) {
return typeof value === 'object' && !types.array(value)
},
method: function method(value) {
return typeof value === 'function'
},
email: function email(value) {
return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255
},
url: function url(value) {
return typeof value === 'string' && !!value.match(pattern.url)
},
hex: function hex(value) {
return typeof value === 'string' && !!value.match(pattern.hex)
}
}
/**
* Rule for validating the type of a value.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function type(rule, value, source, errors, options) {
if (rule.required && value === undefined) {
required(rule, value, source, errors, options)
return
}
const custom = ['integer', 'float', 'array', 'regexp', 'object', 'method', 'email', 'number', 'date', 'url', 'hex']
const ruleType = rule.type
if (custom.indexOf(ruleType) > -1) {
if (!types[ruleType](value)) {
errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type))
} // straight typeof check
} else if (ruleType && typeof value !== rule.type) {
errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type))
}
}
/**
* Rule for validating minimum and maximum allowed values.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function range(rule, value, source, errors, options) {
const len = typeof rule.len === 'number'
const min = typeof rule.min === 'number'
const max = typeof rule.max === 'number' // 正则匹配码点范围从U+010000一直到U+10FFFF的文字(补充平面Supplementary Plane)
const spRegexp = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g
let val = value
let key = null
const num = typeof value === 'number'
const str = typeof value === 'string'
const arr = Array.isArray(value)
if (num) {
key = 'number'
} else if (str) {
key = 'string'
} else if (arr) {
key = 'array'
} // if the value is not of a supported type for range validation
// the validation rule rule should use the
// type property to also test for a particular type
if (!key) {
return false
}
if (arr) {
val = value.length
}
if (str) {
// 处理码点大于U+010000的文字length属性不准确的bug,如"𠮷𠮷𠮷".lenght !== 3
val = value.replace(spRegexp, '_').length
}
if (len) {
if (val !== rule.len) {
errors.push(format(options.messages[key].len, rule.fullField, rule.len))
}
} else if (min && !max && val < rule.min) {
errors.push(format(options.messages[key].min, rule.fullField, rule.min))
} else if (max && !min && val > rule.max) {
errors.push(format(options.messages[key].max, rule.fullField, rule.max))
} else if (min && max && (val < rule.min || val > rule.max)) {
errors.push(format(options.messages[key].range, rule.fullField, rule.min, rule.max))
}
}
const ENUM = 'enum'
/**
* Rule for validating a value exists in an enumerable list.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function enumerable(rule, value, source, errors, options) {
rule[ENUM] = Array.isArray(rule[ENUM]) ? rule[ENUM] : []
if (rule[ENUM].indexOf(value) === -1) {
errors.push(format(options.messages[ENUM], rule.fullField, rule[ENUM].join(', ')))
}
}
/**
* Rule for validating a regular expression pattern.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function pattern$1(rule, value, source, errors, options) {
if (rule.pattern) {
if (rule.pattern instanceof RegExp) {
// if a RegExp instance is passed, reset `lastIndex` in case its `global`
// flag is accidentally set to `true`, which in a validation scenario
// is not necessary and the result might be misleading
rule.pattern.lastIndex = 0
if (!rule.pattern.test(value)) {
errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern))
}
} else if (typeof rule.pattern === 'string') {
const _pattern = new RegExp(rule.pattern)
if (!_pattern.test(value)) {
errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern))
}
}
}
}
const rules = {
required,
whitespace,
type,
range,
enum: enumerable,
pattern: pattern$1
}
/**
* Performs validation for string types.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function string(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value, 'string') && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options, 'string')
if (!isEmptyValue(value, 'string')) {
rules.type(rule, value, source, errors, options)
rules.range(rule, value, source, errors, options)
rules.pattern(rule, value, source, errors, options)
if (rule.whitespace === true) {
rules.whitespace(rule, value, source, errors, options)
}
}
}
callback(errors)
}
/**
* Validates a function.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function method(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
if (value !== undefined) {
rules.type(rule, value, source, errors, options)
}
}
callback(errors)
}
/**
* Validates a number.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function number(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (value === '') {
value = undefined
}
if (isEmptyValue(value) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
if (value !== undefined) {
rules.type(rule, value, source, errors, options)
rules.range(rule, value, source, errors, options)
}
}
callback(errors)
}
/**
* Validates a boolean.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function _boolean(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
if (value !== undefined) {
rules.type(rule, value, source, errors, options)
}
}
callback(errors)
}
/**
* Validates the regular expression type.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function regexp(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
if (!isEmptyValue(value)) {
rules.type(rule, value, source, errors, options)
}
}
callback(errors)
}
/**
* Validates a number is an integer.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function integer(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
if (value !== undefined) {
rules.type(rule, value, source, errors, options)
rules.range(rule, value, source, errors, options)
}
}
callback(errors)
}
/**
* Validates a number is a floating point number.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function floatFn(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
if (value !== undefined) {
rules.type(rule, value, source, errors, options)
rules.range(rule, value, source, errors, options)
}
}
callback(errors)
}
/**
* Validates an array.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function array(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value, 'array') && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options, 'array')
if (!isEmptyValue(value, 'array')) {
rules.type(rule, value, source, errors, options)
rules.range(rule, value, source, errors, options)
}
}
callback(errors)
}
/**
* Validates an object.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function object(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
if (value !== undefined) {
rules.type(rule, value, source, errors, options)
}
}
callback(errors)
}
const ENUM$1 = 'enum'
/**
* Validates an enumerable list.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function enumerable$1(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
if (value !== undefined) {
rules[ENUM$1](rule, value, source, errors, options)
}
}
callback(errors)
}
/**
* Validates a regular expression pattern.
*
* Performs validation when a rule only contains
* a pattern property but is not declared as a string type.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function pattern$2(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value, 'string') && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
if (!isEmptyValue(value, 'string')) {
rules.pattern(rule, value, source, errors, options)
}
}
callback(errors)
}
function date(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
if (!isEmptyValue(value)) {
let dateObject
if (typeof value === 'number') {
dateObject = new Date(value)
} else {
dateObject = value
}
rules.type(rule, dateObject, source, errors, options)
if (dateObject) {
rules.range(rule, dateObject.getTime(), source, errors, options)
}
}
}
callback(errors)
}
function required$1(rule, value, callback, source, options) {
const errors = []
const type = Array.isArray(value) ? 'array' : typeof value
rules.required(rule, value, source, errors, options, type)
callback(errors)
}
function type$1(rule, value, callback, source, options) {
const ruleType = rule.type
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value, ruleType) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options, ruleType)
if (!isEmptyValue(value, ruleType)) {
rules.type(rule, value, source, errors, options)
}
}
callback(errors)
}
/**
* Performs validation for any type.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function any(rule, value, callback, source, options) {
const errors = []
const validate = rule.required || !rule.required && source.hasOwnProperty(rule.field)
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback()
}
rules.required(rule, value, source, errors, options)
}
callback(errors)
}
const validators = {
string,
method,
number,
boolean: _boolean,
regexp,
integer,
float: floatFn,
array,
object,
enum: enumerable$1,
pattern: pattern$2,
date,
url: type$1,
hex: type$1,
email: type$1,
required: required$1,
any
}
function newMessages() {
return {
default: 'Validation error on field %s',
required: '%s is required',
enum: '%s must be one of %s',
whitespace: '%s cannot be empty',
date: {
format: '%s date %s is invalid for format %s',
parse: '%s date could not be parsed, %s is invalid ',
invalid: '%s date %s is invalid'
},
types: {
string: '%s is not a %s',
method: '%s is not a %s (function)',
array: '%s is not an %s',
object: '%s is not an %s',
number: '%s is not a %s',
date: '%s is not a %s',
boolean: '%s is not a %s',
integer: '%s is not an %s',
float: '%s is not a %s',
regexp: '%s is not a valid %s',
email: '%s is not a valid %s',
url: '%s is not a valid %s',
hex: '%s is not a valid %s'
},
string: {
len: '%s must be exactly %s characters',
min: '%s must be at least %s characters',
max: '%s cannot be longer than %s characters',
range: '%s must be between %s and %s characters'
},
number: {
len: '%s must equal %s',
min: '%s cannot be less than %s',
max: '%s cannot be greater than %s',
range: '%s must be between %s and %s'
},
array: {
len: '%s must be exactly %s in length',
min: '%s cannot be less than %s in length',
max: '%s cannot be greater than %s in length',
range: '%s must be between %s and %s in length'
},
pattern: {
mismatch: '%s value %s does not match pattern %s'
},
clone: function clone() {
const cloned = JSON.parse(JSON.stringify(this))
cloned.clone = this.clone
return cloned
}
}
}
const messages = newMessages()
/**
* Encapsulates a validation schema.
*
* @param descriptor An object declaring validation rules
* for this schema.
*/
function Schema(descriptor) {
this.rules = null
this._messages = messages
this.define(descriptor)
}
Schema.prototype = {
messages: function messages(_messages) {
if (_messages) {
this._messages = deepMerge(newMessages(), _messages)
}
return this._messages
},
define: function define(rules) {
if (!rules) {
throw new Error('Cannot configure a schema with no rules')
}
if (typeof rules !== 'object' || Array.isArray(rules)) {
throw new Error('Rules must be an object')
}
this.rules = {}
let z
let item
for (z in rules) {
if (rules.hasOwnProperty(z)) {
item = rules[z]
this.rules[z] = Array.isArray(item) ? item : [item]
}
}
},
validate: function validate(source_, o, oc) {
const _this = this
if (o === void 0) {
o = {}
}
if (oc === void 0) {
oc = function oc() {}
}
let source = source_
let options = o
let callback = oc
if (typeof options === 'function') {
callback = options
options = {}
}
if (!this.rules || Object.keys(this.rules).length === 0) {
if (callback) {
callback()
}
return Promise.resolve()
}
function complete(results) {
let i
let errors = []
let fields = {}
function add(e) {
if (Array.isArray(e)) {
let _errors
errors = (_errors = errors).concat.apply(_errors, e)
} else {
errors.push(e)
}
}
for (i = 0; i < results.length; i++) {
add(results[i])
}
if (!errors.length) {
errors = null
fields = null
} else {
fields = convertFieldsError(errors)
}
callback(errors, fields)
}
if (options.messages) {
let messages$1 = this.messages()
if (messages$1 === messages) {
messages$1 = newMessages()
}
deepMerge(messages$1, options.messages)
options.messages = messages$1
} else {
options.messages = this.messages()
}
let arr
let value
const series = {}
const keys = options.keys || Object.keys(this.rules)
keys.forEach((z) => {
arr = _this.rules[z]
value = source[z]
arr.forEach((r) => {
let rule = r
if (typeof rule.transform === 'function') {
if (source === source_) {
source = { ...source }
}
value = source[z] = rule.transform(value)
}
if (typeof rule === 'function') {
rule = {
validator: rule
}
} else {
rule = { ...rule }
}
rule.validator = _this.getValidationMethod(rule)
rule.field = z
rule.fullField = rule.fullField || z
rule.type = _this.getType(rule)
if (!rule.validator) {
return
}
series[z] = series[z] || []
series[z].push({
rule,
value,
source,
field: z
})
})
})
const errorFields = {}
return asyncMap(series, options, (data, doIt) => {
const { rule } = data
let deep = (rule.type === 'object' || rule.type === 'array') && (typeof rule.fields === 'object' || typeof rule.defaultField
=== 'object')
deep = deep && (rule.required || !rule.required && data.value)
rule.field = data.field
function addFullfield(key, schema) {
return { ...schema, fullField: `${rule.fullField}.${key}` }
}
function cb(e) {
if (e === void 0) {
e = []
}
let errors = e
if (!Array.isArray(errors)) {
errors = [errors]
}
if (!options.suppressWarning && errors.length) {
Schema.warning('async-validator:', errors)
}
if (errors.length && rule.message) {
errors = [].concat(rule.message)
}
errors = errors.map(complementError(rule))
if (options.first && errors.length) {
errorFields[rule.field] = 1
return doIt(errors)
}
if (!deep) {
doIt(errors)
} else {
// if rule is required but the target object
// does not exist fail at the rule level and don't
// go deeper
if (rule.required && !data.value) {
if (rule.message) {
errors = [].concat(rule.message).map(complementError(rule))
} else if (options.error) {
errors = [options.error(rule, format(options.messages.required, rule.field))]
} else {
errors = []
}
return doIt(errors)
}
let fieldsSchema = {}
if (rule.defaultField) {
for (const k in data.value) {
if (data.value.hasOwnProperty(k)) {
fieldsSchema[k] = rule.defaultField
}
}
}
fieldsSchema = { ...fieldsSchema, ...data.rule.fields }
for (const f in fieldsSchema) {
if (fieldsSchema.hasOwnProperty(f)) {
const fieldSchema = Array.isArray(fieldsSchema[f]) ? fieldsSchema[f] : [fieldsSchema[f]]
fieldsSchema[f] = fieldSchema.map(addFullfield.bind(null, f))
}
}
const schema = new Schema(fieldsSchema)
schema.messages(options.messages)
if (data.rule.options) {
data.rule.options.messages = options.messages
data.rule.options.error = options.error
}
schema.validate(data.value, data.rule.options || options, (errs) => {
const finalErrors = []
if (errors && errors.length) {
finalErrors.push.apply(finalErrors, errors)
}
if (errs && errs.length) {
finalErrors.push.apply(finalErrors, errs)
}
doIt(finalErrors.length ? finalErrors : null)
})
}
}
let res
if (rule.asyncValidator) {
res = rule.asyncValidator(rule, data.value, cb, data.source, options)
} else if (rule.validator) {
res = rule.validator(rule, data.value, cb, data.source, options)
if (res === true) {
cb()
} else if (res === false) {
cb(rule.message || `${rule.field} fails`)
} else if (res instanceof Array) {
cb(res)
} else if (res instanceof Error) {
cb(res.message)
}
}
if (res && res.then) {
res.then(() => cb(), (e) => cb(e))
}
}, (results) => {
complete(results)
})
},
getType: function getType(rule) {
if (rule.type === undefined && rule.pattern instanceof RegExp) {
rule.type = 'pattern'
}
if (typeof rule.validator !== 'function' && rule.type && !validators.hasOwnProperty(rule.type)) {
throw new Error(format('Unknown rule type %s', rule.type))
}
return rule.type || 'string'
},
getValidationMethod: function getValidationMethod(rule) {
if (typeof rule.validator === 'function') {
return rule.validator
}
const keys = Object.keys(rule)
const messageIndex = keys.indexOf('message')
if (messageIndex !== -1) {
keys.splice(messageIndex, 1)
}
if (keys.length === 1 && keys[0] === 'required') {
return validators.required
}
return validators[this.getType(rule)] || false
}
}
Schema.register = function register(type, validator) {
if (typeof validator !== 'function') {
throw new Error('Cannot register a validator by type, validator is not a function')
}
validators[type] = validator
}
Schema.warning = warning
Schema.messages = messages
export default Schema
// # sourceMappingURL=index.js.map
var __getOwnPropNames = Object.getOwnPropertyNames;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
// C:/Users/LP/Downloads/wuui-plus_3.1.27_example/node_modules/dayjs/dayjs.min.js
var require_dayjs_min = __commonJS({
"C:/Users/LP/Downloads/wuui-plus_3.1.27_example/node_modules/dayjs/dayjs.min.js"(exports, module) {
!function(t, e) {
"object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e();
}(exports, function() {
"use strict";
var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", f = "month", h = "quarter", c = "year", d = "date", l = "Invalid Date", $ = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), ordinal: function(t2) {
var e2 = ["th", "st", "nd", "rd"], n2 = t2 % 100;
return "[" + t2 + (e2[(n2 - 20) % 10] || e2[n2] || e2[0]) + "]";
} }, m = function(t2, e2, n2) {
var r2 = String(t2);
return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2;
}, v = { s: m, z: function(t2) {
var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60;
return (e2 <= 0 ? "+" : "-") + m(r2, 2, "0") + ":" + m(i2, 2, "0");
}, m: function t2(e2, n2) {
if (e2.date() < n2.date())
return -t2(n2, e2);
var r2 = 12 * (n2.year() - e2.year()) + (n2.month() - e2.month()), i2 = e2.clone().add(r2, f), s2 = n2 - i2 < 0, u2 = e2.clone().add(r2 + (s2 ? -1 : 1), f);
return +(-(r2 + (n2 - i2) / (s2 ? i2 - u2 : u2 - i2)) || 0);
}, a: function(t2) {
return t2 < 0 ? Math.ceil(t2) || 0 : Math.floor(t2);
}, p: function(t2) {
return { M: f, y: c, w: o, d: a, D: d, h: u, m: s, s: i, ms: r, Q: h }[t2] || String(t2 || "").toLowerCase().replace(/s$/, "");
}, u: function(t2) {
return void 0 === t2;
} }, g = "en", D = {};
D[g] = M;
var p = function(t2) {
return t2 instanceof _;
}, S = function t2(e2, n2, r2) {
var i2;
if (!e2)
return g;
if ("string" == typeof e2) {
var s2 = e2.toLowerCase();
D[s2] && (i2 = s2), n2 && (D[s2] = n2, i2 = s2);
var u2 = e2.split("-");
if (!i2 && u2.length > 1)
return t2(u2[0]);
} else {
var a2 = e2.name;
D[a2] = e2, i2 = a2;
}
return !r2 && i2 && (g = i2), i2 || !r2 && g;
}, w = function(t2, e2) {
if (p(t2))
return t2.clone();
var n2 = "object" == typeof e2 ? e2 : {};
return n2.date = t2, n2.args = arguments, new _(n2);
}, O = v;
O.l = S, O.i = p, O.w = function(t2, e2) {
return w(t2, { locale: e2.$L, utc: e2.$u, x: e2.$x, $offset: e2.$offset });
};
var _ = function() {
function M2(t2) {
this.$L = S(t2.locale, null, true), this.parse(t2);
}
var m2 = M2.prototype;
return m2.parse = function(t2) {
this.$d = function(t3) {
var e2 = t3.date, n2 = t3.utc;
if (null === e2)
return new Date(NaN);
if (O.u(e2))
return new Date();
if (e2 instanceof Date)
return new Date(e2);
if ("string" == typeof e2 && !/Z$/i.test(e2)) {
var r2 = e2.match($);
if (r2) {
var i2 = r2[2] - 1 || 0, s2 = (r2[7] || "0").substring(0, 3);
return n2 ? new Date(Date.UTC(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2)) : new Date(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2);
}
}
return new Date(e2);
}(t2), this.$x = t2.x || {}, this.init();
}, m2.init = function() {
var t2 = this.$d;
this.$y = t2.getFullYear(), this.$M = t2.getMonth(), this.$D = t2.getDate(), this.$W = t2.getDay(), this.$H = t2.getHours(), this.$m = t2.getMinutes(), this.$s = t2.getSeconds(), this.$ms = t2.getMilliseconds();
}, m2.$utils = function() {
return O;
}, m2.isValid = function() {
return !(this.$d.toString() === l);
}, m2.isSame = function(t2, e2) {
var n2 = w(t2);
return this.startOf(e2) <= n2 && n2 <= this.endOf(e2);
}, m2.isAfter = function(t2, e2) {
return w(t2) < this.startOf(e2);
}, m2.isBefore = function(t2, e2) {
return this.endOf(e2) < w(t2);
}, m2.$g = function(t2, e2, n2) {
return O.u(t2) ? this[e2] : this.set(n2, t2);
}, m2.unix = function() {
return Math.floor(this.valueOf() / 1e3);
}, m2.valueOf = function() {
return this.$d.getTime();
}, m2.startOf = function(t2, e2) {
var n2 = this, r2 = !!O.u(e2) || e2, h2 = O.p(t2), l2 = function(t3, e3) {
var i2 = O.w(n2.$u ? Date.UTC(n2.$y, e3, t3) : new Date(n2.$y, e3, t3), n2);
return r2 ? i2 : i2.endOf(a);
}, $2 = function(t3, e3) {
return O.w(n2.toDate()[t3].apply(n2.toDate("s"), (r2 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e3)), n2);
}, y2 = this.$W, M3 = this.$M, m3 = this.$D, v2 = "set" + (this.$u ? "UTC" : "");
switch (h2) {
case c:
return r2 ? l2(1, 0) : l2(31, 11);
case f:
return r2 ? l2(1, M3) : l2(0, M3 + 1);
case o:
var g2 = this.$locale().weekStart || 0, D2 = (y2 < g2 ? y2 + 7 : y2) - g2;
return l2(r2 ? m3 - D2 : m3 + (6 - D2), M3);
case a:
case d:
return $2(v2 + "Hours", 0);
case u:
return $2(v2 + "Minutes", 1);
case s:
return $2(v2 + "Seconds", 2);
case i:
return $2(v2 + "Milliseconds", 3);
default:
return this.clone();
}
}, m2.endOf = function(t2) {
return this.startOf(t2, false);
}, m2.$set = function(t2, e2) {
var n2, o2 = O.p(t2), h2 = "set" + (this.$u ? "UTC" : ""), l2 = (n2 = {}, n2[a] = h2 + "Date", n2[d] = h2 + "Date", n2[f] = h2 + "Month", n2[c] = h2 + "FullYear", n2[u] = h2 + "Hours", n2[s] = h2 + "Minutes", n2[i] = h2 + "Seconds", n2[r] = h2 + "Milliseconds", n2)[o2], $2 = o2 === a ? this.$D + (e2 - this.$W) : e2;
if (o2 === f || o2 === c) {
var y2 = this.clone().set(d, 1);
y2.$d[l2]($2), y2.init(), this.$d = y2.set(d, Math.min(this.$D, y2.daysInMonth())).$d;
} else
l2 && this.$d[l2]($2);
return this.init(), this;
}, m2.set = function(t2, e2) {
return this.clone().$set(t2, e2);
}, m2.get = function(t2) {
return this[O.p(t2)]();
}, m2.add = function(r2, h2) {
var d2, l2 = this;
r2 = Number(r2);
var $2 = O.p(h2), y2 = function(t2) {
var e2 = w(l2);
return O.w(e2.date(e2.date() + Math.round(t2 * r2)), l2);
};
if ($2 === f)
return this.set(f, this.$M + r2);
if ($2 === c)
return this.set(c, this.$y + r2);
if ($2 === a)
return y2(1);
if ($2 === o)
return y2(7);
var M3 = (d2 = {}, d2[s] = e, d2[u] = n, d2[i] = t, d2)[$2] || 1, m3 = this.$d.getTime() + r2 * M3;
return O.w(m3, this);
}, m2.subtract = function(t2, e2) {
return this.add(-1 * t2, e2);
}, m2.format = function(t2) {
var e2 = this, n2 = this.$locale();
if (!this.isValid())
return n2.invalidDate || l;
var r2 = t2 || "YYYY-MM-DDTHH:mm:ssZ", i2 = O.z(this), s2 = this.$H, u2 = this.$m, a2 = this.$M, o2 = n2.weekdays, f2 = n2.months, h2 = function(t3, n3, i3, s3) {
return t3 && (t3[n3] || t3(e2, r2)) || i3[n3].slice(0, s3);
}, c2 = function(t3) {
return O.s(s2 % 12 || 12, t3, "0");
}, d2 = n2.meridiem || function(t3, e3, n3) {
var r3 = t3 < 12 ? "AM" : "PM";
return n3 ? r3.toLowerCase() : r3;
}, $2 = { YY: String(this.$y).slice(-2), YYYY: this.$y, M: a2 + 1, MM: O.s(a2 + 1, 2, "0"), MMM: h2(n2.monthsShort, a2, f2, 3), MMMM: h2(f2, a2), D: this.$D, DD: O.s(this.$D, 2, "0"), d: String(this.$W), dd: h2(n2.weekdaysMin, this.$W, o2, 2), ddd: h2(n2.weekdaysShort, this.$W, o2, 3), dddd: o2[this.$W], H: String(s2), HH: O.s(s2, 2, "0"), h: c2(1), hh: c2(2), a: d2(s2, u2, true), A: d2(s2, u2, false), m: String(u2), mm: O.s(u2, 2, "0"), s: String(this.$s), ss: O.s(this.$s, 2, "0"), SSS: O.s(this.$ms, 3, "0"), Z: i2 };
return r2.replace(y, function(t3, e3) {
return e3 || $2[t3] || i2.replace(":", "");
});
}, m2.utcOffset = function() {
return 15 * -Math.round(this.$d.getTimezoneOffset() / 15);
}, m2.diff = function(r2, d2, l2) {
var $2, y2 = O.p(d2), M3 = w(r2), m3 = (M3.utcOffset() - this.utcOffset()) * e, v2 = this - M3, g2 = O.m(this, M3);
return g2 = ($2 = {}, $2[c] = g2 / 12, $2[f] = g2, $2[h] = g2 / 3, $2[o] = (v2 - m3) / 6048e5, $2[a] = (v2 - m3) / 864e5, $2[u] = v2 / n, $2[s] = v2 / e, $2[i] = v2 / t, $2)[y2] || v2, l2 ? g2 : O.a(g2);
}, m2.daysInMonth = function() {
return this.endOf(f).$D;
}, m2.$locale = function() {
return D[this.$L];
}, m2.locale = function(t2, e2) {
if (!t2)
return this.$L;
var n2 = this.clone(), r2 = S(t2, e2, true);
return r2 && (n2.$L = r2), n2;
}, m2.clone = function() {
return O.w(this.$d, this);
}, m2.toDate = function() {
return new Date(this.valueOf());
}, m2.toJSON = function() {
return this.isValid() ? this.toISOString() : null;
}, m2.toISOString = function() {
return this.$d.toISOString();
}, m2.toString = function() {
return this.$d.toUTCString();
}, M2;
}(), T = _.prototype;
return w.prototype = T, [["$ms", r], ["$s", i], ["$m", s], ["$H", u], ["$W", a], ["$M", f], ["$y", c], ["$D", d]].forEach(function(t2) {
T[t2[1]] = function(e2) {
return this.$g(e2, t2[0], t2[1]);
};
}), w.extend = function(t2, e2) {
return t2.$i || (t2(e2, _, w), t2.$i = true), w;
}, w.locale = S, w.isDayjs = p, w.unix = function(t2) {
return w(1e3 * t2);
}, w.en = D[g], w.Ls = D, w.p = {}, w;
});
}
});
export default require_dayjs_min();
//# sourceMappingURL=dayjs.js.map
/**
* 路由跳转方法,该方法相对于直接使用uni.xxx的好处是使用更加简单快捷
* 并且带有路由拦截功能
*/
import { queryParams, deepMerge,page } from '@/uni_modules/wu-ui-tools/libs/function/index.js'
class Router {
constructor() {
// 原始属性定义
this.config = {
type: 'navigateTo',
url: '',
delta: 1, // navigateBack页面后退时,回退的层数
params: {}, // 传递的参数
animationType: 'pop-in', // 窗口动画,只在APP有效
animationDuration: 300, // 窗口动画持续时间,单位毫秒,只在APP有效
intercept: false // 是否需要拦截
}
// 因为route方法是需要对外赋值给另外的对象使用,同时route内部有使用this,会导致route失去上下文
// 这里在构造函数中进行this绑定
this.route = this.route.bind(this)
}
// 判断url前面是否有"/",如果没有则加上,否则无法跳转
addRootPath(url) {
return url[0] === '/' ? url : `/${url}`
}
// 整合路由参数
mixinParam(url, params) {
url = url && this.addRootPath(url)
// 使用正则匹配,主要依据是判断是否有"/","?","="等,如“/page/index/index?name=mary"
// 如果有url中有get参数,转换后无需带上"?"
let query = ''
if (/.*\/.*\?.*=.*/.test(url)) {
// object对象转为get类型的参数
query = queryParams(params, false)
// 因为已有get参数,所以后面拼接的参数需要带上"&"隔开
return url += `&${query}`
}
// 直接拼接参数,因为此处url中没有后面的query参数,也就没有"?/&"之类的符号
query = queryParams(params)
return url += query
}
// 对外的方法名称
async route(options = {}, params = {}) {
// 合并用户的配置和内部的默认配置
let mergeConfig = {}
if (typeof options === 'string') {
// 如果options为字符串,则为route(url, params)的形式
mergeConfig.url = this.mixinParam(options, params)
mergeConfig.type = 'navigateTo'
} else {
mergeConfig = deepMerge(this.config, options)
// 否则正常使用mergeConfig中的url和params进行拼接
mergeConfig.url = this.mixinParam(options.url, options.params)
}
// 如果本次跳转的路径和本页面路径一致,不执行跳转,防止用户快速点击跳转按钮,造成多次跳转同一个页面的问题
if (mergeConfig.url === page()) return
if (params.intercept) {
this.config.intercept = params.intercept
}
// params参数也带给拦截器
mergeConfig.params = params
// 合并内外部参数
mergeConfig = deepMerge(this.config, mergeConfig)
// 判断用户是否定义了拦截器
if (typeof routeIntercept === 'function') {
// 定一个promise,根据用户执行resolve(true)或者resolve(false)来决定是否进行路由跳转
const isNext = await new Promise((resolve, reject) => {
routeIntercept(mergeConfig, resolve)
})
// 如果isNext为true,则执行路由跳转
isNext && this.openPage(mergeConfig)
} else {
this.openPage(mergeConfig)
}
}
// 执行路由跳转
openPage(config) {
// 解构参数
const {
url,
type,
delta,
animationType,
animationDuration
} = config
if (config.type == 'navigateTo' || config.type == 'to') {
uni.navigateTo({
url,
animationType,
animationDuration
})
}
if (config.type == 'redirectTo' || config.type == 'redirect') {
uni.redirectTo({
url
})
}
if (config.type == 'switchTab' || config.type == 'tab') {
uni.switchTab({
url
})
}
if (config.type == 'reLaunch' || config.type == 'launch') {
uni.reLaunch({
url
})
}
if (config.type == 'navigateBack' || config.type == 'back') {
uni.navigateBack({
delta
})
}
}
}
export default (new Router()).route
{
"id": "wu-ui-tools",
"displayName": "wu-ui-tools 工具库 全面兼容小程序、nvue、vue2、vue3",
"version": "1.1.0",
"description": "wu-ui-tools,集成工具库,强大的Http请求封装,清晰的文档说明,开箱即用。方便使用,可以全局使用",
"keywords": [
"wu-ui-tools",
"wu-ui",
"工具库",
"多端全兼容"
],
"dependencies": {
"color": "^4.2.3"
},
"repository": "",
"engines": {
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
}
\ No newline at end of file
## wu-ui-tools 工具集
> **组件名:wu-ui-tools**
wu-ui 工具集成,包括网络Http请求、便捷工具、节流防抖、对象操作、时间格式化、路由跳转、全局唯一标识符、规则校验等等。
需要在自己的项目中使用请参考[扩展配置](https://wu.geeks.ink/zh-CN/components/extendedConfiguration.html)
## <a href="https://wu.geeks.ink/js/intro.html" target="_blank">查看文档</a>
## [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=wu--ui)
(请勿下载插件zip)
<a href="https://ext.dcloud.net.cn/plugin?name=wu--ui">
<img src="https://wu.geeks.ink/intr.png">
</a>
**如使用过程中有任何问题,或者您对wu-ui有一些好的建议,欢迎加入 [wu-ui 交流群](https://wu.geeks.ink/zh-CN/components/qqFeedBack.html)**
// 此文件为wuUI的主题变量,这些变量目前只能通过uni.scss引入才有效,另外由于
// uni.scss中引入的样式会同时混入到全局样式文件和单独每一个页面的样式中,造成微信程序包太大,
// 故uni.scss只建议放scss变量名相关样式,其他的样式可以通过main.js或者App.vue引入
$wu-main-color: #303133;
$wu-content-color: #606266;
$wu-tips-color: #909193;
$wu-light-color: #c0c4cc;
$wu-border-color: #dadbde;
$wu-bg-color: #f3f4f6;
$wu-disabled-color: #c8c9cc;
$wu-primary: #3c9cff;
$wu-primary-dark: #398ade;
$wu-primary-disabled: #9acafc;
$wu-primary-light: #ecf5ff;
$wu-warning: #f9ae3d;
$wu-warning-dark: #f1a532;
$wu-warning-disabled: #f9d39b;
$wu-warning-light: #fdf6ec;
$wu-success: #5ac725;
$wu-success-dark: #53c21d;
$wu-success-disabled: #a9e08f;
$wu-success-light: #f5fff0;
$wu-error: #f56c6c;
$wu-error-dark: #e45656;
$wu-error-disabled: #f7b2b2;
$wu-error-light: #fef0f0;
$wu-info: #909399;
$wu-info-dark: #767a82;
$wu-info-disabled: #c4c6c9;
$wu-info-light: #f4f4f5;
@mixin flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: $direction;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment