已删除62个文件
已添加404个文件
已修改1个文件
对比新文件 |
| | |
| | | ## 0.1.8(2024-08-05) |
| | | - fix: 修复因shared升级导致微信小程序可能不生效的问题 |
| | | ## 0.1.7(2024-05-15) |
| | | - fix: 修复uvue因缺少依赖无法使用 |
| | | ## 0.1.6(2024-05-09) |
| | | - fix: 修复vue2因type问题导致无法使用 |
| | | ## 0.1.5(2024-04-14) |
| | | - fix: 修复缺少依赖 |
| | | ## 0.1.4(2024-04-10) |
| | | - chore: 更新文档 |
| | | ## 0.1.3(2024-04-01) |
| | | - chore: 兼容uniapp x ios(app-js) |
| | | ## 0.1.2(2023-12-14) |
| | | - fix: uvue 引入 API 自定义包出错 |
| | | ## 0.1.1(2023-12-11) |
| | | - chore: uvue的二维码API独立,需要单独下载 |
| | | ## 0.1.0(2023-12-07) |
| | | - fix: 修复因utssdk目录导致无法运行 |
| | | ## 0.0.9(2023-12-06) |
| | | - feat: 支持uvue |
| | | ## 0.0.8(2023-12-06) |
| | | - feat: 支持uvue |
| | | ## 0.0.7(2023-12-06) |
| | | - feat: 支持uvue |
| | | ## 0.0.6(2023-12-06) |
| | | - feat: 支持uvue |
| | | ## 0.0.5(2023-07-30) |
| | | - fix: 修复再次生成前没有清空,导致图形叠加 |
| | | ## 0.0.4(2023-07-27) |
| | | - fix: 修复相同尺寸无法再次生成 |
| | | ## 0.0.3(2023-06-09) |
| | | - feat: 支持通过`@vue/composition-api`在`vue2`上使用 |
| | | - chore: 更新文档 |
| | | ## 0.0.2(2023-06-08) |
| | | - chore: 更新文档 |
| | | ## 0.0.1(2023-06-08) |
| | | - 首次 |
对比新文件 |
| | |
| | | <template> |
| | | <!-- #ifdef MP --> |
| | | <canvas :style="styles" type="2d" :canvas-id="canvasId" :id="canvasId"></canvas> |
| | | <!-- #endif --> |
| | | <!-- #ifdef APP --> |
| | | <view class="l-qrcode" ref="drawableRef" :style="[styles]"> |
| | | <image class="l-qrcode__icon" v-if="icon" :src="icon" :style="[iconStyle]"></image> |
| | | </view> |
| | | <!-- #endif --> |
| | | <!-- #ifdef WEB --> |
| | | <view class="l-qrcode" ref="drawableRef" :style="[styles]"> |
| | | |
| | | </view> |
| | | <!-- #endif --> |
| | | </template> |
| | | <script lang="uts" setup> |
| | | import { type PropType, nextTick } from 'vue' |
| | | // #ifndef APP |
| | | import { QRCodeCanvas } from './qrcode.js'; |
| | | import { QRCodePropsTypes , ImageSettings } from './type' |
| | | // #endif |
| | | // #ifdef APP |
| | | import { QRCodeCanvas, QRCodePropsTypes , ImageSettings } from '@/uni_modules/lime-qrcodegen' |
| | | // #endif |
| | | // import { addUnit } from '@/uni_modules/lime-shared/addUnit' |
| | | // import { unitConvert } from '@/uni_modules/lime-shared/unitConvert' |
| | | // import { toBoolean } from '@/uni_modules/lime-shared/toBoolean' |
| | | import { addUnit, unitConvert } from './utils' |
| | | import { LQrcodeFailCallback, LQrcodeCompleteCallback, LQrcodeSuccessCallback} from './type' |
| | | |
| | | const name = 'l-qrcode' |
| | | const props = defineProps({ |
| | | value: { |
| | | type: String |
| | | }, |
| | | icon: { |
| | | type: String |
| | | }, |
| | | // #ifdef APP-ANDROID |
| | | size: { |
| | | type: Object, |
| | | default: 160 |
| | | }, |
| | | iconSize: { |
| | | type: Object, |
| | | default: 40 |
| | | }, |
| | | // #endif |
| | | // #ifndef APP-ANDROID |
| | | size: { |
| | | type: [Number, String], |
| | | default: 160 |
| | | }, |
| | | iconSize: { |
| | | type: [Number, String], |
| | | default: 40 |
| | | }, |
| | | // #endif |
| | | marginSize: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#000' |
| | | }, |
| | | bgColor: { |
| | | type: String, |
| | | default: 'transparent' |
| | | }, |
| | | bordered: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | errorLevel: { |
| | | type: String as PropType<'L' | 'M' | 'Q' | 'H'>, |
| | | default: 'M' // 'L' | 'M' | 'Q' | 'H' |
| | | }, |
| | | useCanvasToTempFilePath: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | // status: { |
| | | // type: String as PropType<'active'|'expired'|'loading'>, |
| | | // default: 'active' // active | expired | loading |
| | | // } |
| | | }) |
| | | const emits = defineEmits(['success']) |
| | | const context = getCurrentInstance(); |
| | | const canvasId = `l-qrcode${context!.uid}` |
| | | const styles = computed<Map<string, any>>(():Map<string, any>=>{ |
| | | const style = new Map<string, any>() |
| | | const size = addUnit(props.size); |
| | | if(size!=null){ |
| | | style.set('width', size) |
| | | style.set('height', size) |
| | | } |
| | | style.set('background', props.bgColor) |
| | | return style |
| | | }) |
| | | // #ifdef APP |
| | | const iconStyle = computed<Map<string, any>>(():Map<string, any>=>{ |
| | | const style = new Map<string, any>() |
| | | const size = addUnit(props.iconSize); |
| | | if(size!=null){ |
| | | style.set('width', size) |
| | | style.set('height', size) |
| | | } |
| | | return style |
| | | }) |
| | | // #endif |
| | | const drawableRef = ref<UniElement|null>(null); |
| | | // #ifdef WEB |
| | | let canvas:HTMLCanvasElement|null = null |
| | | // #endif |
| | | let qrcode:QRCodeCanvas|null = null |
| | | |
| | | const canvasToTempFilePath = (options: UTSJSONObject)=>{ |
| | | |
| | | const format = options.getString('format') ?? 'png'; |
| | | const fail = options.get('fail') as LQrcodeFailCallback | null; |
| | | const complete = options.get('complete') as LQrcodeCompleteCallback | null; |
| | | const success = options.get('success') as LQrcodeSuccessCallback | null; |
| | | // #ifdef APP |
| | | const newOptions = { |
| | | format, |
| | | fail, |
| | | complete, |
| | | success, |
| | | } as TakeSnapshotOptions |
| | | drawableRef.value!.takeSnapshot(newOptions) |
| | | // #endif |
| | | // #ifdef WEB |
| | | success?.({ |
| | | tempFilePath: canvas?.toDataURL('image/'+format) |
| | | }) |
| | | // #endif |
| | | |
| | | } |
| | | const render = ()=>{ |
| | | const param:QRCodePropsTypes = { |
| | | value: props.value, |
| | | size: unitConvert(props.size), |
| | | fgColor: props.color, |
| | | level: ['L', 'M', 'Q', 'H'].includes(props.errorLevel) ? props.errorLevel : 'M', |
| | | marginSize: props.marginSize, |
| | | includeMargin: props.bordered, |
| | | imageSettings: null, |
| | | } as QRCodePropsTypes |
| | | |
| | | if(props.icon != null){ |
| | | // if(toBoolean(props.iconSize) && toBoolean(props.icon)){ |
| | | const size = unitConvert(props.iconSize) |
| | | param.imageSettings = { |
| | | src: props.icon, |
| | | width: size, |
| | | height: size, |
| | | excavate: true |
| | | } as ImageSettings |
| | | } |
| | | qrcode?.render(param) |
| | | |
| | | if(props.useCanvasToTempFilePath){ |
| | | setTimeout(()=>{ |
| | | canvasToTempFilePath({ |
| | | success: (res: TakeSnapshotSuccess)=>{ |
| | | emits('success', res.tempFilePath) |
| | | } |
| | | }) |
| | | },100) |
| | | } |
| | | } |
| | | defineExpose({ |
| | | canvasToTempFilePath |
| | | }) |
| | | onMounted(()=>{ |
| | | nextTick(()=>{ |
| | | // #ifdef APP |
| | | const ctx = drawableRef.value!.getDrawableContext(); |
| | | qrcode = new QRCodeCanvas(ctx!) |
| | | // #endif |
| | | // #ifdef WEB |
| | | canvas = document.createElement('canvas') |
| | | canvas.style.width = '100%' |
| | | canvas.style.height = '100%' |
| | | drawableRef.value!.appendChild(canvas) |
| | | qrcode = new QRCodeCanvas(canvas, { |
| | | pixelRatio: uni.getSystemInfoSync().pixelRatio, |
| | | createImage: () => { |
| | | const image = new Image(); |
| | | image.crossOrigin = 'anonymous'; |
| | | return image; |
| | | } |
| | | }) |
| | | // #endif |
| | | |
| | | watchEffect(()=>{ |
| | | render() |
| | | }) |
| | | }) |
| | | |
| | | }) |
| | | onUnmounted(()=>{ |
| | | // #ifdef WEB |
| | | canvas?.remove(); |
| | | // #endif |
| | | qrcode = null; |
| | | }) |
| | | </script> |
| | | <style lang="scss"> |
| | | .l-qrcode { |
| | | position: relative; |
| | | background-color: aqua; |
| | | justify-content: center; |
| | | align-items: center; |
| | | |
| | | &-mask { |
| | | position: absolute; |
| | | // inset: 0; |
| | | // inset-block-start: 0; |
| | | // inset-inline-start: 0; |
| | | z-index: 10; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | // width: 100%; |
| | | // height: 100%; |
| | | color: rgba(0, 0, 0, 0.88); |
| | | line-height: 1.5714285714285714; |
| | | background: rgba(255, 255, 255, 0.96); |
| | | text-align: center; |
| | | } |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | <template> |
| | | <view class="l-qrcode" :style="[styles]"> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <canvas :style="styles" type="2d" :canvas-id="canvasId" :id="canvasId"></canvas> |
| | | <!-- #endif --> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <web-view |
| | | ref="qrcodeRef" |
| | | @pagefinish="onFinished" |
| | | @error="onError" |
| | | @onPostMessage="onMessage" |
| | | :style="styles" src="/uni_modules/lime-qrcode/hybrid/html/index.html?v=1"></web-view> |
| | | <!-- #endif --> |
| | | <!-- <view class="l-qrcode-mask" v-if="['loading', 'expired'].includes(props.status)"> |
| | | <l-loading v-if="props.status == 'loading'"></l-loading> |
| | | <view class="l-qrcode-expired" v-if="props.status == 'expired'"> |
| | | <slot></slot> |
| | | </view> |
| | | </view> --> |
| | | </view> |
| | | </template> |
| | | <script lang="ts"> |
| | | // @ts-nocheck |
| | | import { computed, defineComponent, getCurrentInstance, watch, onUnmounted, onMounted } from '@/uni_modules/lime-shared/vue'; |
| | | import QRCodeProps from './props' |
| | | // #ifndef APP-NVUE |
| | | import { getCanvas, isCanvas2d } from './useCanvas' |
| | | import { QRCodeCanvas } from './qrcode.js'; |
| | | // #endif |
| | | import { addUnit } from '@/uni_modules/lime-shared/addUnit' |
| | | import { createImage } from '@/uni_modules/lime-shared/createImage' |
| | | import { unitConvert } from '@/uni_modules/lime-shared/unitConvert' |
| | | import { isBase64 } from '@/uni_modules/lime-shared/isBase64' |
| | | import { pathToBase64 } from '@/uni_modules/lime-shared/pathToBase64' |
| | | import { debounce } from '@/uni_modules/lime-shared/debounce' |
| | | const name = 'l-qrcode' |
| | | export default defineComponent({ |
| | | name, |
| | | props: QRCodeProps, |
| | | emits: ['success'], |
| | | setup(props, {emit}) { |
| | | const context = getCurrentInstance(); |
| | | const canvasId = `l-qrcode${context.uid}` |
| | | const styles = computed(() => `width: ${addUnit(props.size)}; height: ${addUnit(props.size)};`) |
| | | let qrcode = null |
| | | let canvas = null |
| | | const qrCodeProps = computed(() => { |
| | | const { value, icon, size, color, bgColor, bordered, iconSize, errorLevel, marginSize } = props |
| | | const imageSettings = { |
| | | src: icon, |
| | | x: undefined, |
| | | y: undefined, |
| | | height: unitConvert(iconSize), |
| | | width: unitConvert(iconSize), |
| | | excavate: true, |
| | | } |
| | | return { |
| | | value, |
| | | size: unitConvert(size), |
| | | level: errorLevel, |
| | | bgColor, |
| | | fgColor: color, |
| | | imageSettings: icon ? imageSettings : undefined, |
| | | includeMargin: bordered, |
| | | marginSize: marginSize ?? 0 |
| | | } |
| | | |
| | | }) |
| | | |
| | | // #ifdef APP-NVUE |
| | | const stacks = new Map() |
| | | // #endif |
| | | const canvasToTempFilePath = debounce((args: UniNamespace.CanvasToTempFilePathRes) => { |
| | | if(!canvas) return |
| | | // #ifndef APP-NVUE |
| | | const copyArgs = Object.assign({ |
| | | canvasId, |
| | | canvas: null |
| | | }, args) |
| | | |
| | | if (isCanvas2d) { |
| | | copyArgs.canvas = canvas |
| | | } |
| | | if ('toTempFilePath' in canvas) { |
| | | canvas.toTempFilePath(copyArgs) |
| | | } else { |
| | | uni.canvasToTempFilePath(copyArgs, context); |
| | | } |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | if(!stacks.size) { |
| | | const flie = 'file-' + Math.random(); |
| | | const stack = {args, time: +new Date()} |
| | | stacks.set(`${flie}`, stack) |
| | | canvas.toDataURL(flie) |
| | | setTimeout(() => { |
| | | const stack = stacks.get(flie) |
| | | if(stack && 'fail' in stack.args) { |
| | | stack.args.fail({ |
| | | error: '超时' |
| | | }) |
| | | stacks.delete(flie) |
| | | } |
| | | },5000) |
| | | } |
| | | // #endif |
| | | }) |
| | | const useCanvasToTempFilePath = () => { |
| | | if(props.useCanvasToTempFilePath) { |
| | | canvasToTempFilePath({ |
| | | success(res: UniNamespace.CanvasToTempFilePathRes) { |
| | | emit('success', res.tempFilePath) |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | // #ifdef APP-NVUE |
| | | const onFinished = () => { |
| | | const { pixelRatio } = uni.getSystemInfoSync() |
| | | canvas = { |
| | | toDataURL(flie: string) { |
| | | const ref: any = context.refs['qrcodeRef']; |
| | | if(ref) { |
| | | ref?.evalJS(`toDataURL('${flie}')`) |
| | | } |
| | | } |
| | | }; |
| | | qrcode = { |
| | | async render(props: any) { |
| | | const ref: any = context.refs['qrcodeRef']; |
| | | const { src } = props.imageSettings || { }; |
| | | if(!ref) return |
| | | if(src && !isBase64(src) && !/^http/.test(src) && /^\/static/.test(src)) { |
| | | props.imageSettings.src = await pathToBase64(src) |
| | | } |
| | | const _props = JSON.stringify(Object.assign({}, props, {pixelRatio})); |
| | | ref?.evalJS(`render(${_props})`); |
| | | } |
| | | } |
| | | qrcode.render(qrCodeProps.value) |
| | | useCanvasToTempFilePath() |
| | | } |
| | | const onError = () => { |
| | | console.warn('lime-qrcode 加载失败') |
| | | } |
| | | const onMessage = (e: any) => { |
| | | const {detail:{data: [res]}} = e |
| | | if(res.event == 'toDataURL') { |
| | | const {file, image, msg} = res.data; |
| | | const stack = stacks.get(file) |
| | | if(stack && image && 'success' in stack.args) { |
| | | stack.args.success({tempFilePath: image}) |
| | | stacks.delete(file) |
| | | } else if(stack && 'fails' in stack.args) { |
| | | stack.args.fail({error: msg}) |
| | | stacks.delete(file) |
| | | } |
| | | } |
| | | } |
| | | // #endif |
| | | const propsWatch = watch(props, () => { |
| | | if (qrcode) { |
| | | qrcode.render(qrCodeProps.value) |
| | | useCanvasToTempFilePath() |
| | | } |
| | | }) |
| | | onMounted(() => { |
| | | // #ifndef APP-NVUE |
| | | getCanvas(canvasId, { context }).then(res => { |
| | | canvas = res |
| | | qrcode = new QRCodeCanvas(res, { |
| | | // #ifdef H5 |
| | | path2D: false, |
| | | // #endif |
| | | pixelRatio: isCanvas2d ? uni.getSystemInfoSync().pixelRatio : 1, |
| | | createImage |
| | | }) |
| | | qrcode.render(qrCodeProps.value) |
| | | useCanvasToTempFilePath() |
| | | }) |
| | | // #endif |
| | | }) |
| | | onUnmounted(() => { |
| | | propsWatch && propsWatch() |
| | | }) |
| | | return { |
| | | canvasId, |
| | | styles, |
| | | props, |
| | | canvasToTempFilePath, |
| | | |
| | | // #ifdef APP-NVUE |
| | | onFinished, |
| | | onError, |
| | | onMessage |
| | | // #endif |
| | | } |
| | | } |
| | | }) |
| | | </script> |
| | | <style lang="scss"> |
| | | .l-qrcode { |
| | | position: relative; |
| | | |
| | | &-mask { |
| | | position: absolute; |
| | | inset: 0; |
| | | // inset-block-start: 0; |
| | | // inset-inline-start: 0; |
| | | z-index: 10; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | // width: 100%; |
| | | // height: 100%; |
| | | color: rgba(0, 0, 0, 0.88); |
| | | line-height: 1.5714285714285714; |
| | | background: rgba(255, 255, 255, 0.96); |
| | | text-align: center; |
| | | } |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // import type { PropType } from './vue' |
| | | export default { |
| | | value: String, |
| | | icon: String, |
| | | size: { |
| | | type: [Number, String], |
| | | default: 160 |
| | | }, |
| | | iconSize: { |
| | | type: [Number, String], |
| | | default: 40 |
| | | }, |
| | | marginSize: Number, |
| | | color: { |
| | | type: String, |
| | | default: '#000' |
| | | }, |
| | | bgColor: { |
| | | type: String, |
| | | default: 'transparent' |
| | | }, |
| | | bordered: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | errorLevel: { |
| | | type: String as PropType<'L'|'M'|'Q'|'H'>, |
| | | default: 'M' // 'L' | 'M' | 'Q' | 'H' |
| | | }, |
| | | useCanvasToTempFilePath: Boolean |
| | | // status: { |
| | | // type: String as PropType<'active'|'expired'|'loading'>, |
| | | // default: 'active' // active | expired | loading |
| | | // } |
| | | } |
对比新文件 |
| | |
| | | function e(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function t(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function n(e,n,r){return n&&t(e.prototype,n),r&&t(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function r(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);t>n;n++)r[n]=e[n];return r}function o(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return i(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?i(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return e.length>r?{done:!1,value:e[r++]}:{done:!0}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){u=!0,a=e},f:function(){try{s||null==n.return||n.return()}finally{if(u)throw a}}}} |
| | | /** |
| | | * @license QR Code generator library (TypeScript) |
| | | * Copyright (c) Project Nayuki. |
| | | * SPDX-License-Identifier: MIT |
| | | */var a;!function(t){var r=function(){function r(t,n,i,o){if(e(this,r),this.version=void 0,this.errorCorrectionLevel=void 0,this.size=void 0,this.mask=void 0,this.modules=[],this.isFunction=[],this.version=t,this.errorCorrectionLevel=n,r.MIN_VERSION>t||t>r.MAX_VERSION)throw new RangeError("Version value out of range");if(-1>o||o>7)throw new RangeError("Mask value out of range");this.size=4*t+17;for(var a=[],u=0;this.size>u;u++)a.push(!1);for(var l=0;this.size>l;l++)this.modules.push(a.slice()),this.isFunction.push(a.slice());this.drawFunctionPatterns();var h=this.addEccAndInterleave(i);if(this.drawCodewords(h),-1==o)for(var c=1e9,f=0;8>f;f++){this.applyMask(f),this.drawFormatBits(f);var v=this.getPenaltyScore();c>v&&(o=f,c=v),this.applyMask(f)}s(o>=0&&7>=o),this.mask=o,this.applyMask(o),this.drawFormatBits(o),this.isFunction=[]}return n(r,[{key:"getModule",value:function(e,t){return e>=0&&this.size>e&&t>=0&&this.size>t&&this.modules[t][e]}},{key:"getModules",value:function(){return this.modules}},{key:"drawFunctionPatterns",value:function(){for(var e=0;this.size>e;e++)this.setFunctionModule(6,e,e%2==0),this.setFunctionModule(e,6,e%2==0);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);for(var t=this.getAlignmentPatternPositions(),n=t.length,r=0;n>r;r++)for(var i=0;n>i;i++)0==r&&0==i||0==r&&i==n-1||r==n-1&&0==i||this.drawAlignmentPattern(t[r],t[i]);this.drawFormatBits(0),this.drawVersion()}},{key:"drawFormatBits",value:function(e){for(var t=this.errorCorrectionLevel.formatBits<<3|e,n=t,r=0;10>r;r++)n=n<<1^1335*(n>>>9);var i=21522^(t<<10|n);s(i>>>15==0);for(var o=0;5>=o;o++)this.setFunctionModule(8,o,a(i,o));this.setFunctionModule(8,7,a(i,6)),this.setFunctionModule(8,8,a(i,7)),this.setFunctionModule(7,8,a(i,8));for(var u=9;15>u;u++)this.setFunctionModule(14-u,8,a(i,u));for(var l=0;8>l;l++)this.setFunctionModule(this.size-1-l,8,a(i,l));for(var h=8;15>h;h++)this.setFunctionModule(8,this.size-15+h,a(i,h));this.setFunctionModule(8,this.size-8,!0)}},{key:"drawVersion",value:function(){if(this.version>=7){for(var e=this.version,t=0;12>t;t++)e=e<<1^7973*(e>>>11);var n=this.version<<12|e;s(n>>>18==0);for(var r=0;18>r;r++){var i=a(n,r),o=this.size-11+r%3,u=Math.floor(r/3);this.setFunctionModule(o,u,i),this.setFunctionModule(u,o,i)}}}},{key:"drawFinderPattern",value:function(e,t){for(var n=-4;4>=n;n++)for(var r=-4;4>=r;r++){var i=Math.max(Math.abs(r),Math.abs(n)),o=e+r,a=t+n;o>=0&&this.size>o&&a>=0&&this.size>a&&this.setFunctionModule(o,a,2!=i&&4!=i)}}},{key:"drawAlignmentPattern",value:function(e,t){for(var n=-2;2>=n;n++)for(var r=-2;2>=r;r++)this.setFunctionModule(e+r,t+n,1!=Math.max(Math.abs(r),Math.abs(n)))}},{key:"setFunctionModule",value:function(e,t,n){this.modules[t][e]=n,this.isFunction[t][e]=!0}},{key:"addEccAndInterleave",value:function(e){var t=this.version,n=this.errorCorrectionLevel;if(e.length!=r.getNumDataCodewords(t,n))throw new RangeError("Invalid argument");for(var i=r.NUM_ERROR_CORRECTION_BLOCKS[n.ordinal][t],o=r.ECC_CODEWORDS_PER_BLOCK[n.ordinal][t],a=Math.floor(r.getNumRawDataModules(t)/8),u=i-a%i,l=Math.floor(a/i),h=[],c=r.reedSolomonComputeDivisor(o),f=0,v=0;i>f;f++){var d=e.slice(v,v+l-o+(u>f?0:1));v+=d.length;var m=r.reedSolomonComputeRemainder(d,c);u>f&&d.push(0),h.push(d.concat(m))}for(var g=[],y=function(e){h.forEach((function(t,n){e==l-o&&u>n||g.push(t[e])}))},E=0;h[0].length>E;E++)y(E);return s(g.length==a),g}},{key:"drawCodewords",value:function(e){if(e.length!=Math.floor(r.getNumRawDataModules(this.version)/8))throw new RangeError("Invalid argument");for(var t=0,n=this.size-1;n>=1;n-=2){6==n&&(n=5);for(var i=0;this.size>i;i++)for(var o=0;2>o;o++){var u=n-o,l=0==(n+1&2)?this.size-1-i:i;!this.isFunction[l][u]&&8*e.length>t&&(this.modules[l][u]=a(e[t>>>3],7-(7&t)),t++)}}s(t==8*e.length)}},{key:"applyMask",value:function(e){if(0>e||e>7)throw new RangeError("Mask value out of range");for(var t=0;this.size>t;t++)for(var n=0;this.size>n;n++){var r=void 0;switch(e){case 0:r=(n+t)%2==0;break;case 1:r=t%2==0;break;case 2:r=n%3==0;break;case 3:r=(n+t)%3==0;break;case 4:r=(Math.floor(n/3)+Math.floor(t/2))%2==0;break;case 5:r=n*t%2+n*t%3==0;break;case 6:r=(n*t%2+n*t%3)%2==0;break;case 7:r=((n+t)%2+n*t%3)%2==0;break;default:throw Error("Unreachable")}!this.isFunction[t][n]&&r&&(this.modules[t][n]=!this.modules[t][n])}}},{key:"getPenaltyScore",value:function(){for(var e=0,t=0;this.size>t;t++){for(var n=!1,i=0,a=[0,0,0,0,0,0,0],u=0;this.size>u;u++)this.modules[t][u]==n?5==++i?e+=r.PENALTY_N1:i>5&&e++:(this.finderPenaltyAddHistory(i,a),n||(e+=this.finderPenaltyCountPatterns(a)*r.PENALTY_N3),n=this.modules[t][u],i=1);e+=this.finderPenaltyTerminateAndCount(n,i,a)*r.PENALTY_N3}for(var l=0;this.size>l;l++){for(var h=!1,c=0,f=[0,0,0,0,0,0,0],v=0;this.size>v;v++)this.modules[v][l]==h?5==++c?e+=r.PENALTY_N1:c>5&&e++:(this.finderPenaltyAddHistory(c,f),h||(e+=this.finderPenaltyCountPatterns(f)*r.PENALTY_N3),h=this.modules[v][l],c=1);e+=this.finderPenaltyTerminateAndCount(h,c,f)*r.PENALTY_N3}for(var d=0;this.size-1>d;d++)for(var m=0;this.size-1>m;m++){var g=this.modules[d][m];g==this.modules[d][m+1]&&g==this.modules[d+1][m]&&g==this.modules[d+1][m+1]&&(e+=r.PENALTY_N2)}var y,E=0,w=o(this.modules);try{for(w.s();!(y=w.n()).done;){E=y.value.reduce((function(e,t){return e+(t?1:0)}),E)}}catch(e){w.e(e)}finally{w.f()}var M=this.size*this.size,C=Math.ceil(Math.abs(20*E-10*M)/M)-1;return s(C>=0&&9>=C),s((e+=C*r.PENALTY_N4)>=0&&2568888>=e),e}},{key:"getAlignmentPatternPositions",value:function(){if(1==this.version)return[];for(var e=Math.floor(this.version/7)+2,t=32==this.version?26:2*Math.ceil((4*this.version+4)/(2*e-2)),n=[6],r=this.size-7;e>n.length;r-=t)n.splice(1,0,r);return n}},{key:"finderPenaltyCountPatterns",value:function(e){var t=e[1];s(3*this.size>=t);var n=t>0&&e[2]==t&&e[3]==3*t&&e[4]==t&&e[5]==t;return(!n||4*t>e[0]||t>e[6]?0:1)+(!n||4*t>e[6]||t>e[0]?0:1)}},{key:"finderPenaltyTerminateAndCount",value:function(e,t,n){return e&&(this.finderPenaltyAddHistory(t,n),t=0),this.finderPenaltyAddHistory(t+=this.size,n),this.finderPenaltyCountPatterns(n)}},{key:"finderPenaltyAddHistory",value:function(e,t){0==t[0]&&(e+=this.size),t.pop(),t.unshift(e)}}],[{key:"encodeText",value:function(e,n){var i=t.QrSegment.makeSegments(e);return r.encodeSegments(i,n)}},{key:"encodeBinary",value:function(e,n){var i=t.QrSegment.makeBytes(e);return r.encodeSegments([i],n)}},{key:"encodeSegments",value:function(e,t){var n,a,l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,h=arguments.length>3&&void 0!==arguments[3]?arguments[3]:40,c=arguments.length>4&&void 0!==arguments[4]?arguments[4]:-1,f=5>=arguments.length||void 0===arguments[5]||arguments[5];if(r.MIN_VERSION>l||l>h||h>r.MAX_VERSION||-1>c||c>7)throw new RangeError("Invalid value");for(n=l;;n++){var v=8*r.getNumDataCodewords(n,t),d=u.getTotalBits(e,n);if(v>=d){a=d;break}if(n>=h)throw new RangeError("Data too long")}for(var m=0,g=[r.Ecc.MEDIUM,r.Ecc.QUARTILE,r.Ecc.HIGH];g.length>m;m++){var y=g[m];f&&a<=8*r.getNumDataCodewords(n,y)&&(t=y)}var E,w=[],M=o(e);try{for(M.s();!(E=M.n()).done;){var C=E.value;i(C.mode.modeBits,4,w),i(C.numChars,C.mode.numCharCountBits(n),w);var R,A=o(C.getData());try{for(A.s();!(R=A.n()).done;){var p=R.value;w.push(p)}}catch(e){A.e(e)}finally{A.f()}}}catch(e){M.e(e)}finally{M.f()}s(w.length==a);var P=8*r.getNumDataCodewords(n,t);s(P>=w.length),i(0,Math.min(4,P-w.length),w),i(0,(8-w.length%8)%8,w),s(w.length%8==0);for(var N=236;P>w.length;N^=253)i(N,8,w);for(var k=[];w.length>8*k.length;)k.push(0);return w.forEach((function(e,t){return k[t>>>3]|=e<<7-(7&t)})),new r(n,t,k,c)}},{key:"getNumRawDataModules",value:function(e){if(r.MIN_VERSION>e||e>r.MAX_VERSION)throw new RangeError("Version number out of range");var t=(16*e+128)*e+64;if(e>=2){var n=Math.floor(e/7)+2;t-=(25*n-10)*n-55,7>e||(t-=36)}return s(t>=208&&29648>=t),t}},{key:"getNumDataCodewords",value:function(e,t){return Math.floor(r.getNumRawDataModules(e)/8)-r.ECC_CODEWORDS_PER_BLOCK[t.ordinal][e]*r.NUM_ERROR_CORRECTION_BLOCKS[t.ordinal][e]}},{key:"reedSolomonComputeDivisor",value:function(e){if(1>e||e>255)throw new RangeError("Degree out of range");for(var t=[],n=0;e-1>n;n++)t.push(0);t.push(1);for(var i=1,o=0;e>o;o++){for(var a=0;t.length>a;a++)t[a]=r.reedSolomonMultiply(t[a],i),t.length>a+1&&(t[a]^=t[a+1]);i=r.reedSolomonMultiply(i,2)}return t}},{key:"reedSolomonComputeRemainder",value:function(e,t){var n,i=t.map((function(e){return 0})),a=o(e);try{var s=function(){var e=n.value^i.shift();i.push(0),t.forEach((function(t,n){return i[n]^=r.reedSolomonMultiply(t,e)}))};for(a.s();!(n=a.n()).done;)s()}catch(e){a.e(e)}finally{a.f()}return i}},{key:"reedSolomonMultiply",value:function(e,t){if(e>>>8!=0||t>>>8!=0)throw new RangeError("Byte out of range");for(var n=0,r=7;r>=0;r--)n=n<<1^285*(n>>>7),n^=(t>>>r&1)*e;return s(n>>>8==0),n}}]),r}();function i(e,t,n){if(0>t||t>31||e>>>t!=0)throw new RangeError("Value out of range");for(var r=t-1;r>=0;r--)n.push(e>>>r&1)}function a(e,t){return 0!=(e>>>t&1)}function s(e){if(!e)throw Error("Assertion error")}r.MIN_VERSION=1,r.MAX_VERSION=40,r.PENALTY_N1=3,r.PENALTY_N2=3,r.PENALTY_N3=40,r.PENALTY_N4=10,r.ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],r.NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],t.QrCode=r;var u=function(){function t(n,r,i){if(e(this,t),this.mode=void 0,this.numChars=void 0,this.bitData=void 0,this.mode=n,this.numChars=r,this.bitData=i,0>r)throw new RangeError("Invalid argument");this.bitData=i.slice()}return n(t,[{key:"getData",value:function(){return this.bitData.slice()}}],[{key:"makeBytes",value:function(e){var n,r=[],a=o(e);try{for(a.s();!(n=a.n()).done;){i(n.value,8,r)}}catch(e){a.e(e)}finally{a.f()}return new t(t.Mode.BYTE,e.length,r)}},{key:"makeNumeric",value:function(e){if(!t.isNumeric(e))throw new RangeError("String contains non-numeric characters");for(var n=[],r=0;e.length>r;){var o=Math.min(e.length-r,3);i(parseInt(e.substring(r,r+o),10),3*o+1,n),r+=o}return new t(t.Mode.NUMERIC,e.length,n)}},{key:"makeAlphanumeric",value:function(e){if(!t.isAlphanumeric(e))throw new RangeError("String contains unencodable characters in alphanumeric mode");var n,r=[];for(n=0;e.length>=n+2;n+=2){var o=45*t.ALPHANUMERIC_CHARSET.indexOf(e.charAt(n));i(o+=t.ALPHANUMERIC_CHARSET.indexOf(e.charAt(n+1)),11,r)}return e.length>n&&i(t.ALPHANUMERIC_CHARSET.indexOf(e.charAt(n)),6,r),new t(t.Mode.ALPHANUMERIC,e.length,r)}},{key:"makeSegments",value:function(e){return""==e?[]:t.isNumeric(e)?[t.makeNumeric(e)]:t.isAlphanumeric(e)?[t.makeAlphanumeric(e)]:[t.makeBytes(t.toUtf8ByteArray(e))]}},{key:"makeEci",value:function(e){var n=[];if(0>e)throw new RangeError("ECI assignment value out of range");if(128>e)i(e,8,n);else if(16384>e)i(2,2,n),i(e,14,n);else{if(e>=1e6)throw new RangeError("ECI assignment value out of range");i(6,3,n),i(e,21,n)}return new t(t.Mode.ECI,0,n)}},{key:"isNumeric",value:function(e){return t.NUMERIC_REGEX.test(e)}},{key:"isAlphanumeric",value:function(e){return t.ALPHANUMERIC_REGEX.test(e)}},{key:"getTotalBits",value:function(e,t){var n,r=0,i=o(e);try{for(i.s();!(n=i.n()).done;){var a=n.value,s=a.mode.numCharCountBits(t);if(a.numChars>=1<<s)return 1/0;r+=4+s+a.bitData.length}}catch(e){i.e(e)}finally{i.f()}return r}},{key:"toUtf8ByteArray",value:function(e){e=encodeURI(e);for(var t=[],n=0;e.length>n;n++)"%"!=e.charAt(n)?t.push(e.charCodeAt(n)):(t.push(parseInt(e.substring(n+1,n+3),16)),n+=2);return t}}]),t}();u.NUMERIC_REGEX=/^[0-9]*$/,u.ALPHANUMERIC_REGEX=/^[A-Z0-9 $%*+.\/:-]*$/,u.ALPHANUMERIC_CHARSET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:",t.QrSegment=u}(a||(a={})),function(t){!function(t){var r=n((function t(n,r){e(this,t),this.ordinal=void 0,this.formatBits=void 0,this.ordinal=n,this.formatBits=r}));r.LOW=new r(0,1),r.MEDIUM=new r(1,0),r.QUARTILE=new r(2,3),r.HIGH=new r(3,2),t.Ecc=r}(t.QrCode||(t.QrCode={}))}(a||(a={})),function(t){!function(t){var r=function(){function t(n,r){e(this,t),this.modeBits=void 0,this.numBitsCharCount=void 0,this.modeBits=n,this.numBitsCharCount=r}return n(t,[{key:"numCharCountBits",value:function(e){return this.numBitsCharCount[Math.floor((e+7)/17)]}}]),t}();r.NUMERIC=new r(1,[10,12,14]),r.ALPHANUMERIC=new r(2,[9,11,13]),r.BYTE=new r(4,[8,16,16]),r.KANJI=new r(8,[8,10,12]),r.ECI=new r(7,[0,0,0]),t.Mode=r}(t.QrSegment||(t.QrSegment={}))}(a||(a={}));var s=a,u=["value","size","level","bgColor","fgColor","includeMargin","marginSize","imageSettings"],l={L:s.QrCode.Ecc.LOW,M:s.QrCode.Ecc.MEDIUM,Q:s.QrCode.Ecc.QUARTILE,H:s.QrCode.Ecc.HIGH};function h(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=[];return e.forEach((function(e,r){var i=null;e.forEach((function(o,a){if(!o&&null!==i)return n.push("M".concat(i+t," ").concat(r+t,"h").concat(a-i,"v1H").concat(i+t,"z")),void(i=null);if(a!==e.length-1)o&&null===i&&(i=a);else{if(!o)return;n.push(null===i?"M".concat(a+t,",").concat(r+t," h1v1H").concat(a+t,"z"):"M".concat(i+t,",").concat(r+t," h").concat(a+1-i,"v1H").concat(i+t,"z"))}}))})),n.join("")}function c(e,t){return e.slice().map((function(e,n){return t.y>n||n>=t.y+t.h?e:e.map((function(e,n){return(t.x>n||n>=t.x+t.w)&&e}))}))}var f=function(){function t(n,r){var i=this;for(var o in e(this,t),this.canvas=void 0,this.pixelRatio="undefined"!=typeof window?window.devicePixelRatio:1,this.path2D=!0,this.SUPPORTS_PATH2D=void 0,this.createImage=function(){return new Image},this.createPath2D=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,t="createPath2D";return i.canvas&&t in i.canvas?i.canvas[t](e):new Path2D(e)},this.canvas=n,r)o in this&&(this[o]=r[o]);this.SUPPORTS_PATH2D=function(){try{i.createPath2D()}catch(e){return!1}return!0}()}return n(t,[{key:"render",value:function(e,t){var n=this,i=e.value,o=e.size,a=void 0===o?128:o,f=e.level,v=void 0===f?"L":f,d=e.bgColor,m=void 0===d?"#FFFFFF":d,g=e.fgColor,y=void 0===g?"#000000":g,E=e.includeMargin,w=void 0!==E&&E,M=e.marginSize,C=e.imageSettings,R=(r(e,u),null==C?void 0:C.src),A=this.canvas,p=A.getContext("2d");if(p&&i){var P=s.QrCode.encodeText(i,l[v]).getModules(),N=function(e,t){return null!=t?Math.floor(t):e?4:0}(w,M),k=P.length+2*N,S=function(e,t,n,r){if(null==r)return null;var i=e.length+2*n,o=Math.floor(.1*t),a=i/t,s=(r.width||o)*a,u=(r.height||o)*a,l=null==r.x?e.length/2-s/2:r.x*a,h=null==r.y?e.length/2-u/2:r.y*a,c=null;if(r.excavate){var f=Math.floor(l),v=Math.floor(h);c={x:f,y:v,w:Math.ceil(s+l-f),h:Math.ceil(u+h-v)}}return{x:l,y:h,h:u,w:s,excavation:c}}(P,a,N,C),I=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,r=null!=S&&null!==e&&e.complete&&0!==e.naturalHeight&&0!==e.naturalWidth;r&&null!=S.excavation&&(P=c(P,S.excavation)),t&&t(P);var i=n.pixelRatio;p.clearRect(0,0,A.width,A.height),p.setTransform(1,0,0,1,0,0),A.height=A.width=a*i;var o=a/k*i;if(p.scale(o,o),p.fillStyle=m,p.fillRect(0,0,k,k),p.fillStyle=y,n.SUPPORTS_PATH2D&&n.path2D){var s=n.createPath2D(h(P,N));p.fill(s)}else P.forEach((function(e,t){e.forEach((function(e,n){e&&p.fillRect(n+N,t+N,1,1)}))}));var u=(null==e?void 0:e.path)||e;r&&p.drawImage(u,S.x+N,S.y+N,S.w,S.h),"draw"in p&&p.draw()};if(R){var b=this.createImage(A);b.onload=function(){I(b)},b.onerror=function(e){I(),console.warn(e)},b.src=R}else I()}}}]),t}();export{f as QRCodeCanvas}; |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | export type ImageSettings = { |
| | | width: number |
| | | height: number |
| | | x?: number |
| | | y?: number |
| | | excavate: boolean |
| | | } |
| | | export type QRCodePropsTypes = { |
| | | value?: string |
| | | size?: number |
| | | fgColor?: string |
| | | level?: string |
| | | marginSize: number |
| | | includeMargin: boolean |
| | | imageSettings?: ImageSettings |
| | | } |
| | | |
| | | export type QRCodeCallback = (cells : boolean[][]) => void |
| | | |
| | | export type Excavation = { |
| | | x: number |
| | | y: number |
| | | h: number |
| | | w: number |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * 成功回调函数定义 |
| | | */ |
| | | export type TakeSnapshotSuccessCallback = (res: TakeSnapshotSuccess) => void |
| | | /** |
| | | * 失败回调函数定义 |
| | | */ |
| | | export type TakeSnapshotFailCallback = (res: TakeSnapshotFail) => void |
| | | /** |
| | | * 完成回调函数定义 |
| | | */ |
| | | export type TakeSnapshotCompleteCallback = (res: any) => void |
| | | |
| | | |
| | | export type LQrcodeFailCallback = TakeSnapshotFailCallback |
| | | export type LQrcodeCompleteCallback = TakeSnapshotCompleteCallback |
| | | export type LQrcodeSuccessCallback = TakeSnapshotSuccessCallback |
对比新文件 |
| | |
| | | |
| | | // @ts-nocheck |
| | | import type { ComponentInternalInstance } from '@/uni_modules/lime-shared/vue' |
| | | import { getRect } from '@/uni_modules/lime-shared/getRect' |
| | | import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d' |
| | | export const isCanvas2d = canIUseCanvas2d() |
| | | |
| | | export async function getCanvas(canvasId: string, options: {context: ComponentInternalInstance}) { |
| | | let { context } = options |
| | | // #ifdef MP || VUE2 |
| | | if (context.proxy) context = context.proxy |
| | | // #endif |
| | | return getRect('#' + canvasId, context, isCanvas2d).then(res => { |
| | | if(res.node){ |
| | | return res.node |
| | | } else { |
| | | const ctx = uni.createCanvasContext(canvasId, context) |
| | | return { |
| | | getContext(type: string) { |
| | | if(type == '2d') { |
| | | return ctx |
| | | } |
| | | }, |
| | | width: res.width, |
| | | height: res.height, |
| | | } |
| | | // #ifdef H5 |
| | | // canvas.value = context.proxy.$el.querySelector('#'+ canvasId) |
| | | // #endif |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // #ifndef H5 || APP-NVUE |
| | | class Image { |
| | | currentSrc: string | null = null |
| | | naturalHeight: number = 0 |
| | | naturalWidth: number = 0 |
| | | width: number = 0 |
| | | height: number = 0 |
| | | tagName: string = 'IMG' |
| | | path: any = '' |
| | | crossOrigin: any = '' |
| | | referrerPolicy: any = '' |
| | | onload: () => void |
| | | onerror: () => void |
| | | constructor() {} |
| | | set src(src) { |
| | | this.currentSrc = src |
| | | uni.getImageInfo({ |
| | | src, |
| | | success: (res) => { |
| | | this.path = res.path |
| | | this.naturalWidth = this.width = res.width |
| | | this.naturalHeight = this.height = res.height |
| | | this.onload() |
| | | }, |
| | | fail: () => { |
| | | this.onerror() |
| | | } |
| | | }) |
| | | } |
| | | get src() { |
| | | return this.currentSrc |
| | | } |
| | | } |
| | | // #endif |
| | | |
| | | export function createImage(canvas: WechatMiniprogram.Canvas) { |
| | | if(canvas && canvas.createImage) { |
| | | return canvas.createImage() |
| | | } else if(typeof window != 'undefined' && window.Image) { |
| | | return new window.Image() |
| | | } |
| | | // #ifndef H5 || APP-NVUE |
| | | return new Image() |
| | | // #endif |
| | | } |
对比新文件 |
| | |
| | | export function addUnit(value: any|null):string{ |
| | | if(value == null){ |
| | | return '' |
| | | } |
| | | value = `${value}` |
| | | return /^(-)?\d+(\\.\d+)?$/.test(value) ? `${value}px` : value |
| | | } |
| | | |
| | | export function unitConvert(value: any|null): number{ |
| | | if(typeof value == 'number'){ |
| | | return value as number |
| | | } |
| | | if(typeof value == 'string'){ |
| | | value = `${value}` |
| | | if(/^(-)?\d+(\\.\d+)?$/.test(value)){ |
| | | return parseFloat(value); |
| | | } |
| | | |
| | | const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g; |
| | | const results = reg.exec(value); |
| | | if (results == null) { |
| | | return 0; |
| | | } |
| | | const unit = results[3]; |
| | | const v = parseFloat(value); |
| | | if (unit == 'rpx') { |
| | | const { windowWidth } = uni.getWindowInfo() |
| | | return windowWidth / 750 * v; |
| | | } |
| | | if (unit == 'px') { |
| | | return v; |
| | | } |
| | | } |
| | | return 0; |
| | | } |
对比新文件 |
| | |
| | | <template> |
| | | <view class="demo-block"> |
| | | <text class="demo-block__title-text ultra">QRCode</text> |
| | | <text class="demo-block__desc-text">能够将文本转换生成二维码的组件,支持自定义配色和 Logo 配置</text> |
| | | <view class="demo-block__body"> |
| | | <view class="demo-block card"> |
| | | <text class="demo-block__title-text large">基础</text> |
| | | <view class="demo-block__body"> |
| | | <l-qrcode value="https://limeui.qcoon.cn" size="300rpx"></l-qrcode> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="demo-block card"> |
| | | <text class="demo-block__title-text large">icon</text> |
| | | <view class="demo-block__body"> |
| | | <image v-if="image !=''" :src="image" style="width: 300rpx;" mode="widthFix"></image> |
| | | <view style="flex-direction: row; justify-content: space-between"> |
| | | <l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> |
| | | <l-qrcode :useCanvasToTempFilePath="true" @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> |
| | | </view> |
| | | <button type="primary" style="margin-top: 20rpx;" @click="onClick">生成图片</button> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="demo-block card"> |
| | | <text class="demo-block__title-text large">颜色</text> |
| | | <view class="demo-block__body"> |
| | | <view style="flex-direction: row; justify-content: space-between"> |
| | | <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode> |
| | | <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="demo-block card"> |
| | | <text class="demo-block__title-text large">纠错比例</text> |
| | | <view class="demo-block__body"> |
| | | <l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode> |
| | | <button type="primary" style="margin-top: 20rpx;" @click="onToggle">切换纠错等级:{{levels[index]}}</button> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="demo-block card"> |
| | | <text class="demo-block__title-text large">动态</text> |
| | | <view class="demo-block__body"> |
| | | <l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode> |
| | | <button type="primary" style="margin-top: 20rpx;" @click="update">更新</button> |
| | | </view> |
| | | </view> |
| | | |
| | | </view> |
| | | </view> |
| | | |
| | | </template> |
| | | <script> |
| | | // import {ComponentPublicInstance} from 'vue' |
| | | export default { |
| | | name: 'lime-qrcode', |
| | | data() { |
| | | return { |
| | | text: 'qcoon.com.cn', |
| | | image: '', |
| | | index: 0, |
| | | levels: ['L', 'M', 'Q', 'H'] |
| | | } |
| | | }, |
| | | methods:{ |
| | | success(src: string) { |
| | | console.log(`src`, src) |
| | | }, |
| | | update() { |
| | | this.text = `qcoon.cn?v=${Math.random()}` |
| | | }, |
| | | onToggle() { |
| | | this.index++ |
| | | this.index = this.index % this.levels.length |
| | | }, |
| | | onClick() { |
| | | const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance |
| | | el.canvasToTempFilePath({ |
| | | success:(res: TakeSnapshotSuccess)=>{ |
| | | this.image = res.tempFilePath |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | mounted() { |
| | | |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss"> |
| | | |
| | | .demo-block { |
| | | margin: 32px 10px 0; |
| | | overflow: visible; |
| | | &.card{ |
| | | background-color: white; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | &__title { |
| | | margin: 0; |
| | | margin-top: 8px; |
| | | &-text { |
| | | color: rgba(0, 0, 0, 0.6); |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | line-height: 16px; |
| | | |
| | | &.large { |
| | | color: rgba(0, 0, 0, 0.9); |
| | | font-size: 18px; |
| | | font-weight: 700; |
| | | line-height: 26px; |
| | | } |
| | | &.ultra { |
| | | color: rgba(0, 0, 0, 0.9); |
| | | font-size: 24px; |
| | | font-weight: 700; |
| | | line-height: 32px; |
| | | } |
| | | } |
| | | } |
| | | &__desc-text { |
| | | color: rgba(0, 0, 0, 0.6); |
| | | margin: 8px 16px 0 0; |
| | | font-size: 14px; |
| | | line-height: 22px; |
| | | } |
| | | &__body { |
| | | margin: 16px 0; |
| | | overflow: visible; |
| | | .demo-block { |
| | | // margin-top: 0px; |
| | | margin: 0; |
| | | } |
| | | } |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | <template> |
| | | <demo-block title="QRCode" type="ultra"> |
| | | <demo-block title="基础"> |
| | | <l-qrcode value="https://limeui.qcoon.cn" size="300rpx"></l-qrcode> |
| | | </demo-block> |
| | | <demo-block title="icon"> |
| | | <view style="display: flex; gap: 10px"> |
| | | <image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image> |
| | | <l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> |
| | | <l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> |
| | | </view> |
| | | <button @click="onClick">生成图片</button> |
| | | </demo-block> |
| | | <demo-block title="颜色"> |
| | | <view style="display: flex; gap: 10px"> |
| | | <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode> |
| | | <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode> |
| | | </view> |
| | | </demo-block> |
| | | <demo-block title="纠错比例"> |
| | | <l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode> |
| | | <button @click="onToggle">切换纠错等级:{{levels[index]}}</button> |
| | | </demo-block> |
| | | <demo-block title="动态"> |
| | | <l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode> |
| | | <button @click="update">更新</button> |
| | | </demo-block> |
| | | </demo-block> |
| | | </template> |
| | | <script> |
| | | import {ref, defineComponent} from '@/uni_modules/lime-shared/vue' |
| | | export default defineComponent({ |
| | | setup() { |
| | | const qrcodeRef = ref(null) |
| | | const image = ref(null) |
| | | const text = ref('qcoon.com.cn') |
| | | const levels = ['L', 'M', 'Q', 'H'] |
| | | let index = ref(0) |
| | | const onToggle = () => { |
| | | index.value++ |
| | | index.value = index.value % levels.length |
| | | } |
| | | const onClick = () => { |
| | | if(qrcodeRef.value) { |
| | | qrcodeRef.value.canvasToTempFilePath({ |
| | | success(res) { |
| | | image.value = res.tempFilePath |
| | | console.log('success:::', res) |
| | | }, |
| | | fail(err) { |
| | | console.log('err:::', err) |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | const success = (res) => { |
| | | console.log('res', res) |
| | | } |
| | | |
| | | const update = () =>{ |
| | | text.value = `qcoon.cn?v=${Math.random()}` |
| | | } |
| | | |
| | | return { |
| | | levels, |
| | | index, |
| | | image, |
| | | text, |
| | | qrcodeRef, |
| | | onClick, |
| | | update, |
| | | success, |
| | | onToggle, |
| | | } |
| | | } |
| | | }) |
| | | </script> |
| | | <style> |
| | | </style> |
对比新文件 |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="zh"> |
| | | <head> |
| | | <meta charset="UTF-8"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | | <title>lime-qrcode</title> |
| | | <style> |
| | | html,body,canvas { |
| | | margin: 0; |
| | | padding: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | pointer-events: none; |
| | | /* background-color: rgba(255,0,0,0.1) */ |
| | | } |
| | | </style> |
| | | </head> |
| | | <body> |
| | | <canvas id="lime-qrcode"></canvas> |
| | | <script type="text/javascript" src="./uni.webview.1.5.3.js"></script> |
| | | <script type="text/javascript" src="./qrcode.min.js"></script> |
| | | <script> |
| | | var canvas = document.querySelector('#lime-qrcode') |
| | | var pixelRatio = window.devicePixelRatio || 1 |
| | | function appendWatermark(image) { |
| | | emit('append', mark.toDataURL()) |
| | | } |
| | | |
| | | var qrcode = new lime.QRCodeCanvas(canvas, { |
| | | pixelRatio, |
| | | }) |
| | | function render(props) { |
| | | if(props.pixelRatio) { |
| | | pixelRatio = props.pixelRatio |
| | | } |
| | | if(qrcode) { |
| | | qrcode.render(props) |
| | | } |
| | | } |
| | | function toDataURL(file) { |
| | | if(qrcode && canvas) { |
| | | try{ |
| | | const image = canvas.toDataURL() |
| | | emit('toDataURL', { |
| | | file, |
| | | image |
| | | }) |
| | | }catch(e){ |
| | | emit('toDataURL', { |
| | | file, |
| | | msg: e |
| | | }) |
| | | } |
| | | |
| | | } |
| | | } |
| | | function emit(event, data) { |
| | | postMessage({ |
| | | event, |
| | | data |
| | | }); |
| | | }; |
| | | function postMessage(data) { |
| | | uni.postMessage({ |
| | | data |
| | | }); |
| | | }; |
| | | // render({ |
| | | // content: ['Lime UI'], |
| | | // // rotate: -22, |
| | | // // baseSize: 2, |
| | | // // fontGap: 3 |
| | | // }) |
| | | </script> |
| | | </body> |
| | | </html> |
对比新文件 |
| | |
| | | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).lime={})}(this,(function(e){"use strict";function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function r(e,t,r){return t&&n(e.prototype,t),r&&n(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function i(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function o(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);t>n;n++)r[n]=e[n];return r}function a(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return o(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?o(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,i=function(){};return{s:i,n:function(){return e.length>r?{done:!1,value:e[r++]}:{done:!0}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){u=!0,a=e},f:function(){try{s||null==n.return||n.return()}finally{if(u)throw a}}}} |
| | | /** |
| | | * @license QR Code generator library (TypeScript) |
| | | * Copyright (c) Project Nayuki. |
| | | * SPDX-License-Identifier: MIT |
| | | */var s;!function(e){var n=function(){function n(e,r,i,o){if(t(this,n),this.version=void 0,this.errorCorrectionLevel=void 0,this.size=void 0,this.mask=void 0,this.modules=[],this.isFunction=[],this.version=e,this.errorCorrectionLevel=r,n.MIN_VERSION>e||e>n.MAX_VERSION)throw new RangeError("Version value out of range");if(-1>o||o>7)throw new RangeError("Mask value out of range");this.size=4*e+17;for(var a=[],u=0;this.size>u;u++)a.push(!1);for(var l=0;this.size>l;l++)this.modules.push(a.slice()),this.isFunction.push(a.slice());this.drawFunctionPatterns();var h=this.addEccAndInterleave(i);if(this.drawCodewords(h),-1==o)for(var c=1e9,f=0;8>f;f++){this.applyMask(f),this.drawFormatBits(f);var d=this.getPenaltyScore();c>d&&(o=f,c=d),this.applyMask(f)}s(o>=0&&7>=o),this.mask=o,this.applyMask(o),this.drawFormatBits(o),this.isFunction=[]}return r(n,[{key:"getModule",value:function(e,t){return e>=0&&this.size>e&&t>=0&&this.size>t&&this.modules[t][e]}},{key:"getModules",value:function(){return this.modules}},{key:"drawFunctionPatterns",value:function(){for(var e=0;this.size>e;e++)this.setFunctionModule(6,e,e%2==0),this.setFunctionModule(e,6,e%2==0);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);for(var t=this.getAlignmentPatternPositions(),n=t.length,r=0;n>r;r++)for(var i=0;n>i;i++)0==r&&0==i||0==r&&i==n-1||r==n-1&&0==i||this.drawAlignmentPattern(t[r],t[i]);this.drawFormatBits(0),this.drawVersion()}},{key:"drawFormatBits",value:function(e){for(var t=this.errorCorrectionLevel.formatBits<<3|e,n=t,r=0;10>r;r++)n=n<<1^1335*(n>>>9);var i=21522^(t<<10|n);s(i>>>15==0);for(var a=0;5>=a;a++)this.setFunctionModule(8,a,o(i,a));this.setFunctionModule(8,7,o(i,6)),this.setFunctionModule(8,8,o(i,7)),this.setFunctionModule(7,8,o(i,8));for(var u=9;15>u;u++)this.setFunctionModule(14-u,8,o(i,u));for(var l=0;8>l;l++)this.setFunctionModule(this.size-1-l,8,o(i,l));for(var h=8;15>h;h++)this.setFunctionModule(8,this.size-15+h,o(i,h));this.setFunctionModule(8,this.size-8,!0)}},{key:"drawVersion",value:function(){if(this.version>=7){for(var e=this.version,t=0;12>t;t++)e=e<<1^7973*(e>>>11);var n=this.version<<12|e;s(n>>>18==0);for(var r=0;18>r;r++){var i=o(n,r),a=this.size-11+r%3,u=Math.floor(r/3);this.setFunctionModule(a,u,i),this.setFunctionModule(u,a,i)}}}},{key:"drawFinderPattern",value:function(e,t){for(var n=-4;4>=n;n++)for(var r=-4;4>=r;r++){var i=Math.max(Math.abs(r),Math.abs(n)),o=e+r,a=t+n;o>=0&&this.size>o&&a>=0&&this.size>a&&this.setFunctionModule(o,a,2!=i&&4!=i)}}},{key:"drawAlignmentPattern",value:function(e,t){for(var n=-2;2>=n;n++)for(var r=-2;2>=r;r++)this.setFunctionModule(e+r,t+n,1!=Math.max(Math.abs(r),Math.abs(n)))}},{key:"setFunctionModule",value:function(e,t,n){this.modules[t][e]=n,this.isFunction[t][e]=!0}},{key:"addEccAndInterleave",value:function(e){var t=this.version,r=this.errorCorrectionLevel;if(e.length!=n.getNumDataCodewords(t,r))throw new RangeError("Invalid argument");for(var i=n.NUM_ERROR_CORRECTION_BLOCKS[r.ordinal][t],o=n.ECC_CODEWORDS_PER_BLOCK[r.ordinal][t],a=Math.floor(n.getNumRawDataModules(t)/8),u=i-a%i,l=Math.floor(a/i),h=[],c=n.reedSolomonComputeDivisor(o),f=0,d=0;i>f;f++){var v=e.slice(d,d+l-o+(u>f?0:1));d+=v.length;var m=n.reedSolomonComputeRemainder(v,c);u>f&&v.push(0),h.push(v.concat(m))}for(var g=[],y=function(e){h.forEach((function(t,n){e==l-o&&u>n||g.push(t[e])}))},E=0;h[0].length>E;E++)y(E);return s(g.length==a),g}},{key:"drawCodewords",value:function(e){if(e.length!=Math.floor(n.getNumRawDataModules(this.version)/8))throw new RangeError("Invalid argument");for(var t=0,r=this.size-1;r>=1;r-=2){6==r&&(r=5);for(var i=0;this.size>i;i++)for(var a=0;2>a;a++){var u=r-a,l=0==(r+1&2)?this.size-1-i:i;!this.isFunction[l][u]&&8*e.length>t&&(this.modules[l][u]=o(e[t>>>3],7-(7&t)),t++)}}s(t==8*e.length)}},{key:"applyMask",value:function(e){if(0>e||e>7)throw new RangeError("Mask value out of range");for(var t=0;this.size>t;t++)for(var n=0;this.size>n;n++){var r=void 0;switch(e){case 0:r=(n+t)%2==0;break;case 1:r=t%2==0;break;case 2:r=n%3==0;break;case 3:r=(n+t)%3==0;break;case 4:r=(Math.floor(n/3)+Math.floor(t/2))%2==0;break;case 5:r=n*t%2+n*t%3==0;break;case 6:r=(n*t%2+n*t%3)%2==0;break;case 7:r=((n+t)%2+n*t%3)%2==0;break;default:throw Error("Unreachable")}!this.isFunction[t][n]&&r&&(this.modules[t][n]=!this.modules[t][n])}}},{key:"getPenaltyScore",value:function(){for(var e=0,t=0;this.size>t;t++){for(var r=!1,i=0,o=[0,0,0,0,0,0,0],u=0;this.size>u;u++)this.modules[t][u]==r?5==++i?e+=n.PENALTY_N1:i>5&&e++:(this.finderPenaltyAddHistory(i,o),r||(e+=this.finderPenaltyCountPatterns(o)*n.PENALTY_N3),r=this.modules[t][u],i=1);e+=this.finderPenaltyTerminateAndCount(r,i,o)*n.PENALTY_N3}for(var l=0;this.size>l;l++){for(var h=!1,c=0,f=[0,0,0,0,0,0,0],d=0;this.size>d;d++)this.modules[d][l]==h?5==++c?e+=n.PENALTY_N1:c>5&&e++:(this.finderPenaltyAddHistory(c,f),h||(e+=this.finderPenaltyCountPatterns(f)*n.PENALTY_N3),h=this.modules[d][l],c=1);e+=this.finderPenaltyTerminateAndCount(h,c,f)*n.PENALTY_N3}for(var v=0;this.size-1>v;v++)for(var m=0;this.size-1>m;m++){var g=this.modules[v][m];g==this.modules[v][m+1]&&g==this.modules[v+1][m]&&g==this.modules[v+1][m+1]&&(e+=n.PENALTY_N2)}var y,E=0,w=a(this.modules);try{for(w.s();!(y=w.n()).done;){E=y.value.reduce((function(e,t){return e+(t?1:0)}),E)}}catch(e){w.e(e)}finally{w.f()}var M=this.size*this.size,C=Math.ceil(Math.abs(20*E-10*M)/M)-1;return s(C>=0&&9>=C),s((e+=C*n.PENALTY_N4)>=0&&2568888>=e),e}},{key:"getAlignmentPatternPositions",value:function(){if(1==this.version)return[];for(var e=Math.floor(this.version/7)+2,t=32==this.version?26:2*Math.ceil((4*this.version+4)/(2*e-2)),n=[6],r=this.size-7;e>n.length;r-=t)n.splice(1,0,r);return n}},{key:"finderPenaltyCountPatterns",value:function(e){var t=e[1];s(3*this.size>=t);var n=t>0&&e[2]==t&&e[3]==3*t&&e[4]==t&&e[5]==t;return(!n||4*t>e[0]||t>e[6]?0:1)+(!n||4*t>e[6]||t>e[0]?0:1)}},{key:"finderPenaltyTerminateAndCount",value:function(e,t,n){return e&&(this.finderPenaltyAddHistory(t,n),t=0),this.finderPenaltyAddHistory(t+=this.size,n),this.finderPenaltyCountPatterns(n)}},{key:"finderPenaltyAddHistory",value:function(e,t){0==t[0]&&(e+=this.size),t.pop(),t.unshift(e)}}],[{key:"encodeText",value:function(t,r){var i=e.QrSegment.makeSegments(t);return n.encodeSegments(i,r)}},{key:"encodeBinary",value:function(t,r){var i=e.QrSegment.makeBytes(t);return n.encodeSegments([i],r)}},{key:"encodeSegments",value:function(e,t){var r,o,l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,h=arguments.length>3&&void 0!==arguments[3]?arguments[3]:40,c=arguments.length>4&&void 0!==arguments[4]?arguments[4]:-1,f=5>=arguments.length||void 0===arguments[5]||arguments[5];if(n.MIN_VERSION>l||l>h||h>n.MAX_VERSION||-1>c||c>7)throw new RangeError("Invalid value");for(r=l;;r++){var d=8*n.getNumDataCodewords(r,t),v=u.getTotalBits(e,r);if(d>=v){o=v;break}if(r>=h)throw new RangeError("Data too long")}for(var m=0,g=[n.Ecc.MEDIUM,n.Ecc.QUARTILE,n.Ecc.HIGH];g.length>m;m++){var y=g[m];f&&o<=8*n.getNumDataCodewords(r,y)&&(t=y)}var E,w=[],M=a(e);try{for(M.s();!(E=M.n()).done;){var C=E.value;i(C.mode.modeBits,4,w),i(C.numChars,C.mode.numCharCountBits(r),w);var R,p=a(C.getData());try{for(p.s();!(R=p.n()).done;){var A=R.value;w.push(A)}}catch(e){p.e(e)}finally{p.f()}}}catch(e){M.e(e)}finally{M.f()}s(w.length==o);var P=8*n.getNumDataCodewords(r,t);s(P>=w.length),i(0,Math.min(4,P-w.length),w),i(0,(8-w.length%8)%8,w),s(w.length%8==0);for(var N=236;P>w.length;N^=253)i(N,8,w);for(var k=[];w.length>8*k.length;)k.push(0);return w.forEach((function(e,t){return k[t>>>3]|=e<<7-(7&t)})),new n(r,t,k,c)}},{key:"getNumRawDataModules",value:function(e){if(n.MIN_VERSION>e||e>n.MAX_VERSION)throw new RangeError("Version number out of range");var t=(16*e+128)*e+64;if(e>=2){var r=Math.floor(e/7)+2;t-=(25*r-10)*r-55,7>e||(t-=36)}return s(t>=208&&29648>=t),t}},{key:"getNumDataCodewords",value:function(e,t){return Math.floor(n.getNumRawDataModules(e)/8)-n.ECC_CODEWORDS_PER_BLOCK[t.ordinal][e]*n.NUM_ERROR_CORRECTION_BLOCKS[t.ordinal][e]}},{key:"reedSolomonComputeDivisor",value:function(e){if(1>e||e>255)throw new RangeError("Degree out of range");for(var t=[],r=0;e-1>r;r++)t.push(0);t.push(1);for(var i=1,o=0;e>o;o++){for(var a=0;t.length>a;a++)t[a]=n.reedSolomonMultiply(t[a],i),t.length>a+1&&(t[a]^=t[a+1]);i=n.reedSolomonMultiply(i,2)}return t}},{key:"reedSolomonComputeRemainder",value:function(e,t){var r,i=t.map((function(e){return 0})),o=a(e);try{var s=function(){var e=r.value^i.shift();i.push(0),t.forEach((function(t,r){return i[r]^=n.reedSolomonMultiply(t,e)}))};for(o.s();!(r=o.n()).done;)s()}catch(e){o.e(e)}finally{o.f()}return i}},{key:"reedSolomonMultiply",value:function(e,t){if(e>>>8!=0||t>>>8!=0)throw new RangeError("Byte out of range");for(var n=0,r=7;r>=0;r--)n=n<<1^285*(n>>>7),n^=(t>>>r&1)*e;return s(n>>>8==0),n}}]),n}();function i(e,t,n){if(0>t||t>31||e>>>t!=0)throw new RangeError("Value out of range");for(var r=t-1;r>=0;r--)n.push(e>>>r&1)}function o(e,t){return 0!=(e>>>t&1)}function s(e){if(!e)throw Error("Assertion error")}n.MIN_VERSION=1,n.MAX_VERSION=40,n.PENALTY_N1=3,n.PENALTY_N2=3,n.PENALTY_N3=40,n.PENALTY_N4=10,n.ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],n.NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],e.QrCode=n;var u=function(){function e(n,r,i){if(t(this,e),this.mode=void 0,this.numChars=void 0,this.bitData=void 0,this.mode=n,this.numChars=r,this.bitData=i,0>r)throw new RangeError("Invalid argument");this.bitData=i.slice()}return r(e,[{key:"getData",value:function(){return this.bitData.slice()}}],[{key:"makeBytes",value:function(t){var n,r=[],o=a(t);try{for(o.s();!(n=o.n()).done;){i(n.value,8,r)}}catch(e){o.e(e)}finally{o.f()}return new e(e.Mode.BYTE,t.length,r)}},{key:"makeNumeric",value:function(t){if(!e.isNumeric(t))throw new RangeError("String contains non-numeric characters");for(var n=[],r=0;t.length>r;){var o=Math.min(t.length-r,3);i(parseInt(t.substring(r,r+o),10),3*o+1,n),r+=o}return new e(e.Mode.NUMERIC,t.length,n)}},{key:"makeAlphanumeric",value:function(t){if(!e.isAlphanumeric(t))throw new RangeError("String contains unencodable characters in alphanumeric mode");var n,r=[];for(n=0;t.length>=n+2;n+=2){var o=45*e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n));i(o+=e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n+1)),11,r)}return t.length>n&&i(e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n)),6,r),new e(e.Mode.ALPHANUMERIC,t.length,r)}},{key:"makeSegments",value:function(t){return""==t?[]:e.isNumeric(t)?[e.makeNumeric(t)]:e.isAlphanumeric(t)?[e.makeAlphanumeric(t)]:[e.makeBytes(e.toUtf8ByteArray(t))]}},{key:"makeEci",value:function(t){var n=[];if(0>t)throw new RangeError("ECI assignment value out of range");if(128>t)i(t,8,n);else if(16384>t)i(2,2,n),i(t,14,n);else{if(t>=1e6)throw new RangeError("ECI assignment value out of range");i(6,3,n),i(t,21,n)}return new e(e.Mode.ECI,0,n)}},{key:"isNumeric",value:function(t){return e.NUMERIC_REGEX.test(t)}},{key:"isAlphanumeric",value:function(t){return e.ALPHANUMERIC_REGEX.test(t)}},{key:"getTotalBits",value:function(e,t){var n,r=0,i=a(e);try{for(i.s();!(n=i.n()).done;){var o=n.value,s=o.mode.numCharCountBits(t);if(o.numChars>=1<<s)return 1/0;r+=4+s+o.bitData.length}}catch(e){i.e(e)}finally{i.f()}return r}},{key:"toUtf8ByteArray",value:function(e){e=encodeURI(e);for(var t=[],n=0;e.length>n;n++)"%"!=e.charAt(n)?t.push(e.charCodeAt(n)):(t.push(parseInt(e.substring(n+1,n+3),16)),n+=2);return t}}]),e}();u.NUMERIC_REGEX=/^[0-9]*$/,u.ALPHANUMERIC_REGEX=/^[A-Z0-9 $%*+.\/:-]*$/,u.ALPHANUMERIC_CHARSET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:",e.QrSegment=u}(s||(s={})),function(e){!function(e){var n=r((function e(n,r){t(this,e),this.ordinal=void 0,this.formatBits=void 0,this.ordinal=n,this.formatBits=r}));n.LOW=new n(0,1),n.MEDIUM=new n(1,0),n.QUARTILE=new n(2,3),n.HIGH=new n(3,2),e.Ecc=n}(e.QrCode||(e.QrCode={}))}(s||(s={})),function(e){!function(e){var n=function(){function e(n,r){t(this,e),this.modeBits=void 0,this.numBitsCharCount=void 0,this.modeBits=n,this.numBitsCharCount=r}return r(e,[{key:"numCharCountBits",value:function(e){return this.numBitsCharCount[Math.floor((e+7)/17)]}}]),e}();n.NUMERIC=new n(1,[10,12,14]),n.ALPHANUMERIC=new n(2,[9,11,13]),n.BYTE=new n(4,[8,16,16]),n.KANJI=new n(8,[8,10,12]),n.ECI=new n(7,[0,0,0]),e.Mode=n}(e.QrSegment||(e.QrSegment={}))}(s||(s={}));var u=s,l=["value","size","level","bgColor","fgColor","includeMargin","marginSize","imageSettings"],h={L:u.QrCode.Ecc.LOW,M:u.QrCode.Ecc.MEDIUM,Q:u.QrCode.Ecc.QUARTILE,H:u.QrCode.Ecc.HIGH};function c(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=[];return e.forEach((function(e,r){var i=null;e.forEach((function(o,a){if(!o&&null!==i)return n.push("M".concat(i+t," ").concat(r+t,"h").concat(a-i,"v1H").concat(i+t,"z")),void(i=null);if(a!==e.length-1)o&&null===i&&(i=a);else{if(!o)return;n.push(null===i?"M".concat(a+t,",").concat(r+t," h1v1H").concat(a+t,"z"):"M".concat(i+t,",").concat(r+t," h").concat(a+1-i,"v1H").concat(i+t,"z"))}}))})),n.join("")}function f(e,t){return e.slice().map((function(e,n){return t.y>n||n>=t.y+t.h?e:e.map((function(e,n){return(t.x>n||n>=t.x+t.w)&&e}))}))}var d=function(){function e(n,r){var i=this;for(var o in t(this,e),this.canvas=void 0,this.pixelRatio="undefined"!=typeof window?window.devicePixelRatio:1,this.path2D=!0,this.SUPPORTS_PATH2D=void 0,this.createImage=function(){return new Image},this.createPath2D=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,t="createPath2D";return i.canvas&&t in i.canvas?i.canvas[t](e):new Path2D(e)},this.canvas=n,r)o in this&&(this[o]=r[o]);this.SUPPORTS_PATH2D=function(){try{i.createPath2D()}catch(e){return!1}return!0}()}return r(e,[{key:"render",value:function(e,t){var n=this,r=e.value,o=e.size,a=void 0===o?128:o,s=e.level,d=void 0===s?"L":s,v=e.bgColor,m=void 0===v?"#FFFFFF":v,g=e.fgColor,y=void 0===g?"#000000":g,E=e.includeMargin,w=void 0!==E&&E,M=e.marginSize,C=e.imageSettings,R=(i(e,l),null==C?void 0:C.src),p=this.canvas,A=p.getContext("2d");if(A&&r){var P=u.QrCode.encodeText(r,h[d]).getModules(),N=function(e,t){return null!=t?Math.floor(t):e?4:0}(w,M),k=P.length+2*N,S=function(e,t,n,r){if(null==r)return null;var i=e.length+2*n,o=Math.floor(.1*t),a=i/t,s=(r.width||o)*a,u=(r.height||o)*a,l=null==r.x?e.length/2-s/2:r.x*a,h=null==r.y?e.length/2-u/2:r.y*a,c=null;if(r.excavate){var f=Math.floor(l),d=Math.floor(h);c={x:f,y:d,w:Math.ceil(s+l-f),h:Math.ceil(u+h-d)}}return{x:l,y:h,h:u,w:s,excavation:c}}(P,a,N,C),I=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,r=null!=S&&null!==e&&e.complete&&0!==e.naturalHeight&&0!==e.naturalWidth;r&&null!=S.excavation&&(P=f(P,S.excavation)),t&&t(P);var i=n.pixelRatio;A.clearRect(0,0,p.width,p.height),A.setTransform(1,0,0,1,0,0),p.height=p.width=a*i;var o=a/k*i;if(A.scale(o,o),A.fillStyle=m,A.fillRect(0,0,k,k),A.fillStyle=y,n.SUPPORTS_PATH2D&&n.path2D){var s=n.createPath2D(c(P,N));A.fill(s)}else P.forEach((function(e,t){e.forEach((function(e,n){e&&A.fillRect(n+N,t+N,1,1)}))}));var u=(null==e?void 0:e.path)||e;r&&A.drawImage(u,S.x+N,S.y+N,S.w,S.h),"draw"in A&&A.draw()};if(R){var b=this.createImage(p);b.onload=function(){I(b)},b.onerror=function(e){I(),console.warn(e)},b.src=R}else I()}}}]),e}();e.QRCodeCanvas=d,Object.defineProperty(e,"__esModule",{value:!0})})); |
对比新文件 |
| | |
| | | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).uni=n()}(this,(function(){"use strict";try{var e={};Object.defineProperty(e,"passive",{get:function(){!0}}),window.addEventListener("test-passive",null,e)}catch(e){}var n=Object.prototype.hasOwnProperty;function i(e,i){return n.call(e,i)}var t=[];function o(){return window.__dcloud_weex_postMessage||window.__dcloud_weex_}var r=function(e,n){var i={options:{timestamp:+new Date},name:e,arg:n};if(o()){if("postMessage"===e){var r={data:[n]};return window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(r):window.__dcloud_weex_.postMessage(JSON.stringify(r))}var a={type:"WEB_INVOKE_APPSERVICE",args:{data:i,webviewIds:t}};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessageToService(a):window.__dcloud_weex_.postMessageToService(JSON.stringify(a))}if(!window.plus)return window.parent.postMessage({type:"WEB_INVOKE_APPSERVICE",data:i,pageId:""},"*");if(0===t.length){var d=plus.webview.currentWebview();if(!d)throw new Error("plus.webview.currentWebview() is undefined");var s=d.parent(),w="";w=s?s.id:d.id,t.push(w)}if(plus.webview.getWebviewById("__uniapp__service"))plus.webview.postMessageToUniNView({type:"WEB_INVOKE_APPSERVICE",args:{data:i,webviewIds:t}},"__uniapp__service");else{var u=JSON.stringify(i);plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat("WEB_INVOKE_APPSERVICE",'",').concat(u,",").concat(JSON.stringify(t),");"))}},a={navigateTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;r("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("redirectTo",{url:encodeURI(n)})},getEnv:function(e){o()?e({nvue:!0}):window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};r("postMessage",e.data||{})}},d=/uni-app/i.test(navigator.userAgent),s=/Html5Plus/i.test(navigator.userAgent),w=/complete|loaded|interactive/;var u=window.my&&navigator.userAgent.indexOf("AlipayClient")>-1;var g=window.swan&&window.swan.webView&&/swan/i.test(navigator.userAgent);var c=window.qq&&window.qq.miniProgram&&/QQ/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var v=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var m=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);var f=window.ks&&window.ks.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var l=window.tt&&window.tt.miniProgram&&/Lark|Feishu/i.test(navigator.userAgent);var _=window.jd&&window.jd.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);for(var E,b=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},h=[function(e){if(d||s)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&w.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),a},function(e){if(m)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.wx.miniProgram},function(e){if(c)return window.QQJSBridge&&window.QQJSBridge.invoke?setTimeout(e,0):document.addEventListener("QQJSBridgeReady",e),window.qq.miniProgram},function(e){if(u){document.addEventListener("DOMContentLoaded",e);var n=window.my;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(g)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(v)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(p){window.QaJSBridge&&window.QaJSBridge.invoke?setTimeout(e,0):document.addEventListener("QaJSBridgeReady",e);var n=window.qa;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(f)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.ks.miniProgram},function(e){if(l)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(_)return window.JDJSBridgeReady&&window.JDJSBridgeReady.invoke?setTimeout(e,0):document.addEventListener("JDJSBridgeReady",e),window.jd.miniProgram},function(e){return document.addEventListener("DOMContentLoaded",e),a}],y=0;y<h.length&&!(E=h[y](b));y++);E||(E={});var B="undefined"!=typeof uni?uni:{};if(!B.navigateTo)for(var S in E)i(E,S)&&(B[S]=E[S]);return B.webView=E,B})); |
对比新文件 |
| | |
| | | { |
| | | "id": "lime-qrcode", |
| | | "displayName": "qrcode 二维码生成", |
| | | "version": "0.1.8", |
| | | "description": "一款全平台通用的二维码生成插件,支持uniapp/uniappx(web,ios,安卓)", |
| | | "keywords": [ |
| | | "qrcode", |
| | | "qr", |
| | | "uvue", |
| | | "生成图片", |
| | | "二维码" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | }, |
| | | "dcloudext": { |
| | | "type": "component-vue", |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "305716444" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "lime-shared" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y", |
| | | "alipay": "n" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y", |
| | | "app-uvue": "y", |
| | | "app-android": { |
| | | "minVersion": "19" |
| | | } |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "u", |
| | | "Edge": "u", |
| | | "Firefox": "u", |
| | | "Safari": "u" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "u", |
| | | "百度": "u", |
| | | "字节跳动": "u", |
| | | "QQ": "u", |
| | | "钉钉": "u", |
| | | "快手": "u", |
| | | "飞书": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | # lime-qrcode 二维码 |
| | | - 一款全平台通用的二维码生成插件,支持uniapp/uniappx(web,ios,安卓) |
| | | - uvue 需要导入[lime-qrcodegen](https://ext.dcloud.net.cn/plugin?id=15838) |
| | | |
| | | ## 使用 |
| | | - 导入插件后直接使用 |
| | | - uvue 需要导入**[lime-qrcodegen](https://ext.dcloud.net.cn/plugin?id=15838)** |
| | | |
| | | #### 基础使用 |
| | | |
| | | ```html |
| | | <l-qrcode value="http://lime.qcoon.cn" /> |
| | | ``` |
| | | |
| | | |
| | | #### ICON |
| | | - 带 Icon 的二维码 |
| | | |
| | | ```html |
| | | <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" icon="/static/logo.png" iconSize="70rpx"></l-qrcode> |
| | | ``` |
| | | |
| | | #### 颜色 |
| | | - 通过设置 `color` 自定义二维码颜色,通过设置 `bgColor` 自定义背景颜色。 |
| | | |
| | | ```html |
| | | <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode> |
| | | <l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode> |
| | | ``` |
| | | |
| | | #### 纠错比例 |
| | | - 通过设置 `errorLevel` 调整不同的容错等级。 |
| | | |
| | | ```html |
| | | <l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" errorLevel="H"></l-qrcode> |
| | | ``` |
| | | |
| | | #### 生成图片 |
| | | - 1、通过调用插件的`canvasToTempFilePath`方法生成图片。 |
| | | |
| | | ```html |
| | | <image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image> |
| | | <l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> |
| | | <button @click="onClick">生成图片</button> |
| | | ``` |
| | | ```js |
| | | // vue3 |
| | | const qrcodeRef = ref(null) |
| | | const onClick = () => { |
| | | if(!qrcodeRef.value) return |
| | | qrcodeRef.value.canvasToTempFilePath({ |
| | | success(res) { |
| | | image.value = res.tempFilePath |
| | | console.log('success:::', res) |
| | | }, |
| | | fail(err) { |
| | | console.log('err:::', err) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // vue2 |
| | | const el = this.$refs['qrcodeRef'] |
| | | el.canvasToTempFilePath({ |
| | | success:(res)=>{ |
| | | this.image = res.tempFilePath |
| | | }, |
| | | fail(err) { |
| | | console.log('err:::', err) |
| | | } |
| | | }) |
| | | |
| | | // uvue |
| | | const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance |
| | | el.canvasToTempFilePath({ |
| | | success:(res: TakeSnapshotSuccess)=>{ |
| | | this.image = res.tempFilePath |
| | | }, |
| | | fail(err: TakeSnapshotFail) { |
| | | console.log('err:::', err) |
| | | } |
| | | }) |
| | | ``` |
| | | |
| | | - 2、通过设置`useCanvasToTempFilePath`在`success`事件里接收图片地址 |
| | | |
| | | ```html |
| | | <image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image> |
| | | <l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn"></l-qrcode> |
| | | ``` |
| | | ```js |
| | | const image = ref(null) |
| | | const success = (img) => { |
| | | image.value = img |
| | | } |
| | | ``` |
| | | |
| | | ### 关于vue2的使用方式 |
| | | - 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置 |
| | | - 关键代码是: 在main.js中 在vue2部分加上这一段即可 |
| | | |
| | | ```js |
| | | // main.js vue2 |
| | | import Vue from 'vue' |
| | | import VueCompositionAPI from '@vue/composition-api' |
| | | Vue.use(VueCompositionAPI) |
| | | ``` |
| | | 另外插件也用到了TS,vue2可能会遇过官方的TS版本过低的问题,找到HX目录下的`compile-typescript`目录 |
| | | ```cmd |
| | | // \HBuilderX\plugins\compile-typescript |
| | | yarn add typescript -D |
| | | - or - |
| | | npm install typescript -D |
| | | ``` |
| | | |
| | | ### 查看示例 |
| | | - 导入后直接使用这个标签查看演示效果 |
| | | |
| | | ```html |
| | | // 代码位于 uni_modules/lime-qrcode/compoents/lime-qrcode |
| | | <lime-qrcode /> |
| | | ``` |
| | | |
| | | ### 插件标签 |
| | | - 默认 l-qrcode 为 component |
| | | - 默认 lime-qrcode 为 demo |
| | | |
| | | |
| | | |
| | | ## API |
| | | |
| | | ### Props |
| | | |
| | | | 参数 | 说明 | 类型 | 默认值 | |
| | | | --------------------------| ------------------------------------------------------------ | ---------------- | ------------ | |
| | | | value | 扫描后的文本 | <em>string</em> | `-` | |
| | | | icon | 二维码中图片的地址 | <em>string</em> | `-` | |
| | | | size | 二维码大小 | <em>number,string</em> | `160` | |
| | | | iconSize | 二维码中图片的大小 | <em>number,string</em> | `40` | |
| | | | color | 二维码颜色 | <em>string</em> | `-` | |
| | | | bgColor | 二维码背景颜色 | <em>string</em> | `-` | |
| | | | errorLevel | 二维码纠错等级 | `'L' | 'M' | 'Q' | 'H' ` | `M` | |
| | | | marginSize | 边距码大小,默认为0码点 | <em>number</em> | `0` | |
| | | |
| | | ### 常见问题 |
| | | - icon 是网络地址时,H5和Nvue需要解决跨域问题,小程序需要配置download |
| | | |
| | | ## 打赏 |
| | | |
| | | 如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。 |
| | |  |
| | |  |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import {isNumeric} from '../isNumeric' |
| | | import {isDef} from '../isDef' |
| | | /** |
| | | * 给一个值添加单位(像素 px) |
| | | * @param value 要添加单位的值,可以是字符串或数字 |
| | | * @returns 添加了单位的值,如果值为 null 则返回 null |
| | | */ |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export function addUnit(value?: string | number): string | null { |
| | | if (!isDef(value)) { |
| | | return null; |
| | | } |
| | | value = String(value); // 将值转换为字符串 |
| | | // 如果值是数字,则在后面添加单位 "px",否则保持原始值 |
| | | return isNumeric(value) ? `${value}px` : value; |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | function addUnit(value: string): string |
| | | function addUnit(value: number): string |
| | | function addUnit(value: any|null): string|null { |
| | | if (!isDef(value)) { |
| | | return null; |
| | | } |
| | | value = `${value}` //value.toString(); // 将值转换为字符串 |
| | | |
| | | // 如果值是数字,则在后面添加单位 "px",否则保持原始值 |
| | | return isNumeric(value) ? `${value}px` : value; |
| | | } |
| | | export {addUnit} |
| | | // #endif |
| | | |
| | | |
| | | // console.log(addUnit(100)); // 输出: "100px" |
| | | // console.log(addUnit("200")); // 输出: "200px" |
| | | // console.log(addUnit("300px")); // 输出: "300px"(已经包含单位) |
| | | // console.log(addUnit()); // 输出: undefined(值为 undefined) |
| | | // console.log(addUnit(null)); // 输出: undefined(值为 null) |
对比新文件 |
| | |
| | | export function cubicBezier(p1x : number, p1y : number, p2x : number, p2y : number):(x: number)=> number { |
| | | const ZERO_LIMIT = 1e-6; |
| | | // Calculate the polynomial coefficients, |
| | | // implicit first and last control points are (0,0) and (1,1). |
| | | const ax = 3 * p1x - 3 * p2x + 1; |
| | | const bx = 3 * p2x - 6 * p1x; |
| | | const cx = 3 * p1x; |
| | | |
| | | const ay = 3 * p1y - 3 * p2y + 1; |
| | | const by = 3 * p2y - 6 * p1y; |
| | | const cy = 3 * p1y; |
| | | |
| | | function sampleCurveDerivativeX(t : number) : number { |
| | | // `ax t^3 + bx t^2 + cx t` expanded using Horner's rule |
| | | return (3 * ax * t + 2 * bx) * t + cx; |
| | | } |
| | | |
| | | function sampleCurveX(t : number) : number { |
| | | return ((ax * t + bx) * t + cx) * t; |
| | | } |
| | | |
| | | function sampleCurveY(t : number) : number { |
| | | return ((ay * t + by) * t + cy) * t; |
| | | } |
| | | |
| | | // Given an x value, find a parametric value it came from. |
| | | function solveCurveX(x : number) : number { |
| | | let t2 = x; |
| | | let derivative : number; |
| | | let x2 : number; |
| | | |
| | | // https://trac.webkit.org/browser/trunk/Source/WebCore/platform/animation |
| | | // first try a few iterations of Newton's method -- normally very fast. |
| | | // http://en.wikipedia.org/wikiNewton's_method |
| | | for (let i = 0; i < 8; i++) { |
| | | // f(t) - x = 0 |
| | | x2 = sampleCurveX(t2) - x; |
| | | if (Math.abs(x2) < ZERO_LIMIT) { |
| | | return t2; |
| | | } |
| | | derivative = sampleCurveDerivativeX(t2); |
| | | // == 0, failure |
| | | /* istanbul ignore if */ |
| | | if (Math.abs(derivative) < ZERO_LIMIT) { |
| | | break; |
| | | } |
| | | t2 -= x2 / derivative; |
| | | } |
| | | |
| | | // Fall back to the bisection method for reliability. |
| | | // bisection |
| | | // http://en.wikipedia.org/wiki/Bisection_method |
| | | let t1 = 1; |
| | | /* istanbul ignore next */ |
| | | let t0 = 0; |
| | | |
| | | /* istanbul ignore next */ |
| | | t2 = x; |
| | | /* istanbul ignore next */ |
| | | while (t1 > t0) { |
| | | x2 = sampleCurveX(t2) - x; |
| | | if (Math.abs(x2) < ZERO_LIMIT) { |
| | | return t2; |
| | | } |
| | | if (x2 > 0) { |
| | | t1 = t2; |
| | | } else { |
| | | t0 = t2; |
| | | } |
| | | t2 = (t1 + t0) / 2; |
| | | } |
| | | |
| | | // Failure |
| | | return t2; |
| | | } |
| | | |
| | | return function (x : number) : number { |
| | | return sampleCurveY(solveCurveX(x)); |
| | | } |
| | | |
| | | // return solve; |
| | | } |
对比新文件 |
| | |
| | | import {cubicBezier} from './bezier'; |
| | | export let ease = cubicBezier(0.25, 0.1, 0.25, 1); |
| | | export let linear = cubicBezier(0,0,1,1); |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.uts' |
| | | // #endif |
| | | |
| | | |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import { ComponentPublicInstance } from 'vue' |
| | | import { ease, linear } from './ease'; |
| | | import { Timeline, Animation } from './'; |
| | | export type UseTransitionOptions = { |
| | | duration ?: number |
| | | immediate ?: boolean |
| | | context ?: ComponentPublicInstance |
| | | } |
| | | // #ifndef UNI-APP-X && APP |
| | | import { ref, watch, Ref } from '@/uni_modules/lime-shared/vue' |
| | | |
| | | export function useTransition(percent : Ref<number>|(() => number), options : UseTransitionOptions) : Ref<number> { |
| | | const current = ref(0) |
| | | const { immediate, duration = 300 } = options |
| | | let tl:Timeline|null = null; |
| | | let timer = -1 |
| | | const isFunction = typeof percent === 'function' |
| | | watch(isFunction ? percent : () => percent.value, (v) => { |
| | | if(tl == null){ |
| | | tl = new Timeline() |
| | | } |
| | | tl.start(); |
| | | tl.add( |
| | | new Animation( |
| | | current.value, |
| | | v, |
| | | duration, |
| | | 0, |
| | | ease, |
| | | nowValue => { |
| | | current.value = nowValue |
| | | clearTimeout(timer) |
| | | if(current.value == v){ |
| | | timer = setTimeout(()=>{ |
| | | tl?.pause(); |
| | | tl = null |
| | | }, duration) |
| | | } |
| | | } |
| | | ) |
| | | ); |
| | | }, { immediate }) |
| | | |
| | | return current |
| | | } |
| | | |
| | | // #endif |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | type UseTransitionReturnType = Ref<number> |
| | | // export function useTransition<T>(source : any, options : UseTransitionOptions) : UseTransitionReturnType |
| | | export function useTransition(source : any, options : UseTransitionOptions) : UseTransitionReturnType { |
| | | const outputRef : Ref<number> = ref(0) |
| | | const immediate = options.immediate ?? false |
| | | const duration = options.duration ?? 300 |
| | | const context = options.context //as ComponentPublicInstance | null |
| | | let tl:Timeline|null = null; |
| | | let timer = -1 |
| | | const watchFunc = (v : number) => { |
| | | if(tl == null){ |
| | | tl = new Timeline() |
| | | } |
| | | tl!.start(); |
| | | tl!.add( |
| | | new Animation( |
| | | outputRef.value, |
| | | v, |
| | | duration, |
| | | 0, |
| | | ease, |
| | | nowValue => { |
| | | outputRef.value = nowValue //nowValue < 0.0001 ? 0 : Math.abs(v - nowValue) < 0.00001 ? v : nowValue; |
| | | clearTimeout(timer) |
| | | if(outputRef.value == v){ |
| | | timer = setTimeout(()=>{ |
| | | tl?.pause(); |
| | | tl = null |
| | | }, duration) |
| | | } |
| | | } |
| | | ), null |
| | | ); |
| | | } |
| | | |
| | | if (context != null && typeof source == 'string') { |
| | | context.$watch(source, watchFunc, { immediate } as WatchOptions) |
| | | } else { |
| | | watch(source, watchFunc, { immediate } as WatchOptions) |
| | | } |
| | | |
| | | const stop = ()=>{ |
| | | |
| | | } |
| | | return outputRef //as UseTransitionReturnType |
| | | } |
| | | |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import {raf, cancelRaf} from '../raf' |
| | | export class Timeline { |
| | | state : string |
| | | animations : Set<Animation> = new Set<Animation>() |
| | | delAnimations : Animation[] = [] |
| | | startTimes : Map<Animation, number> = new Map<Animation, number>() |
| | | pauseTime : number = 0 |
| | | pauseStart : number = Date.now() |
| | | tickHandler : number = 0 |
| | | tickHandlers : number[] = [] |
| | | tick : (() => void) | null = null |
| | | constructor() { |
| | | this.state = 'Initiated'; |
| | | } |
| | | start() { |
| | | if (!(this.state === 'Initiated')) return; |
| | | this.state = 'Started'; |
| | | |
| | | let startTime = Date.now(); |
| | | this.pauseTime = 0; |
| | | this.tick = () => { |
| | | let now = Date.now(); |
| | | this.animations.forEach((animation : Animation) => { |
| | | let t:number; |
| | | const ani = this.startTimes.get(animation) |
| | | if (ani == null) return |
| | | if (ani < startTime) { |
| | | t = now - startTime - animation.delay - this.pauseTime; |
| | | } else { |
| | | t = now - ani - animation.delay - this.pauseTime; |
| | | } |
| | | if (t > animation.duration) { |
| | | this.delAnimations.push(animation) |
| | | // 不能在 foreach 里面 对 集合进行删除操作 |
| | | // this.animations.delete(animation); |
| | | t = animation.duration; |
| | | } |
| | | if (t > 0) animation.run(t); |
| | | }) |
| | | // 不能在 foreach 里面 对 集合进行删除操作 |
| | | while (this.delAnimations.length > 0) { |
| | | const animation = this.delAnimations.pop(); |
| | | if (animation == null) return |
| | | this.animations.delete(animation); |
| | | } |
| | | clearTimeout(this.tickHandler); |
| | | if (this.state != 'Started') return |
| | | // this.tickHandler = setTimeout(() => { |
| | | // this.tick!() |
| | | // }, 1000 / 60) |
| | | this.tickHandler = raf(()=> { |
| | | this.tick!() |
| | | }) |
| | | // this.tickHandlers.push(this.tickHandler) |
| | | } |
| | | this.tick!() |
| | | } |
| | | pause() { |
| | | if (!(this.state === 'Started')) return; |
| | | this.state = 'Paused'; |
| | | this.pauseStart = Date.now(); |
| | | // clearTimeout(this.tickHandler); |
| | | cancelRaf(this.tickHandler); |
| | | } |
| | | resume() { |
| | | if (!(this.state === 'Paused')) return; |
| | | this.state = 'Started'; |
| | | this.pauseTime += Date.now() - this.pauseStart; |
| | | this.tick!(); |
| | | } |
| | | reset() { |
| | | this.pause(); |
| | | this.state = 'Initiated'; |
| | | this.pauseTime = 0; |
| | | this.pauseStart = 0; |
| | | this.animations.clear() |
| | | this.delAnimations.clear() |
| | | this.startTimes.clear() |
| | | this.tickHandler = 0; |
| | | } |
| | | add(animation : Animation, startTime ?: number | null) { |
| | | if (startTime == null) startTime = Date.now(); |
| | | this.animations.add(animation); |
| | | this.startTimes.set(animation, startTime); |
| | | } |
| | | } |
| | | |
| | | export class Animation { |
| | | startValue : number |
| | | endValue : number |
| | | duration : number |
| | | timingFunction : (t : number) => number |
| | | delay : number |
| | | template : (t : number) => void |
| | | constructor( |
| | | startValue : number, |
| | | endValue : number, |
| | | duration : number, |
| | | delay : number, |
| | | timingFunction : (t : number) => number, |
| | | template : (v : number) => void) { |
| | | this.startValue = startValue; |
| | | this.endValue = endValue; |
| | | this.duration = duration; |
| | | this.timingFunction = timingFunction; |
| | | this.delay = delay; |
| | | this.template = template; |
| | | } |
| | | |
| | | run(time : number) { |
| | | let range = this.endValue - this.startValue; |
| | | let progress = time / this.duration |
| | | if(progress != 1) progress = this.timingFunction(progress) |
| | | this.template(this.startValue + range * progress) |
| | | } |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | const TICK = Symbol('tick'); |
| | | const TICK_HANDLER = Symbol('tick-handler'); |
| | | const ANIMATIONS = Symbol('animations'); |
| | | const START_TIMES = Symbol('start-times'); |
| | | const PAUSE_START = Symbol('pause-start'); |
| | | const PAUSE_TIME = Symbol('pause-time'); |
| | | const _raf = typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame : function(cb: Function) {return setTimeout(cb, 1000/60)} |
| | | const _caf = typeof cancelAnimationFrame !== 'undefined' ? cancelAnimationFrame: function(id: any) {clearTimeout(id)} |
| | | |
| | | // const TICK = 'tick'; |
| | | // const TICK_HANDLER = 'tick-handler'; |
| | | // const ANIMATIONS = 'animations'; |
| | | // const START_TIMES = 'start-times'; |
| | | // const PAUSE_START = 'pause-start'; |
| | | // const PAUSE_TIME = 'pause-time'; |
| | | // const _raf = function(callback):number|null {return setTimeout(callback, 1000/60)} |
| | | // const _caf = function(id: number):void {clearTimeout(id)} |
| | | |
| | | export class Timeline { |
| | | state: string |
| | | constructor() { |
| | | this.state = 'Initiated'; |
| | | this[ANIMATIONS] = new Set(); |
| | | this[START_TIMES] = new Map(); |
| | | } |
| | | start() { |
| | | if (!(this.state === 'Initiated')) return; |
| | | this.state = 'Started'; |
| | | |
| | | let startTime = Date.now(); |
| | | this[PAUSE_TIME] = 0; |
| | | this[TICK] = () => { |
| | | let now = Date.now(); |
| | | this[ANIMATIONS].forEach((animation) => { |
| | | let t: number; |
| | | if (this[START_TIMES].get(animation) < startTime) { |
| | | t = now - startTime - animation.delay - this[PAUSE_TIME]; |
| | | } else { |
| | | t = now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME]; |
| | | } |
| | | |
| | | if (t > animation.duration) { |
| | | this[ANIMATIONS].delete(animation); |
| | | t = animation.duration; |
| | | } |
| | | if (t > 0) animation.run(t); |
| | | }) |
| | | // for (let animation of this[ANIMATIONS]) { |
| | | // let t: number; |
| | | // console.log('animation', animation) |
| | | // if (this[START_TIMES].get(animation) < startTime) { |
| | | // t = now - startTime - animation.delay - this[PAUSE_TIME]; |
| | | // } else { |
| | | // t = now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME]; |
| | | // } |
| | | |
| | | // if (t > animation.duration) { |
| | | // this[ANIMATIONS].delete(animation); |
| | | // t = animation.duration; |
| | | // } |
| | | // if (t > 0) animation.run(t); |
| | | // } |
| | | this[TICK_HANDLER] = _raf(this[TICK]); |
| | | }; |
| | | this[TICK](); |
| | | } |
| | | pause() { |
| | | if (!(this.state === 'Started')) return; |
| | | this.state = 'Paused'; |
| | | |
| | | this[PAUSE_START] = Date.now(); |
| | | _caf(this[TICK_HANDLER]); |
| | | } |
| | | resume() { |
| | | if (!(this.state === 'Paused')) return; |
| | | this.state = 'Started'; |
| | | |
| | | this[PAUSE_TIME] += Date.now() - this[PAUSE_START]; |
| | | this[TICK](); |
| | | } |
| | | reset() { |
| | | this.pause(); |
| | | this.state = 'Initiated'; |
| | | this[PAUSE_TIME] = 0; |
| | | this[PAUSE_START] = 0; |
| | | this[ANIMATIONS] = new Set(); |
| | | this[START_TIMES] = new Map(); |
| | | this[TICK_HANDLER] = null; |
| | | } |
| | | add(animation: any, startTime?: number) { |
| | | if (arguments.length < 2) startTime = Date.now(); |
| | | this[ANIMATIONS].add(animation); |
| | | this[START_TIMES].set(animation, startTime); |
| | | } |
| | | } |
| | | |
| | | export class Animation { |
| | | startValue: number |
| | | endValue: number |
| | | duration: number |
| | | timingFunction: (t: number) => number |
| | | delay: number |
| | | template: (t: number) => void |
| | | constructor(startValue: number, endValue: number, duration: number, delay: number, timingFunction: (t: number) => number, template: (v: number) => void) { |
| | | timingFunction = timingFunction || (v => v); |
| | | template = template || (v => v); |
| | | |
| | | this.startValue = startValue; |
| | | this.endValue = endValue; |
| | | this.duration = duration; |
| | | this.timingFunction = timingFunction; |
| | | this.delay = delay; |
| | | this.template = template; |
| | | } |
| | | |
| | | run(time: number) { |
| | | let range = this.endValue - this.startValue; |
| | | let progress = time / this.duration |
| | | if(progress != 1) progress = this.timingFunction(progress) |
| | | this.template(this.startValue + range * progress) |
| | | } |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.uts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // import {platform} from '../platform' |
| | | /** |
| | | * buffer转路径 |
| | | * @param {Object} buffer |
| | | */ |
| | | // @ts-nocheck |
| | | export function arrayBufferToFile(buffer: ArrayBuffer, name?: string, format?:string):Promise<(File|string)> { |
| | | console.error('[arrayBufferToFile] 当前环境不支持') |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import {platform} from '../platform' |
| | | /** |
| | | * buffer转路径 |
| | | * @param {Object} buffer |
| | | */ |
| | | // @ts-nocheck |
| | | export function arrayBufferToFile(buffer: ArrayBuffer | Blob, name?: string, format?:string):Promise<(File|string)> { |
| | | return new Promise((resolve, reject) => { |
| | | // #ifdef MP |
| | | const fs = uni.getFileSystemManager() |
| | | //自定义文件名 |
| | | if (!name && !format) { |
| | | reject(new Error('ERROR_NAME_PARSE')) |
| | | } |
| | | const fileName = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`; |
| | | let pre = platform() |
| | | const filePath = `${pre.env.USER_DATA_PATH}/${fileName}` |
| | | fs.writeFile({ |
| | | filePath, |
| | | data: buffer, |
| | | success() { |
| | | resolve(filePath) |
| | | }, |
| | | fail(err) { |
| | | console.error(err) |
| | | reject(err) |
| | | } |
| | | }) |
| | | // #endif |
| | | |
| | | // #ifdef H5 |
| | | const file = new File([buffer], name, { |
| | | type: format, |
| | | }); |
| | | resolve(file) |
| | | // #endif |
| | | |
| | | // #ifdef APP-PLUS |
| | | const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now()) |
| | | const base64 = uni.arrayBufferToBase64(buffer) |
| | | bitmap.loadBase64Data(base64, () => { |
| | | if (!name && !format) { |
| | | reject(new Error('ERROR_NAME_PARSE')) |
| | | } |
| | | const fileNmae = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`; |
| | | const filePath = `_doc/uniapp_temp/${fileNmae}` |
| | | bitmap.save(filePath, {}, |
| | | () => { |
| | | bitmap.clear() |
| | | resolve(filePath) |
| | | }, |
| | | (error) => { |
| | | bitmap.clear() |
| | | reject(error) |
| | | }) |
| | | }, (error) => { |
| | | bitmap.clear() |
| | | reject(error) |
| | | }) |
| | | // #endif |
| | | }) |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // 未完成 |
| | | export function base64ToArrayBuffer(base64 : string) { |
| | | const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || []; |
| | | if (!format) { |
| | | new Error('ERROR_BASE64SRC_PARSE') |
| | | } |
| | | if(uni.base64ToArrayBuffer) { |
| | | return uni.base64ToArrayBuffer(bodyData) |
| | | } else { |
| | | |
| | | } |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.uts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import { processFile, ProcessFileOptions } from '@/uni_modules/lime-file-utils' |
| | | |
| | | /** |
| | | * base64转路径 |
| | | * @param {Object} base64 |
| | | */ |
| | | export function base64ToPath(base64: string, filename: string | null = null):Promise<string> { |
| | | return new Promise((resolve,reject) => { |
| | | processFile({ |
| | | type: 'toDataURL', |
| | | path: base64, |
| | | filename, |
| | | success(res: string){ |
| | | resolve(res) |
| | | }, |
| | | fail(err){ |
| | | reject(err) |
| | | } |
| | | } as ProcessFileOptions) |
| | | }) |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import {platform} from '../platform' |
| | | /** |
| | | * base64转路径 |
| | | * @param {Object} base64 |
| | | */ |
| | | export function base64ToPath(base64: string, filename?: string):Promise<string> { |
| | | const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || []; |
| | | return new Promise((resolve, reject) => { |
| | | // #ifdef MP |
| | | const fs = uni.getFileSystemManager() |
| | | //自定义文件名 |
| | | if (!filename && !format) { |
| | | reject(new Error('ERROR_BASE64SRC_PARSE')) |
| | | } |
| | | // const time = new Date().getTime(); |
| | | const name = filename || `${new Date().getTime()}.${format}`; |
| | | let pre = platform() |
| | | const filePath = `${pre.env.USER_DATA_PATH}/${name}` |
| | | fs.writeFile({ |
| | | filePath, |
| | | data: base64.split(',')[1], |
| | | encoding: 'base64', |
| | | success() { |
| | | resolve(filePath) |
| | | }, |
| | | fail(err) { |
| | | console.error(err) |
| | | reject(err) |
| | | } |
| | | }) |
| | | // #endif |
| | | |
| | | // #ifdef H5 |
| | | // mime类型 |
| | | let mimeString = base64.split(',')[0].split(':')[1].split(';')[0]; |
| | | //base64 解码 |
| | | let byteString = atob(base64.split(',')[1]); |
| | | //创建缓冲数组 |
| | | let arrayBuffer = new ArrayBuffer(byteString.length); |
| | | //创建视图 |
| | | let intArray = new Uint8Array(arrayBuffer); |
| | | for (let i = 0; i < byteString.length; i++) { |
| | | intArray[i] = byteString.charCodeAt(i); |
| | | } |
| | | resolve(URL.createObjectURL(new Blob([intArray], { |
| | | type: mimeString |
| | | }))) |
| | | // #endif |
| | | |
| | | // #ifdef APP-PLUS |
| | | const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now()) |
| | | bitmap.loadBase64Data(base64, () => { |
| | | if (!filename && !format) { |
| | | reject(new Error('ERROR_BASE64SRC_PARSE')) |
| | | } |
| | | // const time = new Date().getTime(); |
| | | const name = filename || `${new Date().getTime()}.${format}`; |
| | | const filePath = `_doc/uniapp_temp/${name}` |
| | | bitmap.save(filePath, {}, |
| | | () => { |
| | | bitmap.clear() |
| | | resolve(filePath) |
| | | }, |
| | | (error) => { |
| | | bitmap.clear() |
| | | reject(error) |
| | | }) |
| | | }, (error) => { |
| | | bitmap.clear() |
| | | reject(error) |
| | | }) |
| | | // #endif |
| | | }) |
| | | } |
对比新文件 |
| | |
| | | /** |
| | | * 将字符串转换为 camelCase 或 PascalCase 风格的命名约定 |
| | | * @param str 要转换的字符串 |
| | | * @param isPascalCase 指示是否转换为 PascalCase 的布尔值,默认为 false |
| | | * @returns 转换后的字符串 |
| | | */ |
| | | export function camelCase(str: string, isPascalCase: boolean = false): string { |
| | | // 将字符串分割成单词数组 |
| | | let words: string[] = str.split(/[\s_-]+/); |
| | | |
| | | // 将数组中的每个单词首字母大写(除了第一个单词) |
| | | let camelCased: string[] = words.map((word, index):string => { |
| | | if (index == 0 && !isPascalCase) { |
| | | return word.toLowerCase(); // 第一个单词全小写 |
| | | } |
| | | return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); |
| | | }); |
| | | |
| | | // 将数组中的单词拼接成一个字符串 |
| | | return camelCased.join(''); |
| | | }; |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | |
| | | // #ifdef MP-ALIPAY |
| | | interface My { |
| | | SDKVersion: string |
| | | } |
| | | declare var my: My |
| | | // #endif |
| | | |
| | | function compareVersion(v1:string, v2:string) { |
| | | let a1 = v1.split('.'); |
| | | let a2 = v2.split('.'); |
| | | const len = Math.max(a1.length, a2.length); |
| | | |
| | | while (a1.length < len) { |
| | | a1.push('0'); |
| | | } |
| | | while (a2.length < len) { |
| | | a2.push('0'); |
| | | } |
| | | |
| | | for (let i = 0; i < len; i++) { |
| | | const num1 = parseInt(a1[i], 10); |
| | | const num2 = parseInt(a2[i], 10); |
| | | |
| | | if (num1 > num2) { |
| | | return 1; |
| | | } |
| | | if (num1 < num2) { |
| | | return -1; |
| | | } |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | function gte(version: string) { |
| | | let {SDKVersion} = uni.getSystemInfoSync(); |
| | | // #ifdef MP-ALIPAY |
| | | SDKVersion = my.SDKVersion |
| | | // #endif |
| | | return compareVersion(SDKVersion, version) >= 0; |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | /** 环境是否支持canvas 2d */ |
| | | export function canIUseCanvas2d(): boolean { |
| | | // #ifdef MP-WEIXIN |
| | | return gte('2.9.0'); |
| | | // #endif |
| | | // #ifdef MP-ALIPAY |
| | | return gte('2.7.0'); |
| | | // #endif |
| | | // #ifdef MP-TOUTIAO |
| | | return gte('1.78.0'); |
| | | // #endif |
| | | // #ifndef MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO |
| | | return false |
| | | // #endif |
| | | |
| | | // #ifdef UNI-APP-X && APP || APP-NVUE || APP-VUE |
| | | return false; |
| | | // #endif |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import { isString } from "../isString"; |
| | | import { isNumber } from "../isNumber"; |
| | | /** |
| | | * 将金额转换为中文大写形式 |
| | | * @param {number | string} amount - 需要转换的金额,可以是数字或字符串 |
| | | * @returns {string} 转换后的中文大写金额 |
| | | */ |
| | | function capitalizedAmount(amount : number) : string |
| | | function capitalizedAmount(amount : string) : string |
| | | function capitalizedAmount(amount : any | null) : string { |
| | | try { |
| | | let _amountStr :string; |
| | | let _amountNum :number = 0; |
| | | // 如果输入是字符串,先将其转换为数字,并去除逗号 |
| | | if (typeof amount == 'string') { |
| | | _amountNum = parseFloat((amount as string).replace(/,/g, '')); |
| | | } |
| | | if(isNumber(amount)) { |
| | | _amountNum = amount as number |
| | | } |
| | | // 判断输入是否为有效的金额 || isNaN(amount) |
| | | if (amount == null) throw new Error('不是有效的金额!'); |
| | | |
| | | let result = ''; |
| | | |
| | | // 处理负数情况 |
| | | if (_amountNum < 0) { |
| | | result = '欠'; |
| | | _amountNum = Math.abs(_amountNum); |
| | | } |
| | | |
| | | // 金额不能超过千亿以上 |
| | | if (_amountNum >= 10e11) throw new Error('计算金额过大!'); |
| | | |
| | | // 保留两位小数并转换为字符串 |
| | | _amountStr = _amountNum.toFixed(2); |
| | | |
| | | // 定义数字、单位和小数单位的映射 |
| | | const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']; |
| | | const units = ['', '拾', '佰', '仟']; |
| | | const bigUnits = ['', '万', '亿']; |
| | | const decimalUnits = ['角', '分']; |
| | | |
| | | // 分离整数部分和小数部分 |
| | | const amountArray = _amountStr.split('.'); |
| | | let integerPart = amountArray[0]; // string| number[] |
| | | const decimalPart = amountArray[1]; |
| | | |
| | | // 处理整数部分 |
| | | if (integerPart != '0') { |
| | | let _integerPart = integerPart.split('').map((item):number => parseInt(item)); |
| | | |
| | | // 将整数部分按四位一级进行分组 |
| | | const levels = _integerPart.reverse().reduce((prev:string[][], item, index):string[][] => { |
| | | // const level = prev?.[0]?.length < 4 ? prev[0] : []; |
| | | const level = prev.length > 0 && prev[0].length < 4 ? prev[0]: [] |
| | | |
| | | const value = item == 0 ? digits[item] : digits[item] + units[index % 4]; |
| | | |
| | | level.unshift(value); |
| | | |
| | | if (level.length == 1) { |
| | | prev.unshift(level); |
| | | } else { |
| | | prev[0] = level; |
| | | } |
| | | |
| | | return prev; |
| | | }, [] as string[][]); |
| | | // 将分组后的整数部分转换为中文大写形式 |
| | | result += levels.reduce((prev, item, index):string => { |
| | | let _level = bigUnits[levels.length - index - 1]; |
| | | let _item = item.join('').replace(/(零)\1+/g, '$1'); |
| | | |
| | | if (_item == '零') { |
| | | _level = ''; |
| | | _item = ''; |
| | | } else if (_item.endsWith('零')) { |
| | | _item = _item.slice(0, _item.length - 1); |
| | | } |
| | | |
| | | return prev + _item + _level; |
| | | }, ''); |
| | | } else { |
| | | result += '零'; |
| | | } |
| | | |
| | | // 添加元 |
| | | result += '元'; |
| | | |
| | | // 处理小数部分 |
| | | if (decimalPart != '00') { |
| | | if (result == '零元') result = ''; |
| | | |
| | | for (let i = 0; i < decimalPart.length; i++) { |
| | | const digit = parseInt(decimalPart.charAt(i)); |
| | | |
| | | if (digit != 0) { |
| | | result += digits[digit] + decimalUnits[i]; |
| | | } |
| | | } |
| | | } else { |
| | | result += '整'; |
| | | } |
| | | |
| | | return result; |
| | | } catch (error : Error) { |
| | | return error.message; |
| | | } |
| | | }; |
对比新文件 |
| | |
| | | ## 0.1.8(2024-10-08) |
| | | - fix: vue2 条件编译 // #ifdef APP-IOS || APP-ANDROID 会生效 |
| | | ## 0.1.7(2024-09-23) |
| | | - fix: raf 类型跟随版本变更 |
| | | ## 0.1.6(2024-07-24) |
| | | - fix: vue2 app ts需要明确的后缀,所有补全 |
| | | - chore: 减少依赖 |
| | | ## 0.1.5(2024-07-21) |
| | | - feat: 删除 Hooks |
| | | - feat: 兼容uniappx |
| | | ## 0.1.4(2023-09-05) |
| | | - feat: 增加 Hooks `useIntersectionObserver` |
| | | - feat: 增加 `floatAdd` |
| | | - feat: 因为本人插件兼容 vue2 需要使用 `composition-api`,故增加vue文件代码插件的条件编译 |
| | | ## 0.1.3(2023-08-13) |
| | | - feat: 增加 `camelCase` |
| | | ## 0.1.2(2023-07-17) |
| | | - feat: 增加 `getClassStr` |
| | | ## 0.1.1(2023-07-06) |
| | | - feat: 增加 `isNumeric`, 区别于 `isNumber` |
| | | ## 0.1.0(2023-06-30) |
| | | - fix: `clamp`忘记导出了 |
| | | ## 0.0.9(2023-06-27) |
| | | - feat: 增加`arrayBufferToFile` |
| | | ## 0.0.8(2023-06-19) |
| | | - feat: 增加`createAnimation`、`clamp` |
| | | ## 0.0.7(2023-06-08) |
| | | - chore: 更新注释 |
| | | ## 0.0.6(2023-06-08) |
| | | - chore: 增加`createImage`为`lime-watermark`和`lime-qrcode`提供依赖 |
| | | ## 0.0.5(2023-06-03) |
| | | - chore: 更新注释 |
| | | ## 0.0.4(2023-05-22) |
| | | - feat: 增加`range`,`exif`,`selectComponent` |
| | | ## 0.0.3(2023-05-08) |
| | | - feat: 增加`fillZero`,`debounce`,`throttle`,`random` |
| | | ## 0.0.2(2023-05-05) |
| | | - chore: 更新文档 |
| | | ## 0.0.1(2023-05-05) |
| | | - 无 |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 将一个值限制在指定的范围内 |
| | | * @param val 要限制的值 |
| | | * @param min 最小值 |
| | | * @param max 最大值 |
| | | * @returns 限制后的值 |
| | | */ |
| | | export function clamp(val: number, min: number, max: number): number { |
| | | return Math.max(min, Math.min(max, val)); |
| | | } |
| | | |
| | | |
| | | // console.log(clamp(5 ,0, 10)); // 输出: 5(在范围内,不做更改) |
| | | // console.log(clamp(-5 ,0, 10)); // 输出: 0(小于最小值,被限制为最小值) |
| | | // console.log(clamp(15 ,0, 10)); // 输出: 10(大于最大值,被限制为最大值) |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.ts' |
| | | // #endif |
| | | |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 深度克隆一个对象或数组 |
| | | * @param obj 要克隆的对象或数组 |
| | | * @returns 克隆后的对象或数组 |
| | | */ |
| | | export function cloneDeep<T>(obj: any): T { |
| | | // 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回 |
| | | // if(['number', 'string'].includes(typeof obj) || Array.isArray(obj)){ |
| | | // return obj as T |
| | | // } |
| | | if(typeof obj == 'object'){ |
| | | return JSON.parse(JSON.stringify(obj as T)) as T; |
| | | } |
| | | return obj as T |
| | | } |
| | | |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 深度克隆一个对象或数组 |
| | | * @param obj 要克隆的对象或数组 |
| | | * @returns 克隆后的对象或数组 |
| | | */ |
| | | export function cloneDeep<T>(obj: any): T { |
| | | // 如果传入的对象为空,返回空 |
| | | if (obj === null) { |
| | | return null as unknown as T; |
| | | } |
| | | |
| | | // 如果传入的对象是 Set 类型,则将其转换为数组,并通过新的 Set 构造函数创建一个新的 Set 对象 |
| | | if (obj instanceof Set) { |
| | | return new Set([...obj]) as unknown as T; |
| | | } |
| | | |
| | | // 如果传入的对象是 Map 类型,则将其转换为数组,并通过新的 Map 构造函数创建一个新的 Map 对象 |
| | | if (obj instanceof Map) { |
| | | return new Map([...obj]) as unknown as T; |
| | | } |
| | | |
| | | // 如果传入的对象是 WeakMap 类型,则直接用传入的 WeakMap 对象进行赋值 |
| | | if (obj instanceof WeakMap) { |
| | | let weakMap = new WeakMap(); |
| | | weakMap = obj; |
| | | return weakMap as unknown as T; |
| | | } |
| | | |
| | | // 如果传入的对象是 WeakSet 类型,则直接用传入的 WeakSet 对象进行赋值 |
| | | if (obj instanceof WeakSet) { |
| | | let weakSet = new WeakSet(); |
| | | weakSet = obj; |
| | | return weakSet as unknown as T; |
| | | } |
| | | |
| | | // 如果传入的对象是 RegExp 类型,则通过新的 RegExp 构造函数创建一个新的 RegExp 对象 |
| | | if (obj instanceof RegExp) { |
| | | return new RegExp(obj) as unknown as T; |
| | | } |
| | | |
| | | // 如果传入的对象是 undefined 类型,则返回 undefined |
| | | if (typeof obj === 'undefined') { |
| | | return undefined as unknown as T; |
| | | } |
| | | |
| | | // 如果传入的对象是数组,则递归调用 cloneDeep 函数对数组中的每个元素进行克隆 |
| | | if (Array.isArray(obj)) { |
| | | return obj.map(cloneDeep) as unknown as T; |
| | | } |
| | | |
| | | // 如果传入的对象是 Date 类型,则通过新的 Date 构造函数创建一个新的 Date 对象 |
| | | if (obj instanceof Date) { |
| | | return new Date(obj.getTime()) as unknown as T; |
| | | } |
| | | |
| | | // 如果传入的对象是普通对象,则使用递归调用 cloneDeep 函数对对象的每个属性进行克隆 |
| | | if (typeof obj === 'object') { |
| | | const newObj: any = {}; |
| | | for (const [key, value] of Object.entries(obj)) { |
| | | newObj[key] = cloneDeep(value); |
| | | } |
| | | const symbolKeys = Object.getOwnPropertySymbols(obj); |
| | | for (const key of symbolKeys) { |
| | | newObj[key] = cloneDeep(obj[key]); |
| | | } |
| | | return newObj; |
| | | } |
| | | |
| | | // 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回 |
| | | return obj; |
| | | } |
| | | |
| | | // 示例使用 |
| | | |
| | | // // 克隆一个对象 |
| | | // const obj = { name: 'John', age: 30 }; |
| | | // const clonedObj = cloneDeep(obj); |
| | | |
| | | // console.log(clonedObj); // 输出: { name: 'John', age: 30 } |
| | | // console.log(clonedObj === obj); // 输出: false (副本与原对象是独立的) |
| | | |
| | | // // 克隆一个数组 |
| | | // const arr = [1, 2, 3]; |
| | | // const clonedArr = cloneDeep(arr); |
| | | |
| | | // console.log(clonedArr); // 输出: [1, 2, 3] |
| | | // console.log(clonedArr === arr); // 输出: false (副本与原数组是独立的) |
| | | |
| | | // // 克隆一个包含嵌套对象的对象 |
| | | // const person = { |
| | | // name: 'Alice', |
| | | // age: 25, |
| | | // address: { |
| | | // city: 'New York', |
| | | // country: 'USA', |
| | | // }, |
| | | // }; |
| | | // const clonedPerson = cloneDeep(person); |
| | | |
| | | // console.log(clonedPerson); // 输出: { name: 'Alice', age: 25, address: { city: 'New York', country: 'USA' } } |
| | | // console.log(clonedPerson === person); // 输出: false (副本与原对象是独立的) |
| | | // console.log(clonedPerson.address === person.address); // 输出: false (嵌套对象的副本也是独立的) |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | /** |
| | | * 在给定数组中找到最接近目标数字的元素。 |
| | | * @param arr 要搜索的数字数组。 |
| | | * @param target 目标数字。 |
| | | * @returns 最接近目标数字的数组元素。 |
| | | */ |
| | | export function closest(arr: number[], target: number):number { |
| | | return arr.reduce((pre: number, cur: number):number => |
| | | Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur |
| | | ); |
| | | } |
| | | |
| | | // 示例 |
| | | // // 定义一个数字数组 |
| | | // const numbers = [1, 3, 5, 7, 9]; |
| | | |
| | | // // 在数组中找到最接近目标数字 6 的元素 |
| | | // const closestNumber = closest(numbers, 6); |
| | | |
| | | // console.log(closestNumber); // 输出结果: 5 |
对比新文件 |
| | |
| | | <template> |
| | | <view id="shared" style="height: 500px; width: 300px; background-color: aqua;"> |
| | | |
| | | </view> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { getRect, getAllRect } from '@/uni_modules/lime-shared/getRect' |
| | | |
| | | import { camelCase } from '@/uni_modules/lime-shared/camelCase' |
| | | import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d' |
| | | import { clamp } from '@/uni_modules/lime-shared/clamp' |
| | | import { cloneDeep } from '@/uni_modules/lime-shared/cloneDeep' |
| | | import { closest } from '@/uni_modules/lime-shared/closest' |
| | | import { debounce } from '@/uni_modules/lime-shared/debounce' |
| | | import { fillZero } from '@/uni_modules/lime-shared/fillZero' |
| | | import { floatAdd } from '@/uni_modules/lime-shared/floatAdd' |
| | | import { floatMul } from '@/uni_modules/lime-shared/floatMul' |
| | | import { floatDiv } from '@/uni_modules/lime-shared/floatDiv' |
| | | import { floatSub } from '@/uni_modules/lime-shared/floatSub' |
| | | import { getClassStr } from '@/uni_modules/lime-shared/getClassStr' |
| | | import { getCurrentPage } from '@/uni_modules/lime-shared/getCurrentPage' |
| | | import { getStyleStr } from '@/uni_modules/lime-shared/getStyleStr' |
| | | import { hasOwn } from '@/uni_modules/lime-shared/hasOwn' |
| | | import { isBase64 } from '@/uni_modules/lime-shared/isBase64' |
| | | import { isBrowser } from '@/uni_modules/lime-shared/isBrowser' |
| | | import { isDef } from '@/uni_modules/lime-shared/isDef' |
| | | import { isEmpty } from '@/uni_modules/lime-shared/isEmpty' |
| | | import { isFunction } from '@/uni_modules/lime-shared/isFunction' |
| | | import { isNumber } from '@/uni_modules/lime-shared/isNumber' |
| | | import { isNumeric } from '@/uni_modules/lime-shared/isNumeric' |
| | | import { isObject } from '@/uni_modules/lime-shared/isObject' |
| | | import { isPromise } from '@/uni_modules/lime-shared/isPromise' |
| | | import { isString } from '@/uni_modules/lime-shared/isString' |
| | | import { kebabCase } from '@/uni_modules/lime-shared/kebabCase' |
| | | import { raf, doubleRaf } from '@/uni_modules/lime-shared/raf' |
| | | import { random } from '@/uni_modules/lime-shared/random' |
| | | import { range } from '@/uni_modules/lime-shared/range' |
| | | import { sleep } from '@/uni_modules/lime-shared/sleep' |
| | | import { throttle } from '@/uni_modules/lime-shared/throttle' |
| | | import { toArray } from '@/uni_modules/lime-shared/toArray' |
| | | import { toBoolean } from '@/uni_modules/lime-shared/toBoolean' |
| | | import { toNumber } from '@/uni_modules/lime-shared/toNumber' |
| | | import { unitConvert } from '@/uni_modules/lime-shared/unitConvert' |
| | | import { getCurrentInstance } from '@/uni_modules/lime-shared/vue' |
| | | import { capitalizedAmount } from '@/uni_modules/lime-shared/capitalizedAmount' |
| | | |
| | | // #ifdef VUE2 |
| | | type UTSJSONObject = any |
| | | // #endif |
| | | |
| | | const context = getCurrentInstance() |
| | | // getRect('#shared', context!).then(res =>{ |
| | | // console.log('res', res.bottom) |
| | | // }) |
| | | // getAllRect('#shared', context).then(res =>{ |
| | | // console.log('res', res) |
| | | // }) |
| | | |
| | | |
| | | // console.log('camelCase::', camelCase("hello world")); |
| | | // console.log('camelCase::', camelCase("my_name_is_john", true)); |
| | | // console.log('canIUseCanvas2d::', canIUseCanvas2d()); |
| | | // console.log('clamp::', clamp(5 ,0, 10)); |
| | | // console.log('cloneDeep::', cloneDeep<UTSJSONObject>({a:5})); |
| | | // console.log('closest::', closest([1, 3, 5, 7, 9], 6)); |
| | | |
| | | |
| | | |
| | | |
| | | // const saveData = (data: any) => { |
| | | // // 模拟保存数据的操作 |
| | | // console.log(`Saving data: ${data}`); |
| | | // } |
| | | |
| | | // const debouncedSaveData = debounce(saveData, 500); |
| | | // debouncedSaveData('Data 1'); |
| | | // debouncedSaveData('Data 2'); |
| | | |
| | | // console.log('fillZero', fillZero(1)) |
| | | // console.log('floatAdd', floatAdd(0.1, 0.2), floatAdd(1.05, 0.05), floatAdd(0.1, 0.7), floatAdd(0.0001, 0.0002), floatAdd(123.456 , 789.012)) |
| | | // console.log('floatMul', floatMul(0.1, 0.02), floatMul(1.0255, 100)) |
| | | // console.log('floatDiv', floatDiv(10.44, 100), floatDiv(1.0255, 100), floatDiv(5.419909340994699, 0.2)) |
| | | // console.log('floatSub', floatSub(0.4, 0.1), floatSub(1.0255, 100)) |
| | | const now = () : number => System.nanoTime() / 1_000_000.0 |
| | | console.log('capitalizedAmount', capitalizedAmount(0.4)) |
| | | console.log('capitalizedAmount', capitalizedAmount(100)) |
| | | console.log('capitalizedAmount', capitalizedAmount(100000000)) |
| | | console.log('capitalizedAmount', capitalizedAmount('2023.04')) |
| | | console.log('capitalizedAmount', capitalizedAmount(-1024)) |
| | | console.log('now', now(), Date.now()) |
| | | // console.log('getClassStr', getClassStr({hover: true})) |
| | | // console.log('getStyleStr', getStyleStr({ color: 'red', fontSize: '16px', backgroundColor: '', border: null })) |
| | | // console.log('hasOwn', hasOwn({a: true}, 'key')) |
| | | // console.log('isBase64::', isBase64("SGVsbG8sIFdvcmxkIQ==")); |
| | | // console.log('isBrowser::', isBrowser); |
| | | // console.log('isDef::', isDef('6')); |
| | | // console.log('isEmpty::', isEmpty({a: true})); |
| | | |
| | | // const b = () =>{} |
| | | // console.log('isFunction::', isFunction(b)); |
| | | // console.log('isNumber::', isNumber('6')); |
| | | // console.log('isNumeric::', isNumeric('6')); |
| | | // console.log('isObject::', isObject({})); |
| | | |
| | | // const promise = ():Promise<boolean> => { |
| | | // return new Promise((resolve) => { |
| | | // resolve(true) |
| | | // }) |
| | | // } |
| | | // const a = promise() |
| | | // console.log('isPromise::', isPromise(a)); |
| | | // console.log('isString::', isString('null')); |
| | | // console.log('kebabCase::', kebabCase('my love')); |
| | | // console.log('raf::', raf(()=>{ |
| | | // console.log('raf:::1') |
| | | // })); |
| | | // console.log('doubleRaf::', doubleRaf(()=>{ |
| | | // console.log('doubleRaf:::1') |
| | | // })); |
| | | // console.log('random', random(0, 10)) |
| | | // console.log('random', random(0, 1, 2)) |
| | | // console.log('range', range(0, 10, 2)) |
| | | // console.log('sleep', sleep(300).then(res => { |
| | | // console.log('log') |
| | | // })) |
| | | |
| | | // const handleScroll = (a: string) => { |
| | | // console.log("Scroll event handled!", a); |
| | | // } |
| | | |
| | | // // // 使用节流函数对 handleScroll 进行节流,间隔时间为 500 毫秒 |
| | | // const throttledScroll = throttle(handleScroll, 500); |
| | | // throttledScroll('5'); |
| | | // const page = getCurrentPage() |
| | | // console.log('getCurrentPage::', page) |
| | | |
| | | // console.log('toArray', toArray<number>(5)) |
| | | // console.log('toBoolean', toBoolean(5)) |
| | | // console.log('toNumber', toNumber('5')) |
| | | // console.log('unitConvert', unitConvert('5')) |
| | | |
| | | // uni.getImageInfo({ |
| | | // src: '/static/logo.png', |
| | | // success(res) { |
| | | // console.log('res', res) |
| | | // } |
| | | // }) |
| | | export default { |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | |
| | | </style> |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X |
| | | export * from './type.ts' |
| | | export * from './vue.ts' |
| | | // #endif |
| | | |
| | | // #ifdef UNI-APP-X |
| | | export * from './uvue.ts' |
| | | // #endif |
对比新文件 |
| | |
| | | export type CreateAnimationOptions = { |
| | | /** |
| | | * 动画持续时间,单位ms |
| | | */ |
| | | duration ?: number; |
| | | /** |
| | | * 定义动画的效果 |
| | | * - linear: 动画从头到尾的速度是相同的 |
| | | * - ease: 动画以低速开始,然后加快,在结束前变慢 |
| | | * - ease-in: 动画以低速开始 |
| | | * - ease-in-out: 动画以低速开始和结束 |
| | | * - ease-out: 动画以低速结束 |
| | | * - step-start: 动画第一帧就跳至结束状态直到结束 |
| | | * - step-end: 动画一直保持开始状态,最后一帧跳到结束状态 |
| | | */ |
| | | timingFunction ?: string //'linear' | 'ease' | 'ease-in' | 'ease-in-out' | 'ease-out' | 'step-start' | 'step-end'; |
| | | /** |
| | | * 动画延迟时间,单位 ms |
| | | */ |
| | | delay ?: number; |
| | | /** |
| | | * 设置transform-origin |
| | | */ |
| | | transformOrigin ?: string; |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // export * from '@/uni_modules/lime-animateIt' |
| | | export function createAnimation() { |
| | | console.error('当前环境不支持,请使用:lime-animateIt') |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // nvue 需要在节点上设置ref或在export里传入 |
| | | // const animation = createAnimation({ |
| | | // ref: this.$refs['xxx'], |
| | | // duration: 0, |
| | | // timingFunction: 'linear' |
| | | // }) |
| | | // animation.opacity(1).translate(x, y).step({duration}) |
| | | // animation.export(ref) |
| | | |
| | | // 抹平nvue 与 uni.createAnimation的使用差距 |
| | | // 但是nvue动画太慢 |
| | | |
| | | |
| | | |
| | | import { CreateAnimationOptions } from './type' |
| | | // #ifdef APP-NVUE |
| | | const nvueAnimation = uni.requireNativePlugin('animation') |
| | | |
| | | type AnimationTypes = 'matrix' | 'matrix3d' | 'rotate' | 'rotate3d' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scale3d' | 'scaleX' | 'scaleY' | 'scaleZ' | 'skew' | 'skewX' | 'skewY' | 'translate' | 'translate3d' | 'translateX' | 'translateY' | 'translateZ' |
| | | | 'opacity' | 'backgroundColor' | 'width' | 'height' | 'left' | 'right' | 'top' | 'bottom' |
| | | |
| | | interface Styles { |
| | | [key : string] : any |
| | | } |
| | | |
| | | interface StepConfig { |
| | | duration?: number |
| | | timingFunction?: string |
| | | delay?: number |
| | | needLayout?: boolean |
| | | transformOrigin?: string |
| | | } |
| | | interface StepAnimate { |
| | | styles?: Styles |
| | | config?: StepConfig |
| | | } |
| | | interface StepAnimates { |
| | | [key: number]: StepAnimate |
| | | } |
| | | // export interface CreateAnimationOptions extends UniApp.CreateAnimationOptions { |
| | | // ref?: string |
| | | // } |
| | | |
| | | type Callback = (time: number) => void |
| | | const animateTypes1 : AnimationTypes[] = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d', |
| | | 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY', |
| | | 'translateZ' |
| | | ] |
| | | const animateTypes2 : AnimationTypes[] = ['opacity', 'backgroundColor'] |
| | | const animateTypes3 : AnimationTypes[] = ['width', 'height', 'left', 'right', 'top', 'bottom'] |
| | | |
| | | class LimeAnimation { |
| | | ref : any |
| | | context : any |
| | | options : UniApp.CreateAnimationOptions |
| | | // stack : any[] = [] |
| | | next : number = 0 |
| | | currentStepAnimates : StepAnimates = {} |
| | | duration : number = 0 |
| | | constructor(options : CreateAnimationOptions) { |
| | | const {ref} = options |
| | | this.ref = ref |
| | | this.options = options |
| | | } |
| | | addAnimate(type : AnimationTypes, args: (string | number)[]) { |
| | | let aniObj = this.currentStepAnimates[this.next] |
| | | let stepAnimate:StepAnimate = {} |
| | | if (!aniObj) { |
| | | stepAnimate = {styles: {}, config: {}} |
| | | } else { |
| | | stepAnimate = aniObj |
| | | } |
| | | |
| | | if (animateTypes1.includes(type)) { |
| | | if (!stepAnimate.styles.transform) { |
| | | stepAnimate.styles.transform = '' |
| | | } |
| | | let unit = '' |
| | | if (type === 'rotate') { |
| | | unit = 'deg' |
| | | } |
| | | stepAnimate.styles.transform += `${type}(${args.map((v: number) => v + unit).join(',')}) ` |
| | | } else { |
| | | stepAnimate.styles[type] = `${args.join(',')}` |
| | | } |
| | | this.currentStepAnimates[this.next] = stepAnimate |
| | | } |
| | | animateRun(styles: Styles = {}, config:StepConfig = {}, ref: any) { |
| | | const el = ref || this.ref |
| | | if (!el) return |
| | | return new Promise((resolve) => { |
| | | const time = +new Date() |
| | | nvueAnimation.transition(el, { |
| | | styles, |
| | | ...config |
| | | }, () => { |
| | | resolve(+new Date() - time) |
| | | }) |
| | | }) |
| | | } |
| | | nextAnimate(animates: StepAnimates, step: number = 0, ref: any, cb: Callback) { |
| | | let obj = animates[step] |
| | | if (obj) { |
| | | let { styles, config } = obj |
| | | // this.duration += config.duration |
| | | this.animateRun(styles, config, ref).then((time: number) => { |
| | | step += 1 |
| | | this.duration += time |
| | | this.nextAnimate(animates, step, ref, cb) |
| | | }) |
| | | } else { |
| | | this.currentStepAnimates = {} |
| | | cb && cb(this.duration) |
| | | } |
| | | } |
| | | step(config:StepConfig = {}) { |
| | | this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config) |
| | | this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin |
| | | this.next++ |
| | | return this |
| | | } |
| | | export(ref: any, cb?: Callback) { |
| | | ref = ref || this.ref |
| | | if(!ref) return |
| | | this.duration = 0 |
| | | this.next = 0 |
| | | this.nextAnimate(this.currentStepAnimates, 0, ref, cb) |
| | | return null |
| | | } |
| | | } |
| | | |
| | | |
| | | animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => { |
| | | LimeAnimation.prototype[type] = function(...args: (string | number)[]) { |
| | | this.addAnimate(type, args) |
| | | return this |
| | | } |
| | | }) |
| | | // #endif |
| | | export function createAnimation(options : CreateAnimationOptions) { |
| | | // #ifndef APP-NVUE |
| | | return uni.createAnimation({ ...options }) |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | return new LimeAnimation(options) |
| | | // #endif |
| | | } |
对比新文件 |
| | |
| | | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X && APP |
| | | import type { ComponentInternalInstance } from '@/uni_modules/lime-shared/vue' |
| | | import { getRect } from '@/uni_modules/lime-shared/getRect' |
| | | import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d' |
| | | export const isCanvas2d = canIUseCanvas2d() |
| | | // #endif |
| | | |
| | | |
| | | export function createCanvas(canvasId : string, component : ComponentInternalInstance) { |
| | | // #ifdef UNI-APP-X |
| | | uni.createCanvasContextAsync({ |
| | | canvasId, |
| | | component, |
| | | success(context : CanvasContext) { |
| | | |
| | | }, |
| | | fail(error : UniError) { |
| | | |
| | | } |
| | | }) |
| | | // #endif |
| | | // #ifndef UNI-APP-X |
| | | const isCanvas2d = canIUseCanvas2d() |
| | | getRect('#' + canvasId, context, isCanvas2d).then(res => { |
| | | if (res.node) { |
| | | res.node.width = res.width |
| | | res.node.height = res.height |
| | | return res.node |
| | | } else { |
| | | const ctx = uni.createCanvasContext(canvasId, context) |
| | | if (!ctx._drawImage) { |
| | | ctx._drawImage = ctx.drawImage |
| | | ctx.drawImage = function (...args) { |
| | | const { path } = args.shift() |
| | | ctx._drawImage(path, ...args) |
| | | } |
| | | } |
| | | if (!ctx.getImageData) { |
| | | ctx.getImageData = function () { |
| | | return new Promise((resolve, reject) => { |
| | | uni.canvasGetImageData({ |
| | | canvasId, |
| | | x: parseInt(arguments[0]), |
| | | y: parseInt(arguments[1]), |
| | | width: parseInt(arguments[2]), |
| | | height: parseInt(arguments[3]), |
| | | success(res) { |
| | | resolve(res) |
| | | }, |
| | | fail(err) { |
| | | reject(err) |
| | | } |
| | | }, context) |
| | | }) |
| | | |
| | | } |
| | | return { |
| | | getContext(type: string) { |
| | | if(type == '2d') { |
| | | return ctx |
| | | } |
| | | }, |
| | | width: res.width, |
| | | height: res.height, |
| | | createImage |
| | | } |
| | | } |
| | | } |
| | | }) |
| | | // #endif |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X && APP |
| | | import {isBrowser} from '../isBrowser' |
| | | class Image { |
| | | currentSrc: string | null = null |
| | | naturalHeight: number = 0 |
| | | naturalWidth: number = 0 |
| | | width: number = 0 |
| | | height: number = 0 |
| | | tagName: string = 'IMG' |
| | | path: string = '' |
| | | crossOrigin: string = '' |
| | | referrerPolicy: string = '' |
| | | onload: () => void = () => {} |
| | | onerror: () => void = () => {} |
| | | complete: boolean = false |
| | | constructor() {} |
| | | set src(src: string) { |
| | | console.log('src', src) |
| | | if(!src) { |
| | | return this.onerror() |
| | | } |
| | | src = src.replace(/^@\//,'/') |
| | | this.currentSrc = src |
| | | uni.getImageInfo({ |
| | | src, |
| | | success: (res) => { |
| | | const localReg = /^\.|^\/(?=[^\/])/; |
| | | // #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO |
| | | res.path = localReg.test(src) ? `/${res.path}` : res.path; |
| | | // #endif |
| | | this.complete = true |
| | | this.path = res.path |
| | | this.naturalWidth = this.width = res.width |
| | | this.naturalHeight = this.height = res.height |
| | | this.onload() |
| | | }, |
| | | fail: () => { |
| | | this.onerror() |
| | | } |
| | | }) |
| | | } |
| | | get src() { |
| | | return this.currentSrc |
| | | } |
| | | } |
| | | interface UniImage extends WechatMiniprogram.Image { |
| | | complete?: boolean |
| | | naturalHeight?: number |
| | | naturalWidth?: number |
| | | } |
| | | /** 创建用于 canvas 的 img */ |
| | | export function createImage(canvas?: any): HTMLImageElement | UniImage { |
| | | if(canvas && canvas.createImage) { |
| | | return (canvas as WechatMiniprogram.Canvas).createImage() |
| | | } else if(this && this['tagName'] == 'canvas' && !('toBlob' in this) || canvas && !('toBlob' in canvas)){ |
| | | return new Image() |
| | | } else if(isBrowser) { |
| | | return new window.Image() |
| | | } |
| | | return new Image() |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export function createImage():Image{ |
| | | // console.error('当前环境不支持') |
| | | return new Image() |
| | | } |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.ts' |
| | | |
| | | // #endif |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 防抖函数,通过延迟一定时间来限制函数的执行频率。 |
| | | * @param fn 要防抖的函数。 |
| | | * @param wait 触发防抖的等待时间,单位为毫秒。 |
| | | * @returns 防抖函数。 |
| | | */ |
| | | export function debounce<A extends any>(fn : (args: A)=> void, wait = 300): (args: A)=> void { |
| | | let timer = -1 |
| | | |
| | | return (args: A) => { |
| | | if (timer >-1) {clearTimeout(timer)}; |
| | | |
| | | timer = setTimeout(()=>{ |
| | | fn(args) |
| | | }, wait) |
| | | } |
| | | }; |
| | | |
| | | |
| | | |
| | | // 示例 |
| | | // 定义一个函数 |
| | | // function saveData(data: string) { |
| | | // // 模拟保存数据的操作 |
| | | // console.log(`Saving data: ${data}`); |
| | | // } |
| | | |
| | | // // 创建一个防抖函数,延迟 500 毫秒后调用 saveData 函数 |
| | | // const debouncedSaveData = debounce(saveData, 500); |
| | | |
| | | // // 连续调用防抖函数 |
| | | // debouncedSaveData('Data 1'); // 不会立即调用 saveData 函数 |
| | | // debouncedSaveData('Data 2'); // 不会立即调用 saveData 函数 |
| | | |
| | | // 在 500 毫秒后,只会调用一次 saveData 函数,输出结果为 "Saving data: Data 2" |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | type Timeout = ReturnType<typeof setTimeout> | null; |
| | | /** |
| | | * 防抖函数,通过延迟一定时间来限制函数的执行频率。 |
| | | * @param fn 要防抖的函数。 |
| | | * @param wait 触发防抖的等待时间,单位为毫秒。 |
| | | * @returns 防抖函数。 |
| | | */ |
| | | export function debounce<A extends any, R>( |
| | | fn : (...args : A) => R, |
| | | wait : number = 300) : (...args : A) => void { |
| | | let timer : Timeout = null; |
| | | |
| | | return function (...args : A) { |
| | | if (timer) clearTimeout(timer); // 如果上一个 setTimeout 存在,则清除它 |
| | | |
| | | // 设置一个新的 setTimeout,在指定的等待时间后调用防抖函数 |
| | | timer = setTimeout(() => { |
| | | fn.apply(this, args); // 使用提供的参数调用原始函数 |
| | | }, wait); |
| | | }; |
| | | }; |
| | | |
| | | |
| | | |
| | | // 示例 |
| | | // 定义一个函数 |
| | | // function saveData(data: string) { |
| | | // // 模拟保存数据的操作 |
| | | // console.log(`Saving data: ${data}`); |
| | | // } |
| | | |
| | | // // 创建一个防抖函数,延迟 500 毫秒后调用 saveData 函数 |
| | | // const debouncedSaveData = debounce(saveData, 500); |
| | | |
| | | // // 连续调用防抖函数 |
| | | // debouncedSaveData('Data 1'); // 不会立即调用 saveData 函数 |
| | | // debouncedSaveData('Data 2'); // 不会立即调用 saveData 函数 |
| | | |
| | | // 在 500 毫秒后,只会调用一次 saveData 函数,输出结果为 "Saving data: Data 2" |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.ts' |
| | | // #endif |
对比新文件 |
| | |
| | | class EXIF { |
| | | constructor(){ |
| | | console.error('当前环境不支持') |
| | | } |
| | | } |
| | | |
| | | export const exif = new EXIF() |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import { base64ToArrayBuffer } from '../base64ToArrayBuffer'; |
| | | import { pathToBase64 } from '../pathToBase64'; |
| | | // import { isBase64 } from '../isBase64'; |
| | | import { isBase64DataUri } from '../isBase64'; |
| | | import { isString } from '../isString'; |
| | | |
| | | interface File { |
| | | exifdata : any |
| | | iptcdata : any |
| | | xmpdata : any |
| | | src : string |
| | | } |
| | | class EXIF { |
| | | isXmpEnabled = false |
| | | debug = false |
| | | Tags = { |
| | | // version tags |
| | | 0x9000: "ExifVersion", // EXIF version |
| | | 0xA000: "FlashpixVersion", // Flashpix format version |
| | | |
| | | // colorspace tags |
| | | 0xA001: "ColorSpace", // Color space information tag |
| | | |
| | | // image configuration |
| | | 0xA002: "PixelXDimension", // Valid width of meaningful image |
| | | 0xA003: "PixelYDimension", // Valid height of meaningful image |
| | | 0x9101: "ComponentsConfiguration", // Information about channels |
| | | 0x9102: "CompressedBitsPerPixel", // Compressed bits per pixel |
| | | |
| | | // user information |
| | | 0x927C: "MakerNote", // Any desired information written by the manufacturer |
| | | 0x9286: "UserComment", // Comments by user |
| | | |
| | | // related file |
| | | 0xA004: "RelatedSoundFile", // Name of related sound file |
| | | |
| | | // date and time |
| | | 0x9003: "DateTimeOriginal", // Date and time when the original image was generated |
| | | 0x9004: "DateTimeDigitized", // Date and time when the image was stored digitally |
| | | 0x9290: "SubsecTime", // Fractions of seconds for DateTime |
| | | 0x9291: "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal |
| | | 0x9292: "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized |
| | | |
| | | // picture-taking conditions |
| | | 0x829A: "ExposureTime", // Exposure time (in seconds) |
| | | 0x829D: "FNumber", // F number |
| | | 0x8822: "ExposureProgram", // Exposure program |
| | | 0x8824: "SpectralSensitivity", // Spectral sensitivity |
| | | 0x8827: "ISOSpeedRatings", // ISO speed rating |
| | | 0x8828: "OECF", // Optoelectric conversion factor |
| | | 0x9201: "ShutterSpeedValue", // Shutter speed |
| | | 0x9202: "ApertureValue", // Lens aperture |
| | | 0x9203: "BrightnessValue", // Value of brightness |
| | | 0x9204: "ExposureBias", // Exposure bias |
| | | 0x9205: "MaxApertureValue", // Smallest F number of lens |
| | | 0x9206: "SubjectDistance", // Distance to subject in meters |
| | | 0x9207: "MeteringMode", // Metering mode |
| | | 0x9208: "LightSource", // Kind of light source |
| | | 0x9209: "Flash", // Flash status |
| | | 0x9214: "SubjectArea", // Location and area of main subject |
| | | 0x920A: "FocalLength", // Focal length of the lens in mm |
| | | 0xA20B: "FlashEnergy", // Strobe energy in BCPS |
| | | 0xA20C: "SpatialFrequencyResponse", // |
| | | 0xA20E: "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit |
| | | 0xA20F: "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit |
| | | 0xA210: "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution |
| | | 0xA214: "SubjectLocation", // Location of subject in image |
| | | 0xA215: "ExposureIndex", // Exposure index selected on camera |
| | | 0xA217: "SensingMethod", // Image sensor type |
| | | 0xA300: "FileSource", // Image source (3 == DSC) |
| | | 0xA301: "SceneType", // Scene type (1 == directly photographed) |
| | | 0xA302: "CFAPattern", // Color filter array geometric pattern |
| | | 0xA401: "CustomRendered", // Special processing |
| | | 0xA402: "ExposureMode", // Exposure mode |
| | | 0xA403: "WhiteBalance", // 1 = auto white balance, 2 = manual |
| | | 0xA404: "DigitalZoomRation", // Digital zoom ratio |
| | | 0xA405: "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm) |
| | | 0xA406: "SceneCaptureType", // Type of scene |
| | | 0xA407: "GainControl", // Degree of overall image gain adjustment |
| | | 0xA408: "Contrast", // Direction of contrast processing applied by camera |
| | | 0xA409: "Saturation", // Direction of saturation processing applied by camera |
| | | 0xA40A: "Sharpness", // Direction of sharpness processing applied by camera |
| | | 0xA40B: "DeviceSettingDescription", // |
| | | 0xA40C: "SubjectDistanceRange", // Distance to subject |
| | | |
| | | // other tags |
| | | 0xA005: "InteroperabilityIFDPointer", |
| | | 0xA420: "ImageUniqueID" // Identifier assigned uniquely to each image |
| | | } |
| | | TiffTags = { |
| | | 0x0100: "ImageWidth", |
| | | 0x0101: "ImageHeight", |
| | | 0x8769: "ExifIFDPointer", |
| | | 0x8825: "GPSInfoIFDPointer", |
| | | 0xA005: "InteroperabilityIFDPointer", |
| | | 0x0102: "BitsPerSample", |
| | | 0x0103: "Compression", |
| | | 0x0106: "PhotometricInterpretation", |
| | | 0x0112: "Orientation", |
| | | 0x0115: "SamplesPerPixel", |
| | | 0x011C: "PlanarConfiguration", |
| | | 0x0212: "YCbCrSubSampling", |
| | | 0x0213: "YCbCrPositioning", |
| | | 0x011A: "XResolution", |
| | | 0x011B: "YResolution", |
| | | 0x0128: "ResolutionUnit", |
| | | 0x0111: "StripOffsets", |
| | | 0x0116: "RowsPerStrip", |
| | | 0x0117: "StripByteCounts", |
| | | 0x0201: "JPEGInterchangeFormat", |
| | | 0x0202: "JPEGInterchangeFormatLength", |
| | | 0x012D: "TransferFunction", |
| | | 0x013E: "WhitePoint", |
| | | 0x013F: "PrimaryChromaticities", |
| | | 0x0211: "YCbCrCoefficients", |
| | | 0x0214: "ReferenceBlackWhite", |
| | | 0x0132: "DateTime", |
| | | 0x010E: "ImageDescription", |
| | | 0x010F: "Make", |
| | | 0x0110: "Model", |
| | | 0x0131: "Software", |
| | | 0x013B: "Artist", |
| | | 0x8298: "Copyright" |
| | | } |
| | | GPSTags = { |
| | | 0x0000: "GPSVersionID", |
| | | 0x0001: "GPSLatitudeRef", |
| | | 0x0002: "GPSLatitude", |
| | | 0x0003: "GPSLongitudeRef", |
| | | 0x0004: "GPSLongitude", |
| | | 0x0005: "GPSAltitudeRef", |
| | | 0x0006: "GPSAltitude", |
| | | 0x0007: "GPSTimeStamp", |
| | | 0x0008: "GPSSatellites", |
| | | 0x0009: "GPSStatus", |
| | | 0x000A: "GPSMeasureMode", |
| | | 0x000B: "GPSDOP", |
| | | 0x000C: "GPSSpeedRef", |
| | | 0x000D: "GPSSpeed", |
| | | 0x000E: "GPSTrackRef", |
| | | 0x000F: "GPSTrack", |
| | | 0x0010: "GPSImgDirectionRef", |
| | | 0x0011: "GPSImgDirection", |
| | | 0x0012: "GPSMapDatum", |
| | | 0x0013: "GPSDestLatitudeRef", |
| | | 0x0014: "GPSDestLatitude", |
| | | 0x0015: "GPSDestLongitudeRef", |
| | | 0x0016: "GPSDestLongitude", |
| | | 0x0017: "GPSDestBearingRef", |
| | | 0x0018: "GPSDestBearing", |
| | | 0x0019: "GPSDestDistanceRef", |
| | | 0x001A: "GPSDestDistance", |
| | | 0x001B: "GPSProcessingMethod", |
| | | 0x001C: "GPSAreaInformation", |
| | | 0x001D: "GPSDateStamp", |
| | | 0x001E: "GPSDifferential" |
| | | } |
| | | // EXIF 2.3 Spec |
| | | IFD1Tags = { |
| | | 0x0100: "ImageWidth", |
| | | 0x0101: "ImageHeight", |
| | | 0x0102: "BitsPerSample", |
| | | 0x0103: "Compression", |
| | | 0x0106: "PhotometricInterpretation", |
| | | 0x0111: "StripOffsets", |
| | | 0x0112: "Orientation", |
| | | 0x0115: "SamplesPerPixel", |
| | | 0x0116: "RowsPerStrip", |
| | | 0x0117: "StripByteCounts", |
| | | 0x011A: "XResolution", |
| | | 0x011B: "YResolution", |
| | | 0x011C: "PlanarConfiguration", |
| | | 0x0128: "ResolutionUnit", |
| | | 0x0201: "JpegIFOffset", // When image format is JPEG, this value show offset to JPEG data stored.(aka "ThumbnailOffset" or "JPEGInterchangeFormat") |
| | | 0x0202: "JpegIFByteCount", // When image format is JPEG, this value shows data size of JPEG image (aka "ThumbnailLength" or "JPEGInterchangeFormatLength") |
| | | 0x0211: "YCbCrCoefficients", |
| | | 0x0212: "YCbCrSubSampling", |
| | | 0x0213: "YCbCrPositioning", |
| | | 0x0214: "ReferenceBlackWhite" |
| | | } |
| | | StringValues = { |
| | | ExposureProgram: { |
| | | 0: "Not defined", |
| | | 1: "Manual", |
| | | 2: "Normal program", |
| | | 3: "Aperture priority", |
| | | 4: "Shutter priority", |
| | | 5: "Creative program", |
| | | 6: "Action program", |
| | | 7: "Portrait mode", |
| | | 8: "Landscape mode" |
| | | }, |
| | | MeteringMode: { |
| | | 0: "Unknown", |
| | | 1: "Average", |
| | | 2: "CenterWeightedAverage", |
| | | 3: "Spot", |
| | | 4: "MultiSpot", |
| | | 5: "Pattern", |
| | | 6: "Partial", |
| | | 255: "Other" |
| | | }, |
| | | LightSource: { |
| | | 0: "Unknown", |
| | | 1: "Daylight", |
| | | 2: "Fluorescent", |
| | | 3: "Tungsten (incandescent light)", |
| | | 4: "Flash", |
| | | 9: "Fine weather", |
| | | 10: "Cloudy weather", |
| | | 11: "Shade", |
| | | 12: "Daylight fluorescent (D 5700 - 7100K)", |
| | | 13: "Day white fluorescent (N 4600 - 5400K)", |
| | | 14: "Cool white fluorescent (W 3900 - 4500K)", |
| | | 15: "White fluorescent (WW 3200 - 3700K)", |
| | | 17: "Standard light A", |
| | | 18: "Standard light B", |
| | | 19: "Standard light C", |
| | | 20: "D55", |
| | | 21: "D65", |
| | | 22: "D75", |
| | | 23: "D50", |
| | | 24: "ISO studio tungsten", |
| | | 255: "Other" |
| | | }, |
| | | Flash: { |
| | | 0x0000: "Flash did not fire", |
| | | 0x0001: "Flash fired", |
| | | 0x0005: "Strobe return light not detected", |
| | | 0x0007: "Strobe return light detected", |
| | | 0x0009: "Flash fired, compulsory flash mode", |
| | | 0x000D: "Flash fired, compulsory flash mode, return light not detected", |
| | | 0x000F: "Flash fired, compulsory flash mode, return light detected", |
| | | 0x0010: "Flash did not fire, compulsory flash mode", |
| | | 0x0018: "Flash did not fire, auto mode", |
| | | 0x0019: "Flash fired, auto mode", |
| | | 0x001D: "Flash fired, auto mode, return light not detected", |
| | | 0x001F: "Flash fired, auto mode, return light detected", |
| | | 0x0020: "No flash function", |
| | | 0x0041: "Flash fired, red-eye reduction mode", |
| | | 0x0045: "Flash fired, red-eye reduction mode, return light not detected", |
| | | 0x0047: "Flash fired, red-eye reduction mode, return light detected", |
| | | 0x0049: "Flash fired, compulsory flash mode, red-eye reduction mode", |
| | | 0x004D: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", |
| | | 0x004F: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", |
| | | 0x0059: "Flash fired, auto mode, red-eye reduction mode", |
| | | 0x005D: "Flash fired, auto mode, return light not detected, red-eye reduction mode", |
| | | 0x005F: "Flash fired, auto mode, return light detected, red-eye reduction mode" |
| | | }, |
| | | SensingMethod: { |
| | | 1: "Not defined", |
| | | 2: "One-chip color area sensor", |
| | | 3: "Two-chip color area sensor", |
| | | 4: "Three-chip color area sensor", |
| | | 5: "Color sequential area sensor", |
| | | 7: "Trilinear sensor", |
| | | 8: "Color sequential linear sensor" |
| | | }, |
| | | SceneCaptureType: { |
| | | 0: "Standard", |
| | | 1: "Landscape", |
| | | 2: "Portrait", |
| | | 3: "Night scene" |
| | | }, |
| | | SceneType: { |
| | | 1: "Directly photographed" |
| | | }, |
| | | CustomRendered: { |
| | | 0: "Normal process", |
| | | 1: "Custom process" |
| | | }, |
| | | WhiteBalance: { |
| | | 0: "Auto white balance", |
| | | 1: "Manual white balance" |
| | | }, |
| | | GainControl: { |
| | | 0: "None", |
| | | 1: "Low gain up", |
| | | 2: "High gain up", |
| | | 3: "Low gain down", |
| | | 4: "High gain down" |
| | | }, |
| | | Contrast: { |
| | | 0: "Normal", |
| | | 1: "Soft", |
| | | 2: "Hard" |
| | | }, |
| | | Saturation: { |
| | | 0: "Normal", |
| | | 1: "Low saturation", |
| | | 2: "High saturation" |
| | | }, |
| | | Sharpness: { |
| | | 0: "Normal", |
| | | 1: "Soft", |
| | | 2: "Hard" |
| | | }, |
| | | SubjectDistanceRange: { |
| | | 0: "Unknown", |
| | | 1: "Macro", |
| | | 2: "Close view", |
| | | 3: "Distant view" |
| | | }, |
| | | FileSource: { |
| | | 3: "DSC" |
| | | }, |
| | | |
| | | Components: { |
| | | 0: "", |
| | | 1: "Y", |
| | | 2: "Cb", |
| | | 3: "Cr", |
| | | 4: "R", |
| | | 5: "G", |
| | | 6: "B" |
| | | } |
| | | } |
| | | enableXmp() { |
| | | this.isXmpEnabled = true |
| | | } |
| | | disableXmp() { |
| | | this.isXmpEnabled = false; |
| | | } |
| | | /** |
| | | * 获取图片数据 |
| | | * @param img 图片地址 |
| | | * @param callback 回调 返回图片数据 |
| | | * */ |
| | | getData(img : any, callback : Function) { |
| | | // if (((self.Image && img instanceof self.Image) || (self.HTMLImageElement && img instanceof self.HTMLImageElement)) && !img.complete) |
| | | // return false; |
| | | let file : File = { |
| | | src: '', |
| | | exifdata: null, |
| | | iptcdata: null, |
| | | xmpdata: null, |
| | | } |
| | | if (isBase64(img)) { |
| | | file.src = img |
| | | } else if (img.path) { |
| | | file.src = img.path |
| | | } else if (isString(img)) { |
| | | file.src = img |
| | | } else { |
| | | return false; |
| | | } |
| | | |
| | | |
| | | if (!imageHasData(file)) { |
| | | getImageData(file, callback); |
| | | } else { |
| | | if (callback) { |
| | | callback.call(file); |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | /** |
| | | * 获取图片tag |
| | | * @param img 图片数据 |
| | | * @param tag tag 类型 |
| | | * */ |
| | | getTag(img : File, tag : string) { |
| | | if (!imageHasData(img)) return; |
| | | return img.exifdata[tag]; |
| | | } |
| | | getIptcTag(img : File, tag : string) { |
| | | if (!imageHasData(img)) return; |
| | | return img.iptcdata[tag]; |
| | | } |
| | | getAllTags(img : File) { |
| | | if (!imageHasData(img)) return {}; |
| | | let a, |
| | | data = img.exifdata, |
| | | tags = {}; |
| | | for (a in data) { |
| | | if (data.hasOwnProperty(a)) { |
| | | tags[a] = data[a]; |
| | | } |
| | | } |
| | | return tags; |
| | | } |
| | | getAllIptcTags(img : File) { |
| | | if (!imageHasData(img)) return {}; |
| | | let a, |
| | | data = img.iptcdata, |
| | | tags = {}; |
| | | for (a in data) { |
| | | if (data.hasOwnProperty(a)) { |
| | | tags[a] = data[a]; |
| | | } |
| | | } |
| | | return tags; |
| | | } |
| | | pretty(img : File) { |
| | | if (!imageHasData(img)) return ""; |
| | | let a, |
| | | data = img.exifdata, |
| | | strPretty = ""; |
| | | for (a in data) { |
| | | if (data.hasOwnProperty(a)) { |
| | | if (typeof data[a] == "object") { |
| | | if (data[a] instanceof Number) { |
| | | strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a] |
| | | .denominator + "]\r\n"; |
| | | } else { |
| | | strPretty += a + " : [" + data[a].length + " values]\r\n"; |
| | | } |
| | | } else { |
| | | strPretty += a + " : " + data[a] + "\r\n"; |
| | | } |
| | | } |
| | | } |
| | | return strPretty; |
| | | } |
| | | readFromBinaryFile(file: ArrayBuffer) { |
| | | return findEXIFinJPEG(file); |
| | | } |
| | | } |
| | | |
| | | export const exif = new EXIF() |
| | | // export function getData(img, callback) { |
| | | // const exif = new EXIF() |
| | | // exif.getData(img, callback) |
| | | // } |
| | | |
| | | // export default {getData} |
| | | const ExifTags = exif.Tags |
| | | const TiffTags = exif.TiffTags |
| | | const IFD1Tags = exif.IFD1Tags |
| | | const GPSTags = exif.GPSTags |
| | | const StringValues = exif.StringValues |
| | | |
| | | |
| | | function imageHasData(img : File) : boolean { |
| | | return !!(img.exifdata); |
| | | } |
| | | |
| | | function objectURLToBlob(url : string, callback : Function) { |
| | | try { |
| | | const http = new XMLHttpRequest(); |
| | | http.open("GET", url, true); |
| | | http.responseType = "blob"; |
| | | http.onload = function (e) { |
| | | if (this.status == 200 || this.status === 0) { |
| | | callback(this.response); |
| | | } |
| | | }; |
| | | http.send(); |
| | | } catch (e) { |
| | | console.warn(e) |
| | | } |
| | | } |
| | | |
| | | |
| | | function getImageData(img : File, callback : Function) { |
| | | function handleBinaryFile(binFile: ArrayBuffer) { |
| | | const data = findEXIFinJPEG(binFile); |
| | | img.exifdata = data ?? {}; |
| | | const iptcdata = findIPTCinJPEG(binFile); |
| | | img.iptcdata = iptcdata ?? {}; |
| | | if (exif.isXmpEnabled) { |
| | | const xmpdata = findXMPinJPEG(binFile); |
| | | img.xmpdata = xmpdata ?? {}; |
| | | } |
| | | if (callback) { |
| | | callback.call(img); |
| | | } |
| | | } |
| | | |
| | | if (img.src) { |
| | | if (/^data\:/i.test(img.src)) { // Data URI |
| | | // var arrayBuffer = base64ToArrayBuffer(img.src); |
| | | handleBinaryFile(base64ToArrayBuffer(img.src)); |
| | | |
| | | } else if (/^blob\:/i.test(img.src) && typeof FileReader !== 'undefined') { // Object URL |
| | | var fileReader = new FileReader(); |
| | | fileReader.onload = function (e) { |
| | | handleBinaryFile(e.target.result); |
| | | }; |
| | | objectURLToBlob(img.src, function (blob : Blob) { |
| | | fileReader.readAsArrayBuffer(blob); |
| | | }); |
| | | } else if (typeof XMLHttpRequest !== 'undefined') { |
| | | var http = new XMLHttpRequest(); |
| | | http.onload = function () { |
| | | if (this.status == 200 || this.status === 0) { |
| | | handleBinaryFile(http.response); |
| | | } else { |
| | | throw "Could not load image"; |
| | | } |
| | | http = null; |
| | | }; |
| | | http.open("GET", img.src, true); |
| | | http.responseType = "arraybuffer"; |
| | | http.send(null); |
| | | } else { |
| | | pathToBase64(img.src).then(res => { |
| | | handleBinaryFile(base64ToArrayBuffer(res)); |
| | | }) |
| | | } |
| | | } else if (typeof FileReader !== 'undefined' && self.FileReader && (img instanceof self.Blob || img instanceof self.File)) { |
| | | var fileReader = new FileReader(); |
| | | fileReader.onload = function (e : any) { |
| | | if (exif.debug) console.log("Got file of length " + e.target.result.byteLength); |
| | | handleBinaryFile(e.target.result); |
| | | }; |
| | | |
| | | fileReader.readAsArrayBuffer(img); |
| | | } |
| | | } |
| | | |
| | | function findEXIFinJPEG(file: ArrayBuffer) { |
| | | const dataView = new DataView(file); |
| | | |
| | | if (exif.debug) console.log("Got file of length " + file.byteLength); |
| | | if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { |
| | | if (exif.debug) console.log("Not a valid JPEG"); |
| | | return false; // not a valid jpeg |
| | | } |
| | | |
| | | let offset = 2, |
| | | length = file.byteLength, |
| | | marker; |
| | | |
| | | while (offset < length) { |
| | | if (dataView.getUint8(offset) != 0xFF) { |
| | | if (exif.debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8( |
| | | offset)); |
| | | return false; // not a valid marker, something is wrong |
| | | } |
| | | |
| | | marker = dataView.getUint8(offset + 1); |
| | | if (exif.debug) console.log(marker); |
| | | |
| | | // we could implement handling for other markers here, |
| | | // but we're only looking for 0xFFE1 for EXIF data |
| | | |
| | | if (marker == 225) { |
| | | if (exif.debug) console.log("Found 0xFFE1 marker"); |
| | | |
| | | return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2); |
| | | |
| | | // offset += 2 + file.getShortAt(offset+2, true); |
| | | |
| | | } else { |
| | | offset += 2 + dataView.getUint16(offset + 2); |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | | function findIPTCinJPEG(file: ArrayBuffer) { |
| | | const dataView = new DataView(file); |
| | | |
| | | if (exif.debug) console.log("Got file of length " + file.byteLength); |
| | | if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { |
| | | if (exif.debug) console.log("Not a valid JPEG"); |
| | | return false; // not a valid jpeg |
| | | } |
| | | |
| | | let offset = 2, |
| | | length = file.byteLength; |
| | | |
| | | |
| | | const isFieldSegmentStart = function (dataView, offset: number) { |
| | | return ( |
| | | dataView.getUint8(offset) === 0x38 && |
| | | dataView.getUint8(offset + 1) === 0x42 && |
| | | dataView.getUint8(offset + 2) === 0x49 && |
| | | dataView.getUint8(offset + 3) === 0x4D && |
| | | dataView.getUint8(offset + 4) === 0x04 && |
| | | dataView.getUint8(offset + 5) === 0x04 |
| | | ); |
| | | }; |
| | | |
| | | while (offset < length) { |
| | | |
| | | if (isFieldSegmentStart(dataView, offset)) { |
| | | |
| | | // Get the length of the name header (which is padded to an even number of bytes) |
| | | var nameHeaderLength = dataView.getUint8(offset + 7); |
| | | if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1; |
| | | // Check for pre photoshop 6 format |
| | | if (nameHeaderLength === 0) { |
| | | // Always 4 |
| | | nameHeaderLength = 4; |
| | | } |
| | | |
| | | var startOffset = offset + 8 + nameHeaderLength; |
| | | var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength); |
| | | |
| | | return readIPTCData(file, startOffset, sectionLength); |
| | | |
| | | break; |
| | | |
| | | } |
| | | |
| | | |
| | | // Not the marker, continue searching |
| | | offset++; |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | | const IptcFieldMap = { |
| | | 0x78: 'caption', |
| | | 0x6E: 'credit', |
| | | 0x19: 'keywords', |
| | | 0x37: 'dateCreated', |
| | | 0x50: 'byline', |
| | | 0x55: 'bylineTitle', |
| | | 0x7A: 'captionWriter', |
| | | 0x69: 'headline', |
| | | 0x74: 'copyright', |
| | | 0x0F: 'category' |
| | | }; |
| | | |
| | | function readIPTCData(file: ArrayBuffer, startOffset: number, sectionLength: number) { |
| | | const dataView = new DataView(file); |
| | | let data = {}; |
| | | let fieldValue, fieldName, dataSize, segmentType, segmentSize; |
| | | let segmentStartPos = startOffset; |
| | | while (segmentStartPos < startOffset + sectionLength) { |
| | | if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos + 1) === 0x02) { |
| | | segmentType = dataView.getUint8(segmentStartPos + 2); |
| | | if (segmentType in IptcFieldMap) { |
| | | dataSize = dataView.getInt16(segmentStartPos + 3); |
| | | segmentSize = dataSize + 5; |
| | | fieldName = IptcFieldMap[segmentType]; |
| | | fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize); |
| | | // Check if we already stored a value with this name |
| | | if (data.hasOwnProperty(fieldName)) { |
| | | // Value already stored with this name, create multivalue field |
| | | if (data[fieldName] instanceof Array) { |
| | | data[fieldName].push(fieldValue); |
| | | } else { |
| | | data[fieldName] = [data[fieldName], fieldValue]; |
| | | } |
| | | } else { |
| | | data[fieldName] = fieldValue; |
| | | } |
| | | } |
| | | |
| | | } |
| | | segmentStartPos++; |
| | | } |
| | | return data; |
| | | } |
| | | |
| | | function readTags(file: DataView, tiffStart: number, dirStart: number, strings: any[], bigEnd: number) { |
| | | let entries = file.getUint16(dirStart, !bigEnd), |
| | | tags = {}, |
| | | entryOffset, tag; |
| | | |
| | | for (let i = 0; i < entries; i++) { |
| | | entryOffset = dirStart + i * 12 + 2; |
| | | tag = strings[file.getUint16(entryOffset, !bigEnd)]; |
| | | if (!tag && exif.debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd)); |
| | | tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd); |
| | | } |
| | | return tags; |
| | | } |
| | | |
| | | function readTagValue(file: DataView, entryOffset: number, tiffStart: number, dirStart: number, bigEnd: number) { |
| | | let type = file.getUint16(entryOffset + 2, !bigEnd), |
| | | numValues = file.getUint32(entryOffset + 4, !bigEnd), |
| | | valueOffset = file.getUint32(entryOffset + 8, !bigEnd) + tiffStart, |
| | | offset, |
| | | vals, val, n, |
| | | numerator, denominator; |
| | | |
| | | switch (type) { |
| | | case 1: // byte, 8-bit unsigned int |
| | | case 7: // undefined, 8-bit byte, value depending on field |
| | | if (numValues == 1) { |
| | | return file.getUint8(entryOffset + 8, !bigEnd); |
| | | } else { |
| | | offset = numValues > 4 ? valueOffset : (entryOffset + 8); |
| | | vals = []; |
| | | for (n = 0; n < numValues; n++) { |
| | | vals[n] = file.getUint8(offset + n); |
| | | } |
| | | return vals; |
| | | } |
| | | |
| | | case 2: // ascii, 8-bit byte |
| | | offset = numValues > 4 ? valueOffset : (entryOffset + 8); |
| | | return getStringFromDB(file, offset, numValues - 1); |
| | | |
| | | case 3: // short, 16 bit int |
| | | if (numValues == 1) { |
| | | return file.getUint16(entryOffset + 8, !bigEnd); |
| | | } else { |
| | | offset = numValues > 2 ? valueOffset : (entryOffset + 8); |
| | | vals = []; |
| | | for (n = 0; n < numValues; n++) { |
| | | vals[n] = file.getUint16(offset + 2 * n, !bigEnd); |
| | | } |
| | | return vals; |
| | | } |
| | | |
| | | case 4: // long, 32 bit int |
| | | if (numValues == 1) { |
| | | return file.getUint32(entryOffset + 8, !bigEnd); |
| | | } else { |
| | | vals = []; |
| | | for (n = 0; n < numValues; n++) { |
| | | vals[n] = file.getUint32(valueOffset + 4 * n, !bigEnd); |
| | | } |
| | | return vals; |
| | | } |
| | | |
| | | case 5: // rational = two long values, first is numerator, second is denominator |
| | | if (numValues == 1) { |
| | | numerator = file.getUint32(valueOffset, !bigEnd); |
| | | denominator = file.getUint32(valueOffset + 4, !bigEnd); |
| | | val = new Number(numerator / denominator); |
| | | val.numerator = numerator; |
| | | val.denominator = denominator; |
| | | return val; |
| | | } else { |
| | | vals = []; |
| | | for (n = 0; n < numValues; n++) { |
| | | numerator = file.getUint32(valueOffset + 8 * n, !bigEnd); |
| | | denominator = file.getUint32(valueOffset + 4 + 8 * n, !bigEnd); |
| | | vals[n] = new Number(numerator / denominator); |
| | | vals[n].numerator = numerator; |
| | | vals[n].denominator = denominator; |
| | | } |
| | | return vals; |
| | | } |
| | | |
| | | case 9: // slong, 32 bit signed int |
| | | if (numValues == 1) { |
| | | return file.getInt32(entryOffset + 8, !bigEnd); |
| | | } else { |
| | | vals = []; |
| | | for (n = 0; n < numValues; n++) { |
| | | vals[n] = file.getInt32(valueOffset + 4 * n, !bigEnd); |
| | | } |
| | | return vals; |
| | | } |
| | | |
| | | case 10: // signed rational, two slongs, first is numerator, second is denominator |
| | | if (numValues == 1) { |
| | | return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset + 4, !bigEnd); |
| | | } else { |
| | | vals = []; |
| | | for (n = 0; n < numValues; n++) { |
| | | vals[n] = file.getInt32(valueOffset + 8 * n, !bigEnd) / file.getInt32(valueOffset + 4 + 8 * |
| | | n, !bigEnd); |
| | | } |
| | | return vals; |
| | | } |
| | | } |
| | | } |
| | | /** |
| | | * Given an IFD (Image File Directory) start offset |
| | | * returns an offset to next IFD or 0 if it's the last IFD. |
| | | */ |
| | | function getNextIFDOffset(dataView: DataView, dirStart: number, bigEnd: number) { |
| | | //the first 2bytes means the number of directory entries contains in this IFD |
| | | var entries = dataView.getUint16(dirStart, !bigEnd); |
| | | |
| | | // After last directory entry, there is a 4bytes of data, |
| | | // it means an offset to next IFD. |
| | | // If its value is '0x00000000', it means this is the last IFD and there is no linked IFD. |
| | | |
| | | return dataView.getUint32(dirStart + 2 + entries * 12, !bigEnd); // each entry is 12 bytes long |
| | | } |
| | | |
| | | function readThumbnailImage(dataView: DataView, tiffStart: number, firstIFDOffset: number, bigEnd: number) { |
| | | // get the IFD1 offset |
| | | const IFD1OffsetPointer = getNextIFDOffset(dataView, tiffStart + firstIFDOffset, bigEnd); |
| | | |
| | | if (!IFD1OffsetPointer) { |
| | | // console.log('******** IFD1Offset is empty, image thumb not found ********'); |
| | | return {}; |
| | | } else if (IFD1OffsetPointer > dataView.byteLength) { // this should not happen |
| | | // console.log('******** IFD1Offset is outside the bounds of the DataView ********'); |
| | | return {}; |
| | | } |
| | | // console.log('******* thumbnail IFD offset (IFD1) is: %s', IFD1OffsetPointer); |
| | | |
| | | let thumbTags : any = readTags(dataView, tiffStart, tiffStart + IFD1OffsetPointer, IFD1Tags, bigEnd) |
| | | |
| | | // EXIF 2.3 specification for JPEG format thumbnail |
| | | |
| | | // If the value of Compression(0x0103) Tag in IFD1 is '6', thumbnail image format is JPEG. |
| | | // Most of Exif image uses JPEG format for thumbnail. In that case, you can get offset of thumbnail |
| | | // by JpegIFOffset(0x0201) Tag in IFD1, size of thumbnail by JpegIFByteCount(0x0202) Tag. |
| | | // Data format is ordinary JPEG format, starts from 0xFFD8 and ends by 0xFFD9. It seems that |
| | | // JPEG format and 160x120pixels of size are recommended thumbnail format for Exif2.1 or later. |
| | | |
| | | if (thumbTags['Compression'] && typeof Blob !== 'undefined') { |
| | | // console.log('Thumbnail image found!'); |
| | | |
| | | switch (thumbTags['Compression']) { |
| | | case 6: |
| | | // console.log('Thumbnail image format is JPEG'); |
| | | if (thumbTags.JpegIFOffset && thumbTags.JpegIFByteCount) { |
| | | // extract the thumbnail |
| | | var tOffset = tiffStart + thumbTags.JpegIFOffset; |
| | | var tLength = thumbTags.JpegIFByteCount; |
| | | thumbTags['blob'] = new Blob([new Uint8Array(dataView.buffer, tOffset, tLength)], { |
| | | type: 'image/jpeg' |
| | | }); |
| | | } |
| | | break; |
| | | |
| | | case 1: |
| | | console.log("Thumbnail image format is TIFF, which is not implemented."); |
| | | break; |
| | | default: |
| | | console.log("Unknown thumbnail image format '%s'", thumbTags['Compression']); |
| | | } |
| | | } else if (thumbTags['PhotometricInterpretation'] == 2) { |
| | | console.log("Thumbnail image format is RGB, which is not implemented."); |
| | | } |
| | | return thumbTags; |
| | | } |
| | | |
| | | function getStringFromDB(buffer: DataView, start: number, length: number) { |
| | | let outstr = ""; |
| | | for (let n = start; n < start + length; n++) { |
| | | outstr += String.fromCharCode(buffer.getUint8(n)); |
| | | } |
| | | return outstr; |
| | | } |
| | | |
| | | function readEXIFData(file: DataView, start: number) { |
| | | if (getStringFromDB(file, start, 4) != "Exif") { |
| | | if (exif.debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4)); |
| | | return false; |
| | | } |
| | | |
| | | let bigEnd, |
| | | tags, tag, |
| | | exifData, gpsData, |
| | | tiffOffset = start + 6; |
| | | |
| | | // test for TIFF validity and endianness |
| | | if (file.getUint16(tiffOffset) == 0x4949) { |
| | | bigEnd = false; |
| | | } else if (file.getUint16(tiffOffset) == 0x4D4D) { |
| | | bigEnd = true; |
| | | } else { |
| | | if (exif.debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)"); |
| | | return false; |
| | | } |
| | | |
| | | if (file.getUint16(tiffOffset + 2, !bigEnd) != 0x002A) { |
| | | if (exif.debug) console.log("Not valid TIFF data! (no 0x002A)"); |
| | | return false; |
| | | } |
| | | |
| | | const firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd); |
| | | |
| | | if (firstIFDOffset < 0x00000008) { |
| | | if (exif.debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset + 4, |
| | | !bigEnd)); |
| | | return false; |
| | | } |
| | | |
| | | tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd); |
| | | |
| | | if (tags.ExifIFDPointer) { |
| | | exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd); |
| | | for (tag in exifData) { |
| | | switch (tag) { |
| | | case "LightSource": |
| | | case "Flash": |
| | | case "MeteringMode": |
| | | case "ExposureProgram": |
| | | case "SensingMethod": |
| | | case "SceneCaptureType": |
| | | case "SceneType": |
| | | case "CustomRendered": |
| | | case "WhiteBalance": |
| | | case "GainControl": |
| | | case "Contrast": |
| | | case "Saturation": |
| | | case "Sharpness": |
| | | case "SubjectDistanceRange": |
| | | case "FileSource": |
| | | exifData[tag] = StringValues[tag][exifData[tag]]; |
| | | break; |
| | | |
| | | case "ExifVersion": |
| | | case "FlashpixVersion": |
| | | exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], |
| | | exifData[tag][3]); |
| | | break; |
| | | |
| | | case "ComponentsConfiguration": |
| | | exifData[tag] = |
| | | StringValues.Components[exifData[tag][0]] + |
| | | StringValues.Components[exifData[tag][1]] + |
| | | StringValues.Components[exifData[tag][2]] + |
| | | StringValues.Components[exifData[tag][3]]; |
| | | break; |
| | | } |
| | | tags[tag] = exifData[tag]; |
| | | } |
| | | } |
| | | |
| | | if (tags.GPSInfoIFDPointer) { |
| | | gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd); |
| | | for (tag in gpsData) { |
| | | switch (tag) { |
| | | case "GPSVersionID": |
| | | gpsData[tag] = gpsData[tag][0] + |
| | | "." + gpsData[tag][1] + |
| | | "." + gpsData[tag][2] + |
| | | "." + gpsData[tag][3]; |
| | | break; |
| | | } |
| | | tags[tag] = gpsData[tag]; |
| | | } |
| | | } |
| | | |
| | | // extract thumbnail |
| | | tags['thumbnail'] = readThumbnailImage(file, tiffOffset, firstIFDOffset, bigEnd); |
| | | |
| | | return tags; |
| | | } |
| | | |
| | | function findXMPinJPEG(file: ArrayBuffer) { |
| | | |
| | | if (!('DOMParser' in self)) { |
| | | // console.warn('XML parsing not supported without DOMParser'); |
| | | return; |
| | | } |
| | | const dataView = new DataView(file); |
| | | |
| | | if (exif.debug) console.log("Got file of length " + file.byteLength); |
| | | if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { |
| | | if (exif.debug) console.log("Not a valid JPEG"); |
| | | return false; // not a valid jpeg |
| | | } |
| | | |
| | | let offset = 2, |
| | | length = file.byteLength, |
| | | dom = new DOMParser(); |
| | | |
| | | while (offset < (length - 4)) { |
| | | if (getStringFromDB(dataView, offset, 4) == "http") { |
| | | const startOffset = offset - 1; |
| | | const sectionLength = dataView.getUint16(offset - 2) - 1; |
| | | let xmpString = getStringFromDB(dataView, startOffset, sectionLength) |
| | | const xmpEndIndex = xmpString.indexOf('xmpmeta>') + 8; |
| | | xmpString = xmpString.substring(xmpString.indexOf('<x:xmpmeta'), xmpEndIndex); |
| | | |
| | | const indexOfXmp = xmpString.indexOf('x:xmpmeta') + 10 |
| | | //Many custom written programs embed xmp/xml without any namespace. Following are some of them. |
| | | //Without these namespaces, XML is thought to be invalid by parsers |
| | | xmpString = xmpString.slice(0, indexOfXmp) + |
| | | 'xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" ' + |
| | | 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + |
| | | 'xmlns:tiff="http://ns.adobe.com/tiff/1.0/" ' + |
| | | 'xmlns:plus="http://schemas.android.com/apk/lib/com.google.android.gms.plus" ' + |
| | | 'xmlns:ext="http://www.gettyimages.com/xsltExtension/1.0" ' + |
| | | 'xmlns:exif="http://ns.adobe.com/exif/1.0/" ' + |
| | | 'xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" ' + |
| | | 'xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" ' + |
| | | 'xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/" ' + |
| | | 'xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/" ' + |
| | | 'xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" ' + |
| | | xmpString.slice(indexOfXmp) |
| | | |
| | | var domDocument = dom.parseFromString(xmpString, 'text/xml'); |
| | | return xml2Object(domDocument); |
| | | } else { |
| | | offset++; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | function xml2json(xml: any) { |
| | | var json = {}; |
| | | |
| | | if (xml.nodeType == 1) { // element node |
| | | if (xml.attributes.length > 0) { |
| | | json['@attributes'] = {}; |
| | | for (var j = 0; j < xml.attributes.length; j++) { |
| | | var attribute = xml.attributes.item(j); |
| | | json['@attributes'][attribute.nodeName] = attribute.nodeValue; |
| | | } |
| | | } |
| | | } else if (xml.nodeType == 3) { // text node |
| | | return xml.nodeValue; |
| | | } |
| | | |
| | | // deal with children |
| | | if (xml.hasChildNodes()) { |
| | | for (var i = 0; i < xml.childNodes.length; i++) { |
| | | var child = xml.childNodes.item(i); |
| | | var nodeName = child.nodeName; |
| | | if (json[nodeName] == null) { |
| | | json[nodeName] = xml2json(child); |
| | | } else { |
| | | if (json[nodeName].push == null) { |
| | | var old = json[nodeName]; |
| | | json[nodeName] = []; |
| | | json[nodeName].push(old); |
| | | } |
| | | json[nodeName].push(xml2json(child)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return json; |
| | | } |
| | | |
| | | function xml2Object(xml: any) { |
| | | try { |
| | | var obj = {}; |
| | | if (xml.children.length > 0) { |
| | | for (var i = 0; i < xml.children.length; i++) { |
| | | var item = xml.children.item(i); |
| | | var attributes = item.attributes; |
| | | for (var idx in attributes) { |
| | | var itemAtt = attributes[idx]; |
| | | var dataKey = itemAtt.nodeName; |
| | | var dataValue = itemAtt.nodeValue; |
| | | |
| | | if (dataKey !== undefined) { |
| | | obj[dataKey] = dataValue; |
| | | } |
| | | } |
| | | var nodeName = item.nodeName; |
| | | |
| | | if (typeof (obj[nodeName]) == "undefined") { |
| | | obj[nodeName] = xml2json(item); |
| | | } else { |
| | | if (typeof (obj[nodeName].push) == "undefined") { |
| | | var old = obj[nodeName]; |
| | | |
| | | obj[nodeName] = []; |
| | | obj[nodeName].push(old); |
| | | } |
| | | obj[nodeName].push(xml2json(item)); |
| | | } |
| | | } |
| | | } else { |
| | | obj = xml.textContent; |
| | | } |
| | | return obj; |
| | | } catch (e) { |
| | | console.log(e.message); |
| | | } |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 在数字前填充零,返回字符串形式的结果 |
| | | * @param number 要填充零的数字 |
| | | * @param length 填充零后的字符串长度,默认为2 |
| | | * @returns 填充零后的字符串 |
| | | */ |
| | | export function fillZero(number: number, length: number = 2): string { |
| | | // 将数字转换为字符串,然后使用 padStart 方法填充零到指定长度 |
| | | return `${number}`.padStart(length, '0'); |
| | | } |
对比新文件 |
| | |
| | | import { isNumber } from '../isNumber' |
| | | /** |
| | | * 返回两个浮点数相加的结果 |
| | | * @param num1 第一个浮点数 |
| | | * @param num2 第二个浮点数 |
| | | * @returns 两个浮点数的相加结果 |
| | | */ |
| | | export function floatAdd(num1 : number, num2 : number) : number { |
| | | // 检查 num1 和 num2 是否为数字类型 |
| | | if (!(isNumber(num1) || isNumber(num2))) { |
| | | console.warn('Please pass in the number type'); |
| | | return NaN; |
| | | } |
| | | |
| | | let r1 : number, r2 : number, m : number; |
| | | |
| | | try { |
| | | // 获取 num1 小数点后的位数 |
| | | r1 = num1.toString().split('.')[1].length; |
| | | } catch (error) { |
| | | r1 = 0; |
| | | } |
| | | |
| | | try { |
| | | // 获取 num2 小数点后的位数 |
| | | r2 = num2.toString().split('.')[1].length; |
| | | } catch (error) { |
| | | r2 = 0; |
| | | } |
| | | |
| | | // 计算需要扩大的倍数 |
| | | m = Math.pow(10, Math.max(r1, r2)); |
| | | |
| | | // 返回相加结果 |
| | | return (num1 * m + num2 * m) / m; |
| | | } |
对比新文件 |
| | |
| | | import { floatMul } from '../floatMul'; |
| | | import { isNumber } from '../isNumber'; |
| | | |
| | | /** |
| | | * 除法函数,用于处理浮点数除法并保持精度。 |
| | | * @param {number} num1 - 被除数。 |
| | | * @param {number} num2 - 除数。 |
| | | * @returns {number} 除法运算的结果,保留正确的精度。 |
| | | */ |
| | | export function floatDiv(num1:number, num2:number):number { |
| | | // 如果传入的不是数字类型,则打印警告并返回NaN |
| | | if (!isNumber(num1) || !isNumber(num2)) { |
| | | console.warn('请传入数字类型'); |
| | | return NaN; |
| | | } |
| | | |
| | | let m1 = 0, // 被除数小数点后的位数 |
| | | m2 = 0, // 除数小数点后的位数 |
| | | s1 = num1.toString(), // 将被除数转换为字符串 |
| | | s2 = num2.toString(); // 将除数转换为字符串 |
| | | |
| | | // 计算被除数小数点后的位数 |
| | | try { |
| | | m1 += s1.split('.')[1].length; |
| | | } catch (error) {} |
| | | |
| | | // 计算除数小数点后的位数 |
| | | try { |
| | | m2 += s2.split('.')[1].length; |
| | | } catch (error) {} |
| | | |
| | | // 进行除法运算并处理小数点后的位数,使用之前定义的乘法函数保持精度 |
| | | // #ifdef APP-ANDROID |
| | | return floatMul( |
| | | parseFloat(s1.replace('.', '')) / parseFloat(s2.replace('.', '')), |
| | | Math.pow(10, m2 - m1), |
| | | ); |
| | | // #endif |
| | | // #ifndef APP-ANDROID |
| | | return floatMul( |
| | | Number(s1.replace('.', '')) / Number(s2.replace('.', '')), |
| | | Math.pow(10, m2 - m1), |
| | | ); |
| | | // #endif |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import {isNumber} from '../isNumber'; |
| | | // #ifdef APP-ANDROID |
| | | import BigDecimal from 'java.math.BigDecimal' |
| | | // import BigDecimal from 'java.math.BigDecimal' |
| | | // import StringBuilder from 'java.lang.StringBuilder' |
| | | // import java.math.BigDecimal; |
| | | // #endif |
| | | |
| | | /** |
| | | * 乘法函数,用于处理浮点数乘法并保持精度。 |
| | | * @param {number} num1 - 第一个乘数。 |
| | | * @param {number} num2 - 第二个乘数。 |
| | | * @returns {number} 乘法运算的结果,保留正确的精度。 |
| | | */ |
| | | export function floatMul(num1 : number, num2 : number) : number { |
| | | if (!(isNumber(num1) || isNumber(num2))) { |
| | | console.warn('Please pass in the number type'); |
| | | return NaN; |
| | | } |
| | | let m = 0; |
| | | // #ifdef APP-ANDROID |
| | | let s1 = BigDecimal.valueOf(num1.toDouble()).toPlainString(); //new UTSNumber(num1).toString() // //`${num1.toFloat()}`// num1.toString(), |
| | | let s2 = BigDecimal.valueOf(num2.toDouble()).toPlainString(); //new UTSNumber(num2).toString() //`${num2.toFloat()}`//.toString(); |
| | | // #endif |
| | | // #ifndef APP-ANDROID |
| | | let s1:string = `${num1}`// num1.toString(), |
| | | let s2:string = `${num2}`//.toString(); |
| | | // #endif |
| | | |
| | | try { |
| | | m += s1.split('.')[1].length; |
| | | } catch (error) { } |
| | | try { |
| | | m += s2.split('.')[1].length; |
| | | } catch (error) { } |
| | | |
| | | // #ifdef APP-ANDROID |
| | | return parseFloat(s1.replace('.', '')) * parseFloat(s2.replace('.', '')) / Math.pow(10, m); |
| | | // #endif |
| | | // #ifndef APP-ANDROID |
| | | return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m); |
| | | // #endif |
| | | } |
对比新文件 |
| | |
| | | import { isNumber } from '../isNumber'; |
| | | /** |
| | | * 减法函数,用于处理浮点数减法并保持精度。 |
| | | * @param {number} num1 - 被减数。 |
| | | * @param {number} num2 - 减数。 |
| | | * @returns {number} 减法运算的结果,保留正确的精度。 |
| | | */ |
| | | export function floatSub(num1 : number, num2 : number) : number { |
| | | if (!(isNumber(num1) || isNumber(num2))) { |
| | | console.warn('Please pass in the number type'); |
| | | return NaN; |
| | | } |
| | | let r1:number, r2:number, m:number, n:number; |
| | | try { |
| | | r1 = num1.toString().split('.')[1].length; |
| | | } catch (error) { |
| | | r1 = 0; |
| | | } |
| | | try { |
| | | r2 = num2.toString().split('.')[1].length; |
| | | } catch (error) { |
| | | r2 = 0; |
| | | } |
| | | m = Math.pow(10, Math.max(r1, r2)); |
| | | n = r1 >= r2 ? r1 : r2; |
| | | // #ifndef APP-ANDROID |
| | | return Number(((num1 * m - num2 * m) / m).toFixed(n)); |
| | | // #endif |
| | | // #ifdef APP-ANDROID |
| | | return parseFloat(((num1 * m - num2 * m) / m).toFixed(n)); |
| | | // #endif |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | import { isNumber } from '../isNumber' |
| | | import { isString } from '../isString' |
| | | import { isDef } from '../isDef' |
| | | // #endif |
| | | |
| | | /** |
| | | * 获取对象的类名字符串 |
| | | * @param obj - 需要处理的对象 |
| | | * @returns 由对象属性作为类名组成的字符串 |
| | | */ |
| | | export function getClassStr<T>(obj : T) : string { |
| | | let classNames : string[] = []; |
| | | // #ifdef UNI-APP-X && APP |
| | | if (obj instanceof UTSJSONObject) { |
| | | (obj as UTSJSONObject).toMap().forEach((value, key) => { |
| | | if (isDef(value)) { |
| | | if (isNumber(value)) { |
| | | classNames.push(key); |
| | | } |
| | | if (isString(value) && value !== '') { |
| | | classNames.push(key); |
| | | } |
| | | if (typeof value == 'boolean' && (value as boolean)) { |
| | | classNames.push(key); |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | // #endif |
| | | // #ifndef UNI-APP-X && APP |
| | | // 遍历对象的属性 |
| | | for (let key in obj) { |
| | | // 检查属性确实属于对象自身且其值为true |
| | | if ((obj as any).hasOwnProperty(key) && obj[key]) { |
| | | // 将属性名添加到类名数组中 |
| | | classNames.push(key); |
| | | } |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // 将类名数组用空格连接成字符串并返回 |
| | | return classNames.join(' '); |
| | | } |
| | | |
| | | |
| | | // 示例 |
| | | // const obj = { foo: true, bar: false, baz: true }; |
| | | // const classNameStr = getClassStr(obj); |
| | | // console.log(classNameStr); // 输出: "foo baz" |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.uts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | export const getCurrentPage = ():Page => { |
| | | const pages = getCurrentPages(); |
| | | return pages[pages.length - 1] |
| | | }; |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** 获取当前页 */ |
| | | export const getCurrentPage = () => { |
| | | const pages = getCurrentPages(); |
| | | return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance; |
| | | }; |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifdef APP-NVUE || APP-VUE |
| | | export const getLocalFilePath = (path : string) => { |
| | | if (typeof plus == 'undefined') return path |
| | | if (/^(_www|_doc|_documents|_downloads|file:\/\/|\/storage\/emulated\/0\/)/.test(path)) return path |
| | | if (/^\//.test(path)) { |
| | | const localFilePath = plus.io.convertAbsoluteFileSystem(path) |
| | | if (localFilePath !== path) { |
| | | return localFilePath |
| | | } else { |
| | | path = path.slice(1) |
| | | } |
| | | } |
| | | return '_www/' + path |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export { getResourcePath as getLocalFilePath } from '@/uni_modules/lime-file-utils' |
| | | // export const getLocalFilePath = (path : string) : string => { |
| | | // let uri = path |
| | | // if (uri.startsWith("http") || uri.startsWith("<svg") || uri.startsWith("data:image/svg+xml")) { |
| | | // return uri |
| | | // } |
| | | // if (uri.startsWith("file://")) { |
| | | // uri = uri.substring("file://".length) |
| | | // } else if (uri.startsWith("unifile://")) { |
| | | // uri = UTSAndroid.convert2AbsFullPath(uri) |
| | | // } else { |
| | | // uri = UTSAndroid.convert2AbsFullPath(uri) |
| | | // if (uri.startsWith("/android_asset/")) { |
| | | // uri = uri.replace("/android_asset/", "") |
| | | // } |
| | | // } |
| | | // if (new File(uri).exists()) { |
| | | // return uri |
| | | // } else { |
| | | // return null |
| | | // } |
| | | // // return UTSAndroid.convert2AbsFullPath(path) |
| | | // } |
| | | // #endif |
| | | // #ifdef APP-IOS |
| | | // export const getLocalFilePath = (path : string) : string => { |
| | | // try { |
| | | // let uri = path |
| | | // if (uri.startsWith("http") || uri.startsWith("<svg") || uri.startsWith("data:image/svg+xml")) { |
| | | // return uri |
| | | // } |
| | | // if (uri.startsWith("file://")) { |
| | | // return uri.substring("file://".length) |
| | | // } else if (path.startsWith("/var/")) { |
| | | // return path |
| | | // } |
| | | // return UTSiOS.getResourcePath(path) |
| | | // } catch (e) { |
| | | // return null |
| | | // } |
| | | // // return UTSiOS.getResourcePath(path) |
| | | // } |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.uts' |
| | | // #endif |
| | | |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | export function getRect(selector : string, context: ComponentInternalInstance):Promise<NodeInfo> { |
| | | return new Promise((resolve)=>{ |
| | | uni.createSelectorQuery().in(context).select(selector).boundingClientRect(res =>{ |
| | | resolve(res as NodeInfo) |
| | | }).exec(); |
| | | }) |
| | | } |
| | | |
| | | export function getAllRect(selector : string, context: ComponentInternalInstance):Promise<NodeInfo[]> { |
| | | return new Promise((resolve)=>{ |
| | | uni.createSelectorQuery().in(context).selectAll(selector).boundingClientRect(res =>{ |
| | | resolve(res as NodeInfo[]) |
| | | }).exec(); |
| | | }) |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | // #ifdef APP-NVUE |
| | | // 当编译环境是 APP-NVUE 时,引入 uni.requireNativePlugin('dom'),具体插件用途未知 |
| | | const dom = uni.requireNativePlugin('dom') |
| | | // #endif |
| | | |
| | | /** |
| | | * 获取节点信息 |
| | | * @param selector 选择器字符串 |
| | | * @param context ComponentInternalInstance 对象 |
| | | * @param node 是否获取node |
| | | * @returns 包含节点信息的 Promise 对象 |
| | | */ |
| | | export function getRect(selector : string, context : ComponentInternalInstance, node: boolean = false) { |
| | | // 之前是个对象,现在改成实例,防止旧版会报错 |
| | | if(context== null) { |
| | | return Promise.reject('context is null') |
| | | } |
| | | if(context.context){ |
| | | context = context.context |
| | | } |
| | | // #ifdef MP || VUE2 |
| | | if (context.proxy) context = context.proxy |
| | | // #endif |
| | | return new Promise<UniNamespace.NodeInfo>((resolve, reject) => { |
| | | // #ifndef APP-NVUE |
| | | const dom = uni.createSelectorQuery().in(context).select(selector); |
| | | const result = (rect: UniNamespace.NodeInfo) => { |
| | | if (rect) { |
| | | resolve(rect) |
| | | } else { |
| | | reject('no rect') |
| | | } |
| | | } |
| | | |
| | | if (!node) { |
| | | dom.boundingClientRect(result).exec() |
| | | } else { |
| | | dom.fields({ |
| | | node: true, |
| | | size: true, |
| | | rect: true |
| | | }, result).exec() |
| | | } |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | let { context } = options |
| | | if (/#|\./.test(selector) && context.refs) { |
| | | selector = selector.replace(/#|\./, '') |
| | | if (context.refs[selector]) { |
| | | selector = context.refs[selector] |
| | | if(Array.isArray(selector)) { |
| | | selector = selector[0] |
| | | } |
| | | } |
| | | } |
| | | dom.getComponentRect(selector, (res) => { |
| | | if (res.size) { |
| | | resolve(res.size) |
| | | } else { |
| | | reject('no rect') |
| | | } |
| | | }) |
| | | // #endif |
| | | }); |
| | | }; |
| | | |
| | | |
| | | export function getAllRect(selector : string, context: ComponentInternalInstance, node:boolean = false) { |
| | | if(context== null) { |
| | | return Promise.reject('context is null') |
| | | } |
| | | // #ifdef MP || VUE2 |
| | | if (context.proxy) context = context.proxy |
| | | // #endif |
| | | return new Promise<UniNamespace.NodeInfo>((resolve, reject) => { |
| | | // #ifndef APP-NVUE |
| | | const dom = uni.createSelectorQuery().in(context).selectAll(selector); |
| | | const result = (rect: UniNamespace.NodeInfo[]) => { |
| | | if (rect) { |
| | | resolve(rect) |
| | | } else { |
| | | reject('no rect') |
| | | } |
| | | } |
| | | if (!node) { |
| | | dom.boundingClientRect(result).exec() |
| | | } else { |
| | | dom.fields({ |
| | | node: true, |
| | | size: true, |
| | | rect: true |
| | | }, result).exec() |
| | | } |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | let { context } = options |
| | | if (/#|\./.test(selector) && context.refs) { |
| | | selector = selector.replace(/#|\./, '') |
| | | if (context.refs[selector]) { |
| | | selector = context.refs[selector] |
| | | if(Array.isArray(selector)) { |
| | | selector = selector[0] |
| | | } |
| | | } |
| | | } |
| | | dom.getComponentRect(selector, (res) => { |
| | | if (res.size) { |
| | | resolve([res.size]) |
| | | } else { |
| | | reject('no rect') |
| | | } |
| | | }) |
| | | // #endif |
| | | }); |
| | | }; |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X && APP |
| | | interface CSSProperties { |
| | | [key : string] : string | number | null |
| | | } |
| | | // #endif |
| | | |
| | | // #ifdef VUE3 |
| | | // #ifdef UNI-APP-X && APP |
| | | type CSSProperties = UTSJSONObject |
| | | // #endif |
| | | // #endif |
| | | /** |
| | | * 将字符串转换为带有连字符分隔的小写形式 |
| | | * @param key - 要转换的字符串 |
| | | * @returns 转换后的字符串 |
| | | */ |
| | | export function toLowercaseSeparator(key : string):string { |
| | | return key.replace(/([A-Z])/g, '-$1').toLowerCase(); |
| | | } |
| | | |
| | | /** |
| | | * 获取样式对象对应的样式字符串 |
| | | * @param style - CSS样式对象 |
| | | * @returns 由非空有效样式属性键值对组成的字符串 |
| | | */ |
| | | export function getStyleStr(style : CSSProperties) : string { |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | let styleStr = ''; |
| | | style.toMap().forEach((value, key) => { |
| | | if(value !== null && value != '') { |
| | | styleStr += `${toLowercaseSeparator(key as string)}: ${value};` |
| | | } |
| | | }) |
| | | return styleStr |
| | | // #endif |
| | | // #ifndef UNI-APP-X && APP |
| | | return Object.keys(style) |
| | | .filter( |
| | | (key) => |
| | | style[key] !== undefined && |
| | | style[key] !== null && |
| | | style[key] !== '') |
| | | .map((key : string) => `${toLowercaseSeparator(key)}: ${style[key]};`) |
| | | .join(' '); |
| | | // #endif |
| | | } |
| | | |
| | | // 示例 |
| | | // const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null }; |
| | | // const styleStr = getStyleStr(style); |
| | | // console.log(styleStr); |
| | | // 输出: "color: red; font-size: 16px;" |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X |
| | | interface CSSProperties { |
| | | [key : string] : string | number |
| | | } |
| | | // #endif |
| | | |
| | | // #ifdef UNI-APP-X |
| | | type CSSProperties = UTSJSONObject |
| | | // #endif |
| | | /** |
| | | * 将字符串转换为带有连字符分隔的小写形式 |
| | | * @param key - 要转换的字符串 |
| | | * @returns 转换后的字符串 |
| | | */ |
| | | export function toLowercaseSeparator(key : string) : string { |
| | | return key.replace(/([A-Z])/g, '-$1').toLowerCase(); |
| | | } |
| | | |
| | | /** |
| | | * 获取样式对象对应的样式字符串 |
| | | * @param style - CSS样式对象 |
| | | * @returns 由非空有效样式属性键值对组成的字符串 |
| | | */ |
| | | export function getStyleStr(style : CSSProperties) : string { |
| | | let styleStr = ''; |
| | | style.toMap().forEach((value, key) => { |
| | | if(value !== null && value != '') { |
| | | styleStr += `${toLowercaseSeparator(key as string)}: ${value};` |
| | | } |
| | | }) |
| | | return styleStr |
| | | } |
| | | |
| | | // 示例 |
| | | // const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null }; |
| | | // const styleStr = getStyleStr(style); |
| | | // console.log(styleStr); |
| | | // 输出: "color: red; font-size: 16px;" |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.ts' |
| | | // #endif |
| | | |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 检查对象或数组是否具有指定的属性或键 |
| | | * @param obj 要检查的对象或数组 |
| | | * @param key 指定的属性或键 |
| | | * @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false |
| | | */ |
| | | function hasOwn(obj: UTSJSONObject, key: string): boolean |
| | | function hasOwn(obj: Map<string, unknown>, key: string): boolean |
| | | function hasOwn(obj: any, key: string): boolean { |
| | | if(obj instanceof UTSJSONObject){ |
| | | return obj[key] != null |
| | | } |
| | | if(obj instanceof Map<string, unknown>){ |
| | | return (obj as Map<string, unknown>).has(key) |
| | | } |
| | | return false |
| | | } |
| | | export { |
| | | hasOwn |
| | | } |
| | | // 示例 |
| | | // const obj = { name: 'John', age: 30 }; |
| | | |
| | | // if (hasOwn(obj, 'name')) { |
| | | // console.log("对象具有 'name' 属性"); |
| | | // } else { |
| | | // console.log("对象不具有 'name' 属性"); |
| | | // } |
| | | // // 输出: 对象具有 'name' 属性 |
| | | |
| | | // const arr = [1, 2, 3]; |
| | | |
| | | // if (hasOwn(arr, 'length')) { |
| | | // console.log("数组具有 'length' 属性"); |
| | | // } else { |
| | | // console.log("数组不具有 'length' 属性"); |
| | | // } |
| | | // 输出: 数组具有 'length' 属性 |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | const hasOwnProperty = Object.prototype.hasOwnProperty |
| | | /** |
| | | * 检查对象或数组是否具有指定的属性或键 |
| | | * @param obj 要检查的对象或数组 |
| | | * @param key 指定的属性或键 |
| | | * @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false |
| | | */ |
| | | export function hasOwn(obj: Object | Array<any>, key: string): boolean { |
| | | return hasOwnProperty.call(obj, key); |
| | | } |
| | | |
| | | // 示例 |
| | | // const obj = { name: 'John', age: 30 }; |
| | | |
| | | // if (hasOwn(obj, 'name')) { |
| | | // console.log("对象具有 'name' 属性"); |
| | | // } else { |
| | | // console.log("对象不具有 'name' 属性"); |
| | | // } |
| | | // // 输出: 对象具有 'name' 属性 |
| | | |
| | | // const arr = [1, 2, 3]; |
| | | |
| | | // if (hasOwn(arr, 'length')) { |
| | | // console.log("数组具有 'length' 属性"); |
| | | // } else { |
| | | // console.log("数组不具有 'length' 属性"); |
| | | // } |
| | | // 输出: 数组具有 'length' 属性 |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // validator |
| | | // export * from './isString' |
| | | // export * from './isNumber' |
| | | // export * from './isNumeric' |
| | | // export * from './isDef' |
| | | // export * from './isFunction' |
| | | // export * from './isObject' |
| | | // export * from './isPromise' |
| | | // export * from './isBase64' |
| | | |
| | | // export * from './hasOwn' |
| | | |
| | | // // 单位转换 |
| | | // export * from './addUnit' |
| | | // export * from './unitConvert' |
| | | // export * from './toNumber' |
| | | |
| | | // export * from './random' |
| | | // export * from './range' |
| | | // export * from './fillZero' |
| | | |
| | | // // image |
| | | // export * from './base64ToPath' |
| | | // export * from './pathToBase64' |
| | | // export * from './exif' |
| | | |
| | | // // canvas |
| | | // export * from './canIUseCanvas2d' |
| | | |
| | | // // page |
| | | // export * from './getCurrentPage' |
| | | |
| | | // // dom |
| | | // export * from './getRect' |
| | | // export * from './selectComponent' |
| | | // export * from './createAnimation' |
| | | |
| | | // // delay |
| | | // export * from './sleep' |
| | | // export * from './debounce' |
| | | // export * from './throttle' |
| | | |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | /** |
| | | * 判断一个字符串是否为Base64编码。 |
| | | * Base64编码的字符串只包含A-Z、a-z、0-9、+、/ 和 = 这些字符。 |
| | | * @param {string} str - 要检查的字符串。 |
| | | * @returns {boolean} 如果字符串是Base64编码,返回true,否则返回false。 |
| | | */ |
| | | export function isBase64(str: string): boolean { |
| | | const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; |
| | | return base64Regex.test(str); |
| | | } |
| | | |
| | | /** |
| | | * 判断一个字符串是否为Base64编码的data URI。 |
| | | * Base64编码的data URI通常以"data:"开头,后面跟着MIME类型和编码信息,然后是Base64编码的数据。 |
| | | * @param {string} str - 要检查的字符串。 |
| | | * @returns {boolean} 如果字符串是Base64编码的data URI,返回true,否则返回false。 |
| | | */ |
| | | export function isBase64DataUri(str: string): boolean { |
| | | const dataUriRegex = /^data:([a-zA-Z]+\/[a-zA-Z0-9-+.]+)(;base64)?,([a-zA-Z0-9+/]+={0,2})$/; |
| | | return dataUriRegex.test(str); |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifdef WEB |
| | | export const isBrowser = typeof window !== 'undefined'; |
| | | // #endif |
| | | |
| | | // #ifndef WEB |
| | | export const isBrowser = false; |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 检查一个值是否已定义(不为 undefined)且不为 null |
| | | * @param value 要检查的值 |
| | | * @returns 如果值已定义且不为 null,则返回 true;否则返回 false |
| | | */ |
| | | // #ifndef UNI-APP-X |
| | | export function isDef(value: unknown): boolean { |
| | | return value !== undefined && value !== null; |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X |
| | | export function isDef(value : any|null) : boolean { |
| | | // #ifdef UNI-APP-X && APP |
| | | return value != null; |
| | | // #endif |
| | | // #ifndef UNI-APP-X && APP |
| | | return value != null && value != undefined; |
| | | // #endif |
| | | } |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import {isDef} from '../isDef' |
| | | import {isString} from '../isString' |
| | | import {isNumber} from '../isNumber' |
| | | /** |
| | | * 判断一个值是否为空。 |
| | | * |
| | | * 对于字符串,去除首尾空格后判断长度是否为0。 |
| | | * 对于数组,判断长度是否为0。 |
| | | * 对于对象,判断键的数量是否为0。 |
| | | * 对于null或undefined,直接返回true。 |
| | | * 其他类型(如数字、布尔值等)默认不为空。 |
| | | * |
| | | * @param {any} value - 要检查的值。 |
| | | * @returns {boolean} 如果值为空,返回true,否则返回false。 |
| | | */ |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export function isEmpty(value : any | null) : boolean { |
| | | // 为null |
| | | if(!isDef(value)){ |
| | | return true |
| | | } |
| | | // 为空字符 |
| | | if(isString(value)){ |
| | | return value.toString().trim().length == 0 |
| | | } |
| | | // 为数值 |
| | | if(isNumber(value)){ |
| | | return false |
| | | } |
| | | |
| | | if(typeof value == 'object'){ |
| | | // 数组 |
| | | if(Array.isArray(value)){ |
| | | return (value as Array<unknown>).length == 0 |
| | | } |
| | | // Map |
| | | if(value instanceof Map<unknown, unknown>) { |
| | | return value.size == 0 |
| | | } |
| | | // Set |
| | | if(value instanceof Set<unknown>) { |
| | | return value.size == 0 |
| | | } |
| | | if(value instanceof UTSJSONObject) { |
| | | return value.toMap().size == 0 |
| | | } |
| | | return JSON.stringify(value) == '{}' |
| | | } |
| | | |
| | | return true |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export function isEmpty(value: any): boolean { |
| | | // 检查是否为null或undefined |
| | | if (value == null) { |
| | | return true; |
| | | } |
| | | |
| | | // 检查字符串是否为空 |
| | | if (typeof value === 'string') { |
| | | return value.trim().length === 0; |
| | | } |
| | | |
| | | // 检查数组是否为空 |
| | | if (Array.isArray(value)) { |
| | | return value.length === 0; |
| | | } |
| | | |
| | | // 检查对象是否为空 |
| | | if (typeof value === 'object') { |
| | | return Object.keys(value).length === 0; |
| | | } |
| | | |
| | | // 其他类型(如数字、布尔值等)不为空 |
| | | return false; |
| | | } |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 检查一个值是否为函数类型 |
| | | * @param val 要检查的值 |
| | | * @returns 如果值的类型是函数类型,则返回 true;否则返回 false |
| | | */ |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export const isFunction = (val: any):boolean => typeof val == 'function'; |
| | | // #endif |
| | | |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export const isFunction = (val: unknown): val is Function => |
| | | typeof val === 'function'; |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 检查一个值是否为数字类型 |
| | | * @param value 要检查的值,可以是 number 类型或 string 类型的数字 |
| | | * @returns 如果值是数字类型且不是 NaN,则返回 true;否则返回 false |
| | | */ |
| | | |
| | | // #ifndef UNI-APP-X |
| | | export function isNumber(value: number | string | null): boolean { |
| | | return typeof value === 'number' && !isNaN(value); |
| | | } |
| | | // #endif |
| | | |
| | | // #ifdef UNI-APP-X |
| | | export function isNumber(value: any|null): boolean { |
| | | // #ifdef APP-ANDROID |
| | | return ['Byte', 'UByte','Short','UShort','Int','UInt','Long','ULong','Float','Double','number'].includes(typeof value) |
| | | // #endif |
| | | // #ifdef APP-IOS |
| | | return ['Int8', 'UInt8','Int16','UInt16','Int32','UInt32','Int64','UInt64','Int','UInt','Float','Float16','Float32','Float64','Double', 'number'].includes(typeof value) |
| | | // #endif |
| | | // #ifndef UNI-APP-X && APP |
| | | return typeof value === 'number' && !isNaN(value); |
| | | // #endif |
| | | } |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | /** |
| | | * 检查一个值是否为数字类型或表示数字的字符串 |
| | | * @param value 要检查的值,可以是 string 类型或 number 类型 |
| | | * @returns 如果值是数字类型或表示数字的字符串,则返回 true;否则返回 false |
| | | */ |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export function isNumeric(value: string | number | undefined | null): boolean { |
| | | return /^(-)?\d+(\.\d+)?$/.test(value); |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | import {isNumber} from '../isNumber'; |
| | | import {isString} from '../isString'; |
| | | export function isNumeric(value : any|null) : boolean { |
| | | if(value == null) { |
| | | return false |
| | | } |
| | | if(isNumber(value)) { |
| | | return true |
| | | } else if(isString(value)) { |
| | | // const regex = "-?\\d+(\\.\\d+)?".toRegex() |
| | | const regex = new RegExp("^(-)?\\d+(\\.\\d+)?$") |
| | | return regex.test(value as string) //regex.matches(value as string) |
| | | } |
| | | return false |
| | | // return /^(-)?\d+(\.\d+)?$/.test(value); |
| | | } |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 检查一个值是否为对象类型 |
| | | * @param val 要检查的值 |
| | | * @returns 如果值的类型是对象类型,则返回 true;否则返回 false |
| | | */ |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export const isObject = (val : unknown) : val is Record<any, any> => |
| | | val !== null && typeof val === 'object'; |
| | | |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export const isObject = (val : any | null) : boolean =>{ |
| | | return val !== null && typeof val === 'object'; |
| | | } |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import {isFunction} from '../isFunction' |
| | | import {isObject} from '../isObject' |
| | | /** |
| | | * 检查一个值是否为 Promise 类型 |
| | | * @param val 要检查的值 |
| | | * @returns 如果值的类型是 Promise 类型,则返回 true;否则返回 false |
| | | */ |
| | | // #ifndef APP-ANDROID |
| | | export const isPromise = <T = any>(val: unknown): val is Promise<T> => { |
| | | // 使用 isObject 函数判断值是否为对象类型 |
| | | // 使用 isFunction 函数判断值是否具有 then 方法和 catch 方法 |
| | | return isObject(val) && isFunction(val.then) && isFunction(val.catch); |
| | | }; |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef APP-ANDROID |
| | | export const isPromise = (val: any): boolean => { |
| | | return val instanceof Promise<unknown> |
| | | }; |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 检查一个值是否为字符串类型 |
| | | * @param str 要检查的值 |
| | | * @returns 如果值的类型是字符串类型,则返回 true;否则返回 false |
| | | */ |
| | | // #ifndef UNI-APP-X && APP |
| | | // export const isString = (str: unknown): str is string => typeof str === 'string'; |
| | | export function isString (str: unknown): str is string { |
| | | return typeof str == 'string' |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export function isString (str: any|null): boolean { |
| | | return typeof str == 'string' |
| | | } |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // export function toLowercaseSeparator(key: string) { |
| | | // return key.replace(/([A-Z])/g, '-$1').toLowerCase(); |
| | | // } |
| | | |
| | | /** |
| | | * 将字符串转换为指定连接符的命名约定 |
| | | * @param str 要转换的字符串 |
| | | * @param separator 指定的连接符,默认为 "-" |
| | | * @returns 转换后的字符串 |
| | | */ |
| | | export function kebabCase(str : string, separator : string = "-") : string { |
| | | return str |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | .replace(/[A-Z]/g, (match : string, _ : number, _ : string) : string => `${separator}${match.toLowerCase()}`) // 将大写字母替换为连接符加小写字母 |
| | | // #endif |
| | | // #ifndef UNI-APP-X && APP |
| | | .replace(/[A-Z]/g, (match : string) : string => `${separator}${match.toLowerCase()}`) // 将大写字母替换为连接符加小写字母 |
| | | // #endif |
| | | .replace(/[\s_-]+/g, separator) // 将空格、下划线和短横线替换为指定连接符 |
| | | .replace(new RegExp(`^${separator}|${separator}$`, "g"), "") // 删除开头和结尾的连接符 |
| | | .toLowerCase(); // 将结果转换为全小写 |
| | | } |
对比新文件 |
| | |
| | | { |
| | | "id": "lime-shared", |
| | | "displayName": "lime-shared", |
| | | "version": "0.1.8", |
| | | "description": "本人插件的几个公共函数,获取当前页,图片的base64转临时路径,图片的exif信息等", |
| | | "keywords": [ |
| | | "lime-shared", |
| | | "exif" |
| | | ], |
| | | "repository": "", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.0" |
| | | }, |
| | | "dcloudext": { |
| | | "type": "sdk-js", |
| | | "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", |
| | | "alipay": "n" |
| | | }, |
| | | "client": { |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | }, |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-uvue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "u", |
| | | "Edge": "u", |
| | | "Firefox": "u", |
| | | "Safari": "u" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "钉钉": "y", |
| | | "快手": "y", |
| | | "飞书": "y", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.uts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // import { processFile, ProcessFileOptions } from '@/uni_modules/lime-file-utils' |
| | | export function pathToBase64(path : string) : Promise<string> { |
| | | console.error('pathToBase64: 当前环境不支持,请使用 【lime-file-utils】') |
| | | // return new Promise((resolve, reject) => { |
| | | // processFile({ |
| | | // type: 'toDataURL', |
| | | // path, |
| | | // success(res : string) { |
| | | // resolve(res) |
| | | // }, |
| | | // fail(err: any){ |
| | | // reject(err) |
| | | // } |
| | | // } as ProcessFileOptions) |
| | | // }) |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | // #ifdef APP-PLUS |
| | | import { getLocalFilePath } from '../getLocalFilePath' |
| | | // #endif |
| | | function isImage(extension : string) { |
| | | const imageExtensions = ["jpg", "jpeg", "png", "gif", "bmp", "svg"]; |
| | | return imageExtensions.includes(extension.toLowerCase()); |
| | | } |
| | | // #ifdef H5 |
| | | function getSVGFromURL(url: string) { |
| | | return new Promise((resolve, reject) => { |
| | | const xhr = new XMLHttpRequest(); |
| | | xhr.open('GET', url, true); |
| | | xhr.responseType = 'text'; |
| | | |
| | | xhr.onload = function () { |
| | | if (xhr.status === 200) { |
| | | const svg = xhr.responseText; |
| | | resolve(svg); |
| | | } else { |
| | | reject(new Error(xhr.statusText)); |
| | | } |
| | | }; |
| | | |
| | | xhr.onerror = function () { |
| | | reject(new Error('Network error')); |
| | | }; |
| | | |
| | | xhr.send(); |
| | | }); |
| | | } |
| | | // #endif |
| | | /** |
| | | * 路径转base64 |
| | | * @param {Object} string |
| | | */ |
| | | export function pathToBase64(path : string) : Promise<string> { |
| | | if (/^data:/.test(path)) return path |
| | | let extension = path.substring(path.lastIndexOf('.') + 1); |
| | | const isImageFile = isImage(extension) |
| | | let prefix = '' |
| | | if (isImageFile) { |
| | | prefix = 'image/'; |
| | | if(extension == 'svg') { |
| | | extension += '+xml' |
| | | } |
| | | } else if (extension === 'pdf') { |
| | | prefix = 'application/pdf'; |
| | | } else if (extension === 'txt') { |
| | | prefix = 'text/plain'; |
| | | } else { |
| | | // 添加更多文件类型的判断 |
| | | // 如果不是图片、PDF、文本等类型,可以设定默认的前缀或采取其他处理 |
| | | prefix = 'application/octet-stream'; |
| | | } |
| | | return new Promise((resolve, reject) => { |
| | | // #ifdef H5 |
| | | if (isImageFile) { |
| | | if(extension == 'svg') { |
| | | getSVGFromURL(path).then(svg => { |
| | | const base64 = btoa(svg); |
| | | resolve(`data:image/svg+xml;base64,${base64}`); |
| | | }) |
| | | } else { |
| | | let image = new Image(); |
| | | image.setAttribute("crossOrigin", 'Anonymous'); |
| | | image.onload = function () { |
| | | let canvas = document.createElement('canvas'); |
| | | canvas.width = this.naturalWidth; |
| | | canvas.height = this.naturalHeight; |
| | | canvas.getContext('2d').drawImage(image, 0, 0); |
| | | let result = canvas.toDataURL(`${prefix}${extension}`) |
| | | resolve(result); |
| | | canvas.height = canvas.width = 0 |
| | | } |
| | | image.src = path + '?v=' + Math.random() |
| | | image.onerror = (error) => { |
| | | reject(error); |
| | | }; |
| | | } |
| | | |
| | | } else { |
| | | reject('not image'); |
| | | } |
| | | |
| | | // #endif |
| | | |
| | | // #ifdef MP |
| | | if (uni.canIUse('getFileSystemManager')) { |
| | | uni.getFileSystemManager().readFile({ |
| | | filePath: path, |
| | | encoding: 'base64', |
| | | success: (res) => { |
| | | resolve(`data:${prefix}${extension};base64,${res.data}`) |
| | | }, |
| | | fail: (error) => { |
| | | console.error({ error, path }) |
| | | reject(error) |
| | | } |
| | | }) |
| | | } |
| | | // #endif |
| | | |
| | | // #ifdef APP-PLUS |
| | | plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => { |
| | | entry.file((file : any) => { |
| | | const fileReader = new plus.io.FileReader() |
| | | fileReader.onload = (data) => { |
| | | resolve(data.target.result) |
| | | } |
| | | fileReader.onerror = (error) => { |
| | | console.error({ error, path }) |
| | | reject(error) |
| | | } |
| | | fileReader.readAsDataURL(file) |
| | | }, reject) |
| | | }, reject) |
| | | // #endif |
| | | }) |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | export function getPlatform():Uni { |
| | | // #ifdef MP-WEIXIN |
| | | return wx |
| | | // #endif |
| | | // #ifdef MP-BAIDU |
| | | return swan |
| | | // #endif |
| | | // #ifdef MP-ALIPAY |
| | | return my |
| | | // #endif |
| | | // #ifdef MP-JD |
| | | return jd |
| | | // #endif |
| | | // #ifdef MP-QQ |
| | | return qq |
| | | // #endif |
| | | // #ifdef MP-360 |
| | | return qh |
| | | // #endif |
| | | // #ifdef MP-KUAISHOU |
| | | return ks |
| | | // #endif |
| | | // #ifdef MP-LARK||MP-TOUTIAO |
| | | return tt |
| | | // #endif |
| | | // #ifdef MP-DINGTALK |
| | | return dd |
| | | // #endif |
| | | // #ifdef QUICKAPP-WEBVIEW || QUICKAPP-WEBVIEW-UNION || QUICKAPP-WEBVIEW-HUAWEI |
| | | return qa |
| | | // #endif |
| | | return uni |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export * from './uvue.ts' |
| | | // #endif |
| | | |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export * from './vue.ts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // 是否支持被动事件监听 |
| | | export const supportsPassive = true; |
| | | |
| | | // #ifdef uniVersion < 4.25 |
| | | // 请求动画帧 |
| | | export function raf(fn: TimerCallback): number { |
| | | return setTimeout(fn, 1000 / 60); |
| | | } |
| | | |
| | | // 取消动画帧 |
| | | export function cancelRaf(id: number) { |
| | | clearTimeout(id); |
| | | } |
| | | |
| | | |
| | | // 双倍动画帧 |
| | | export function doubleRaf(fn: TimerCallback): void { |
| | | raf(():number => raf(fn)); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果 |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef uniVersion >= 4.25 |
| | | // 请求动画帧 |
| | | export function raf(fn: UniAnimationFrameCallback): number |
| | | export function raf(fn: UniAnimationFrameCallbackWithNoArgument): number |
| | | export function raf(fn: any): number { |
| | | if(typeof fn == 'UniAnimationFrameCallback') { |
| | | return requestAnimationFrame(fn as UniAnimationFrameCallback); |
| | | } else { |
| | | return requestAnimationFrame(fn as UniAnimationFrameCallbackWithNoArgument); |
| | | } |
| | | } |
| | | |
| | | // 取消动画帧 |
| | | export function cancelRaf(id: number) { |
| | | cancelAnimationFrame(id); |
| | | } |
| | | |
| | | // 双倍动画帧 |
| | | export function doubleRaf(fn: UniAnimationFrameCallback): void |
| | | export function doubleRaf(fn: UniAnimationFrameCallbackWithNoArgument): void |
| | | export function doubleRaf(fn: any): void { |
| | | raf(():number => raf(fn)); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果 |
| | | } |
| | | // #endif |
| | | |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | type Callback = () => void//Function |
| | | // 是否支持被动事件监听 |
| | | export const supportsPassive = true; |
| | | |
| | | // 请求动画帧 |
| | | export function raf(fn : Callback) : number { |
| | | // #ifndef WEB |
| | | return setTimeout(fn, 1000 / 60); // 请求动画帧 |
| | | // #endif |
| | | // #ifdef WEB |
| | | return requestAnimationFrame(fn); // 请求动画帧 |
| | | // #endif |
| | | } |
| | | |
| | | // 取消动画帧 |
| | | export function cancelRaf(id : number) { |
| | | // 如果是在浏览器环境下,使用 cancelAnimationFrame 方法 |
| | | // #ifdef WEB |
| | | cancelAnimationFrame(id); // 取消动画帧 |
| | | // #endif |
| | | // #ifndef WEB |
| | | clearTimeout(id); // 取消动画帧 |
| | | // #endif |
| | | } |
| | | |
| | | // 双倍动画帧 |
| | | export function doubleRaf(fn : Callback) : void { |
| | | raf(() => { |
| | | raf(fn) |
| | | }); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果 |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 生成一个指定范围内的随机数 |
| | | * @param min 随机数的最小值 |
| | | * @param max 随机数的最大值 |
| | | * @param fixed 随机数的小数位数,默认为 0 |
| | | * @returns 生成的随机数 |
| | | */ |
| | | |
| | | export function random(min: number, max: number, fixed: number = 0):number { |
| | | // 将 min 和 max 转换为数字类型 |
| | | // min = +min || 0; |
| | | // max = +max || 0; |
| | | // 计算随机数范围内的一个随机数 |
| | | const num = Math.random() * (max - min) + min; |
| | | // 如果 fixed 参数为 0,则返回四舍五入的整数随机数;否则保留固定小数位数 |
| | | // Number |
| | | return fixed == 0 ? Math.round(num) : parseFloat(num.toFixed(fixed)); |
| | | } |
| | | |
| | | // 示例 |
| | | // console.log(random(0, 10)); // 输出:在 0 和 10 之间的一个整数随机数 |
| | | // console.log(random(0, 1, 2)); // 输出:在 0 和 1 之间的一个保留两位小数的随机数 |
| | | // console.log(random(1, 100, 3)); // 输出:在 1 和 100 之间的一个保留三位小数的随机数 |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 生成一个数字范围的数组 |
| | | * @param start 范围的起始值 |
| | | * @param end 范围的结束值 |
| | | * @param step 步长,默认为 1 |
| | | * @param fromRight 是否从右侧开始生成,默认为 false |
| | | * @returns 生成的数字范围数组 |
| | | */ |
| | | export function range(start : number, end : number, step : number = 1, fromRight : boolean = false) : number[] { |
| | | let index = -1; |
| | | // 计算范围的长度 |
| | | let length = Math.max(Math.ceil((end - start) / step), 0); |
| | | // 创建一个长度为 length 的数组 |
| | | // #ifdef APP-ANDROID |
| | | const result = Array.fromNative(new IntArray(length.toInt())); |
| | | // #endif |
| | | // #ifndef APP-ANDROID |
| | | const result = new Array(length); |
| | | // #endif |
| | | |
| | | // 使用循环生成数字范围数组 |
| | | let _start = start |
| | | while (length-- > 0) { |
| | | // 根据 fromRight 参数决定从左侧还是右侧开始填充数组 |
| | | result[fromRight ? length : ++index] = _start; |
| | | _start += step; |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | |
| | | // 示例 |
| | | // console.log(range(0, 5)); // 输出: [0, 1, 2, 3, 4] |
| | | // console.log(range(1, 10, 2, true)); // 输出: [9, 7, 5, 3, 1] |
| | | // console.log(range(5, 0, -1)); // 输出: [5, 4, 3, 2, 1] |
对比新文件 |
| | |
| | | # lime-shared 工具库 |
| | | - 本人插件的几个公共函数 |
| | | - 按需引入 |
| | | |
| | | |
| | | ## 引入 |
| | | 按需引入只会引入相关的方法,不要看着 插件函数列表多 而占空间,只要不引用不会被打包 |
| | | ```js |
| | | import {getRect} from '@/uni_modules/lime-shared/getRect' |
| | | ``` |
| | | |
| | | ## 目录 |
| | | + [getRect](#api_getRect): 获取节点尺寸信息 |
| | | + [addUnit](#api_addUnit): 将未带单位的数值添加px,如果有单位则返回原值 |
| | | + [unitConvert](#api_unitConvert): 将带有rpx|px的字符转成number,若本身是number则直接返回 |
| | | + [canIUseCanvas2d](#api_canIUseCanvas2d): 环境是否支持使用 canvas 2d |
| | | + [getCurrentPage](#api_getCurrentPage): 获取当前页 |
| | | + [base64ToPath](#api_base64ToPath): 把base64的图片转成临时路径 |
| | | + [pathToBase64](#api_pathToBase64): 把图片的临时路径转成base64 |
| | | + [sleep](#api_sleep): async 内部程序等待一定时间后再执行 |
| | | + [throttle](#api_throttle): 节流 |
| | | + [debounce](#api_debounce): 防抖 |
| | | + [random](#api_random): 返回指定范围的随机数 |
| | | + [range](#api_range): 生成区间数组 |
| | | + [clamp](#api_clamp): 夹在min和max之间的数值 |
| | | + [floatAdd](#api_floatAdd): 返回两个浮点数相加的结果 |
| | | + [fillZero](#api_fillZero): 补零,如果传入的是个位数则在前面补0 |
| | | + [exif](#api_exif): 获取图片exif |
| | | + [selectComponent](#api_selectComponent): 获取页面或当前实例的指定组件 |
| | | + [createAnimation](#api_createAnimation): uni.createAnimation |
| | | + [animation](#api_animation): 数值从一个值到另一个值的过渡 |
| | | + [camelCase](#api_camelCase): 字符串转换为 camelCase 或 PascalCase 风格的命名约定 |
| | | + [kebabCase](#api_kebabCase): 将字符串转换为指定连接符的命名约定 |
| | | + [closest](#api_closest): 在给定数组中找到最接近目标数字的元素 |
| | | + [isBase64](#api_isBase64): 判断字符串是否为base64 |
| | | + [isNumber](#api_isNumber): 检查一个值是否为数字类型 |
| | | + [isNumeric](#api_isNumeric): 检查一个值是否为数字类型或表示数字的字符串 |
| | | + [isString](#api_isString): 检查一个值是否为字符串类型 |
| | | + [composition-api](#api_composition-api): 为兼容vue2 |
| | | |
| | | ## Utils |
| | | |
| | | |
| | | ### getRect <a id="api_getRect"></a> |
| | | - 返回节点尺寸信息 |
| | | |
| | | ```js |
| | | // 组件内需要传入上下文 |
| | | // 如果是nvue 则需要在节点上加与id或class同名的ref |
| | | getRect('#id',{context: this}).then(res => {}) |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | |
| | | ### addUnit <a id="api_addUnit"></a> |
| | | - 将未带单位的数值添加px,如果有单位则返回原值 |
| | | |
| | | ```js |
| | | addUnit(10) |
| | | // 10px |
| | | ``` |
| | | |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | |
| | | ### unitConvert <a id="api_unitConvert"></a> |
| | | - 将带有rpx|px的字符转成number,若本身是number则直接返回 |
| | | |
| | | ```js |
| | | unitConvert('10rpx') |
| | | // 5 设备不同 返回的值也不同 |
| | | unitConvert('10px') |
| | | // 10 |
| | | unitConvert(10) |
| | | // 10 |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | ### canIUseCanvas2d <a id="api_canIUseCanvas2d"></a> |
| | | - 环境是否支持使用 canvas 2d |
| | | |
| | | ```js |
| | | canIUseCanvas2d() |
| | | // 若支持返回 true 否则 false |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | ### getCurrentPage <a id="api_getCurrentPage"></a> |
| | | - 获取当前页 |
| | | |
| | | ```js |
| | | const page = getCurrentPage() |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | ### base64ToPath <a id="api_base64ToPath"></a> |
| | | - 把base64的图片转成临时路径 |
| | | |
| | | ```js |
| | | base64ToPath(`xxxxx`).then(res => {}) |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | ### pathToBase64 <a id="api_pathToBase64"></a> |
| | | - 把图片的临时路径转成base64 |
| | | |
| | | ```js |
| | | pathToBase64(`xxxxx/xxx.png`).then(res => {}) |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | ### sleep <a id="api_sleep"></a> |
| | | - 睡眠,让 async 内部程序等待一定时间后再执行 |
| | | |
| | | ```js |
| | | async next () => { |
| | | await sleep(300) |
| | | console.log('limeui'); |
| | | } |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | ### throttle <a id="api_throttle"></a> |
| | | - 节流 |
| | | |
| | | ```js |
| | | throttle((nama) => {console.log(nama)}, 200)('limeui'); |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | ### debounce <a id="api_debounce"></a> |
| | | - 防抖 |
| | | |
| | | ```js |
| | | debounce((nama) => {console.log(nama)}, 200)('limeui'); |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | ### random <a id="api_random"></a> |
| | | - 返回指定范围的随机数 |
| | | |
| | | ```js |
| | | random(1, 5); |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | ### range <a id="api_range"></a> |
| | | - 生成区间数组 |
| | | |
| | | ```js |
| | | range(0, 5) |
| | | // [0,1,2,3,4,5] |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | ### clamp <a id="api_clamp"></a> |
| | | - 夹在min和max之间的数值,如小于min,返回min, 如大于max,返回max,否侧原值返回 |
| | | |
| | | ```js |
| | | clamp(0, 10, -1) |
| | | // 0 |
| | | clamp(0, 10, 11) |
| | | // 10 |
| | | clamp(0, 10, 9) |
| | | // 9 |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | ### floatAdd <a id="api_floatAdd"></a> |
| | | - 返回两个浮点数相加的结果 |
| | | |
| | | ```js |
| | | floatAdd(0.1, 0.2) // 0.3 |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | ### fillZero <a id="api_fillZero"></a> |
| | | - 补零,如果传入的是`个位数`则在前面补0 |
| | | |
| | | ```js |
| | | fillZero(9); |
| | | // 09 |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | ### exif <a id="api_exif"></a> |
| | | - 获取图片exif |
| | | - 支持临时路径、base64 |
| | | |
| | | ```js |
| | | uni.chooseImage({ |
| | | count: 1, //最多可以选择的图片张数 |
| | | sizeType: "original", |
| | | success: (res) => { |
| | | exif.getData(res.tempFiles[0], function() { |
| | | let tagj = exif.getTag(this, "GPSLongitude"); |
| | | let Orientation = exif.getTag(this, 'Orientation'); |
| | | console.log(tagj, Orientation) |
| | | }) |
| | | } |
| | | }) |
| | | ``` |
| | | |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | x | |
| | | |
| | | |
| | | ### selectComponent <a id="api_selectComponent"></a> |
| | | - 获取页面或当前实例的指定组件,会在页面或实例向所有的节点查找(包括子组件或子子组件) |
| | | - 仅vue3,vue2没有测试过 |
| | | |
| | | ```js |
| | | // 当前页面 |
| | | const page = getCurrentPage() |
| | | selectComponent('.custom', {context: page}).then(res => { |
| | | }) |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | x | |
| | | |
| | | |
| | | |
| | | ### createAnimation <a id="api_createAnimation"></a> |
| | | - 创建动画,与uni.createAnimation使用方法一致,只为了抹平nvue |
| | | |
| | | ```html |
| | | <view ref="ball" :animation="animationData"></view> |
| | | ``` |
| | | ```js |
| | | const ball = ref(null) |
| | | const animation = createAnimation({ |
| | | transformOrigin: "50% 50%", |
| | | duration: 1000, |
| | | timingFunction: "ease", |
| | | delay: 0 |
| | | }) |
| | | |
| | | animation.scale(2,2).rotate(45).step() |
| | | // nvue 无导出数据,这样写只为了平台一致, |
| | | // nvue 需要把 ref 传入,其它平台不需要 |
| | | const animationData = animation.export(ball.value) |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | ### camelCase <a id="api_camelCase"></a> |
| | | - 将字符串转换为 camelCase 或 PascalCase 风格的命名约定 |
| | | |
| | | ```js |
| | | camelCase("hello world") // helloWorld |
| | | camelCase("hello world", true) // HelloWorld |
| | | ``` |
| | | |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | ### kebabCase <a id="api_kebabCase"></a> |
| | | - 将字符串转换为指定连接符的命名约定 |
| | | |
| | | ```js |
| | | kebabCase("helloWorld") // hello-world |
| | | kebabCase("hello world_example") // hello-world-example |
| | | kebabCase("helloWorld", "_") // hello_world |
| | | ``` |
| | | |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | |
| | | ### closest <a id="api_closest"></a> |
| | | - 在给定数组中找到最接近目标数字的元素 |
| | | |
| | | ```js |
| | | closest([1, 3, 5, 7, 9], 6) // 5 |
| | | ``` |
| | | |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | ### isBase64 <a id="api_isBase64"></a> |
| | | - 判断字符串是否为base64 |
| | | |
| | | ```js |
| | | isBase64('xxxxx') |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | ### isNumber <a id="api_isNumber"></a> |
| | | - 检查一个值是否为数字类型 |
| | | |
| | | ```js |
| | | isNumber('0') // false |
| | | isNumber(0) // true |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | ### isNumeric <a id="api_isNumeric"></a> |
| | | - 检查一个值是否为数字类型或表示数字的字符串 |
| | | |
| | | ```js |
| | | isNumeric('0') // true |
| | | isNumeric(0) // true |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | ### isString <a id="api_isString"></a> |
| | | - 检查一个值是否为数字类型或表示数字的字符串 |
| | | |
| | | ```js |
| | | isString('0') // true |
| | | isString(0) // false |
| | | ``` |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | √ | |
| | | |
| | | |
| | | |
| | | |
| | | ## composition-api <a id="api_composition-api"></a> |
| | | - 因本人插件需要兼容vue2/vue3,故增加一个vue文件,代替条件编译 |
| | | - vue2需要在main.js加上这一段 |
| | | ```js |
| | | // vue2 |
| | | import Vue from 'vue' |
| | | import VueCompositionAPI from '@vue/composition-api' |
| | | Vue.use(VueCompositionAPI) |
| | | ``` |
| | | |
| | | ```js |
| | | //使用 |
| | | import {computed, onMounted, watch, reactive} from '@/uni_modules/lime-shared/vue' |
| | | ``` |
| | | |
| | | ##### 兼容性 |
| | | | uni-app | uni-app x | |
| | | |------------|----------------------------------| |
| | | | √ | x | |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifdef UNI-APP-X |
| | | export * from './uvue.uts' |
| | | // #endif |
| | | |
| | | // #ifndef UNI-APP-X |
| | | export * from './vue.ts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import { type ComponentPublicInstance } from 'vue'; |
| | | |
| | | type SelectOptions = { |
| | | context : ComponentPublicInstance, |
| | | needAll : boolean | null, |
| | | |
| | | } |
| | | |
| | | export function selectAllComponent(selector : string, options : UTSJSONObject) : ComponentPublicInstance[]|null { |
| | | const context = options.get('context')! as ComponentPublicInstance; |
| | | let needAll = options.get('needAll') as boolean; |
| | | let result:ComponentPublicInstance[] = [] |
| | | |
| | | if(needAll == null) { needAll = true }; |
| | | |
| | | if(context.$children.length > 0) { |
| | | const queue:ComponentPublicInstance[] = [...context.$children]; |
| | | while(queue.length > 0) { |
| | | const child = queue.shift(); |
| | | const name = child?.$options?.name; |
| | | if(name == selector) { |
| | | result.push(child as ComponentPublicInstance) |
| | | } else { |
| | | const children = child?.$children |
| | | if(children !== null) { |
| | | queue.push(...children) |
| | | } |
| | | } |
| | | if(result.length > 0 && !needAll) { |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | if(result.length > 0) { |
| | | return result |
| | | } |
| | | return null |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | interface SelectOptions { |
| | | context?: any |
| | | needAll?: boolean |
| | | node?: boolean |
| | | } |
| | | // #ifdef MP |
| | | function selectMPComponent(key: string, name: string, context: any, needAll: boolean) { |
| | | const {proxy, $vm} = context |
| | | context = $vm || proxy |
| | | if(!['ref','component'].includes(key)) { |
| | | const queue = [context] |
| | | let result = null |
| | | const selector = (key == 'id' ? '#': '.') + name; |
| | | while(queue.length > 0) { |
| | | const child = queue.shift(); |
| | | const flag = child?.selectComponent(selector) |
| | | if(flag) { |
| | | if(!needAll) {return result = flag.$vm} |
| | | return result = child.selectAllComponents(selector).map(item => item.$vm) |
| | | } else { |
| | | child.$children && (queue.push(...child.$children)); |
| | | } |
| | | } |
| | | return result |
| | | } else { |
| | | const {$templateRefs} = context.$ |
| | | const nameMap = {} |
| | | for (var i = 0; i < $templateRefs.length; i++) { |
| | | const item = $templateRefs[i] |
| | | nameMap[item.i] = item.r |
| | | } |
| | | let result = [] |
| | | if(context.$children.length) { |
| | | const queue = [...context.$children] |
| | | while(queue.length > 0) { |
| | | const child = queue.shift(); |
| | | if(key == 'component' && (child.type?.name === name || child.$?.type?.name === name)) { |
| | | result.push(child) |
| | | } else if(child.$refs && child.$refs[name]) { |
| | | result = child.$refs[name] |
| | | } else if(nameMap[child.id] === name){ |
| | | result.push(child) |
| | | } else { |
| | | child.$children && (queue.push(...child.$children)); |
| | | } |
| | | if(result.length && !needAll) { |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | return needAll ? result : result[0] |
| | | } |
| | | } |
| | | // #endif |
| | | // #ifdef H5 |
| | | function selectH5Component(key: string, name: string, context: any, needAll: boolean) { |
| | | const {_, component } = context |
| | | const child = {component: _ || component || context, children: null , subTree: null, props: null} |
| | | let result = [] |
| | | let queue = [child] |
| | | while(queue.length > 0 ) { |
| | | const child = queue.shift() |
| | | const {component, children , props, subTree} = child |
| | | if(key === 'component' && component?.type?.name == name) { |
| | | result.push(component) |
| | | } else if(key === 'ref' && component && (props?.ref == name || component[key][name])) { |
| | | if(props?.ref == name) { |
| | | //exposed |
| | | result.push(component) |
| | | } else if(component[key][name]) { |
| | | result.push(component[key][name]) |
| | | } |
| | | } else if(key !== 'ref' && component?.exposed && new RegExp(`\\b${name}\\b`).test(component.attrs[key])) { |
| | | // exposed |
| | | result.push(component) |
| | | } else if(children && Array.isArray(children)) { |
| | | queue.push(...children) |
| | | } else if(!component && subTree) { |
| | | queue.push(subTree) |
| | | } else if(component?.subTree) { |
| | | queue.push(component.subTree) |
| | | } |
| | | if(result.length && !needAll) { |
| | | break |
| | | } |
| | | } |
| | | return needAll ? result : result[0] |
| | | } |
| | | // #endif |
| | | // #ifdef APP |
| | | function selectAPPComponent(key: string, name: string, context: any, needAll: boolean, node: boolean) { |
| | | let result = [] |
| | | // const {_, component} = context |
| | | // const child = {component: _ || component || context, children: null, props: null, subTree: null} |
| | | const queue = [context] |
| | | while(queue.length > 0) { |
| | | const child = queue.shift() |
| | | const {component, children, props, subTree} = child |
| | | const isComp = component && props && component.exposed && !node |
| | | if(key == 'component' && child.type && child.type.name === name) { |
| | | result.push(component) |
| | | } else if(props?.[key] === name && node) { |
| | | result.push(child) |
| | | } else if(key === 'ref' && isComp && (props.ref === name || props.ref_key === name)) { |
| | | // exposed |
| | | result.push(component) |
| | | } else if(key !== 'ref' && isComp && new RegExp(`\\b${name}\\b`).test(props[key])) { |
| | | // exposed |
| | | result.push(component) |
| | | } |
| | | // else if(component && component.subTree && Array.isArray(component.subTree.children)){ |
| | | // queue.push(...component.subTree.children) |
| | | // } |
| | | else if(subTree) { |
| | | queue.push(subTree) |
| | | } else if(component && component.subTree){ |
| | | queue.push(component.subTree) |
| | | } |
| | | else if(children && Array.isArray(children)) { |
| | | queue.push(...children) |
| | | } |
| | | if(result.length && !needAll) { |
| | | break; |
| | | } |
| | | } |
| | | return needAll ? result : result[0] |
| | | } |
| | | // #endif |
| | | export function selectAllComponent(selector: string, options: SelectOptions = {}) { |
| | | // . class |
| | | // # id |
| | | // $ ref |
| | | // @ component name |
| | | const reg = /^(\.|#|@|\$)([a-zA-Z_0-9\-]+)$/; |
| | | if(!reg.test(selector)) return null |
| | | let { context, needAll = true, node} = options |
| | | const [,prefix, name] = selector.match(reg) |
| | | const symbolMappings = {'.': 'class', '#': 'id', '$':'ref', '@':'component'} |
| | | |
| | | const key = symbolMappings [prefix] //prefix === '.' ? 'class' : prefix === '#' ? 'id' : 'ref'; |
| | | // #ifdef MP |
| | | return selectMPComponent(key, name, context, needAll) |
| | | // #endif |
| | | // #ifdef H5 |
| | | return selectH5Component(key, name, context, needAll) |
| | | // #endif |
| | | // #ifdef APP |
| | | return selectAPPComponent(key, name, context, needAll, node) |
| | | // #endif |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifndef UNI-APP-X |
| | | export * from './vue.ts' |
| | | // #endif |
| | | // #ifdef UNI-APP-X |
| | | export * from './uvue.uts' |
| | | // #endif |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import { type ComponentPublicInstance } from 'vue'; |
| | | // #ifdef APP |
| | | function findChildren(selector: string, context: ComponentPublicInstance, needAll: boolean): ComponentPublicInstance [] | null{ |
| | | let result:ComponentPublicInstance[] = [] |
| | | |
| | | if(context !== null && context.$children.length > 0) { |
| | | const queue:ComponentPublicInstance[] = [...context.$children]; |
| | | while(queue.length > 0) { |
| | | const child = queue.shift(); |
| | | const name = child?.$options?.name; |
| | | if(name == selector) { |
| | | result.push(child as ComponentPublicInstance) |
| | | } else { |
| | | const children = child?.$children |
| | | if(children !== null) { |
| | | queue.push(...children) |
| | | } |
| | | } |
| | | if(result.length > 0 && !needAll) { |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | if(result.length > 0) { |
| | | return result |
| | | } |
| | | return null |
| | | } |
| | | |
| | | class Query { |
| | | context : ComponentPublicInstance | null = null |
| | | selector : string = '' |
| | | // components : ComponentPublicInstance[] = [] |
| | | constructor(selector : string, context : ComponentPublicInstance | null) { |
| | | this.selector = selector |
| | | this.context = context |
| | | } |
| | | in(context : ComponentPublicInstance) : Query { |
| | | return new Query(this.selector, context) |
| | | } |
| | | find(): ComponentPublicInstance | null { |
| | | const selector = this.selector |
| | | if(selector == '') return null |
| | | const component = findChildren(selector, this.context!, false) |
| | | return component != null ? component[0]: null |
| | | } |
| | | findAll():ComponentPublicInstance[] | null { |
| | | const selector = this.selector |
| | | if(selector == '') return null |
| | | return findChildren(selector, this.context!, true) |
| | | } |
| | | closest(): ComponentPublicInstance | null { |
| | | const selector = this.selector |
| | | if(selector == '') return null |
| | | let parent = this.context!.$parent |
| | | let name = parent?.$options?.name; |
| | | while (parent != null && (name == null || selector != name)) { |
| | | parent = parent.$parent |
| | | if (parent != null) { |
| | | name = parent.$options.name |
| | | } |
| | | } |
| | | return parent |
| | | } |
| | | } |
| | | |
| | | export function selectComponent(selector: string): Query{ |
| | | return new Query(selector, null) |
| | | } |
| | | // #endif |
| | | |
| | | // selectComponent('selector').in(this).find() |
| | | // selectComponent('selector').in(this).findAll() |
| | | // selectComponent('selector').in(this).closest() |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | // #ifdef MP |
| | | function findChildren(selector : string, context : ComponentPublicInstance, needAll : boolean) { |
| | | const { proxy, $vm } = context |
| | | context = $vm || proxy |
| | | if ((selector.startsWith('.') || selector.startsWith('#'))) { |
| | | const queue = [context] |
| | | let result = null |
| | | while (queue.length > 0) { |
| | | const child = queue.shift(); |
| | | const flag = child?.selectComponent(selector) |
| | | if (flag) { |
| | | if (!needAll) { return result = flag.$vm } |
| | | return result = child.selectAllComponents(selector).map(item => item.$vm) |
| | | } else { |
| | | child.$children && (queue.push(...child.$children)); |
| | | } |
| | | } |
| | | return result |
| | | } else { |
| | | const { $templateRefs } = context.$ |
| | | const selectorValue = /#|\.|@|$/.test(selector) ? selector.substring(1) : selector |
| | | const nameMap = {} |
| | | for (var i = 0; i < $templateRefs.length; i++) { |
| | | const item = $templateRefs[i] |
| | | nameMap[item.i] = item.r |
| | | } |
| | | let result = [] |
| | | if (context.$children.length) { |
| | | const queue = [...context.$children] |
| | | while (queue.length > 0) { |
| | | const child = queue.shift(); |
| | | if (child.type?.name === selectorValue || child.$?.type?.name === selectorValue) { |
| | | result.push(child) |
| | | } else if (child.$refs && child.$refs[selectorValue]) { |
| | | result = child.$refs[selectorValue] |
| | | } else if (nameMap[child.id] === selectorValue) { |
| | | result.push(child) |
| | | } else { |
| | | child.$children && (queue.push(...child.$children)); |
| | | } |
| | | if (result.length && !needAll) { |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | return needAll ? result : result[0] |
| | | } |
| | | } |
| | | // #endif |
| | | |
| | | // #ifdef H5 |
| | | function findChildren(selector : string, context : ComponentPublicInstance, needAll : boolean){ |
| | | const {_, component } = context |
| | | const child = {component: _ || component || context, children: null , subTree: null, props: null} |
| | | let result = [] |
| | | let queue = [child] |
| | | const selectorValue = /#|\.|@|$/.test(selector) ? selector.substring(1) : selector |
| | | while(queue.length > 0 ) { |
| | | const child = queue.shift() |
| | | const {component, children , props, subTree} = child |
| | | if(component?.type?.name == selectorValue) { |
| | | result.push(component) |
| | | } else if(selector.startsWith('$') && component && (props?.ref == selectorValue || component[key][selectorValue])) { |
| | | if(props?.ref == selectorValue) { |
| | | //exposed |
| | | result.push(component) |
| | | } else if(component[key][selectorValue]) { |
| | | result.push(component[key][selectorValue]) |
| | | } |
| | | } else if(!selector.startsWith('$') && component?.exposed && new RegExp(`\\b${selectorValue}\\b`).test(component.attrs[key])) { |
| | | // exposed |
| | | result.push(component) |
| | | } else if(children && Array.isArray(children)) { |
| | | queue.push(...children) |
| | | } else if(!component && subTree) { |
| | | queue.push(subTree) |
| | | } else if(component?.subTree) { |
| | | queue.push(component.subTree) |
| | | } |
| | | if(result.length && !needAll) { |
| | | break |
| | | } |
| | | } |
| | | return needAll ? result : result[0] |
| | | } |
| | | // #endif |
| | | |
| | | // #ifdef APP |
| | | function findChildren(selector : string, context : ComponentPublicInstance, needAll : boolean){ |
| | | let result = [] |
| | | const selectorValue = /#|\.|@|$/.test(selector) ? selector.substring(1) : selector |
| | | const queue = [context] |
| | | while(queue.length > 0) { |
| | | const child = queue.shift() |
| | | const {component, children, props, subTree} = child |
| | | const isComp = component && props && component.exposed && !node |
| | | if(child.type && child.type.name === selectorValue) { |
| | | result.push(component) |
| | | } else if(props?.[key] === selectorValue && node) { |
| | | result.push(child) |
| | | } else if(selector.startsWith('$') && isComp && (props.ref === selectorValue || props.ref_key === selectorValue)) { |
| | | // exposed |
| | | result.push(component) |
| | | } else if(!selector.startsWith('$') && isComp && new RegExp(`\\b${selectorValue}\\b`).test(props[key])) { |
| | | // exposed |
| | | result.push(component) |
| | | } |
| | | else if(subTree) { |
| | | queue.push(subTree) |
| | | } else if(component && component.subTree){ |
| | | queue.push(component.subTree) |
| | | } |
| | | else if(children && Array.isArray(children)) { |
| | | queue.push(...children) |
| | | } |
| | | if(result.length && !needAll) { |
| | | break; |
| | | } |
| | | } |
| | | return needAll ? result : result[0] |
| | | } |
| | | // #endif |
| | | |
| | | class Query { |
| | | context : ComponentPublicInstance | null = null |
| | | selector : string = '' |
| | | // components : ComponentPublicInstance[] = [] |
| | | constructor(selector : string, context : ComponentPublicInstance | null) { |
| | | this.selector = selector |
| | | this.context = context |
| | | } |
| | | in(context : ComponentPublicInstance) : Query { |
| | | return new Query(this.selector, context) |
| | | } |
| | | find() : ComponentPublicInstance | null { |
| | | return findChildren(this.selector, this.context, false) |
| | | } |
| | | findAll() : ComponentPublicInstance[] | null { |
| | | return findChildren(this.selector, this.context, true) |
| | | } |
| | | closest() : ComponentPublicInstance | null { |
| | | return null |
| | | } |
| | | } |
| | | |
| | | export function selectComponent(selector: string) { |
| | | return new Query(selector) |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import {isDef} from '../isDef' |
| | | import {ComponentPublicInstance} from 'vue' |
| | | |
| | | type HasSelectorFunc = (selector : string, element : UniElement) => boolean |
| | | |
| | | const hasSelectorClassName : HasSelectorFunc = (selector : string, element : UniElement) : boolean => { |
| | | return element.classList.includes(selector) |
| | | } |
| | | const hasSelectorId : HasSelectorFunc = (selector : string, element : UniElement) : boolean => { |
| | | return element.getAttribute("id") == selector |
| | | } |
| | | const hasSelectorTagName : HasSelectorFunc = (selector : string, element : UniElement) : boolean => { |
| | | return element.tagName!.toLowerCase() == selector.toLowerCase() |
| | | } |
| | | |
| | | type ProcessSelectorResult = { |
| | | selectorValue : string |
| | | hasSelector : HasSelectorFunc |
| | | } |
| | | const processSelector = (selector : string) : ProcessSelectorResult => { |
| | | |
| | | const selectorValue = /#|\./.test(selector) ? selector.substring(1) : selector |
| | | let hasSelector : HasSelectorFunc |
| | | |
| | | if (selector.startsWith('.')) { |
| | | hasSelector = hasSelectorClassName |
| | | } else if (selector.startsWith('#')) { |
| | | hasSelector = hasSelectorId |
| | | } else { |
| | | hasSelector = hasSelectorTagName |
| | | } |
| | | |
| | | return { |
| | | selectorValue, |
| | | hasSelector |
| | | } as ProcessSelectorResult |
| | | } |
| | | |
| | | |
| | | function isNotEmptyString(str:string): boolean { |
| | | return str.length > 0; |
| | | } |
| | | |
| | | function isElement(element:UniElement|null):boolean { |
| | | return isDef(element) && element?.tagName != 'COMMENT'; |
| | | } |
| | | |
| | | type ElementArray = Array<UniElement|null> |
| | | class Query { |
| | | context : ComponentPublicInstance | null = null |
| | | selector : string = '' |
| | | elements : ElementArray = [] |
| | | constructor(selector : string | null, context : ComponentPublicInstance | null) { |
| | | this.context = context |
| | | if(selector != null){ |
| | | this.selector = selector |
| | | } |
| | | this.find(this.selector) |
| | | } |
| | | in(context : ComponentPublicInstance) : Query { |
| | | return new Query(this.selector, context) |
| | | } |
| | | findAll(selector : string): Query { |
| | | if (isDef(this.context)) { |
| | | const root = this.context?.$el //as Element | null; |
| | | if (isDef(root)) { |
| | | this.elements = [root!] //as ElementArray |
| | | } |
| | | const { selectorValue, hasSelector } = processSelector(selector) |
| | | const foundElements : ElementArray = []; |
| | | |
| | | function findChildren(element : UniElement) { |
| | | element.children.forEach((child : UniElement) => { |
| | | if (hasSelector(selectorValue, child)) { |
| | | foundElements.push(child) |
| | | } |
| | | }) |
| | | } |
| | | this.elements.forEach(el => { |
| | | findChildren(el!); |
| | | }); |
| | | this.elements = foundElements |
| | | } else if (selector.startsWith('#')) { |
| | | const element = uni.getElementById(selector) |
| | | if (isElement(element!)) { |
| | | this.elements = [element] |
| | | } |
| | | } |
| | | return this; |
| | | } |
| | | /** |
| | | * 在当前元素集合中查找匹配的元素 |
| | | */ |
| | | find(selector : string) : Query { |
| | | if (isDef(this.context)) { |
| | | const root = this.context?.$el //as Element | null; |
| | | if (isElement(root)) { |
| | | this.elements = [root] //as ElementArray |
| | | } |
| | | if(isNotEmptyString(selector) && this.elements.length > 0){ |
| | | const { selectorValue, hasSelector } = processSelector(selector) |
| | | const foundElements : ElementArray = []; |
| | | function findChildren(element : UniElement) { |
| | | element.children.forEach((child : UniElement) => { |
| | | if (hasSelector(selectorValue, child) && foundElements.length < 1) { |
| | | foundElements.push(child) |
| | | } |
| | | if (foundElements.length < 1) { |
| | | findChildren(child); |
| | | } |
| | | }) |
| | | } |
| | | this.elements.forEach(el => { |
| | | findChildren(el!); |
| | | }); |
| | | this.elements = foundElements |
| | | } |
| | | |
| | | } else if (selector.startsWith('#')) { |
| | | const element = uni.getElementById(selector) |
| | | if (isElement(element!)) { |
| | | this.elements = [element] |
| | | } |
| | | } |
| | | return this; |
| | | } |
| | | /** |
| | | * 获取当前元素集合的直接子元素 |
| | | */ |
| | | children() : Query { |
| | | // if (this.elements.length > 0) { |
| | | // const children = this.elements.reduce((acc, el) => [...acc, ...Array.from(el.children)], []); |
| | | // this.elements = children; |
| | | // } |
| | | return this; |
| | | } |
| | | /** |
| | | * 获取当前元素集合的父元素 |
| | | */ |
| | | parent() : Query { |
| | | // if (this.elements.length > 0) { |
| | | // const parents = this.elements.map(el => el.parentElement).filter(parent => parent !== null) as ElementArray; |
| | | // this.elements = parents |
| | | // // this.elements = Array.from(new Set(parents)); |
| | | // } |
| | | return this; |
| | | } |
| | | /** |
| | | * 获取当前元素集合的兄弟元素 |
| | | */ |
| | | siblings() : Query { |
| | | // if (this.elements.length > 0) { |
| | | // const siblings = this.elements.reduce((acc, el) => [...acc, ...Array.from(el.parentElement?.children || [])], []); |
| | | // this.elements = siblings.filter(sibling => sibling !== null && !this.elements?.includes(sibling)); |
| | | // } |
| | | return this; |
| | | } |
| | | /** |
| | | * 获取当前元素集合的下一个兄弟元素 |
| | | */ |
| | | next() : Query { |
| | | // if (this.elements.length > 0) { |
| | | // const nextElements = this.elements.map(el => el.nextElementSibling).filter(next => next !== null) as ElementArray; |
| | | // this.elements = nextElements; |
| | | // } |
| | | return this; |
| | | } |
| | | /** |
| | | * 获取当前元素集合的上一个兄弟元素 |
| | | */ |
| | | prev() : Query { |
| | | // if (this.elements.length > 0) { |
| | | // const prevElements = this.elements.map(el => el.previousElementSibling).filter(prev => prev !== null) as ElementArray; |
| | | // this.elements = prevElements; |
| | | // } |
| | | return this; |
| | | } |
| | | /** |
| | | * 从当前元素开始向上查找匹配的元素 |
| | | */ |
| | | closest(selector : string) : Query { |
| | | if (isDef(this.context)) { |
| | | // && this.context.$parent != null && this.context.$parent.$el !== null |
| | | if(this.elements.length == 0 && isDef(this.context?.$parent) && isElement(this.context!.$parent?.$el)){ |
| | | this.elements = [this.context!.$parent?.$el!] |
| | | } |
| | | |
| | | const selectorsArray = selector.split(',') |
| | | // const { selectorValue, hasSelector } = processSelector(selector) |
| | | const processedSelectors = selectorsArray.map((selector: string):ProcessSelectorResult => processSelector(selector)) |
| | | const closestElements = this.elements.map((el) : UniElement | null => { |
| | | let closestElement : UniElement | null = el |
| | | while (closestElement !== null) { |
| | | // if (hasSelector(selectorValue, closestElement)) { |
| | | // break; |
| | | // } |
| | | const isMatchingSelector = processedSelectors.some(({selectorValue, hasSelector}):boolean => { |
| | | return hasSelector(selectorValue, closestElement!) |
| | | }) |
| | | if(isMatchingSelector){ |
| | | break; |
| | | } |
| | | closestElement = closestElement.parentElement; |
| | | } |
| | | return closestElement |
| | | }) |
| | | this.elements = closestElements.filter((closest : UniElement | null) : boolean => isDef(closest))// as ElementArray |
| | | |
| | | } |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 从当前元素集合中过滤出匹配的元素 |
| | | */ |
| | | filter() : Query { |
| | | |
| | | return this; |
| | | } |
| | | /** |
| | | * 从当前元素集合中排除匹配的元素 |
| | | */ |
| | | not() { } |
| | | /** |
| | | * 从当前元素集合中查找包含匹配元素的元素 |
| | | */ |
| | | has() { } |
| | | /** |
| | | * 获取当前元素集合的第一个 |
| | | */ |
| | | first() : Query { |
| | | if (this.elements.length > 0) { |
| | | // this.elements = [this.elements[0]]; |
| | | } |
| | | return this; |
| | | } |
| | | /** |
| | | * 最后一个元素 |
| | | */ |
| | | last() : Query { |
| | | if (this.elements.length > 0) { |
| | | // this.elements = [this.elements[this.elements.length - 1]]; |
| | | } |
| | | return this; |
| | | } |
| | | /** |
| | | * 获取当前元素在其兄弟元素中的索引 |
| | | */ |
| | | index() : number | null { |
| | | // if (this.elements.length > 0 && this.elements.length > 0 && this.elements[0].parentElement !== null) { |
| | | // return Array.from(this.elements[0].parentElement.children).indexOf(this.elements[0]); |
| | | // } |
| | | return null; |
| | | } |
| | | get(index : number) : UniElement | null { |
| | | if (this.elements.length > index) { |
| | | return this.elements[index] //as Element |
| | | } |
| | | return null |
| | | } |
| | | } |
| | | |
| | | export function selectElement(selector : string | null = null) : Query { |
| | | // if(typeof selector == 'string' || selector == null){ |
| | | // return new Query(selector as string | null, null) |
| | | // } |
| | | // else if(selector instanceof ComponentPublicInstance){ |
| | | // return new Query(null, selector) |
| | | // } |
| | | return new Query(selector, null) |
| | | } |
| | | |
| | | // $('xxx').in(this).find('xxx') |
| | | // $('xxx').in(this).get() |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 延迟指定时间后解析的 Promise |
| | | * @param delay 延迟的时间(以毫秒为单位),默认为 300 毫秒 |
| | | * @returns 一个 Promise,在延迟结束后解析 |
| | | */ |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | function sleep(delay: number = 300):Promise<boolean> { |
| | | return new Promise((resolve):void => {setTimeout(() => {resolve(true)}, delay)}); |
| | | } |
| | | export { |
| | | sleep |
| | | } |
| | | |
| | | // #endif |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export const sleep = (delay: number = 300) => |
| | | new Promise(resolve => setTimeout(resolve, delay)); |
| | | |
| | | // #endif |
| | | |
| | | // 示例 |
| | | // async function example() { |
| | | // console.log("Start"); |
| | | |
| | | // // 延迟 1 秒后执行 |
| | | // await sleep(1000); |
| | | // console.log("1 second later"); |
| | | |
| | | // // 延迟 500 毫秒后执行 |
| | | // await sleep(500); |
| | | // console.log("500 milliseconds later"); |
| | | |
| | | // // 延迟 2 秒后执行 |
| | | // await sleep(2000); |
| | | // console.log("2 seconds later"); |
| | | |
| | | // console.log("End"); |
| | | // } |
| | | |
| | | // example(); |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 节流函数,用于限制函数的调用频率 |
| | | * @param fn 要进行节流的函数 |
| | | * @param delay 两次调用之间的最小间隔时间 |
| | | * @returns 节流后的函数 |
| | | */ |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export function throttle(fn: (...args: any[]) => void, delay: number) { |
| | | let flag = true; // 标记是否可以执行函数 |
| | | |
| | | return (...args: any[]) => { |
| | | if (flag) { |
| | | flag = false; // 设置为不可执行状态 |
| | | fn(...args); // 执行传入的函数 |
| | | |
| | | setTimeout(() => { |
| | | flag = true; // 经过指定时间后,设置为可执行状态 |
| | | }, delay); |
| | | } |
| | | }; |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | // type Rfun = (...args: any[]) => void |
| | | // type Rfun = (...args: any[]) => void |
| | | |
| | | export function throttle<T extends any|null>( |
| | | fn: (args : T) => void, |
| | | delay: number):(args : T) => void { |
| | | let flag = true; // 标记是否可以执行函数 |
| | | |
| | | return (args : T) =>{ |
| | | if(flag){ |
| | | flag = false; |
| | | fn(args); |
| | | |
| | | setTimeout(()=>{ |
| | | flag = true; |
| | | }, delay) |
| | | } |
| | | } |
| | | // return (...args: any[]) => { |
| | | // // if (flag) { |
| | | // // flag = false; // 设置为不可执行状态 |
| | | // // fn(...args); // 执行传入的函数 |
| | | |
| | | // // setTimeout(() => { |
| | | // // flag = true; // 经过指定时间后,设置为可执行状态 |
| | | // // }, delay); |
| | | // // } |
| | | // }; |
| | | } |
| | | |
| | | // #endif |
| | | |
| | | // // 示例 |
| | | // // 定义一个被节流的函数 |
| | | // function handleScroll() { |
| | | // console.log("Scroll event handled!"); |
| | | // } |
| | | |
| | | // // 使用节流函数对 handleScroll 进行节流,间隔时间为 500 毫秒 |
| | | // const throttledScroll = throttle(handleScroll, 500); |
| | | |
| | | // // 模拟多次调用 handleScroll |
| | | // throttledScroll(); // 输出 "Scroll event handled!" |
| | | // throttledScroll(); // 不会输出 |
| | | // throttledScroll(); // 不会输出 |
| | | |
| | | // // 经过 500 毫秒后,再次调用 handleScroll |
| | | // setTimeout(() => { |
| | | // throttledScroll(); // 输出 "Scroll event handled!" |
| | | // }, 500); |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 将一个或多个元素转换为数组 |
| | | * @param item 要转换为数组的元素 |
| | | * @returns 转换后的数组 |
| | | */ |
| | | // #ifndef UNI-APP-X && APP |
| | | export const toArray = <T>(item: T | T[]): T[] => Array.isArray(item) ? item : [item]; |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | export function toArray<T extends any>(item: any): T[] { |
| | | return Array.isArray(item) ? item as T[] : [item as T]// as T[] |
| | | }; |
| | | // #endif |
| | | // 示例 |
| | | // console.log(toArray(5)); // 输出: [5] |
| | | // console.log(toArray("hello")); // 输出: ["hello"] |
| | | // console.log(toArray([1, 2, 3])); // 输出: [1, 2, 3] |
| | | // console.log(toArray(["apple", "banana"])); // 输出: ["apple", "banana"] |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import { isNumber } from '../isNumber' |
| | | import { isString } from '../isString' |
| | | // 函数重载,定义多个函数签名 |
| | | // function toBoolean(value : any) : boolean; |
| | | // function toBoolean(value : string) : boolean; |
| | | // function toBoolean(value : number) : boolean; |
| | | // function toBoolean(value : boolean) : boolean; |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | function toBoolean(value : any | null) : boolean { |
| | | // 根据输入值的类型,返回相应的布尔值 |
| | | // if (isNumber(value)) { |
| | | // return (value as number) != 0; |
| | | // } |
| | | // if (isString(value)) { |
| | | // return `${value}`.length > 0; |
| | | // } |
| | | // if (typeof value == 'boolean') { |
| | | // return value as boolean; |
| | | // } |
| | | // #ifdef APP-IOS |
| | | return value != null && value != undefined |
| | | // #endif |
| | | // #ifdef APP-ANDROID |
| | | return value != null |
| | | // #endif |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | function toBoolean(value : any | null) : value is NonNullable<typeof value> { |
| | | return !!value//value !== null && value !== undefined; |
| | | } |
| | | // #endif |
| | | |
| | | export { |
| | | toBoolean |
| | | } |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | /** |
| | | * 将字符串转换为数字 |
| | | * @param val 要转换的字符串 |
| | | * @returns 转换后的数字或原始字符串 |
| | | */ |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | // function toNumber(val: string): number |
| | | // function toNumber(val: string): string |
| | | function toNumber(val: string): number|null { |
| | | const n = parseFloat(val); // 使用 parseFloat 函数将字符串转换为浮点数 |
| | | return isNaN(n) ? null : n; // 使用 isNaN 函数判断是否为非数字,返回转换后的数字或原始字符串 |
| | | } |
| | | export {toNumber} |
| | | // #endif |
| | | |
| | | // #ifndef UNI-APP-X && APP |
| | | export function toNumber(val: string): number | string { |
| | | const n = parseFloat(val); // 使用 parseFloat 函数将字符串转换为浮点数 |
| | | return isNaN(n) ? val : n; // 使用 isNaN 函数判断是否为非数字,返回转换后的数字或原始字符串 |
| | | } |
| | | // #endif |
| | | |
| | | // 示例 |
| | | // console.log(toNumber("123")); // 输出: 123 |
| | | // console.log(toNumber("3.14")); // 输出: 3.14 |
| | | // console.log(toNumber("hello")); // 输出: "hello" |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | import { isString } from '../isString' |
| | | import { isNumeric } from '../isNumeric' |
| | | |
| | | /** |
| | | * 单位转换函数,将字符串数字或带有单位的字符串转换为数字 |
| | | * @param value 要转换的值,可以是字符串数字或带有单位的字符串 |
| | | * @returns 转换后的数字,如果无法转换则返回0 |
| | | */ |
| | | // #ifndef UNI-APP-X && APP |
| | | export function unitConvert(value : string | number, base: number = 0) : number { |
| | | // 如果是字符串数字 |
| | | if (isNumeric(value)) { |
| | | return Number(value); |
| | | } |
| | | // 如果有单位 |
| | | if (isString(value)) { |
| | | const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g; |
| | | const results = reg.exec(value); |
| | | if (!value || !results) { |
| | | return 0; |
| | | } |
| | | const unit = results[3]; |
| | | const _value = parseFloat(value); |
| | | if (unit === 'rpx') { |
| | | return uni.upx2px(_value); |
| | | } |
| | | if (unit === 'px') { |
| | | return _value * 1; |
| | | } |
| | | if(unit == '%') { |
| | | return _value / 100 * base |
| | | } |
| | | // 如果是其他单位,可以继续添加对应的转换逻辑 |
| | | } |
| | | return 0; |
| | | } |
| | | // #endif |
| | | |
| | | |
| | | // #ifdef UNI-APP-X && APP |
| | | import { isNumber } from '../isNumber' |
| | | export function unitConvert(value : any | null, base: number = 0) : number { |
| | | if (isNumber(value)) { |
| | | return value as number |
| | | } |
| | | // 如果是字符串数字 |
| | | if (isNumeric(value)) { |
| | | return parseFloat(value as string); |
| | | } |
| | | // 如果有单位 |
| | | if (isString(value)) { |
| | | const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g; |
| | | const results = reg.exec(value as string); |
| | | if (results == null) { |
| | | return 0; |
| | | } |
| | | const unit = results[3]; |
| | | const _value = parseFloat(value); |
| | | if (unit == 'rpx') { |
| | | const { windowWidth } = uni.getWindowInfo() |
| | | return windowWidth / 750 * _value; |
| | | } |
| | | if (unit == 'px') { |
| | | return _value; |
| | | } |
| | | if(unit == '%') { |
| | | return _value / 100 * base |
| | | } |
| | | // 如果是其他单位,可以继续添加对应的转换逻辑 |
| | | } |
| | | return 0; |
| | | } |
| | | // #endif |
| | | // 示例 |
| | | // console.log(unitConvert("123")); // 输出: 123 (字符串数字转换为数字) |
| | | // console.log(unitConvert("3.14em")); // 输出: 0 (无法识别的单位) |
| | | // console.log(unitConvert("20rpx")); // 输出: 根据具体情况而定 (根据单位进行转换) |
| | | // console.log(unitConvert(10)); // 输出: 10 (数字不需要转换) |
对比新文件 |
| | |
| | | // @ts-nocheck |
| | | |
| | | // #ifdef VUE3 |
| | | export * from 'vue'; |
| | | // #endif |
| | | |
| | | // #ifndef VUE3 |
| | | export * from '@vue/composition-api'; |
| | | |
| | | // #ifdef APP-NVUE |
| | | import Vue from 'vue' |
| | | import VueCompositionAPI from '@vue/composition-api' |
| | | Vue.use(VueCompositionAPI) |
| | | // #endif |
| | | |
| | | // #endif |
对比新文件 |
| | |
| | | ## 1.2.2(2023-01-28) |
| | | - 修复 运行/打包 控制台警告问题 |
| | | ## 1.2.1(2022-09-05) |
| | | - 修复 当 text 超过 max-num 时,badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473) |
| | | ## 1.2.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge) |
| | | ## 1.1.7(2021-11-08) |
| | | - 优化 升级ui |
| | | - 修改 size 属性默认值调整为 small |
| | | - 修改 type 属性,默认值调整为 error,info 替换 default |
| | | ## 1.1.6(2021-09-22) |
| | | - 修复 在字节小程序上样式不生效的 bug |
| | | ## 1.1.5(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.1.4(2021-07-29) |
| | | - 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性 |
| | | ## 1.1.3(2021-06-24) |
| | | - 优化 示例项目 |
| | | ## 1.1.1(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.1.0(2021-05-12) |
| | | - 新增 uni-badge 的 absolute 属性,支持定位 |
| | | - 新增 uni-badge 的 offset 属性,支持定位偏移 |
| | | - 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点 |
| | | - 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+ |
| | | - 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式 |
| | | ## 1.0.7(2021-05-07) |
| | | - 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug |
| | | - 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug |
| | | - 新增 uni-badge 属性 custom-style, 支持自定义样式 |
| | | ## 1.0.6(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-badge--x"> |
| | | <slot /> |
| | | <text v-if="text" :class="classNames" :style="[positionStyle, customStyle, dotStyle]" |
| | | class="uni-badge" @click="onClick()">{{displayValue}}</text> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Badge 数字角标 |
| | | * @description 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=21 |
| | | * @property {String} text 角标内容 |
| | | * @property {String} size = [normal|small] 角标内容 |
| | | * @property {String} type = [info|primary|success|warning|error] 颜色类型 |
| | | * @value info 灰色 |
| | | * @value primary 蓝色 |
| | | * @value success 绿色 |
| | | * @value warning 黄色 |
| | | * @value error 红色 |
| | | * @property {String} inverted = [true|false] 是否无需背景颜色 |
| | | * @property {Number} maxNum 展示封顶的数字值,超过 99 显示 99+ |
| | | * @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上 |
| | | * @value rightTop 右上 |
| | | * @value rightBottom 右下 |
| | | * @value leftTop 左上 |
| | | * @value leftBottom 左下 |
| | | * @property {Array[number]} offset 距定位角中心点的偏移量,只有存在 absolute 属性时有效,例如:[-10, -10] 表示向外偏移 10px,[10, 10] 表示向 absolute 指定的内偏移 10px |
| | | * @property {String} isDot = [true|false] 是否显示为一个小点 |
| | | * @event {Function} click 点击 Badge 触发事件 |
| | | * @example <uni-badge text="1"></uni-badge> |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniBadge', |
| | | emits: ['click'], |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: 'error' |
| | | }, |
| | | inverted: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | isDot: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | maxNum: { |
| | | type: Number, |
| | | default: 99 |
| | | }, |
| | | absolute: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | offset: { |
| | | type: Array, |
| | | default () { |
| | | return [0, 0] |
| | | } |
| | | }, |
| | | text: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | size: { |
| | | type: String, |
| | | default: 'small' |
| | | }, |
| | | customStyle: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return {}; |
| | | }, |
| | | computed: { |
| | | width() { |
| | | return String(this.text).length * 8 + 12 |
| | | }, |
| | | classNames() { |
| | | const { |
| | | inverted, |
| | | type, |
| | | size, |
| | | absolute |
| | | } = this |
| | | return [ |
| | | inverted ? 'uni-badge--' + type + '-inverted' : '', |
| | | 'uni-badge--' + type, |
| | | 'uni-badge--' + size, |
| | | absolute ? 'uni-badge--absolute' : '' |
| | | ].join(' ') |
| | | }, |
| | | positionStyle() { |
| | | if (!this.absolute) return {} |
| | | let w = this.width / 2, |
| | | h = 10 |
| | | if (this.isDot) { |
| | | w = 5 |
| | | h = 5 |
| | | } |
| | | const x = `${- w + this.offset[0]}px` |
| | | const y = `${- h + this.offset[1]}px` |
| | | |
| | | const whiteList = { |
| | | rightTop: { |
| | | right: x, |
| | | top: y |
| | | }, |
| | | rightBottom: { |
| | | right: x, |
| | | bottom: y |
| | | }, |
| | | leftBottom: { |
| | | left: x, |
| | | bottom: y |
| | | }, |
| | | leftTop: { |
| | | left: x, |
| | | top: y |
| | | } |
| | | } |
| | | const match = whiteList[this.absolute] |
| | | return match ? match : whiteList['rightTop'] |
| | | }, |
| | | dotStyle() { |
| | | if (!this.isDot) return {} |
| | | return { |
| | | width: '10px', |
| | | minWidth: '0', |
| | | height: '10px', |
| | | padding: '0', |
| | | borderRadius: '10px' |
| | | } |
| | | }, |
| | | displayValue() { |
| | | const { |
| | | isDot, |
| | | text, |
| | | maxNum |
| | | } = this |
| | | return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text) |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick() { |
| | | this.$emit('click'); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | $uni-primary: #2979ff !default; |
| | | $uni-success: #4cd964 !default; |
| | | $uni-warning: #f0ad4e !default; |
| | | $uni-error: #dd524d !default; |
| | | $uni-info: #909399 !default; |
| | | |
| | | |
| | | $bage-size: 12px; |
| | | $bage-small: scale(0.8); |
| | | |
| | | .uni-badge--x { |
| | | /* #ifdef APP-NVUE */ |
| | | // align-self: flex-start; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | display: inline-block; |
| | | /* #endif */ |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-badge--absolute { |
| | | position: absolute; |
| | | } |
| | | |
| | | .uni-badge--small { |
| | | transform: $bage-small; |
| | | transform-origin: center center; |
| | | } |
| | | |
| | | .uni-badge { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | overflow: hidden; |
| | | box-sizing: border-box; |
| | | font-feature-settings: "tnum"; |
| | | min-width: 20px; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | flex-direction: row; |
| | | height: 20px; |
| | | padding: 0 4px; |
| | | line-height: 18px; |
| | | color: #fff; |
| | | border-radius: 100px; |
| | | background-color: $uni-info; |
| | | background-color: transparent; |
| | | border: 1px solid #fff; |
| | | text-align: center; |
| | | font-family: 'Helvetica Neue', Helvetica, sans-serif; |
| | | font-size: $bage-size; |
| | | /* #ifdef H5 */ |
| | | z-index: 999; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | |
| | | &--info { |
| | | color: #fff; |
| | | background-color: $uni-info; |
| | | } |
| | | |
| | | &--primary { |
| | | background-color: $uni-primary; |
| | | } |
| | | |
| | | &--success { |
| | | background-color: $uni-success; |
| | | } |
| | | |
| | | &--warning { |
| | | background-color: $uni-warning; |
| | | } |
| | | |
| | | &--error { |
| | | background-color: $uni-error; |
| | | } |
| | | |
| | | &--inverted { |
| | | padding: 0 5px 0 0; |
| | | color: $uni-info; |
| | | } |
| | | |
| | | &--info-inverted { |
| | | color: $uni-info; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | &--primary-inverted { |
| | | color: $uni-primary; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | &--success-inverted { |
| | | color: $uni-success; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | &--warning-inverted { |
| | | color: $uni-warning; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | &--error-inverted { |
| | | color: $uni-error; |
| | | background-color: transparent; |
| | | } |
| | | |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | { |
| | | "id": "uni-badge", |
| | | "displayName": "uni-badge 数字角标", |
| | | "version": "1.2.2", |
| | | "description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。", |
| | | "keywords": [ |
| | | "", |
| | | "badge", |
| | | "uni-ui", |
| | | "uniui", |
| | | "数字角标", |
| | | "徽章" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-scss"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "y", |
| | | "联盟": "y" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | ## Badge 数字角标 |
| | | > **组件名:uni-badge** |
| | | > 代码块: `uBadge` |
| | | |
| | | 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景, |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-badge) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
| | | |
| | | |
对比新文件 |
| | |
| | | ## 1.4.12(2024-09-21) |
| | | - 修复 calendar在选择日期范围后重新选择日期需要点两次的Bug |
| | | ## 1.4.11(2024-01-10) |
| | | - 修复 回到今天时,月份显示不一致问题 |
| | | ## 1.4.10(2023-04-10) |
| | | - 修复 某些情况 monthSwitch 未触发的Bug |
| | | ## 1.4.9(2023-02-02) |
| | | - 修复 某些情况切换月份错误的Bug |
| | | ## 1.4.8(2023-01-30) |
| | | - 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/161964) |
| | | ## 1.4.7(2022-09-16) |
| | | - 优化 支持使用 uni-scss 控制主题色 |
| | | ## 1.4.6(2022-09-08) |
| | | - 修复 表头年月切换,导致改变当前日期为选择月1号,且未触发change事件的Bug |
| | | ## 1.4.5(2022-02-25) |
| | | - 修复 条件编译 nvue 不支持的 css 样式的Bug |
| | | ## 1.4.4(2022-02-25) |
| | | - 修复 条件编译 nvue 不支持的 css 样式的Bug |
| | | ## 1.4.3(2021-09-22) |
| | | - 修复 startDate、 endDate 属性失效的Bug |
| | | ## 1.4.2(2021-08-24) |
| | | - 新增 支持国际化 |
| | | ## 1.4.1(2021-08-05) |
| | | - 修复 弹出层被 tabbar 遮盖的Bug |
| | | ## 1.4.0(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.3.16(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.3.15(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
对比新文件 |
| | |
| | | /** |
| | | * @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'], |
| | | |
| | | /** |
| | | * 数字转中文速查表 |
| | | * @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 |
| | | case 30: |
| | | s = '\u4e09\u5341'; 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] |
| | | } |
| | | // 日柱 当月一日与 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': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), '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 } |
| | | }, |
| | | |
| | | /** |
| | | * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历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 |
对比新文件 |
| | |
| | | { |
| | | "uni-calender.ok": "ok", |
| | | "uni-calender.cancel": "cancel", |
| | | "uni-calender.today": "today", |
| | | "uni-calender.MON": "MON", |
| | | "uni-calender.TUE": "TUE", |
| | | "uni-calender.WED": "WED", |
| | | "uni-calender.THU": "THU", |
| | | "uni-calender.FRI": "FRI", |
| | | "uni-calender.SAT": "SAT", |
| | | "uni-calender.SUN": "SUN" |
| | | } |
对比新文件 |
| | |
| | | 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 |
| | | } |
对比新文件 |
| | |
| | | { |
| | | "uni-calender.ok": "确定", |
| | | "uni-calender.cancel": "取消", |
| | | "uni-calender.today": "今日", |
| | | "uni-calender.SUN": "日", |
| | | "uni-calender.MON": "一", |
| | | "uni-calender.TUE": "二", |
| | | "uni-calender.WED": "三", |
| | | "uni-calender.THU": "四", |
| | | "uni-calender.FRI": "五", |
| | | "uni-calender.SAT": "六" |
| | | } |
对比新文件 |
| | |
| | | { |
| | | "uni-calender.ok": "確定", |
| | | "uni-calender.cancel": "取消", |
| | | "uni-calender.today": "今日", |
| | | "uni-calender.SUN": "日", |
| | | "uni-calender.MON": "一", |
| | | "uni-calender.TUE": "二", |
| | | "uni-calender.WED": "三", |
| | | "uni-calender.THU": "四", |
| | | "uni-calender.FRI": "五", |
| | | "uni-calender.SAT": "六" |
| | | } |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-calendar-item__weeks-box" :class="{ |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
| | | 'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) , |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | }" |
| | | @click="choiceDate(weeks)"> |
| | | <view class="uni-calendar-item__weeks-box-item"> |
| | | <text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text> |
| | | <text class="uni-calendar-item__weeks-box-text" :class="{ |
| | | 'uni-calendar-item--isDay-text': weeks.isDay, |
| | | 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
| | | 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | }">{{weeks.date}}</text> |
| | | <text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{ |
| | | 'uni-calendar-item--isDay-text':weeks.isDay, |
| | | 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
| | | 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | }">{{todayText}}</text> |
| | | <text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{ |
| | | 'uni-calendar-item--isDay-text':weeks.isDay, |
| | | 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
| | | 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | }">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text> |
| | | <text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{ |
| | | 'uni-calendar-item--extra':weeks.extraInfo.info, |
| | | 'uni-calendar-item--isDay-text':weeks.isDay, |
| | | 'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
| | | 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | }">{{weeks.extraInfo.info}}</text> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { initVueI18n } from '@dcloudio/uni-i18n' |
| | | import i18nMessages from './i18n/index.js' |
| | | const { t } = initVueI18n(i18nMessages) |
| | | |
| | | export default { |
| | | emits:['change'], |
| | | props: { |
| | | weeks: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | calendar: { |
| | | type: Object, |
| | | default: () => { |
| | | return {} |
| | | } |
| | | }, |
| | | selected: { |
| | | type: Array, |
| | | default: () => { |
| | | return [] |
| | | } |
| | | }, |
| | | lunar: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | computed: { |
| | | todayText() { |
| | | return t("uni-calender.today") |
| | | }, |
| | | }, |
| | | methods: { |
| | | choiceDate(weeks) { |
| | | this.$emit('change', weeks) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $uni-font-size-base:14px; |
| | | $uni-text-color:#333; |
| | | $uni-font-size-sm:12px; |
| | | $uni-color-error: #e43d33; |
| | | $uni-opacity-disabled: 0.3; |
| | | $uni-text-color-disable:#c0c0c0; |
| | | $uni-primary: #2979ff !default; |
| | | .uni-calendar-item__weeks-box { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box-text { |
| | | font-size: $uni-font-size-base; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-lunar-text { |
| | | font-size: $uni-font-size-sm; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box-item { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box-circle { |
| | | position: absolute; |
| | | top: 5px; |
| | | right: 5px; |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 8px; |
| | | background-color: $uni-color-error; |
| | | |
| | | } |
| | | |
| | | .uni-calendar-item--disable { |
| | | background-color: rgba(249, 249, 249, $uni-opacity-disabled); |
| | | color: $uni-text-color-disable; |
| | | } |
| | | |
| | | .uni-calendar-item--isDay-text { |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | .uni-calendar-item--isDay { |
| | | background-color: $uni-primary; |
| | | opacity: 0.8; |
| | | color: #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--extra { |
| | | color: $uni-color-error; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .uni-calendar-item--checked { |
| | | background-color: $uni-primary; |
| | | color: #fff; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .uni-calendar-item--multiple { |
| | | background-color: $uni-primary; |
| | | color: #fff; |
| | | opacity: 0.8; |
| | | } |
| | | .uni-calendar-item--before-checked { |
| | | background-color: #ff5a5f; |
| | | color: #fff; |
| | | } |
| | | .uni-calendar-item--after-checked { |
| | | background-color: #ff5a5f; |
| | | color: #fff; |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-calendar"> |
| | | <view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view> |
| | | <view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}"> |
| | | <view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top"> |
| | | <view class="uni-calendar__header-btn-box" @click="close"> |
| | | <text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__header-btn-box" @click="confirm"> |
| | | <text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text> |
| | | </view> |
| | | </view> |
| | | <view class="uni-calendar__header"> |
| | | <view class="uni-calendar__header-btn-box" @click.stop="pre"> |
| | | <view class="uni-calendar__header-btn uni-calendar--left"></view> |
| | | </view> |
| | | <picker mode="date" :value="date" fields="month" @change="bindDateChange"> |
| | | <text class="uni-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text> |
| | | </picker> |
| | | <view class="uni-calendar__header-btn-box" @click.stop="next"> |
| | | <view class="uni-calendar__header-btn uni-calendar--right"></view> |
| | | </view> |
| | | <text class="uni-calendar__backtoday" @click="backToday">{{todayText}}</text> |
| | | |
| | | </view> |
| | | <view class="uni-calendar__box"> |
| | | <view v-if="showMonth" class="uni-calendar__box-bg"> |
| | | <text class="uni-calendar__box-bg-text">{{nowDate.month}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks"> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{SUNText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{monText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{TUEText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{WEDText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{THUText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{FRIText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{SATText}}</text> |
| | | </view> |
| | | </view> |
| | | <view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex"> |
| | | <view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex"> |
| | | <calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import Calendar from './util.js'; |
| | | import CalendarItem from './uni-calendar-item.vue' |
| | | |
| | | import { initVueI18n } from '@dcloudio/uni-i18n' |
| | | import i18nMessages from './i18n/index.js' |
| | | const { t } = initVueI18n(i18nMessages) |
| | | |
| | | /** |
| | | * Calendar 日历 |
| | | * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=56 |
| | | * @property {String} date 自定义当前时间,默认为今天 |
| | | * @property {Boolean} lunar 显示农历 |
| | | * @property {String} startDate 日期选择范围-开始日期 |
| | | * @property {String} endDate 日期选择范围-结束日期 |
| | | * @property {Boolean} range 范围选择 |
| | | * @property {Boolean} insert = [true|false] 插入模式,默认为false |
| | | * @value true 弹窗模式 |
| | | * @value false 插入模式 |
| | | * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容 |
| | | * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
| | | * @property {Boolean} showMonth 是否选择月份为背景 |
| | | * @event {Function} change 日期改变,`insert :ture` 时生效 |
| | | * @event {Function} confirm 确认选择`insert :false` 时生效 |
| | | * @event {Function} monthSwitch 切换月份时触发 |
| | | * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" /> |
| | | */ |
| | | export default { |
| | | components: { |
| | | CalendarItem |
| | | }, |
| | | emits:['close','confirm','change','monthSwitch'], |
| | | props: { |
| | | date: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | selected: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | lunar: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | startDate: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | endDate: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | range: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | insert: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showMonth: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | clearDate: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | show: false, |
| | | weeks: [], |
| | | calendar: {}, |
| | | nowDate: '', |
| | | aniMaskShow: false |
| | | } |
| | | }, |
| | | computed:{ |
| | | /** |
| | | * for i18n |
| | | */ |
| | | |
| | | okText() { |
| | | return t("uni-calender.ok") |
| | | }, |
| | | cancelText() { |
| | | return t("uni-calender.cancel") |
| | | }, |
| | | todayText() { |
| | | return t("uni-calender.today") |
| | | }, |
| | | monText() { |
| | | return t("uni-calender.MON") |
| | | }, |
| | | TUEText() { |
| | | return t("uni-calender.TUE") |
| | | }, |
| | | WEDText() { |
| | | return t("uni-calender.WED") |
| | | }, |
| | | THUText() { |
| | | return t("uni-calender.THU") |
| | | }, |
| | | FRIText() { |
| | | return t("uni-calender.FRI") |
| | | }, |
| | | SATText() { |
| | | return t("uni-calender.SAT") |
| | | }, |
| | | SUNText() { |
| | | return t("uni-calender.SUN") |
| | | }, |
| | | }, |
| | | watch: { |
| | | date(newVal) { |
| | | // this.cale.setDate(newVal) |
| | | this.init(newVal) |
| | | }, |
| | | startDate(val){ |
| | | this.cale.resetSatrtDate(val) |
| | | this.cale.setDate(this.nowDate.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | }, |
| | | endDate(val){ |
| | | this.cale.resetEndDate(val) |
| | | this.cale.setDate(this.nowDate.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | }, |
| | | selected(newVal) { |
| | | this.cale.setSelectInfo(this.nowDate.fullDate, newVal) |
| | | this.weeks = this.cale.weeks |
| | | } |
| | | }, |
| | | created() { |
| | | this.cale = new Calendar({ |
| | | selected: this.selected, |
| | | startDate: this.startDate, |
| | | endDate: this.endDate, |
| | | range: this.range, |
| | | }) |
| | | this.init(this.date) |
| | | }, |
| | | methods: { |
| | | // 取消穿透 |
| | | clean() {}, |
| | | bindDateChange(e) { |
| | | const value = e.detail.value + '-1' |
| | | this.setDate(value) |
| | | |
| | | const { year,month } = this.cale.getDate(value) |
| | | this.$emit('monthSwitch', { |
| | | year, |
| | | month |
| | | }) |
| | | }, |
| | | /** |
| | | * 初始化日期显示 |
| | | * @param {Object} date |
| | | */ |
| | | init(date) { |
| | | this.cale.setDate(date) |
| | | this.weeks = this.cale.weeks |
| | | this.nowDate = this.calendar = this.cale.getInfo(date) |
| | | }, |
| | | /** |
| | | * 打开日历弹窗 |
| | | */ |
| | | open() { |
| | | // 弹窗模式并且清理数据 |
| | | if (this.clearDate && !this.insert) { |
| | | this.cale.cleanMultipleStatus() |
| | | // this.cale.setDate(this.date) |
| | | this.init(this.date) |
| | | } |
| | | this.show = true |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | this.aniMaskShow = true |
| | | }, 50) |
| | | }) |
| | | }, |
| | | /** |
| | | * 关闭日历弹窗 |
| | | */ |
| | | close() { |
| | | this.aniMaskShow = false |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | this.show = false |
| | | this.$emit('close') |
| | | }, 300) |
| | | }) |
| | | }, |
| | | /** |
| | | * 确认按钮 |
| | | */ |
| | | confirm() { |
| | | this.setEmit('confirm') |
| | | this.close() |
| | | }, |
| | | /** |
| | | * 变化触发 |
| | | */ |
| | | change() { |
| | | if (!this.insert) return |
| | | this.setEmit('change') |
| | | }, |
| | | /** |
| | | * 选择月份触发 |
| | | */ |
| | | monthSwitch() { |
| | | let { |
| | | year, |
| | | month |
| | | } = this.nowDate |
| | | this.$emit('monthSwitch', { |
| | | year, |
| | | month: Number(month) |
| | | }) |
| | | }, |
| | | /** |
| | | * 派发事件 |
| | | * @param {Object} name |
| | | */ |
| | | setEmit(name) { |
| | | let { |
| | | year, |
| | | month, |
| | | date, |
| | | fullDate, |
| | | lunar, |
| | | extraInfo |
| | | } = this.calendar |
| | | this.$emit(name, { |
| | | range: this.cale.multipleStatus, |
| | | year, |
| | | month, |
| | | date, |
| | | fulldate: fullDate, |
| | | lunar, |
| | | extraInfo: extraInfo || {} |
| | | }) |
| | | }, |
| | | /** |
| | | * 选择天触发 |
| | | * @param {Object} weeks |
| | | */ |
| | | choiceDate(weeks) { |
| | | if (weeks.disable) return |
| | | this.calendar = weeks |
| | | // 设置多选 |
| | | this.cale.setMultiple(this.calendar.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | this.change() |
| | | }, |
| | | /** |
| | | * 回到今天 |
| | | */ |
| | | backToday() { |
| | | const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}` |
| | | const date = this.cale.getDate(new Date()) |
| | | const todayYearMonth = `${date.year}-${date.month}` |
| | | |
| | | this.init(date.fullDate) |
| | | |
| | | if(nowYearMonth !== todayYearMonth) { |
| | | this.monthSwitch() |
| | | } |
| | | |
| | | this.change() |
| | | }, |
| | | /** |
| | | * 上个月 |
| | | */ |
| | | pre() { |
| | | const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate |
| | | this.setDate(preDate) |
| | | this.monthSwitch() |
| | | |
| | | }, |
| | | /** |
| | | * 下个月 |
| | | */ |
| | | next() { |
| | | const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate |
| | | this.setDate(nextDate) |
| | | this.monthSwitch() |
| | | }, |
| | | /** |
| | | * 设置日期 |
| | | * @param {Object} date |
| | | */ |
| | | setDate(date) { |
| | | this.cale.setDate(date) |
| | | this.weeks = this.cale.weeks |
| | | this.nowDate = this.cale.getInfo(date) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | $uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4); |
| | | $uni-border-color: #EDEDED; |
| | | $uni-text-color: #333; |
| | | $uni-bg-color-hover:#f1f1f1; |
| | | $uni-font-size-base:14px; |
| | | $uni-text-color-placeholder: #808080; |
| | | $uni-color-subtitle: #555555; |
| | | $uni-text-color-grey:#999; |
| | | .uni-calendar { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-calendar__mask { |
| | | position: fixed; |
| | | bottom: 0; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background-color: $uni-bg-color-mask; |
| | | transition-property: opacity; |
| | | transition-duration: 0.3s; |
| | | opacity: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | z-index: 99; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-calendar--mask-show { |
| | | opacity: 1 |
| | | } |
| | | |
| | | .uni-calendar--fixed { |
| | | position: fixed; |
| | | /* #ifdef APP-NVUE */ |
| | | bottom: 0; |
| | | /* #endif */ |
| | | left: 0; |
| | | right: 0; |
| | | transition-property: transform; |
| | | transition-duration: 0.3s; |
| | | transform: translateY(460px); |
| | | /* #ifndef APP-NVUE */ |
| | | bottom: calc(var(--window-bottom)); |
| | | z-index: 99; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-calendar--ani-show { |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | .uni-calendar__content { |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .uni-calendar__header { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 50px; |
| | | border-bottom-color: $uni-border-color; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar--fixed-top { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | border-top-color: $uni-border-color; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar--fixed-width { |
| | | width: 50px; |
| | | } |
| | | |
| | | .uni-calendar__backtoday { |
| | | position: absolute; |
| | | right: 0; |
| | | top: 25rpx; |
| | | padding: 0 5px; |
| | | padding-left: 10px; |
| | | height: 25px; |
| | | line-height: 25px; |
| | | font-size: 12px; |
| | | border-top-left-radius: 25px; |
| | | border-bottom-left-radius: 25px; |
| | | color: $uni-text-color; |
| | | background-color: $uni-bg-color-hover; |
| | | } |
| | | |
| | | .uni-calendar__header-text { |
| | | text-align: center; |
| | | width: 100px; |
| | | font-size: $uni-font-size-base; |
| | | color: $uni-text-color; |
| | | } |
| | | |
| | | .uni-calendar__header-btn-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 50px; |
| | | height: 50px; |
| | | } |
| | | |
| | | .uni-calendar__header-btn { |
| | | width: 10px; |
| | | height: 10px; |
| | | border-left-color: $uni-text-color-placeholder; |
| | | border-left-style: solid; |
| | | border-left-width: 2px; |
| | | border-top-color: $uni-color-subtitle; |
| | | border-top-style: solid; |
| | | border-top-width: 2px; |
| | | } |
| | | |
| | | .uni-calendar--left { |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .uni-calendar--right { |
| | | transform: rotate(135deg); |
| | | } |
| | | |
| | | |
| | | .uni-calendar__weeks { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-calendar__weeks-item { |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-calendar__weeks-day { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 45px; |
| | | border-bottom-color: #F5F5F5; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar__weeks-day-text { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-calendar__box { |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-calendar__box-bg { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | } |
| | | |
| | | .uni-calendar__box-bg-text { |
| | | font-size: 200px; |
| | | font-weight: bold; |
| | | color: $uni-text-color-grey; |
| | | opacity: 0.1; |
| | | text-align: center; |
| | | /* #ifndef APP-NVUE */ |
| | | line-height: 1; |
| | | /* #endif */ |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | import CALENDAR from './calendar.js' |
| | | |
| | | class Calendar { |
| | | constructor({ |
| | | date, |
| | | selected, |
| | | startDate, |
| | | endDate, |
| | | range |
| | | } = {}) { |
| | | // 当前日期 |
| | | this.date = this.getDate(new Date()) // 当前初入日期 |
| | | // 打点信息 |
| | | this.selected = selected || []; |
| | | // 范围开始 |
| | | this.startDate = startDate |
| | | // 范围结束 |
| | | this.endDate = endDate |
| | | this.range = range |
| | | // 多选状态 |
| | | this.cleanMultipleStatus() |
| | | // 每周日期 |
| | | this.weeks = {} |
| | | // this._getWeek(this.date.fullDate) |
| | | } |
| | | /** |
| | | * 设置日期 |
| | | * @param {Object} date |
| | | */ |
| | | setDate(date) { |
| | | this.selectDate = this.getDate(date) |
| | | this._getWeek(this.selectDate.fullDate) |
| | | } |
| | | |
| | | /** |
| | | * 清理多选状态 |
| | | */ |
| | | cleanMultipleStatus() { |
| | | this.multipleStatus = { |
| | | before: '', |
| | | after: '', |
| | | data: [] |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 重置开始日期 |
| | | */ |
| | | resetSatrtDate(startDate) { |
| | | // 范围开始 |
| | | this.startDate = startDate |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 重置结束日期 |
| | | */ |
| | | resetEndDate(endDate) { |
| | | // 范围结束 |
| | | this.endDate = endDate |
| | | } |
| | | |
| | | /** |
| | | * 获取任意时间 |
| | | */ |
| | | 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 '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, |
| | | lunar: this.getlunar(full.year, full.month - 1, beforeDate), |
| | | disable: true |
| | | }) |
| | | } |
| | | return dateArr |
| | | } |
| | | /** |
| | | * 获取本月天数 |
| | | */ |
| | | _currentMonthDys(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) |
| | | // 是否今天 |
| | | let isDay = 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) { |
| | | // let dateCompBefore = this.dateCompare(this.startDate, fullDate) |
| | | // disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate) |
| | | disableBefore = this.dateCompare(this.startDate, nowDate) |
| | | } |
| | | |
| | | if (this.endDate) { |
| | | // let dateCompAfter = this.dateCompare(fullDate, this.endDate) |
| | | // disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate) |
| | | disableAfter = this.dateCompare(nowDate, this.endDate) |
| | | } |
| | | let multiples = this.multipleStatus.data |
| | | let checked = false |
| | | let multiplesStatus = -1 |
| | | if (this.range) { |
| | | if (multiples) { |
| | | multiplesStatus = multiples.findIndex((item) => { |
| | | return this.dateEqual(item, nowDate) |
| | | }) |
| | | } |
| | | if (multiplesStatus !== -1) { |
| | | checked = true |
| | | } |
| | | } |
| | | let data = { |
| | | fullDate: nowDate, |
| | | year: full.year, |
| | | date: i, |
| | | multiple: this.range ? checked : false, |
| | | beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate), |
| | | afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate), |
| | | month: full.month, |
| | | lunar: this.getlunar(full.year, full.month, i), |
| | | disable: !(disableBefore && disableAfter), |
| | | isDay |
| | | } |
| | | if (info) { |
| | | data.extraInfo = info |
| | | } |
| | | |
| | | dateArr.push(data) |
| | | } |
| | | 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 |
| | | } |
| | | |
| | | /** |
| | | * 获取当前日期详情 |
| | | * @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 |
| | | */ |
| | | geDateAll(begin, end) { |
| | | var arr = [] |
| | | var ab = begin.split('-') |
| | | var ae = end.split('-') |
| | | var db = new Date() |
| | | db.setFullYear(ab[0], ab[1] - 1, ab[2]) |
| | | var de = new Date() |
| | | de.setFullYear(ae[0], ae[1] - 1, ae[2]) |
| | | var unixDb = db.getTime() - 24 * 60 * 60 * 1000 |
| | | var unixDe = de.getTime() - 24 * 60 * 60 * 1000 |
| | | for (var k = unixDb; k <= unixDe;) { |
| | | k = k + 24 * 60 * 60 * 1000 |
| | | arr.push(this.getDate(new Date(parseInt(k))).fullDate) |
| | | } |
| | | return arr |
| | | } |
| | | /** |
| | | * 计算阴历日期显示 |
| | | */ |
| | | getlunar(year, month, date) { |
| | | return CALENDAR.solar2lunar(year, month, date) |
| | | } |
| | | /** |
| | | * 设置打点 |
| | | */ |
| | | setSelectInfo(data, value) { |
| | | this.selected = value |
| | | this._getWeek(data) |
| | | } |
| | | |
| | | /** |
| | | * 获取多选状态 |
| | | */ |
| | | setMultiple(fullDate) { |
| | | let { |
| | | before, |
| | | after |
| | | } = this.multipleStatus |
| | | |
| | | if (!this.range) return |
| | | if (before && after) { |
| | | this.multipleStatus.before = fullDate |
| | | this.multipleStatus.after = '' |
| | | this.multipleStatus.data = [] |
| | | } else { |
| | | if (!before) { |
| | | this.multipleStatus.before = fullDate |
| | | } else { |
| | | this.multipleStatus.after = fullDate |
| | | if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after); |
| | | } else { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before); |
| | | } |
| | | } |
| | | } |
| | | this._getWeek(fullDate) |
| | | } |
| | | |
| | | /** |
| | | * 获取每周数据 |
| | | * @param {Object} dateData |
| | | */ |
| | | _getWeek(dateData) { |
| | | const { |
| | | year, |
| | | month |
| | | } = this.getDate(dateData) |
| | | let firstDay = new Date(year, month - 1, 1).getDay() |
| | | let currentDay = new Date(year, month, 0).getDate() |
| | | let dates = { |
| | | lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天 |
| | | currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数 |
| | | nextMonthDays: [], // 下个月开始几天 |
| | | weeks: [] |
| | | } |
| | | let canlender = [] |
| | | const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length) |
| | | dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData)) |
| | | canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays) |
| | | let weeks = {} |
| | | // 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天 |
| | | 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] |
| | | } |
| | | this.canlender = canlender |
| | | this.weeks = weeks |
| | | } |
| | | |
| | | //静态方法 |
| | | // static init(date) { |
| | | // if (!this.instance) { |
| | | // this.instance = new Calendar(date); |
| | | // } |
| | | // return this.instance; |
| | | // } |
| | | } |
| | | |
| | | |
| | | export default Calendar |
对比新文件 |
| | |
| | | { |
| | | "id": "uni-calendar", |
| | | "displayName": "uni-calendar 日历", |
| | | "version": "1.4.12", |
| | | "description": "日历组件", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "日历", |
| | | "", |
| | | "打卡", |
| | | "日历选择" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y", |
| | | "alipay": "n" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | |
| | | |
| | | ## Calendar 日历 |
| | | > **组件名:uni-calendar** |
| | | > 代码块: `uCalendar` |
| | | |
| | | |
| | | 日历组件 |
| | | |
| | | > **注意事项** |
| | | > 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 |
| | | > - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js) |
| | | > - 仅支持自定义组件模式 |
| | | > - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date() |
| | | > - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意 |
| | | > - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动 |
| | | |
| | | |
| | | ### 安装方式 |
| | | |
| | | 本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 |
| | | |
| | | 如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) |
| | | |
| | | ### 基本用法 |
| | | |
| | | 在 ``template`` 中使用组件 |
| | | |
| | | ```html |
| | | <view> |
| | | <uni-calendar |
| | | :insert="true" |
| | | :lunar="true" |
| | | :start-date="'2019-3-2'" |
| | | :end-date="'2019-5-20'" |
| | | @change="change" |
| | | /> |
| | | </view> |
| | | ``` |
| | | |
| | | ### 通过方法打开日历 |
| | | |
| | | 需要设置 `insert` 为 `false` |
| | | |
| | | ```html |
| | | <view> |
| | | <uni-calendar |
| | | ref="calendar" |
| | | :insert="false" |
| | | @confirm="confirm" |
| | | /> |
| | | <button @click="open">打开日历</button> |
| | | </view> |
| | | ``` |
| | | |
| | | ```javascript |
| | | |
| | | export default { |
| | | data() { |
| | | return {}; |
| | | }, |
| | | methods: { |
| | | open(){ |
| | | this.$refs.calendar.open(); |
| | | }, |
| | | confirm(e) { |
| | | console.log(e); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | ``` |
| | | |
| | | |
| | | ## API |
| | | |
| | | ### Calendar Props |
| | | |
| | | | 属性名 | 类型 | 默认值| 说明 | |
| | | | - | - | - | - | |
| | | | date | String |- | 自定义当前时间,默认为今天 | |
| | | | lunar | Boolean | false | 显示农历 | |
| | | | startDate | String |- | 日期选择范围-开始日期 | |
| | | | endDate | String |- | 日期选择范围-结束日期 | |
| | | | range | Boolean | false | 范围选择 | |
| | | | insert | Boolean | false | 插入模式,可选值,ture:插入模式;false:弹窗模式;默认为插入模式 | |
| | | |clearDate |Boolean |true |弹窗模式是否清空上次选择内容 | |
| | | | selected | Array |- | 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] | |
| | | |showMonth | Boolean | true | 是否显示月份为背景 | |
| | | |
| | | ### Calendar Events |
| | | |
| | | | 事件名 | 说明 |返回值| |
| | | | - | - | - | |
| | | | open | 弹出日历组件,`insert :false` 时生效|- | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | ## 组件示例 |
| | | |
| | | 点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar) |
对比新文件 |
| | |
| | | ## 1.3.1(2021-12-20) |
| | | - 修复 在vue页面下略缩图显示不正常的bug |
| | | ## 1.3.0(2021-11-19) |
| | | - 重构插槽的用法 ,header 替换为 title |
| | | - 新增 actions 插槽 |
| | | - 新增 cover 封面图属性和插槽 |
| | | - 新增 padding 内容默认内边距离 |
| | | - 新增 margin 卡片默认外边距离 |
| | | - 新增 spacing 卡片默认内边距 |
| | | - 新增 shadow 卡片阴影属性 |
| | | - 取消 mode 属性,可使用组合插槽代替 |
| | | - 取消 note 属性 ,使用actions插槽代替 |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-card](https://uniapp.dcloud.io/component/uniui/uni-card) |
| | | ## 1.2.1(2021-07-30) |
| | | - 优化 vue3下事件警告的问题 |
| | | ## 1.2.0(2021-07-13) |
| | | - 组件兼容 vue3,如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.1.8(2021-07-01) |
| | | - 优化 图文卡片无图片加载时,提供占位图标 |
| | | - 新增 header 插槽,自定义卡片头部( 图文卡片 mode="style" 时,不支持) |
| | | - 修复 thumbnail 不存在仍然占位的 bug |
| | | ## 1.1.7(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.1.6(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-card" :class="{ 'uni-card--full': isFull, 'uni-card--shadow': isShadow,'uni-card--border':border}" |
| | | :style="{'margin':isFull?0:margin,'padding':spacing,'box-shadow':isShadow?shadow:''}"> |
| | | <!-- 封面 --> |
| | | <slot name="cover"> |
| | | <view v-if="cover" class="uni-card__cover"> |
| | | <image class="uni-card__cover-image" mode="widthFix" @click="onClick('cover')" :src="cover"></image> |
| | | </view> |
| | | </slot> |
| | | <slot name="title"> |
| | | <view v-if="title || extra" class="uni-card__header"> |
| | | <!-- 卡片标题 --> |
| | | <view class="uni-card__header-box" @click="onClick('title')"> |
| | | <view v-if="thumbnail" class="uni-card__header-avatar"> |
| | | <image class="uni-card__header-avatar-image" :src="thumbnail" mode="aspectFit" /> |
| | | </view> |
| | | <view class="uni-card__header-content"> |
| | | <text class="uni-card__header-content-title uni-ellipsis">{{ title }}</text> |
| | | <text v-if="title&&subTitle" |
| | | class="uni-card__header-content-subtitle uni-ellipsis">{{ subTitle }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="uni-card__header-extra" @click="onClick('extra')"> |
| | | <text class="uni-card__header-extra-text">{{ extra }}</text> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | <!-- 卡片内容 --> |
| | | <view class="uni-card__content" :style="{padding:padding}" @click="onClick('content')"> |
| | | <slot></slot> |
| | | </view> |
| | | <view class="uni-card__actions" @click="onClick('actions')"> |
| | | <slot name="actions"></slot> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Card 卡片 |
| | | * @description 卡片视图组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=22 |
| | | * @property {String} title 标题文字 |
| | | * @property {String} subTitle 副标题 |
| | | * @property {Number} padding 内容内边距 |
| | | * @property {Number} margin 卡片外边距 |
| | | * @property {Number} spacing 卡片内边距 |
| | | * @property {String} extra 标题额外信息 |
| | | * @property {String} cover 封面图(本地路径需要引入) |
| | | * @property {String} thumbnail 标题左侧缩略图 |
| | | * @property {Boolean} is-full = [true | false] 卡片内容是否通栏,为 true 时将去除padding值 |
| | | * @property {Boolean} is-shadow = [true | false] 卡片内容是否开启阴影 |
| | | * @property {String} shadow 卡片阴影 |
| | | * @property {Boolean} border 卡片边框 |
| | | * @event {Function} click 点击 Card 触发事件 |
| | | */ |
| | | export default { |
| | | name: 'UniCard', |
| | | emits: ['click'], |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | subTitle: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | padding: { |
| | | type: String, |
| | | default: '10px' |
| | | }, |
| | | margin: { |
| | | type: String, |
| | | default: '15px' |
| | | }, |
| | | spacing: { |
| | | type: String, |
| | | default: '0 10px' |
| | | }, |
| | | extra: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | cover: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | thumbnail: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | isFull: { |
| | | // 内容区域是否通栏 |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | isShadow: { |
| | | // 是否开启阴影 |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | shadow: { |
| | | type: String, |
| | | default: '0px 0px 3px 1px rgba(0, 0, 0, 0.08)' |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | methods: { |
| | | onClick(type) { |
| | | this.$emit('click', type) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-border-3: #EBEEF5 !default; |
| | | $uni-shadow-base:0 0px 6px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default; |
| | | $uni-main-color: #3a3a3a !default; |
| | | $uni-base-color: #6a6a6a !default; |
| | | $uni-secondary-color: #909399 !default; |
| | | $uni-spacing-sm: 8px !default; |
| | | $uni-border-color:$uni-border-3; |
| | | $uni-shadow: $uni-shadow-base; |
| | | $uni-card-title: 15px; |
| | | $uni-cart-title-color:$uni-main-color; |
| | | $uni-card-subtitle: 12px; |
| | | $uni-cart-subtitle-color:$uni-secondary-color; |
| | | $uni-card-spacing: 10px; |
| | | $uni-card-content-color: $uni-base-color; |
| | | |
| | | .uni-card { |
| | | margin: $uni-card-spacing; |
| | | padding: 0 $uni-spacing-sm; |
| | | border-radius: 4px; |
| | | overflow: hidden; |
| | | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif; |
| | | background-color: #fff; |
| | | flex: 1; |
| | | |
| | | .uni-card__cover { |
| | | position: relative; |
| | | margin-top: $uni-card-spacing; |
| | | flex-direction: row; |
| | | overflow: hidden; |
| | | border-radius: 4px; |
| | | .uni-card__cover-image { |
| | | flex: 1; |
| | | // width: 100%; |
| | | /* #ifndef APP-PLUS */ |
| | | vertical-align: middle; |
| | | /* #endif */ |
| | | } |
| | | } |
| | | |
| | | .uni-card__header { |
| | | display: flex; |
| | | border-bottom: 1px $uni-border-color solid; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | padding: $uni-card-spacing; |
| | | overflow: hidden; |
| | | |
| | | .uni-card__header-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-card__header-avatar { |
| | | width: 40px; |
| | | height: 40px; |
| | | overflow: hidden; |
| | | border-radius: 5px; |
| | | margin-right: $uni-card-spacing; |
| | | .uni-card__header-avatar-image { |
| | | flex: 1; |
| | | width: 40px; |
| | | height: 40px; |
| | | } |
| | | } |
| | | |
| | | .uni-card__header-content { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | flex: 1; |
| | | // height: 40px; |
| | | overflow: hidden; |
| | | |
| | | .uni-card__header-content-title { |
| | | font-size: $uni-card-title; |
| | | color: $uni-cart-title-color; |
| | | // line-height: 22px; |
| | | } |
| | | |
| | | .uni-card__header-content-subtitle { |
| | | font-size: $uni-card-subtitle; |
| | | margin-top: 5px; |
| | | color: $uni-cart-subtitle-color; |
| | | } |
| | | } |
| | | |
| | | .uni-card__header-extra { |
| | | line-height: 12px; |
| | | |
| | | .uni-card__header-extra-text { |
| | | font-size: 12px; |
| | | color: $uni-cart-subtitle-color; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .uni-card__content { |
| | | padding: $uni-card-spacing; |
| | | font-size: 14px; |
| | | color: $uni-card-content-color; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .uni-card__actions { |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | |
| | | .uni-card--border { |
| | | border: 1px solid $uni-border-color; |
| | | } |
| | | |
| | | .uni-card--shadow { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | box-shadow: $uni-shadow; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-card--full { |
| | | margin: 0; |
| | | border-left-width: 0; |
| | | border-left-width: 0; |
| | | border-radius: 0; |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-card--full:after { |
| | | border-radius: 0; |
| | | } |
| | | |
| | | /* #endif */ |
| | | .uni-ellipsis { |
| | | /* #ifndef APP-NVUE */ |
| | | overflow: hidden; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 1; |
| | | /* #endif */ |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | { |
| | | "id": "uni-card", |
| | | "displayName": "uni-card 卡片", |
| | | "version": "1.3.1", |
| | | "description": "Card 组件,提供常见的卡片样式。", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "card", |
| | | "", |
| | | "卡片" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-icons", |
| | | "uni-scss" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | |
| | | |
| | | ## Card 卡片 |
| | | > **组件名:uni-card** |
| | | > 代码块: `uCard` |
| | | |
| | | 卡片视图组件。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-card) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
| | | |
| | | |
对比新文件 |
| | |
| | | ## 1.4.4(2024-03-20) |
| | | - 修复 titleBorder类型修正 |
| | | ## 1.4.3(2022-01-25) |
| | | - 修复 初始化的时候 ,open 属性失效的bug |
| | | ## 1.4.2(2022-01-21) |
| | | - 修复 微信小程序resize后组件收起的bug |
| | | ## 1.4.1(2021-11-22) |
| | | - 修复 vue3中个别scss变量无法找到的问题 |
| | | ## 1.4.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-collapse](https://uniapp.dcloud.io/component/uniui/uni-collapse) |
| | | ## 1.3.3(2021-08-17) |
| | | - 优化 show-arrow 属性默认为true |
| | | ## 1.3.2(2021-08-17) |
| | | - 新增 show-arrow 属性,控制是否显示右侧箭头 |
| | | ## 1.3.1(2021-07-30) |
| | | - 优化 vue3下小程序事件警告的问题 |
| | | ## 1.3.0(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.2.2(2021-07-21) |
| | | - 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug |
| | | ## 1.2.1(2021-07-21) |
| | | - 优化 组件示例 |
| | | ## 1.2.0(2021-07-21) |
| | | - 新增 组件折叠动画 |
| | | - 新增 value\v-model 属性 ,动态修改面板折叠状态 |
| | | - 新增 title 插槽 ,可定义面板标题 |
| | | - 新增 border 属性 ,显示隐藏面板内容分隔线 |
| | | - 新增 title-border 属性 ,显示隐藏面板标题分隔线 |
| | | - 修复 resize 方法失效的Bug |
| | | - 修复 change 事件返回参数不正确的Bug |
| | | - 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法 |
| | | ## 1.1.7(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.1.6(2021-02-05) |
| | | - 优化 组件引用关系,通过uni_modules引用组件 |
| | | ## 1.1.5(2021-02-05) |
| | | - 调整为uni_modules目录规范 |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-collapse-item"> |
| | | <!-- onClick(!isOpen) --> |
| | | <view @click="onClick(!isOpen)" class="uni-collapse-item__title" |
| | | :class="{'is-open':isOpen &&titleBorder === 'auto' ,'uni-collapse-item-border':titleBorder !== 'none'}"> |
| | | <view class="uni-collapse-item__title-wrap"> |
| | | <slot name="title"> |
| | | <view class="uni-collapse-item__title-box" :class="{'is-disabled':disabled}"> |
| | | <image v-if="thumb" :src="thumb" class="uni-collapse-item__title-img" /> |
| | | <text class="uni-collapse-item__title-text">{{ title }}</text> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | <view v-if="showArrow" |
| | | :class="{ 'uni-collapse-item__title-arrow-active': isOpen, 'uni-collapse-item--animation': showAnimation === true }" |
| | | class="uni-collapse-item__title-arrow"> |
| | | <uni-icons :color="disabled?'#ddd':'#bbb'" size="14" type="bottom" /> |
| | | </view> |
| | | </view> |
| | | <view class="uni-collapse-item__wrap" :class="{'is--transition':showAnimation}" |
| | | :style="{height: (isOpen?height:0) +'px'}"> |
| | | <view :id="elId" ref="collapse--hook" class="uni-collapse-item__wrap-content" |
| | | :class="{open:isheight,'uni-collapse-item--border':border&&isOpen}"> |
| | | <slot></slot> |
| | | </view> |
| | | </view> |
| | | |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | // #ifdef APP-NVUE |
| | | const dom = weex.requireModule('dom') |
| | | // #endif |
| | | /** |
| | | * CollapseItem 折叠面板子组件 |
| | | * @description 折叠面板子组件 |
| | | * @property {String} title 标题文字 |
| | | * @property {String} thumb 标题左侧缩略图 |
| | | * @property {String} name 唯一标志符 |
| | | * @property {Boolean} open = [true|false] 是否展开组件 |
| | | * @property {Boolean} titleBorder = [true|false] 是否显示标题分隔线 |
| | | * @property {String} border = ['auto'|'show'|'none'] 是否显示分隔线 |
| | | * @property {Boolean} disabled = [true|false] 是否展开面板 |
| | | * @property {Boolean} showAnimation = [true|false] 开启动画 |
| | | * @property {Boolean} showArrow = [true|false] 是否显示右侧箭头 |
| | | */ |
| | | export default { |
| | | name: 'uniCollapseItem', |
| | | props: { |
| | | // 列表标题 |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | name: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | // 是否禁用 |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // #ifdef APP-PLUS |
| | | // 是否显示动画,app 端默认不开启动画,卡顿严重 |
| | | showAnimation: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // #endif |
| | | // #ifndef APP-PLUS |
| | | // 是否显示动画 |
| | | showAnimation: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | // #endif |
| | | // 是否展开 |
| | | open: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 缩略图 |
| | | thumb: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | // 标题分隔线显示类型 |
| | | titleBorder: { |
| | | type: String, |
| | | default: 'auto' |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showArrow: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | // TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug |
| | | const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` |
| | | return { |
| | | isOpen: false, |
| | | isheight: null, |
| | | height: 0, |
| | | elId, |
| | | nameSync: 0 |
| | | } |
| | | }, |
| | | watch: { |
| | | open(val) { |
| | | this.isOpen = val |
| | | this.onClick(val, 'init') |
| | | } |
| | | }, |
| | | updated(e) { |
| | | this.$nextTick(() => { |
| | | this.init(true) |
| | | }) |
| | | }, |
| | | created() { |
| | | this.collapse = this.getCollapse() |
| | | this.oldHeight = 0 |
| | | this.onClick(this.open, 'init') |
| | | }, |
| | | // #ifndef VUE3 |
| | | // TODO vue2 |
| | | destroyed() { |
| | | if (this.__isUnmounted) return |
| | | this.uninstall() |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | // TODO vue3 |
| | | unmounted() { |
| | | this.__isUnmounted = true |
| | | this.uninstall() |
| | | }, |
| | | // #endif |
| | | mounted() { |
| | | if (!this.collapse) return |
| | | if (this.name !== '') { |
| | | this.nameSync = this.name |
| | | } else { |
| | | this.nameSync = this.collapse.childrens.length + '' |
| | | } |
| | | if (this.collapse.names.indexOf(this.nameSync) === -1) { |
| | | this.collapse.names.push(this.nameSync) |
| | | } else { |
| | | console.warn(`name 值 ${this.nameSync} 重复`); |
| | | } |
| | | if (this.collapse.childrens.indexOf(this) === -1) { |
| | | this.collapse.childrens.push(this) |
| | | } |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init(type) { |
| | | // #ifndef APP-NVUE |
| | | this.getCollapseHeight(type) |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | this.getNvueHwight(type) |
| | | // #endif |
| | | }, |
| | | uninstall() { |
| | | if (this.collapse) { |
| | | this.collapse.childrens.forEach((item, index) => { |
| | | if (item === this) { |
| | | this.collapse.childrens.splice(index, 1) |
| | | } |
| | | }) |
| | | this.collapse.names.forEach((item, index) => { |
| | | if (item === this.nameSync) { |
| | | this.collapse.names.splice(index, 1) |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | onClick(isOpen, type) { |
| | | if (this.disabled) return |
| | | this.isOpen = isOpen |
| | | if (this.isOpen && this.collapse) { |
| | | this.collapse.setAccordion(this) |
| | | } |
| | | if (type !== 'init') { |
| | | this.collapse.onChange(isOpen, this) |
| | | } |
| | | }, |
| | | getCollapseHeight(type, index = 0) { |
| | | const views = uni.createSelectorQuery().in(this) |
| | | views |
| | | .select(`#${this.elId}`) |
| | | .fields({ |
| | | size: true |
| | | }, data => { |
| | | // TODO 百度中可能获取不到节点信息 ,需要循环获取 |
| | | if (index >= 10) return |
| | | if (!data) { |
| | | index++ |
| | | this.getCollapseHeight(false, index) |
| | | return |
| | | } |
| | | // #ifdef APP-NVUE |
| | | this.height = data.height + 1 |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | this.height = data.height |
| | | // #endif |
| | | this.isheight = true |
| | | if (type) return |
| | | this.onClick(this.isOpen, 'init') |
| | | }) |
| | | .exec() |
| | | }, |
| | | getNvueHwight(type) { |
| | | const result = dom.getComponentRect(this.$refs['collapse--hook'], option => { |
| | | if (option && option.result && option.size) { |
| | | // #ifdef APP-NVUE |
| | | this.height = option.size.height + 1 |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | this.height = option.size.height |
| | | // #endif |
| | | this.isheight = true |
| | | if (type) return |
| | | this.onClick(this.open, 'init') |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getCollapse(name = 'uniCollapse') { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== name) { |
| | | parent = parent.$parent; |
| | | if (!parent) return false; |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .uni-collapse-item { |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | |
| | | /* #endif */ |
| | | &__title { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | width: 100%; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | transition: border-bottom-color .3s; |
| | | |
| | | // transition-property: border-bottom-color; |
| | | // transition-duration: 5s; |
| | | &-wrap { |
| | | width: 100%; |
| | | flex: 1; |
| | | |
| | | } |
| | | |
| | | &-box { |
| | | padding: 0 15px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | width: 100%; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | height: 48px; |
| | | line-height: 48px; |
| | | background-color: #fff; |
| | | color: #303133; |
| | | font-size: 13px; |
| | | font-weight: 500; |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | outline: none; |
| | | |
| | | /* #endif */ |
| | | &.is-disabled { |
| | | .uni-collapse-item__title-text { |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | &.uni-collapse-item-border { |
| | | border-bottom: 1px solid #ebeef5; |
| | | } |
| | | |
| | | &.is-open { |
| | | border-bottom-color: transparent; |
| | | } |
| | | |
| | | &-img { |
| | | height: 22px; |
| | | width: 22px; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | &-text { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | /* #ifndef APP-NVUE */ |
| | | white-space: nowrap; |
| | | color: inherit; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | lines: 1; |
| | | /* #endif */ |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | |
| | | &-arrow { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-right: 10px; |
| | | transform: rotate(0deg); |
| | | |
| | | &-active { |
| | | transform: rotate(-180deg); |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | &__wrap { |
| | | /* #ifndef APP-NVUE */ |
| | | will-change: height; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | background-color: #fff; |
| | | overflow: hidden; |
| | | position: relative; |
| | | height: 0; |
| | | |
| | | &.is--transition { |
| | | // transition: all 0.3s; |
| | | transition-property: height, border-bottom-width; |
| | | transition-duration: 0.3s; |
| | | /* #ifndef APP-NVUE */ |
| | | will-change: height; |
| | | /* #endif */ |
| | | } |
| | | |
| | | |
| | | |
| | | &-content { |
| | | position: absolute; |
| | | font-size: 13px; |
| | | color: #303133; |
| | | // transition: height 0.3s; |
| | | border-bottom-color: transparent; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 0; |
| | | |
| | | &.uni-collapse-item--border { |
| | | border-bottom-width: 1px; |
| | | border-bottom-color: red; |
| | | border-bottom-color: #ebeef5; |
| | | } |
| | | |
| | | &.open { |
| | | position: relative; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &--animation { |
| | | transition-property: transform; |
| | | transition-duration: 0.3s; |
| | | transition-timing-function: ease; |
| | | } |
| | | |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-collapse"> |
| | | <slot /> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | /** |
| | | * Collapse 折叠面板 |
| | | * @description 展示可以折叠 / 展开的内容区域 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=23 |
| | | * @property {String|Array} value 当前激活面板改变时触发(如果是手风琴模式,参数类型为string,否则为array) |
| | | * @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果 |
| | | * @event {Function} change 切换面板时触发,如果是手风琴模式,返回类型为string,否则为array |
| | | */ |
| | | export default { |
| | | name: 'uniCollapse', |
| | | emits:['change','activeItem','input','update:modelValue'], |
| | | props: { |
| | | value: { |
| | | type: [String, Array], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Array], |
| | | default: '' |
| | | }, |
| | | accordion: { |
| | | // 是否开启手风琴效果 |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | }, |
| | | data() { |
| | | return {} |
| | | }, |
| | | computed: { |
| | | // TODO 兼容 vue2 和 vue3 |
| | | dataValue() { |
| | | let value = (typeof this.value === 'string' && this.value === '') || |
| | | (Array.isArray(this.value) && this.value.length === 0) |
| | | let modelValue = (typeof this.modelValue === 'string' && this.modelValue === '') || |
| | | (Array.isArray(this.modelValue) && this.modelValue.length === 0) |
| | | if (value) { |
| | | return this.modelValue |
| | | } |
| | | if (modelValue) { |
| | | return this.value |
| | | } |
| | | |
| | | return this.value |
| | | } |
| | | }, |
| | | watch: { |
| | | dataValue(val) { |
| | | this.setOpen(val) |
| | | } |
| | | }, |
| | | created() { |
| | | this.childrens = [] |
| | | this.names = [] |
| | | }, |
| | | mounted() { |
| | | this.$nextTick(()=>{ |
| | | this.setOpen(this.dataValue) |
| | | }) |
| | | }, |
| | | methods: { |
| | | setOpen(val) { |
| | | let str = typeof val === 'string' |
| | | let arr = Array.isArray(val) |
| | | this.childrens.forEach((vm, index) => { |
| | | if (str) { |
| | | if (val === vm.nameSync) { |
| | | if (!this.accordion) { |
| | | console.warn('accordion 属性为 false ,v-model 类型应该为 array') |
| | | return |
| | | } |
| | | vm.isOpen = true |
| | | } |
| | | } |
| | | if (arr) { |
| | | val.forEach(v => { |
| | | if (v === vm.nameSync) { |
| | | if (this.accordion) { |
| | | console.warn('accordion 属性为 true ,v-model 类型应该为 string') |
| | | return |
| | | } |
| | | vm.isOpen = true |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | this.emit(val) |
| | | }, |
| | | setAccordion(self) { |
| | | if (!this.accordion) return |
| | | this.childrens.forEach((vm, index) => { |
| | | if (self !== vm) { |
| | | vm.isOpen = false |
| | | } |
| | | }) |
| | | }, |
| | | resize() { |
| | | this.childrens.forEach((vm, index) => { |
| | | // #ifndef APP-NVUE |
| | | vm.getCollapseHeight() |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | vm.getNvueHwight() |
| | | // #endif |
| | | }) |
| | | }, |
| | | onChange(isOpen, self) { |
| | | let activeItem = [] |
| | | |
| | | if (this.accordion) { |
| | | activeItem = isOpen ? self.nameSync : '' |
| | | } else { |
| | | this.childrens.forEach((vm, index) => { |
| | | if (vm.isOpen) { |
| | | activeItem.push(vm.nameSync) |
| | | } |
| | | }) |
| | | } |
| | | this.$emit('change', activeItem) |
| | | this.emit(activeItem) |
| | | }, |
| | | emit(val){ |
| | | this.$emit('input', val) |
| | | this.$emit('update:modelValue', val) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" > |
| | | .uni-collapse { |
| | | /* #ifndef APP-NVUE */ |
| | | width: 100%; |
| | | display: flex; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | flex: 1; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | background-color: #fff; |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | { |
| | | "id": "uni-collapse", |
| | | "displayName": "uni-collapse 折叠面板", |
| | | "version": "1.4.4", |
| | | "description": "Collapse 组件,可以折叠 / 展开的内容区域。", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "折叠", |
| | | "折叠面板", |
| | | "手风琴" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-scss", |
| | | "uni-icons" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | |
| | | |
| | | ## Collapse 折叠面板 |
| | | > **组件名:uni-collapse** |
| | | > 代码块: `uCollapse` |
| | | > 关联组件:`uni-collapse-item`、`uni-icons`。 |
| | | |
| | | |
| | | 折叠面板用来折叠/显示过长的内容或者是列表。通常是在多内容分类项使用,折叠不重要的内容,显示重要内容。点击可以展开折叠部分。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-collapse) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
对比新文件 |
| | |
| | | ## 1.0.2(2024-09-21) |
| | | - 新增 clearAble属性 |
| | | ## 1.0.1(2021-11-23) |
| | | - 优化 label、label-width 属性 |
| | | ## 1.0.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-combox](https://uniapp.dcloud.io/component/uniui/uni-combox) |
| | | ## 0.1.0(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 0.0.6(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 0.0.5(2021-04-21) |
| | | - 优化 添加依赖 uni-icons, 导入后自动下载依赖 |
| | | ## 0.0.4(2021-02-05) |
| | | - 优化 组件引用关系,通过uni_modules引用组件 |
| | | ## 0.0.3(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-combox" :class="border ? '' : 'uni-combox__no-border'"> |
| | | <view v-if="label" class="uni-combox__label" :style="labelStyle"> |
| | | <text>{{label}}</text> |
| | | </view> |
| | | <view class="uni-combox__input-box"> |
| | | <input class="uni-combox__input" type="text" :placeholder="placeholder" placeholder-class="uni-combox__input-plac" |
| | | v-model="inputVal" @input="onInput" @focus="onFocus" @blur="onBlur" /> |
| | | <uni-icons v-if="!inputVal || !clearAble" :type="showSelector? 'top' : 'bottom'" size="14" color="#999" @click="toggleSelector"> |
| | | </uni-icons> |
| | | <uni-icons v-if="inputVal && clearAble" type="clear" size="24" color="#999" @click="clean"> |
| | | </uni-icons> |
| | | </view> |
| | | <view class="uni-combox__selector" v-if="showSelector"> |
| | | <view class="uni-popper__arrow"></view> |
| | | <scroll-view scroll-y="true" class="uni-combox__selector-scroll"> |
| | | <view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0"> |
| | | <text>{{emptyTips}}</text> |
| | | </view> |
| | | <view class="uni-combox__selector-item" v-for="(item,index) in filterCandidates" :key="index" |
| | | @click="onSelectorClick(index)"> |
| | | <text>{{item}}</text> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * Combox 组合输入框 |
| | | * @description 组合输入框一般用于既可以输入也可以选择的场景 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=1261 |
| | | * @property {String} label 左侧文字 |
| | | * @property {String} labelWidth 左侧内容宽度 |
| | | * @property {String} placeholder 输入框占位符 |
| | | * @property {Array} candidates 候选项列表 |
| | | * @property {String} emptyTips 筛选结果为空时显示的文字 |
| | | * @property {String} value 组合框的值 |
| | | */ |
| | | export default { |
| | | name: 'uniCombox', |
| | | emits: ['input', 'update:modelValue'], |
| | | props: { |
| | | clearAble: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | label: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | labelWidth: { |
| | | type: String, |
| | | default: 'auto' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | candidates: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | emptyTips: { |
| | | type: String, |
| | | default: '无匹配项' |
| | | }, |
| | | // #ifndef VUE3 |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | modelValue: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | // #endif |
| | | }, |
| | | data() { |
| | | return { |
| | | showSelector: false, |
| | | inputVal: '' |
| | | } |
| | | }, |
| | | computed: { |
| | | labelStyle() { |
| | | if (this.labelWidth === 'auto') { |
| | | return "" |
| | | } |
| | | return `width: ${this.labelWidth}` |
| | | }, |
| | | filterCandidates() { |
| | | return this.candidates.filter((item) => { |
| | | return item.toString().indexOf(this.inputVal) > -1 |
| | | }) |
| | | }, |
| | | filterCandidatesLength() { |
| | | return this.filterCandidates.length |
| | | } |
| | | }, |
| | | watch: { |
| | | // #ifndef VUE3 |
| | | value: { |
| | | handler(newVal) { |
| | | this.inputVal = newVal |
| | | }, |
| | | immediate: true |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | modelValue: { |
| | | handler(newVal) { |
| | | this.inputVal = newVal |
| | | }, |
| | | immediate: true |
| | | }, |
| | | // #endif |
| | | }, |
| | | methods: { |
| | | toggleSelector() { |
| | | this.showSelector = !this.showSelector |
| | | }, |
| | | onFocus() { |
| | | this.showSelector = true |
| | | }, |
| | | onBlur() { |
| | | setTimeout(() => { |
| | | this.showSelector = false |
| | | }, 153) |
| | | }, |
| | | onSelectorClick(index) { |
| | | this.inputVal = this.filterCandidates[index] |
| | | this.showSelector = false |
| | | this.$emit('input', this.inputVal) |
| | | this.$emit('update:modelValue', this.inputVal) |
| | | }, |
| | | onInput() { |
| | | setTimeout(() => { |
| | | this.$emit('input', this.inputVal) |
| | | this.$emit('update:modelValue', this.inputVal) |
| | | }) |
| | | }, |
| | | clean() { |
| | | this.inputVal = '' |
| | | this.onInput() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .uni-combox { |
| | | font-size: 14px; |
| | | border: 1px solid #DCDFE6; |
| | | border-radius: 4px; |
| | | padding: 6px 10px; |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | // height: 40px; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | // border-bottom: solid 1px #DDDDDD; |
| | | } |
| | | |
| | | .uni-combox__label { |
| | | font-size: 16px; |
| | | line-height: 22px; |
| | | padding-right: 10px; |
| | | color: #999999; |
| | | } |
| | | |
| | | .uni-combox__input-box { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-combox__input { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | height: 22px; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .uni-combox__input-plac { |
| | | font-size: 14px; |
| | | color: #999; |
| | | } |
| | | |
| | | .uni-combox__selector { |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | position: absolute; |
| | | top: calc(100% + 12px); |
| | | left: 0; |
| | | width: 100%; |
| | | background-color: #FFFFFF; |
| | | border: 1px solid #EBEEF5; |
| | | border-radius: 6px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | z-index: 2; |
| | | padding: 4px 0; |
| | | } |
| | | |
| | | .uni-combox__selector-scroll { |
| | | /* #ifndef APP-NVUE */ |
| | | max-height: 200px; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-combox__selector-empty, |
| | | .uni-combox__selector-item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | line-height: 36px; |
| | | font-size: 14px; |
| | | text-align: center; |
| | | // border-bottom: solid 1px #DDDDDD; |
| | | padding: 0px 10px; |
| | | } |
| | | |
| | | .uni-combox__selector-item:hover { |
| | | background-color: #f9f9f9; |
| | | } |
| | | |
| | | .uni-combox__selector-empty:last-child, |
| | | .uni-combox__selector-item:last-child { |
| | | /* #ifndef APP-NVUE */ |
| | | border-bottom: none; |
| | | /* #endif */ |
| | | } |
| | | |
| | | // picker 弹出层通用的指示小三角 |
| | | .uni-popper__arrow, |
| | | .uni-popper__arrow::after { |
| | | position: absolute; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | border-color: transparent; |
| | | border-style: solid; |
| | | border-width: 6px; |
| | | } |
| | | |
| | | .uni-popper__arrow { |
| | | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
| | | top: -6px; |
| | | left: 10%; |
| | | margin-right: 3px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #EBEEF5; |
| | | } |
| | | |
| | | .uni-popper__arrow::after { |
| | | content: " "; |
| | | top: 1px; |
| | | margin-left: -6px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #fff; |
| | | } |
| | | |
| | | .uni-combox__no-border { |
| | | border: none; |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | { |
| | | "id": "uni-combox", |
| | | "displayName": "uni-combox 组合框", |
| | | "version": "1.0.2", |
| | | "description": "可以选择也可以输入的表单项 ", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "combox", |
| | | "组合框", |
| | | "select" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-scss", |
| | | "uni-icons" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y", |
| | | "alipay": "n" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "n" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | |
| | | |
| | | ## Combox 组合框 |
| | | > **组件名:uni-combox** |
| | | > 代码块: `uCombox` |
| | | |
| | | |
| | | 组合框组件。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-combox) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
对比新文件 |
| | |
| | | ## 1.2.4(2024-09-21) |
| | | - 新增 支持控制显示位数 默认显示2位 |
| | | ## 1.2.3(2024-02-20) |
| | | - 新增 支持控制小时,分钟的显隐:showHour showMinute |
| | | ## 1.2.2(2022-01-19) |
| | | - 修复 在微信小程序中样式不生效的bug |
| | | ## 1.2.1(2022-01-18) |
| | | - 新增 update 方法 ,在动态更新时间后,刷新组件 |
| | | ## 1.2.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-countdown](https://uniapp.dcloud.io/component/uniui/uni-countdown) |
| | | ## 1.1.3(2021-10-18) |
| | | - 重构 |
| | | - 新增 font-size 支持自定义字体大小 |
| | | ## 1.1.2(2021-08-24) |
| | | - 新增 支持国际化 |
| | | ## 1.1.1(2021-07-30) |
| | | - 优化 vue3下小程序事件警告的问题 |
| | | ## 1.1.0(2021-07-30) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 1.0.5(2021-06-18) |
| | | - 修复 uni-countdown 重复赋值跳两秒的 bug |
| | | ## 1.0.4(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 1.0.3(2021-05-08) |
| | | - 修复 uni-countdown 不能控制倒计时的 bug |
| | | ## 1.0.2(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
对比新文件 |
| | |
| | | { |
| | | "uni-countdown.day": "day", |
| | | "uni-countdown.h": "h", |
| | | "uni-countdown.m": "m", |
| | | "uni-countdown.s": "s" |
| | | } |
对比新文件 |
| | |
| | | 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 |
| | | } |
对比新文件 |
| | |
| | | { |
| | | "uni-countdown.day": "天", |
| | | "uni-countdown.h": "时", |
| | | "uni-countdown.m": "分", |
| | | "uni-countdown.s": "秒" |
| | | } |
对比新文件 |
| | |
| | | { |
| | | "uni-countdown.day": "天", |
| | | "uni-countdown.h": "時", |
| | | "uni-countdown.m": "分", |
| | | "uni-countdown.s": "秒" |
| | | } |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-countdown"> |
| | | <text v-if="showDay" :style="[timeStyle]" class="uni-countdown__number">{{ d }}</text> |
| | | <text v-if="showDay" :style="[splitorStyle]" class="uni-countdown__splitor">{{dayText}}</text> |
| | | <text v-if="showHour" :style="[timeStyle]" class="uni-countdown__number">{{ h }}</text> |
| | | <text v-if="showHour" :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : hourText }}</text> |
| | | <text v-if="showMinute" :style="[timeStyle]" class="uni-countdown__number">{{ i }}</text> |
| | | <text v-if="showMinute" :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : minuteText }}</text> |
| | | <text :style="[timeStyle]" class="uni-countdown__number">{{ s }}</text> |
| | | <text v-if="!showColon" :style="[splitorStyle]" class="uni-countdown__splitor">{{secondText}}</text> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | import { |
| | | initVueI18n |
| | | } from '@dcloudio/uni-i18n' |
| | | import messages from './i18n/index.js' |
| | | const { |
| | | t |
| | | } = initVueI18n(messages) |
| | | /** |
| | | * Countdown 倒计时 |
| | | * @description 倒计时组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=25 |
| | | * @property {String} backgroundColor 背景色 |
| | | * @property {String} color 文字颜色 |
| | | * @property {Number} day 天数 |
| | | * @property {Number} hour 小时 |
| | | * @property {Number} minute 分钟 |
| | | * @property {Number} second 秒 |
| | | * @property {Number} timestamp 时间戳 |
| | | * @property {Boolean} showDay = [true|false] 是否显示天数 |
| | | * @property {Boolean} showHour = [true|false] 是否显示小时 |
| | | * @property {Boolean} showMinute = [true|false] 是否显示分钟 |
| | | * @property {Boolean} show-colon = [true|false] 是否以冒号为分隔符 |
| | | * @property {String} splitorColor 分割符号颜色 |
| | | * @event {Function} timeup 倒计时时间到触发事件 |
| | | * @example <uni-countdown :day="1" :hour="1" :minute="12" :second="40"></uni-countdown> |
| | | */ |
| | | export default { |
| | | name: 'UniCountdown', |
| | | emits: ['timeup'], |
| | | props: { |
| | | showDay: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showHour: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showMinute: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showColon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | start: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | backgroundColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#333' |
| | | }, |
| | | fontSize: { |
| | | type: Number, |
| | | default: 14 |
| | | }, |
| | | splitorColor: { |
| | | type: String, |
| | | default: '#333' |
| | | }, |
| | | day: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | hour: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | minute: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | second: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | timestamp: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | filterShow : { |
| | | type:Object, |
| | | default:{} |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | timer: null, |
| | | syncFlag: false, |
| | | d: '00', |
| | | h: '00', |
| | | i: '00', |
| | | s: '00', |
| | | leftTime: 0, |
| | | seconds: 0 |
| | | } |
| | | }, |
| | | computed: { |
| | | dayText() { |
| | | return t("uni-countdown.day") |
| | | }, |
| | | hourText(val) { |
| | | return t("uni-countdown.h") |
| | | }, |
| | | minuteText(val) { |
| | | return t("uni-countdown.m") |
| | | }, |
| | | secondText(val) { |
| | | return t("uni-countdown.s") |
| | | }, |
| | | timeStyle() { |
| | | const { |
| | | color, |
| | | backgroundColor, |
| | | fontSize |
| | | } = this |
| | | return { |
| | | color, |
| | | backgroundColor, |
| | | fontSize: `${fontSize}px`, |
| | | width: `${fontSize * 22 / 14}px`, // 按字体大小为 14px 时的比例缩放 |
| | | lineHeight: `${fontSize * 20 / 14}px`, |
| | | borderRadius: `${fontSize * 3 / 14}px`, |
| | | } |
| | | }, |
| | | splitorStyle() { |
| | | const { splitorColor, fontSize, backgroundColor } = this |
| | | return { |
| | | color: splitorColor, |
| | | fontSize: `${fontSize * 12 / 14}px`, |
| | | margin: backgroundColor ? `${fontSize * 4 / 14}px` : '' |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | day(val) { |
| | | this.changeFlag() |
| | | }, |
| | | hour(val) { |
| | | this.changeFlag() |
| | | }, |
| | | minute(val) { |
| | | this.changeFlag() |
| | | }, |
| | | second(val) { |
| | | this.changeFlag() |
| | | }, |
| | | start: { |
| | | immediate: true, |
| | | handler(newVal, oldVal) { |
| | | if (newVal) { |
| | | this.startData(); |
| | | } else { |
| | | if (!oldVal) return |
| | | clearInterval(this.timer) |
| | | } |
| | | } |
| | | |
| | | } |
| | | }, |
| | | created: function(e) { |
| | | this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second) |
| | | this.countDown() |
| | | }, |
| | | // #ifndef VUE3 |
| | | destroyed() { |
| | | clearInterval(this.timer) |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | unmounted() { |
| | | clearInterval(this.timer) |
| | | }, |
| | | // #endif |
| | | methods: { |
| | | toSeconds(timestamp, day, hours, minutes, seconds) { |
| | | if (timestamp) { |
| | | return timestamp - parseInt(new Date().getTime() / 1000, 10) |
| | | } |
| | | return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds |
| | | }, |
| | | timeUp() { |
| | | clearInterval(this.timer) |
| | | this.$emit('timeup') |
| | | }, |
| | | countDown() { |
| | | let seconds = this.seconds |
| | | let [day, hour, minute, second] = [0, 0, 0, 0] |
| | | if (seconds > 0) { |
| | | day = Math.floor(seconds / (60 * 60 * 24)) |
| | | hour = Math.floor(seconds / (60 * 60)) - (day * 24) |
| | | minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60) |
| | | second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60) |
| | | } else { |
| | | this.timeUp() |
| | | } |
| | | this.d = String(day).padStart(this.validFilterShow(this.filterShow.d), '0') |
| | | this.h = String(hour).padStart(this.validFilterShow(this.filterShow.h), '0') |
| | | this.i = String(minute).padStart(this.validFilterShow(this.filterShow.m), '0') |
| | | this.s = String(second).padStart(this.validFilterShow(this.filterShow.s), '0') |
| | | }, |
| | | validFilterShow(filter){ |
| | | return (filter && filter > 0) ? filter : 2; |
| | | }, |
| | | startData() { |
| | | this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second) |
| | | if (this.seconds <= 0) { |
| | | this.seconds = this.toSeconds(0, 0, 0, 0, 0) |
| | | this.countDown() |
| | | return |
| | | } |
| | | clearInterval(this.timer) |
| | | this.countDown() |
| | | this.timer = setInterval(() => { |
| | | this.seconds-- |
| | | if (this.seconds < 0) { |
| | | this.timeUp() |
| | | return |
| | | } |
| | | this.countDown() |
| | | }, 1000) |
| | | }, |
| | | update(){ |
| | | this.startData(); |
| | | }, |
| | | changeFlag() { |
| | | if (!this.syncFlag) { |
| | | this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second) |
| | | this.startData(); |
| | | this.syncFlag = true; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | $font-size: 14px; |
| | | |
| | | .uni-countdown { |
| | | display: flex; |
| | | flex-direction: row; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | |
| | | &__splitor { |
| | | margin: 0 2px; |
| | | font-size: $font-size; |
| | | color: #333; |
| | | } |
| | | |
| | | &__number { |
| | | border-radius: 3px; |
| | | text-align: center; |
| | | font-size: $font-size; |
| | | } |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | { |
| | | "id": "uni-countdown", |
| | | "displayName": "uni-countdown 倒计时", |
| | | "version": "1.2.4", |
| | | "description": "CountDown 倒计时组件", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "countdown", |
| | | "倒计时" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-scss"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y", |
| | | "alipay": "n" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | |
| | | |
| | | ## CountDown 倒计时 |
| | | > **组件名:uni-countdown** |
| | | > 代码块: `uCountDown` |
| | | |
| | | 倒计时组件。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-countdown) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
对比新文件 |
| | |
| | | ## 1.0.5(2024-03-20) |
| | | - 修复 单选模式下选中样式不生效的bug |
| | | ## 1.0.4(2024-01-27) |
| | | - 修复 修复错别字chagne为change |
| | | ## 1.0.3(2022-09-16) |
| | | - 可以使用 uni-scss 控制主题色 |
| | | ## 1.0.2(2022-06-30) |
| | | - 优化 在 uni-forms 中的依赖注入方式 |
| | | ## 1.0.1(2022-02-07) |
| | | - 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug |
| | | ## 1.0.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-checkbox](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox) |
| | | ## 0.2.5(2021-08-23) |
| | | - 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题 |
| | | ## 0.2.4(2021-08-17) |
| | | - 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题 |
| | | ## 0.2.3(2021-08-11) |
| | | - 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 |
| | | ## 0.2.2(2021-07-30) |
| | | - 优化 在uni-forms组件,与label不对齐的问题 |
| | | ## 0.2.1(2021-07-27) |
| | | - 修复 单选默认值为0不能选中的Bug |
| | | ## 0.2.0(2021-07-13) |
| | | - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 0.1.11(2021-07-06) |
| | | - 优化 删除无用日志 |
| | | ## 0.1.10(2021-07-05) |
| | | - 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题 |
| | | ## 0.1.9(2021-07-05) |
| | | - 修复 nvue 黑框样式问题 |
| | | ## 0.1.8(2021-06-28) |
| | | - 修复 selectedTextColor 属性不生效的Bug |
| | | ## 0.1.7(2021-06-02) |
| | | - 新增 map 属性,可以方便映射text/value属性 |
| | | ## 0.1.6(2021-05-26) |
| | | - 修复 不关联服务空间的情况下组件报错的Bug |
| | | ## 0.1.5(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 0.1.4(2021-04-09) |
| | | - 修复 nvue 下无法选中的问题 |
| | | ## 0.1.3(2021-03-22) |
| | | - 新增 disabled属性 |
| | | ## 0.1.2(2021-02-24) |
| | | - 优化 默认颜色显示 |
| | | ## 0.1.1(2021-02-24) |
| | | - 新增 支持nvue |
| | | ## 0.1.0(2021-02-18) |
| | | - “暂无数据”显示居中 |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-data-checklist" :style="{'margin-top':isTop+'px'}"> |
| | | <template v-if="!isLocal"> |
| | | <view class="uni-data-loading"> |
| | | <uni-load-more v-if="!mixinDatacomErrorMessage" status="loading" iconType="snow" :iconSize="18" |
| | | :content-text="contentText"></uni-load-more> |
| | | <text v-else>{{mixinDatacomErrorMessage}}</text> |
| | | </view> |
| | | </template> |
| | | <template v-else> |
| | | <checkbox-group v-if="multiple" class="checklist-group" :class="{'is-list':mode==='list' || wrap}" |
| | | @change="change"> |
| | | <label class="checklist-box" |
| | | :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']" |
| | | :style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index"> |
| | | <checkbox class="hidden" hidden :disabled="disabled || !!item.disabled" :value="item[map.value]+''" |
| | | :checked="item.selected" /> |
| | | <view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" |
| | | class="checkbox__inner" :style="item.styleIcon"> |
| | | <view class="checkbox__inner-icon"></view> |
| | | </view> |
| | | <view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}"> |
| | | <text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text> |
| | | <view v-if="mode === 'list' && icon === 'right'" class="checkobx__list" :style="item.styleBackgroud"></view> |
| | | </view> |
| | | </label> |
| | | </checkbox-group> |
| | | <radio-group v-else class="checklist-group" :class="{'is-list':mode==='list','is-wrap':wrap}" @change="change"> |
| | | <label class="checklist-box" |
| | | :class="['is--'+mode,item.selected?'is-checked':'',(disabled || !!item.disabled)?'is-disable':'',index!==0&&mode==='list'?'is-list-border':'']" |
| | | :style="item.styleBackgroud" v-for="(item,index) in dataList" :key="index"> |
| | | <radio class="hidden" hidden :disabled="disabled || item.disabled" :value="item[map.value]+''" |
| | | :checked="item.selected" /> |
| | | <view v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')" class="radio__inner" |
| | | :style="item.styleBackgroud"> |
| | | <view class="radio__inner-icon" :style="item.styleIcon"></view> |
| | | </view> |
| | | <view class="checklist-content" :class="{'list-content':mode === 'list' && icon ==='left'}"> |
| | | <text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text> |
| | | <view v-if="mode === 'list' && icon === 'right'" :style="item.styleRightIcon" class="checkobx__list"></view> |
| | | </view> |
| | | </label> |
| | | </radio-group> |
| | | </template> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * DataChecklist 数据选择器 |
| | | * @description 通过数据渲染 checkbox 和 radio |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx |
| | | * @property {String} mode = [default| list | button | tag] 显示模式 |
| | | * @value default 默认横排模式 |
| | | * @value list 列表模式 |
| | | * @value button 按钮模式 |
| | | * @value tag 标签模式 |
| | | * @property {Boolean} multiple = [true|false] 是否多选 |
| | | * @property {Array|String|Number} value 默认值 |
| | | * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}] |
| | | * @property {Number|String} min 最小选择个数 ,multiple为true时生效 |
| | | * @property {Number|String} max 最大选择个数 ,multiple为true时生效 |
| | | * @property {Boolean} wrap 是否换行显示 |
| | | * @property {String} icon = [left|right] list 列表模式下icon显示位置 |
| | | * @property {Boolean} selectedColor 选中颜色 |
| | | * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效 |
| | | * @property {Boolean} selectedTextColor 选中文本颜色,如不填写则自动显示 |
| | | * @property {Object} map 字段映射, 默认 map={text:'text',value:'value'} |
| | | * @value left 左侧显示 |
| | | * @value right 右侧显示 |
| | | * @event {Function} change 选中发生变化触发 |
| | | */ |
| | | |
| | | export default { |
| | | name: 'uniDataChecklist', |
| | | mixins: [uniCloud.mixinDatacom || {}], |
| | | emits: ['input', 'update:modelValue', 'change'], |
| | | props: { |
| | | mode: { |
| | | type: String, |
| | | default: 'default' |
| | | }, |
| | | |
| | | multiple: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | value: { |
| | | type: [Array, String, Number], |
| | | default () { |
| | | return '' |
| | | } |
| | | }, |
| | | // TODO vue3 |
| | | modelValue: { |
| | | type: [Array, String, Number], |
| | | default () { |
| | | return ''; |
| | | } |
| | | }, |
| | | localdata: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | min: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | max: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | wrap: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | icon: { |
| | | type: String, |
| | | default: 'left' |
| | | }, |
| | | selectedColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | selectedTextColor: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | emptyText: { |
| | | type: String, |
| | | default: '暂无数据' |
| | | }, |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | map: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | text: 'text', |
| | | value: 'value' |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | localdata: { |
| | | handler(newVal) { |
| | | this.range = newVal |
| | | this.dataList = this.getDataList(this.getSelectedValue(newVal)) |
| | | }, |
| | | deep: true |
| | | }, |
| | | mixinDatacomResData(newVal) { |
| | | this.range = newVal |
| | | this.dataList = this.getDataList(this.getSelectedValue(newVal)) |
| | | }, |
| | | value(newVal) { |
| | | this.dataList = this.getDataList(newVal) |
| | | // fix by mehaotian is_reset 在 uni-forms 中定义 |
| | | // if(!this.is_reset){ |
| | | // this.is_reset = false |
| | | // this.formItem && this.formItem.setValue(newVal) |
| | | // } |
| | | }, |
| | | modelValue(newVal) { |
| | | this.dataList = this.getDataList(newVal); |
| | | // if(!this.is_reset){ |
| | | // this.is_reset = false |
| | | // this.formItem && this.formItem.setValue(newVal) |
| | | // } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | dataList: [], |
| | | range: [], |
| | | contentText: { |
| | | contentdown: '查看更多', |
| | | contentrefresh: '加载中', |
| | | contentnomore: '没有更多' |
| | | }, |
| | | isLocal: true, |
| | | styles: { |
| | | selectedColor: '#2979ff', |
| | | selectedTextColor: '#666', |
| | | }, |
| | | isTop: 0 |
| | | }; |
| | | }, |
| | | computed: { |
| | | dataValue() { |
| | | if (this.value === '') return this.modelValue |
| | | if (this.modelValue === '') return this.value |
| | | return this.value |
| | | } |
| | | }, |
| | | created() { |
| | | // this.form = this.getForm('uniForms') |
| | | // this.formItem = this.getForm('uniFormsItem') |
| | | // this.formItem && this.formItem.setValue(this.value) |
| | | |
| | | // if (this.formItem) { |
| | | // this.isTop = 6 |
| | | // if (this.formItem.name) { |
| | | // // 如果存在name添加默认值,否则formData 中不存在这个字段不校验 |
| | | // if(!this.is_reset){ |
| | | // this.is_reset = false |
| | | // this.formItem.setValue(this.dataValue) |
| | | // } |
| | | // this.rename = this.formItem.name |
| | | // this.form.inputChildrens.push(this) |
| | | // } |
| | | // } |
| | | |
| | | if (this.localdata && this.localdata.length !== 0) { |
| | | this.isLocal = true |
| | | this.range = this.localdata |
| | | this.dataList = this.getDataList(this.getSelectedValue(this.range)) |
| | | } else { |
| | | if (this.collection) { |
| | | this.isLocal = false |
| | | this.loadData() |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | loadData() { |
| | | this.mixinDatacomGet().then(res => { |
| | | this.mixinDatacomResData = res.result.data |
| | | if (this.mixinDatacomResData.length === 0) { |
| | | this.isLocal = false |
| | | this.mixinDatacomErrorMessage = this.emptyText |
| | | } else { |
| | | this.isLocal = true |
| | | } |
| | | }).catch(err => { |
| | | this.mixinDatacomErrorMessage = err.message |
| | | }) |
| | | }, |
| | | /** |
| | | * 获取父元素实例 |
| | | */ |
| | | getForm(name = 'uniForms') { |
| | | let parent = this.$parent; |
| | | let parentName = parent.$options.name; |
| | | while (parentName !== name) { |
| | | parent = parent.$parent; |
| | | if (!parent) return false |
| | | parentName = parent.$options.name; |
| | | } |
| | | return parent; |
| | | }, |
| | | change(e) { |
| | | const values = e.detail.value |
| | | |
| | | let detail = { |
| | | value: [], |
| | | data: [] |
| | | } |
| | | |
| | | if (this.multiple) { |
| | | this.range.forEach(item => { |
| | | |
| | | if (values.includes(item[this.map.value] + '')) { |
| | | detail.value.push(item[this.map.value]) |
| | | detail.data.push(item) |
| | | } |
| | | }) |
| | | } else { |
| | | const range = this.range.find(item => (item[this.map.value] + '') === values) |
| | | if (range) { |
| | | detail = { |
| | | value: range[this.map.value], |
| | | data: range |
| | | } |
| | | } |
| | | } |
| | | // this.formItem && this.formItem.setValue(detail.value) |
| | | // TODO 兼容 vue2 |
| | | this.$emit('input', detail.value); |
| | | // // TOTO 兼容 vue3 |
| | | this.$emit('update:modelValue', detail.value); |
| | | this.$emit('change', { |
| | | detail |
| | | }) |
| | | if (this.multiple) { |
| | | // 如果 v-model 没有绑定 ,则走内部逻辑 |
| | | // if (this.value.length === 0) { |
| | | this.dataList = this.getDataList(detail.value, true) |
| | | // } |
| | | } else { |
| | | this.dataList = this.getDataList(detail.value) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 获取渲染的新数组 |
| | | * @param {Object} value 选中内容 |
| | | */ |
| | | getDataList(value) { |
| | | // 解除引用关系,破坏原引用关系,避免污染源数据 |
| | | let dataList = JSON.parse(JSON.stringify(this.range)) |
| | | let list = [] |
| | | if (this.multiple) { |
| | | if (!Array.isArray(value)) { |
| | | value = [] |
| | | } |
| | | } |
| | | dataList.forEach((item, index) => { |
| | | item.disabled = item.disable || item.disabled || false |
| | | if (this.multiple) { |
| | | if (value.length > 0) { |
| | | let have = value.find(val => val === item[this.map.value]) |
| | | item.selected = have !== undefined |
| | | } else { |
| | | item.selected = false |
| | | } |
| | | } else { |
| | | item.selected = value === item[this.map.value] |
| | | } |
| | | |
| | | list.push(item) |
| | | }) |
| | | return this.setRange(list) |
| | | }, |
| | | /** |
| | | * 处理最大最小值 |
| | | * @param {Object} list |
| | | */ |
| | | setRange(list) { |
| | | let selectList = list.filter(item => item.selected) |
| | | let min = Number(this.min) || 0 |
| | | let max = Number(this.max) || '' |
| | | list.forEach((item, index) => { |
| | | if (this.multiple) { |
| | | if (selectList.length <= min) { |
| | | let have = selectList.find(val => val[this.map.value] === item[this.map.value]) |
| | | if (have !== undefined) { |
| | | item.disabled = true |
| | | } |
| | | } |
| | | |
| | | if (selectList.length >= max && max !== '') { |
| | | let have = selectList.find(val => val[this.map.value] === item[this.map.value]) |
| | | if (have === undefined) { |
| | | item.disabled = true |
| | | } |
| | | } |
| | | } |
| | | this.setStyles(item, index) |
| | | list[index] = item |
| | | }) |
| | | return list |
| | | }, |
| | | /** |
| | | * 设置 class |
| | | * @param {Object} item |
| | | * @param {Object} index |
| | | */ |
| | | setStyles(item, index) { |
| | | // 设置自定义样式 |
| | | item.styleBackgroud = this.setStyleBackgroud(item) |
| | | item.styleIcon = this.setStyleIcon(item) |
| | | item.styleIconText = this.setStyleIconText(item) |
| | | item.styleRightIcon = this.setStyleRightIcon(item) |
| | | }, |
| | | |
| | | /** |
| | | * 获取选中值 |
| | | * @param {Object} range |
| | | */ |
| | | getSelectedValue(range) { |
| | | if (!this.multiple) return this.dataValue |
| | | let selectedArr = [] |
| | | range.forEach((item) => { |
| | | if (item.selected) { |
| | | selectedArr.push(item[this.map.value]) |
| | | } |
| | | }) |
| | | return this.dataValue.length > 0 ? this.dataValue : selectedArr |
| | | }, |
| | | |
| | | /** |
| | | * 设置背景样式 |
| | | */ |
| | | setStyleBackgroud(item) { |
| | | let styles = {} |
| | | let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff' |
| | | if (this.selectedColor) { |
| | | if (this.mode !== 'list') { |
| | | styles['border-color'] = item.selected ? selectedColor : '#DCDFE6' |
| | | } |
| | | if (this.mode === 'tag') { |
| | | styles['background-color'] = item.selected ? selectedColor : '#f5f5f5' |
| | | } |
| | | } |
| | | let classles = '' |
| | | for (let i in styles) { |
| | | classles += `${i}:${styles[i]};` |
| | | } |
| | | return classles |
| | | }, |
| | | setStyleIcon(item) { |
| | | let styles = {} |
| | | let classles = '' |
| | | if (this.selectedColor) { |
| | | let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff' |
| | | styles['background-color'] = item.selected ? selectedColor : '#fff' |
| | | styles['border-color'] = item.selected ? selectedColor : '#DCDFE6' |
| | | |
| | | if (!item.selected && item.disabled) { |
| | | styles['background-color'] = '#F2F6FC' |
| | | styles['border-color'] = item.selected ? selectedColor : '#DCDFE6' |
| | | } |
| | | } |
| | | for (let i in styles) { |
| | | classles += `${i}:${styles[i]};` |
| | | } |
| | | return classles |
| | | }, |
| | | setStyleIconText(item) { |
| | | let styles = {} |
| | | let classles = '' |
| | | if (this.selectedColor) { |
| | | let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff' |
| | | if (this.mode === 'tag') { |
| | | styles.color = item.selected ? (this.selectedTextColor ? this.selectedTextColor : '#fff') : '#666' |
| | | } else { |
| | | styles.color = item.selected ? (this.selectedTextColor ? this.selectedTextColor : selectedColor) : '#666' |
| | | } |
| | | if (!item.selected && item.disabled) { |
| | | styles.color = '#999' |
| | | } |
| | | } |
| | | for (let i in styles) { |
| | | classles += `${i}:${styles[i]};` |
| | | } |
| | | return classles |
| | | }, |
| | | setStyleRightIcon(item) { |
| | | let styles = {} |
| | | let classles = '' |
| | | if (this.mode === 'list') { |
| | | styles['border-color'] = item.selected ? this.styles.selectedColor : '#DCDFE6' |
| | | } |
| | | for (let i in styles) { |
| | | classles += `${i}:${styles[i]};` |
| | | } |
| | | |
| | | return classles |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-primary: #2979ff !default; |
| | | $border-color: #DCDFE6; |
| | | $disable: 0.4; |
| | | |
| | | @mixin flex { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-data-loading { |
| | | @include flex; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 36px; |
| | | padding-left: 10px; |
| | | color: #999; |
| | | } |
| | | |
| | | .uni-data-checklist { |
| | | position: relative; |
| | | z-index: 0; |
| | | flex: 1; |
| | | |
| | | // 多选样式 |
| | | .checklist-group { |
| | | @include flex; |
| | | flex-direction: row; |
| | | flex-wrap: wrap; |
| | | |
| | | &.is-list { |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .checklist-box { |
| | | @include flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | position: relative; |
| | | margin: 5px 0; |
| | | margin-right: 25px; |
| | | |
| | | .hidden { |
| | | position: absolute; |
| | | opacity: 0; |
| | | } |
| | | |
| | | // 文字样式 |
| | | .checklist-content { |
| | | @include flex; |
| | | flex: 1; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | |
| | | .checklist-text { |
| | | font-size: 14px; |
| | | color: #666; |
| | | margin-left: 5px; |
| | | line-height: 14px; |
| | | } |
| | | |
| | | .checkobx__list { |
| | | border-right-width: 1px; |
| | | border-right-color: #007aff; |
| | | border-right-style: solid; |
| | | border-bottom-width: 1px; |
| | | border-bottom-color: #007aff; |
| | | border-bottom-style: solid; |
| | | height: 12px; |
| | | width: 6px; |
| | | left: -5px; |
| | | transform-origin: center; |
| | | transform: rotate(45deg); |
| | | opacity: 0; |
| | | } |
| | | } |
| | | |
| | | // 多选样式 |
| | | .checkbox__inner { |
| | | /* #ifndef APP-NVUE */ |
| | | flex-shrink: 0; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | position: relative; |
| | | width: 16px; |
| | | height: 16px; |
| | | border: 1px solid $border-color; |
| | | border-radius: 4px; |
| | | background-color: #fff; |
| | | z-index: 1; |
| | | |
| | | .checkbox__inner-icon { |
| | | position: absolute; |
| | | /* #ifdef APP-NVUE */ |
| | | top: 2px; |
| | | /* #endif */ |
| | | /* #ifndef APP-NVUE */ |
| | | top: 1px; |
| | | /* #endif */ |
| | | left: 5px; |
| | | height: 8px; |
| | | width: 4px; |
| | | border-right-width: 1px; |
| | | border-right-color: #fff; |
| | | border-right-style: solid; |
| | | border-bottom-width: 1px; |
| | | border-bottom-color: #fff; |
| | | border-bottom-style: solid; |
| | | opacity: 0; |
| | | transform-origin: center; |
| | | transform: rotate(40deg); |
| | | } |
| | | } |
| | | |
| | | // 单选样式 |
| | | .radio__inner { |
| | | @include flex; |
| | | /* #ifndef APP-NVUE */ |
| | | flex-shrink: 0; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: relative; |
| | | width: 16px; |
| | | height: 16px; |
| | | border: 1px solid $border-color; |
| | | border-radius: 16px; |
| | | background-color: #fff; |
| | | z-index: 1; |
| | | |
| | | .radio__inner-icon { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 10px; |
| | | opacity: 0; |
| | | } |
| | | } |
| | | |
| | | // 默认样式 |
| | | &.is--default { |
| | | |
| | | // 禁用 |
| | | &.is-disable { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | |
| | | /* #endif */ |
| | | .checkbox__inner { |
| | | background-color: #F2F6FC; |
| | | border-color: $border-color; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .radio__inner { |
| | | background-color: #F2F6FC; |
| | | border-color: $border-color; |
| | | } |
| | | |
| | | .checklist-text { |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | // 选中 |
| | | &.is-checked { |
| | | .checkbox__inner { |
| | | border-color: $uni-primary; |
| | | background-color: $uni-primary; |
| | | |
| | | .checkbox__inner-icon { |
| | | opacity: 1; |
| | | transform: rotate(45deg); |
| | | } |
| | | } |
| | | |
| | | .radio__inner { |
| | | border-color: $uni-primary; |
| | | |
| | | .radio__inner-icon { |
| | | opacity: 1; |
| | | background-color: $uni-primary; |
| | | } |
| | | } |
| | | |
| | | .checklist-text { |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | // 选中禁用 |
| | | &.is-disable { |
| | | .checkbox__inner { |
| | | opacity: $disable; |
| | | } |
| | | |
| | | .checklist-text { |
| | | opacity: $disable; |
| | | } |
| | | |
| | | .radio__inner { |
| | | opacity: $disable; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 按钮样式 |
| | | &.is--button { |
| | | margin-right: 10px; |
| | | padding: 5px 10px; |
| | | border: 1px $border-color solid; |
| | | border-radius: 3px; |
| | | transition: border-color 0.2s; |
| | | |
| | | // 禁用 |
| | | &.is-disable { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | border: 1px #eee solid; |
| | | opacity: $disable; |
| | | |
| | | .checkbox__inner { |
| | | background-color: #F2F6FC; |
| | | border-color: $border-color; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .radio__inner { |
| | | background-color: #F2F6FC; |
| | | border-color: $border-color; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .checklist-text { |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | &.is-checked { |
| | | border-color: $uni-primary; |
| | | |
| | | .checkbox__inner { |
| | | border-color: $uni-primary; |
| | | background-color: $uni-primary; |
| | | |
| | | .checkbox__inner-icon { |
| | | opacity: 1; |
| | | transform: rotate(45deg); |
| | | } |
| | | } |
| | | |
| | | .radio__inner { |
| | | border-color: $uni-primary; |
| | | |
| | | .radio__inner-icon { |
| | | opacity: 1; |
| | | background-color: $uni-primary; |
| | | } |
| | | } |
| | | |
| | | .checklist-text { |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | // 选中禁用 |
| | | &.is-disable { |
| | | opacity: $disable; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 标签样式 |
| | | &.is--tag { |
| | | margin-right: 10px; |
| | | padding: 5px 10px; |
| | | border: 1px $border-color solid; |
| | | border-radius: 3px; |
| | | background-color: #f5f5f5; |
| | | |
| | | .checklist-text { |
| | | margin: 0; |
| | | color: #666; |
| | | } |
| | | |
| | | // 禁用 |
| | | &.is-disable { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | opacity: $disable; |
| | | } |
| | | |
| | | &.is-checked { |
| | | background-color: $uni-primary; |
| | | border-color: $uni-primary; |
| | | |
| | | .checklist-text { |
| | | color: #fff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 列表样式 |
| | | &.is--list { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | padding: 10px 15px; |
| | | padding-left: 0; |
| | | margin: 0; |
| | | |
| | | &.is-list-border { |
| | | border-top: 1px #eee solid; |
| | | } |
| | | |
| | | // 禁用 |
| | | &.is-disable { |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | |
| | | /* #endif */ |
| | | .checkbox__inner { |
| | | background-color: #F2F6FC; |
| | | border-color: $border-color; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .checklist-text { |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | &.is-checked { |
| | | .checkbox__inner { |
| | | border-color: $uni-primary; |
| | | background-color: $uni-primary; |
| | | |
| | | .checkbox__inner-icon { |
| | | opacity: 1; |
| | | transform: rotate(45deg); |
| | | } |
| | | } |
| | | |
| | | .radio__inner { |
| | | border-color: $uni-primary; |
| | | .radio__inner-icon { |
| | | opacity: 1; |
| | | background-color: $uni-primary; |
| | | } |
| | | } |
| | | |
| | | .checklist-text { |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | .checklist-content { |
| | | .checkobx__list { |
| | | opacity: 1; |
| | | border-color: $uni-primary; |
| | | } |
| | | } |
| | | |
| | | // 选中禁用 |
| | | &.is-disable { |
| | | .checkbox__inner { |
| | | opacity: $disable; |
| | | } |
| | | |
| | | .checklist-text { |
| | | opacity: $disable; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | { |
| | | "id": "uni-data-checkbox", |
| | | "displayName": "uni-data-checkbox 数据选择器", |
| | | "version": "1.0.5", |
| | | "description": "通过数据驱动的单选框和复选框", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "checkbox", |
| | | "单选", |
| | | "多选", |
| | | "单选多选" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.1" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-load-more","uni-scss"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | |
| | | |
| | | ## DataCheckbox 数据驱动的单选复选框 |
| | | > **组件名:uni-data-checkbox** |
| | | > 代码块: `uDataCheckbox` |
| | | |
| | | |
| | | 本组件是基于uni-app基础组件checkbox的封装。本组件要解决问题包括: |
| | | |
| | | 1. 数据绑定型组件:给本组件绑定一个data,会自动渲染一组候选内容。再以往,开发者需要编写不少代码实现类似功能 |
| | | 2. 自动的表单校验:组件绑定了data,且符合[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)组件的表单校验规范,搭配使用会自动实现表单校验 |
| | | 3. 本组件合并了单选多选 |
| | | 4. 本组件有若干风格选择,如普通的单选多选框、并列button风格、tag风格。开发者可以快速选择需要的风格。但作为一个封装组件,样式代码虽然不用自己写了,却会牺牲一定的样式自定义性 |
| | | |
| | | 在uniCloud开发中,`DB Schema`中配置了enum枚举等类型后,在web控制台的[自动生成表单](https://uniapp.dcloud.io/uniCloud/schema?id=autocode)功能中,会自动生成``uni-data-checkbox``组件并绑定好data |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-checkbox) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
对比新文件 |
| | |
| | | ## 2.0.1(2024-08-22) |
| | | - 修复 uni-app-x v-model 没有更新传入值的 bug |
| | | ## 2.0.0(2023-12-11) |
| | | - 新增 支持 uni-app-x |
| | | ## 1.1.2(2023-04-11) |
| | | - 修复 更改 modelValue 报错的 bug |
| | | - 修复 v-for 未使用 key 值控制台 warning |
| | | ## 1.1.1(2023-02-21) |
| | | - 修复代码合并时引发 value 属性为空时不渲染数据的问题 |
| | | ## 1.1.0(2023-02-15) |
| | | - 修复 localdata 不支持动态更新的bug |
| | | ## 1.0.9(2023-02-15) |
| | | - 修复 localdata 不支持动态更新的bug |
| | | ## 1.0.8(2022-09-16) |
| | | - 可以使用 uni-scss 控制主题色 |
| | | ## 1.0.7(2022-07-06) |
| | | - 优化 pc端图标位置不正确的问题 |
| | | ## 1.0.6(2022-07-05) |
| | | - 优化 显示样式 |
| | | ## 1.0.5(2022-07-04) |
| | | - 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug |
| | | ## 1.0.4(2022-04-19) |
| | | - 修复 字节小程序 本地数据无法选择下一级的Bug |
| | | ## 1.0.3(2022-02-25) |
| | | - 修复 nvue 不支持的 v-show 的 bug |
| | | ## 1.0.2(2022-02-25) |
| | | - 修复 条件编译 nvue 不支持的 css 样式 |
| | | ## 1.0.1(2021-11-23) |
| | | - 修复 由上个版本引发的map、v-model等属性不生效的bug |
| | | ## 1.0.0(2021-11-19) |
| | | - 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-data-picker](https://uniapp.dcloud.io/component/uniui/uni-data-picker) |
| | | ## 0.4.9(2021-10-28) |
| | | - 修复 VUE2 v-model 概率无效的 bug |
| | | ## 0.4.8(2021-10-27) |
| | | - 修复 v-model 概率无效的 bug |
| | | ## 0.4.7(2021-10-25) |
| | | - 新增 属性 spaceInfo 服务空间配置 HBuilderX 3.2.11+ |
| | | - 修复 树型 uniCloud 数据类型为 int 时报错的 bug |
| | | ## 0.4.6(2021-10-19) |
| | | - 修复 非 VUE3 v-model 为 0 时无法选中的 bug |
| | | ## 0.4.5(2021-09-26) |
| | | - 新增 清除已选项的功能(通过 clearIcon 属性配置是否显示按钮),同时提供 clear 方法以供调用,二者等效 |
| | | - 修复 readonly 为 true 时报错的 bug |
| | | ## 0.4.4(2021-09-26) |
| | | - 修复 上一版本造成的 map 属性失效的 bug |
| | | - 新增 ellipsis 属性,支持配置 tab 选项长度过长时是否自动省略 |
| | | ## 0.4.3(2021-09-24) |
| | | - 修复 某些情况下级联未触发的 bug |
| | | ## 0.4.2(2021-09-23) |
| | | - 新增 提供 show 和 hide 方法,开发者可以通过 ref 调用 |
| | | - 新增 选项内容过长自动添加省略号 |
| | | ## 0.4.1(2021-09-15) |
| | | - 新增 map 属性 字段映射,将 text/value 映射到数据中的其他字段 |
| | | ## 0.4.0(2021-07-13) |
| | | - 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
| | | ## 0.3.5(2021-06-04) |
| | | - 修复 无法加载云端数据的问题 |
| | | ## 0.3.4(2021-05-28) |
| | | - 修复 v-model 无效问题 |
| | | - 修复 loaddata 为空数据组时加载时间过长问题 |
| | | - 修复 上个版本引出的本地数据无法选择带有 children 的 2 级节点 |
| | | ## 0.3.3(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 0.3.2(2021-04-22) |
| | | - 修复 非树形数据有 where 属性查询报错的问题 |
| | | ## 0.3.1(2021-04-15) |
| | | - 修复 本地数据概率无法回显时问题 |
| | | ## 0.3.0(2021-04-07) |
| | | - 新增 支持云端非树形表结构数据 |
| | | - 修复 根节点 parent_field 字段等于 null 时选择界面错乱问题 |
| | | ## 0.2.0(2021-03-15) |
| | | - 修复 nodeclick、popupopened、popupclosed 事件无法触发的问题 |
| | | ## 0.1.9(2021-03-09) |
| | | - 修复 微信小程序某些情况下无法选择的问题 |
| | | ## 0.1.8(2021-02-05) |
| | | - 优化 部分样式在 nvue 上的兼容表现 |
| | | ## 0.1.7(2021-02-05) |
| | | - 调整为 uni_modules 目录规范 |
对比新文件 |
| | |
| | | // #ifdef H5 |
| | | export default { |
| | | name: 'Keypress', |
| | | props: { |
| | | disable: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | mounted () { |
| | | const keyNames = { |
| | | esc: ['Esc', 'Escape'], |
| | | tab: 'Tab', |
| | | enter: 'Enter', |
| | | space: [' ', 'Spacebar'], |
| | | up: ['Up', 'ArrowUp'], |
| | | left: ['Left', 'ArrowLeft'], |
| | | right: ['Right', 'ArrowRight'], |
| | | down: ['Down', 'ArrowDown'], |
| | | delete: ['Backspace', 'Delete', 'Del'] |
| | | } |
| | | const listener = ($event) => { |
| | | if (this.disable) { |
| | | return |
| | | } |
| | | const keyName = Object.keys(keyNames).find(key => { |
| | | const keyName = $event.key |
| | | const value = keyNames[key] |
| | | return value === keyName || (Array.isArray(value) && value.includes(keyName)) |
| | | }) |
| | | if (keyName) { |
| | | // 避免和其他按键事件冲突 |
| | | setTimeout(() => { |
| | | this.$emit(keyName, {}) |
| | | }, 0) |
| | | } |
| | | } |
| | | document.addEventListener('keyup', listener) |
| | | this.$once('hook:beforeDestroy', () => { |
| | | document.removeEventListener('keyup', listener) |
| | | }) |
| | | }, |
| | | render: () => {} |
| | | } |
| | | // #endif |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-data-tree"> |
| | | <view class="uni-data-tree-input" @click="handleInput"> |
| | | <slot :data="selectedPaths" :error="error"> |
| | | <view class="input-value" :class="{'input-value-border': border}"> |
| | | <text v-if="error!=null" class="error-text">{{error!.errMsg}}</text> |
| | | <scroll-view v-if="selectedPaths.length" class="selected-path" scroll-x="true"> |
| | | <view class="selected-list"> |
| | | <template v-for="(item, index) in selectedPaths"> |
| | | <text class="text-color">{{item[mappingTextName]}}</text> |
| | | <text v-if="index<selectedPaths.length-1" class="input-split-line">{{split}}</text> |
| | | </template> |
| | | </view> |
| | | </scroll-view> |
| | | <text v-else-if="error==null&&!loading" class="placeholder">{{placeholder}}</text> |
| | | <view v-if="!readonly" class="arrow-area"> |
| | | <view class="input-arrow"></view> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | <view v-if="loading && !isOpened" class="selected-loading"> |
| | | <slot name="picker-loading" :loading="loading"></slot> |
| | | </view> |
| | | </view> |
| | | <view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view> |
| | | <view class="uni-data-tree-dialog" v-if="isOpened"> |
| | | <view class="uni-popper__arrow"></view> |
| | | <view class="dialog-caption"> |
| | | <view class="dialog-title-view"> |
| | | <text class="dialog-title">{{popupTitle}}</text> |
| | | </view> |
| | | <view class="dialog-close" @click="handleClose"> |
| | | <view class="dialog-close-plus" data-id="close"></view> |
| | | <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view> |
| | | </view> |
| | | </view> |
| | | <view ref="pickerView" class="uni-data-pickerview"> |
| | | <view v-if="error!=null" class="error"> |
| | | <text class="error-text">{{error!.errMsg}}</text> |
| | | </view> |
| | | <scroll-view v-if="!isCloudDataList" :scroll-x="true"> |
| | | <view class="selected-node-list"> |
| | | <template v-for="(item, index) in selectedNodes"> |
| | | <text class="selected-node-item" :class="{'selected-node-item-active':index==selectedIndex}" |
| | | @click="onTabSelect(index)"> |
| | | {{item[mappingTextName]}} |
| | | </text> |
| | | </template> |
| | | </view> |
| | | </scroll-view> |
| | | <list-view class="list-view" :scroll-y="true"> |
| | | <list-item class="list-item" v-for="(item, _) in currentDataList" @click="onNodeClick(item)"> |
| | | <text class="item-text" :class="{'item-text-disabled': item['disable']}">{{item[mappingTextName]}}</text> |
| | | <text class="check" v-if="item[mappingValueName] == selectedNodes[selectedIndex][mappingValueName]"></text> |
| | | </list-item> |
| | | </list-view> |
| | | <view class="loading-cover" v-if="loading"> |
| | | <slot name="pickerview-loading" :loading="loading"></slot> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { dataPicker } from "../uni-data-pickerview/uni-data-picker.uts" |
| | | |
| | | /** |
| | | * DataPicker 级联选择 |
| | | * @description 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3796 |
| | | * @property {String} popup-title 弹出窗口标题 |
| | | * @property {Array} localdata 本地数据,参考 |
| | | * @property {Boolean} border = [true|false] 是否有边框 |
| | | * @property {Boolean} readonly = [true|false] 是否仅读 |
| | | * @property {Boolean} preload = [true|false] 是否预加载数据 |
| | | * @value true 开启预加载数据,点击弹出窗口后显示已加载数据 |
| | | * @value false 关闭预加载数据,点击弹出窗口后开始加载数据 |
| | | * @property {Boolean} step-searh = [true|false] 是否分布查询 |
| | | * @value true 启用分布查询,仅查询当前选中节点 |
| | | * @value false 关闭分布查询,一次查询出所有数据 |
| | | * @property {String|DBFieldString} self-field 分布查询当前字段名称 |
| | | * @property {String|DBFieldString} parent-field 分布查询父字段名称 |
| | | * @property {String|DBCollectionString} collection 表名 |
| | | * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 |
| | | * @property {String} orderby 排序字段及正序倒叙设置 |
| | | * @property {String|JQLString} where 查询条件 |
| | | * @event {Function} popupshow 弹出的选择窗口打开时触发此事件 |
| | | * @event {Function} popuphide 弹出的选择窗口关闭时触发此事件 |
| | | */ |
| | | export default { |
| | | name: 'UniDataPicker', |
| | | emits: ['popupopened', 'popupclosed', 'nodeclick', 'change', 'input', 'update:modelValue', 'inputclick'], |
| | | mixins: [dataPicker], |
| | | props: { |
| | | popupTitle: { |
| | | type: String, |
| | | default: '请选择' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '请选择' |
| | | }, |
| | | heightMobile: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | readonly: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | clearIcon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | split: { |
| | | type: String, |
| | | default: '/' |
| | | }, |
| | | ellipsis: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | isOpened: false |
| | | } |
| | | }, |
| | | computed: { |
| | | isShowClearIcon() : boolean { |
| | | if (this.readonly) { |
| | | return false |
| | | } |
| | | |
| | | if (this.clearIcon && this.selectedPaths.length > 0) { |
| | | return true |
| | | } |
| | | |
| | | return false |
| | | } |
| | | }, |
| | | created() { |
| | | this.load() |
| | | }, |
| | | methods: { |
| | | clear() { |
| | | }, |
| | | load() { |
| | | if (this.isLocalData) { |
| | | this.loadLocalData() |
| | | } else if (this.isCloudDataList || this.isCloudDataTree) { |
| | | this.loadCloudDataPath() |
| | | } |
| | | }, |
| | | show() { |
| | | this.isOpened = true |
| | | this.$emit('popupopened') |
| | | if (!this.hasCloudTreeData) { |
| | | this.loadData() |
| | | } |
| | | }, |
| | | hide() { |
| | | this.isOpened = false |
| | | this.$emit('popupclosed') |
| | | }, |
| | | handleInput() { |
| | | if (this.readonly) { |
| | | this.$emit('inputclick') |
| | | } else { |
| | | this.show() |
| | | } |
| | | }, |
| | | handleClose() { |
| | | this.hide() |
| | | }, |
| | | onFinish() { |
| | | this.selectedPaths = this.getChangeNodes() |
| | | this.$emit('update:modelValue', this.selectedPaths) |
| | | this.$emit('change', this.selectedPaths) |
| | | this.hide() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | @import url("../uni-data-pickerview/uni-data-pickerview.css"); |
| | | |
| | | .uni-data-tree { |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-data-tree-input { |
| | | position: relative; |
| | | } |
| | | |
| | | .selected-loading { |
| | | display: flex; |
| | | justify-content: center; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | } |
| | | |
| | | .error-text { |
| | | flex: 1; |
| | | font-size: 12px; |
| | | color: #DD524D; |
| | | } |
| | | |
| | | .input-value { |
| | | flex-direction: row; |
| | | align-items: center; |
| | | flex-wrap: nowrap; |
| | | padding: 5px 5px; |
| | | padding-right: 5px; |
| | | overflow: hidden; |
| | | min-height: 28px; |
| | | } |
| | | |
| | | .input-value-border { |
| | | border: 1px solid #e5e5e5; |
| | | border-radius: 5px; |
| | | } |
| | | |
| | | .selected-path { |
| | | flex: 1; |
| | | flex-direction: row; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .load-more { |
| | | width: 40px; |
| | | } |
| | | |
| | | .selected-list { |
| | | flex-direction: row; |
| | | flex-wrap: nowrap; |
| | | } |
| | | |
| | | .selected-item { |
| | | flex-direction: row; |
| | | flex-wrap: nowrap; |
| | | } |
| | | |
| | | .text-color { |
| | | font-size: 14px; |
| | | color: #333; |
| | | } |
| | | |
| | | .placeholder { |
| | | color: grey; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .input-split-line { |
| | | opacity: .5; |
| | | margin-left: 1px; |
| | | margin-right: 1px; |
| | | } |
| | | |
| | | .arrow-area { |
| | | position: relative; |
| | | padding: 0 12px; |
| | | margin-left: auto; |
| | | justify-content: center; |
| | | transform: rotate(-45deg); |
| | | transform-origin: center; |
| | | } |
| | | |
| | | .input-arrow { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-left: 2px solid #999; |
| | | border-bottom: 2px solid #999; |
| | | } |
| | | |
| | | .uni-data-tree-cover { |
| | | position: fixed; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: rgba(0, 0, 0, .4); |
| | | flex-direction: column; |
| | | z-index: 100; |
| | | } |
| | | |
| | | .uni-data-tree-dialog { |
| | | position: fixed; |
| | | left: 0; |
| | | top: 20%; |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: #FFFFFF; |
| | | border-top-left-radius: 10px; |
| | | border-top-right-radius: 10px; |
| | | flex-direction: column; |
| | | z-index: 102; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .dialog-caption { |
| | | position: relative; |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .dialog-title-view { |
| | | flex: 1; |
| | | } |
| | | |
| | | .dialog-title { |
| | | align-self: center; |
| | | padding: 0 10px; |
| | | line-height: 44px; |
| | | } |
| | | |
| | | .dialog-close { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | padding: 0 15px; |
| | | } |
| | | |
| | | .dialog-close-plus { |
| | | width: 16px; |
| | | height: 2px; |
| | | background-color: #666; |
| | | border-radius: 2px; |
| | | transform: rotate(45deg); |
| | | } |
| | | |
| | | .dialog-close-rotate { |
| | | position: absolute; |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .uni-data-pickerview { |
| | | flex: 1; |
| | | } |
| | | |
| | | .icon-clear { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | /* #ifdef H5 */ |
| | | @media all and (min-width: 768px) { |
| | | .uni-data-tree-cover { |
| | | background-color: transparent; |
| | | } |
| | | |
| | | .uni-data-tree-dialog { |
| | | position: absolute; |
| | | top: 55px; |
| | | height: auto; |
| | | min-height: 400px; |
| | | max-height: 50vh; |
| | | background-color: #fff; |
| | | border: 1px solid #EBEEF5; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | border-radius: 4px; |
| | | overflow: unset; |
| | | } |
| | | |
| | | .dialog-caption { |
| | | display: none; |
| | | } |
| | | } |
| | | /* #endif */ |
| | | </style> |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-data-tree"> |
| | | <view class="uni-data-tree-input" @click="handleInput"> |
| | | <slot :options="options" :data="inputSelected" :error="errorMessage"> |
| | | <view class="input-value" :class="{'input-value-border': border}"> |
| | | <text v-if="errorMessage" class="selected-area error-text">{{errorMessage}}</text> |
| | | <view v-else-if="loading && !isOpened" class="selected-area"> |
| | | <uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more> |
| | | </view> |
| | | <scroll-view v-else-if="inputSelected.length" class="selected-area" scroll-x="true"> |
| | | <view class="selected-list"> |
| | | <view class="selected-item" v-for="(item,index) in inputSelected" :key="index"> |
| | | <text class="text-color">{{item.text}}</text><text v-if="index<inputSelected.length-1" |
| | | class="input-split-line">{{split}}</text> |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | <text v-else class="selected-area placeholder">{{placeholder}}</text> |
| | | <view v-if="clearIcon && !readonly && inputSelected.length" class="icon-clear" @click.stop="clear"> |
| | | <uni-icons type="clear" color="#c0c4cc" size="24"></uni-icons> |
| | | </view> |
| | | <view class="arrow-area" v-if="(!clearIcon || !inputSelected.length) && !readonly "> |
| | | <view class="input-arrow"></view> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | <view class="uni-data-tree-cover" v-if="isOpened" @click="handleClose"></view> |
| | | <view class="uni-data-tree-dialog" v-if="isOpened"> |
| | | <view class="uni-popper__arrow"></view> |
| | | <view class="dialog-caption"> |
| | | <view class="title-area"> |
| | | <text class="dialog-title">{{popupTitle}}</text> |
| | | </view> |
| | | <view class="dialog-close" @click="handleClose"> |
| | | <view class="dialog-close-plus" data-id="close"></view> |
| | | <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view> |
| | | </view> |
| | | </view> |
| | | <data-picker-view class="picker-view" ref="pickerView" v-model="dataValue" :localdata="localdata" |
| | | :preload="preload" :collection="collection" :field="field" :orderby="orderby" :where="where" |
| | | :step-searh="stepSearh" :self-field="selfField" :parent-field="parentField" :managed-mode="true" :map="map" |
| | | :ellipsis="ellipsis" @change="onchange" @datachange="ondatachange" @nodeclick="onnodeclick"> |
| | | </data-picker-view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import dataPicker from "../uni-data-pickerview/uni-data-picker.js" |
| | | import DataPickerView from "../uni-data-pickerview/uni-data-pickerview.vue" |
| | | |
| | | /** |
| | | * DataPicker 级联选择 |
| | | * @description 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3796 |
| | | * @property {String} popup-title 弹出窗口标题 |
| | | * @property {Array} localdata 本地数据,参考 |
| | | * @property {Boolean} border = [true|false] 是否有边框 |
| | | * @property {Boolean} readonly = [true|false] 是否仅读 |
| | | * @property {Boolean} preload = [true|false] 是否预加载数据 |
| | | * @value true 开启预加载数据,点击弹出窗口后显示已加载数据 |
| | | * @value false 关闭预加载数据,点击弹出窗口后开始加载数据 |
| | | * @property {Boolean} step-searh = [true|false] 是否分布查询 |
| | | * @value true 启用分布查询,仅查询当前选中节点 |
| | | * @value false 关闭分布查询,一次查询出所有数据 |
| | | * @property {String|DBFieldString} self-field 分布查询当前字段名称 |
| | | * @property {String|DBFieldString} parent-field 分布查询父字段名称 |
| | | * @property {String|DBCollectionString} collection 表名 |
| | | * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 |
| | | * @property {String} orderby 排序字段及正序倒叙设置 |
| | | * @property {String|JQLString} where 查询条件 |
| | | * @event {Function} popupshow 弹出的选择窗口打开时触发此事件 |
| | | * @event {Function} popuphide 弹出的选择窗口关闭时触发此事件 |
| | | */ |
| | | export default { |
| | | name: 'UniDataPicker', |
| | | emits: ['popupopened', 'popupclosed', 'nodeclick', 'input', 'change', 'update:modelValue','inputclick'], |
| | | mixins: [dataPicker], |
| | | components: { |
| | | DataPickerView |
| | | }, |
| | | props: { |
| | | options: { |
| | | type: [Object, Array], |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | popupTitle: { |
| | | type: String, |
| | | default: '请选择' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '请选择' |
| | | }, |
| | | heightMobile: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | readonly: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | clearIcon: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | border: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | split: { |
| | | type: String, |
| | | default: '/' |
| | | }, |
| | | ellipsis: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | isOpened: false, |
| | | inputSelected: [] |
| | | } |
| | | }, |
| | | created() { |
| | | this.$nextTick(() => { |
| | | this.load(); |
| | | }) |
| | | }, |
| | | watch: { |
| | | localdata: { |
| | | handler() { |
| | | this.load() |
| | | }, |
| | | deep: true |
| | | }, |
| | | }, |
| | | methods: { |
| | | clear() { |
| | | this._dispatchEvent([]); |
| | | }, |
| | | onPropsChange() { |
| | | this._treeData = []; |
| | | this.selectedIndex = 0; |
| | | |
| | | this.load(); |
| | | }, |
| | | load() { |
| | | if (this.readonly) { |
| | | this._processReadonly(this.localdata, this.dataValue); |
| | | return; |
| | | } |
| | | |
| | | // 回显本地数据 |
| | | if (this.isLocalData) { |
| | | this.loadData(); |
| | | this.inputSelected = this.selected.slice(0); |
| | | } else if (this.isCloudDataList || this.isCloudDataTree) { // 回显 Cloud 数据 |
| | | this.loading = true; |
| | | this.getCloudDataValue().then((res) => { |
| | | this.loading = false; |
| | | this.inputSelected = res; |
| | | }).catch((err) => { |
| | | this.loading = false; |
| | | this.errorMessage = err; |
| | | }) |
| | | } |
| | | }, |
| | | show() { |
| | | this.isOpened = true |
| | | setTimeout(() => { |
| | | this.$refs.pickerView.updateData({ |
| | | treeData: this._treeData, |
| | | selected: this.selected, |
| | | selectedIndex: this.selectedIndex |
| | | }) |
| | | }, 200) |
| | | this.$emit('popupopened') |
| | | }, |
| | | hide() { |
| | | this.isOpened = false |
| | | this.$emit('popupclosed') |
| | | }, |
| | | handleInput() { |
| | | if (this.readonly) { |
| | | this.$emit('inputclick') |
| | | return |
| | | } |
| | | this.show() |
| | | }, |
| | | handleClose(e) { |
| | | this.hide() |
| | | }, |
| | | onnodeclick(e) { |
| | | this.$emit('nodeclick', e) |
| | | }, |
| | | ondatachange(e) { |
| | | this._treeData = this.$refs.pickerView._treeData |
| | | }, |
| | | onchange(e) { |
| | | this.hide() |
| | | this.$nextTick(() => { |
| | | this.inputSelected = e; |
| | | }) |
| | | this._dispatchEvent(e) |
| | | }, |
| | | _processReadonly(dataList, value) { |
| | | var isTree = dataList.findIndex((item) => { |
| | | return item.children |
| | | }) |
| | | if (isTree > -1) { |
| | | let inputValue |
| | | if (Array.isArray(value)) { |
| | | inputValue = value[value.length - 1] |
| | | if (typeof inputValue === 'object' && inputValue.value) { |
| | | inputValue = inputValue.value |
| | | } |
| | | } else { |
| | | inputValue = value |
| | | } |
| | | this.inputSelected = this._findNodePath(inputValue, this.localdata) |
| | | return |
| | | } |
| | | |
| | | if (!this.hasValue) { |
| | | this.inputSelected = [] |
| | | return |
| | | } |
| | | |
| | | let result = [] |
| | | for (let i = 0; i < value.length; i++) { |
| | | var val = value[i] |
| | | var item = dataList.find((v) => { |
| | | return v.value == val |
| | | }) |
| | | if (item) { |
| | | result.push(item) |
| | | } |
| | | } |
| | | if (result.length) { |
| | | this.inputSelected = result |
| | | } |
| | | }, |
| | | _filterForArray(data, valueArray) { |
| | | var result = [] |
| | | for (let i = 0; i < valueArray.length; i++) { |
| | | var value = valueArray[i] |
| | | var found = data.find((item) => { |
| | | return item.value == value |
| | | }) |
| | | if (found) { |
| | | result.push(found) |
| | | } |
| | | } |
| | | return result |
| | | }, |
| | | _dispatchEvent(selected) { |
| | | let item = {} |
| | | if (selected.length) { |
| | | var value = new Array(selected.length) |
| | | for (var i = 0; i < selected.length; i++) { |
| | | value[i] = selected[i].value |
| | | } |
| | | item = selected[selected.length - 1] |
| | | } else { |
| | | item.value = '' |
| | | } |
| | | if (this.formItem) { |
| | | this.formItem.setValue(item.value) |
| | | } |
| | | |
| | | this.$emit('input', item.value) |
| | | this.$emit('update:modelValue', item.value) |
| | | this.$emit('change', { |
| | | detail: { |
| | | value: selected |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .uni-data-tree { |
| | | flex: 1; |
| | | position: relative; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .error-text { |
| | | color: #DD524D; |
| | | } |
| | | |
| | | .input-value { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | flex-wrap: nowrap; |
| | | font-size: 14px; |
| | | /* line-height: 35px; */ |
| | | padding: 0 10px; |
| | | padding-right: 5px; |
| | | overflow: hidden; |
| | | height: 35px; |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .input-value-border { |
| | | border: 1px solid #e5e5e5; |
| | | border-radius: 5px; |
| | | } |
| | | |
| | | .selected-area { |
| | | flex: 1; |
| | | overflow: hidden; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .load-more { |
| | | /* #ifndef APP-NVUE */ |
| | | margin-right: auto; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | width: 40px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .selected-list { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | flex-wrap: nowrap; |
| | | /* padding: 0 5px; */ |
| | | } |
| | | |
| | | .selected-item { |
| | | flex-direction: row; |
| | | /* padding: 0 1px; */ |
| | | /* #ifndef APP-NVUE */ |
| | | white-space: nowrap; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .text-color { |
| | | color: #333; |
| | | } |
| | | |
| | | .placeholder { |
| | | color: grey; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .input-split-line { |
| | | opacity: .5; |
| | | } |
| | | |
| | | .arrow-area { |
| | | position: relative; |
| | | width: 20px; |
| | | /* #ifndef APP-NVUE */ |
| | | margin-bottom: 5px; |
| | | margin-left: auto; |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | transform: rotate(-45deg); |
| | | transform-origin: center; |
| | | } |
| | | |
| | | .input-arrow { |
| | | width: 7px; |
| | | height: 7px; |
| | | border-left: 1px solid #999; |
| | | border-bottom: 1px solid #999; |
| | | } |
| | | |
| | | .uni-data-tree-cover { |
| | | position: fixed; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: rgba(0, 0, 0, .4); |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | z-index: 100; |
| | | } |
| | | |
| | | .uni-data-tree-dialog { |
| | | position: fixed; |
| | | left: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | top: 20%; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | top: 200px; |
| | | /* #endif */ |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: #FFFFFF; |
| | | border-top-left-radius: 10px; |
| | | border-top-right-radius: 10px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | z-index: 102; |
| | | overflow: hidden; |
| | | /* #ifdef APP-NVUE */ |
| | | width: 750rpx; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .dialog-caption { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | /* border-bottom: 1px solid #f0f0f0; */ |
| | | } |
| | | |
| | | .title-area { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | /* #ifndef APP-NVUE */ |
| | | margin: auto; |
| | | /* #endif */ |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .dialog-title { |
| | | /* font-weight: bold; */ |
| | | line-height: 44px; |
| | | } |
| | | |
| | | .dialog-close { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | padding: 0 15px; |
| | | } |
| | | |
| | | .dialog-close-plus { |
| | | width: 16px; |
| | | height: 2px; |
| | | background-color: #666; |
| | | border-radius: 2px; |
| | | transform: rotate(45deg); |
| | | } |
| | | |
| | | .dialog-close-rotate { |
| | | position: absolute; |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .picker-view { |
| | | flex: 1; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .icon-clear { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | /* #ifdef H5 */ |
| | | @media all and (min-width: 768px) { |
| | | .uni-data-tree-cover { |
| | | background-color: transparent; |
| | | } |
| | | |
| | | .uni-data-tree-dialog { |
| | | position: absolute; |
| | | top: 55px; |
| | | height: auto; |
| | | min-height: 400px; |
| | | max-height: 50vh; |
| | | background-color: #fff; |
| | | border: 1px solid #EBEEF5; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | border-radius: 4px; |
| | | overflow: unset; |
| | | } |
| | | |
| | | .dialog-caption { |
| | | display: none; |
| | | } |
| | | |
| | | .icon-clear { |
| | | /* margin-right: 5px; */ |
| | | } |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | /* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */ |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-popper__arrow, |
| | | .uni-popper__arrow::after { |
| | | position: absolute; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | border-color: transparent; |
| | | border-style: solid; |
| | | border-width: 6px; |
| | | } |
| | | |
| | | .uni-popper__arrow { |
| | | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
| | | top: -6px; |
| | | left: 10%; |
| | | margin-right: 3px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #EBEEF5; |
| | | } |
| | | |
| | | .uni-popper__arrow::after { |
| | | content: " "; |
| | | top: 1px; |
| | | margin-left: -6px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #fff; |
| | | } |
| | | |
| | | /* #endif */ |
| | | </style> |
对比新文件 |
| | |
| | | export default { |
| | | props: { |
| | | localdata: { |
| | | type: [Array, Object], |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | spaceInfo: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | collection: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | action: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | field: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | orderby: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | where: { |
| | | type: [String, Object], |
| | | default: '' |
| | | }, |
| | | pageData: { |
| | | type: String, |
| | | default: 'add' |
| | | }, |
| | | pageCurrent: { |
| | | type: Number, |
| | | default: 1 |
| | | }, |
| | | pageSize: { |
| | | type: Number, |
| | | default: 500 |
| | | }, |
| | | getcount: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | getone: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | gettree: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | manual: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | value: { |
| | | type: [Array, String, Number], |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | modelValue: { |
| | | type: [Array, String, Number], |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | preload: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | stepSearh: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | selfField: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | parentField: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | multiple: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | map: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | text: "text", |
| | | value: "value" |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | errorMessage: '', |
| | | loadMore: { |
| | | contentdown: '', |
| | | contentrefresh: '', |
| | | contentnomore: '' |
| | | }, |
| | | dataList: [], |
| | | selected: [], |
| | | selectedIndex: 0, |
| | | page: { |
| | | current: this.pageCurrent, |
| | | size: this.pageSize, |
| | | count: 0 |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | isLocalData() { |
| | | return !this.collection.length; |
| | | }, |
| | | isCloudData() { |
| | | return this.collection.length > 0; |
| | | }, |
| | | isCloudDataList() { |
| | | return (this.isCloudData && (!this.parentField && !this.selfField)); |
| | | }, |
| | | isCloudDataTree() { |
| | | return (this.isCloudData && this.parentField && this.selfField); |
| | | }, |
| | | dataValue() { |
| | | let isModelValue = Array.isArray(this.modelValue) ? (this.modelValue.length > 0) : (this.modelValue !== null || |
| | | this.modelValue !== undefined); |
| | | return isModelValue ? this.modelValue : this.value; |
| | | }, |
| | | hasValue() { |
| | | if (typeof this.dataValue === 'number') { |
| | | return true |
| | | } |
| | | return (this.dataValue != null) && (this.dataValue.length > 0) |
| | | } |
| | | }, |
| | | created() { |
| | | this.$watch(() => { |
| | | var al = []; |
| | | ['pageCurrent', |
| | | 'pageSize', |
| | | 'spaceInfo', |
| | | 'value', |
| | | 'modelValue', |
| | | 'localdata', |
| | | 'collection', |
| | | 'action', |
| | | 'field', |
| | | 'orderby', |
| | | 'where', |
| | | 'getont', |
| | | 'getcount', |
| | | 'gettree' |
| | | ].forEach(key => { |
| | | al.push(this[key]) |
| | | }); |
| | | return al |
| | | }, (newValue, oldValue) => { |
| | | let needReset = false |
| | | for (let i = 2; i < newValue.length; i++) { |
| | | if (newValue[i] != oldValue[i]) { |
| | | needReset = true |
| | | break |
| | | } |
| | | } |
| | | if (newValue[0] != oldValue[0]) { |
| | | this.page.current = this.pageCurrent |
| | | } |
| | | this.page.size = this.pageSize |
| | | |
| | | this.onPropsChange() |
| | | }) |
| | | this._treeData = [] |
| | | }, |
| | | methods: { |
| | | onPropsChange() { |
| | | this._treeData = []; |
| | | }, |
| | | |
| | | // 填充 pickview 数据 |
| | | async loadData() { |
| | | if (this.isLocalData) { |
| | | this.loadLocalData(); |
| | | } else if (this.isCloudDataList) { |
| | | this.loadCloudDataList(); |
| | | } else if (this.isCloudDataTree) { |
| | | this.loadCloudDataTree(); |
| | | } |
| | | }, |
| | | |
| | | // 加载本地数据 |
| | | async loadLocalData() { |
| | | this._treeData = []; |
| | | this._extractTree(this.localdata, this._treeData); |
| | | |
| | | let inputValue = this.dataValue; |
| | | if (inputValue === undefined) { |
| | | return; |
| | | } |
| | | |
| | | if (Array.isArray(inputValue)) { |
| | | inputValue = inputValue[inputValue.length - 1]; |
| | | if (typeof inputValue === 'object' && inputValue[this.map.value]) { |
| | | inputValue = inputValue[this.map.value]; |
| | | } |
| | | } |
| | | |
| | | this.selected = this._findNodePath(inputValue, this.localdata); |
| | | }, |
| | | |
| | | // 加载 Cloud 数据 (单列) |
| | | async loadCloudDataList() { |
| | | if (this.loading) { |
| | | return; |
| | | } |
| | | this.loading = true; |
| | | |
| | | try { |
| | | let response = await this.getCommand(); |
| | | let responseData = response.result.data; |
| | | |
| | | this._treeData = responseData; |
| | | |
| | | this._updateBindData(); |
| | | this._updateSelected(); |
| | | |
| | | this.onDataChange(); |
| | | } catch (e) { |
| | | this.errorMessage = e; |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | // 加载 Cloud 数据 (树形) |
| | | async loadCloudDataTree() { |
| | | if (this.loading) { |
| | | return; |
| | | } |
| | | this.loading = true; |
| | | |
| | | try { |
| | | let commandOptions = { |
| | | field: this._cloudDataPostField(), |
| | | where: this._cloudDataTreeWhere() |
| | | }; |
| | | if (this.gettree) { |
| | | commandOptions.startwith = `${this.selfField}=='${this.dataValue}'`; |
| | | } |
| | | |
| | | let response = await this.getCommand(commandOptions); |
| | | let responseData = response.result.data; |
| | | |
| | | this._treeData = responseData; |
| | | this._updateBindData(); |
| | | this._updateSelected(); |
| | | |
| | | this.onDataChange(); |
| | | } catch (e) { |
| | | this.errorMessage = e; |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | // 加载 Cloud 数据 (节点) |
| | | async loadCloudDataNode(callback) { |
| | | if (this.loading) { |
| | | return; |
| | | } |
| | | this.loading = true; |
| | | |
| | | try { |
| | | let commandOptions = { |
| | | field: this._cloudDataPostField(), |
| | | where: this._cloudDataNodeWhere() |
| | | }; |
| | | |
| | | let response = await this.getCommand(commandOptions); |
| | | let responseData = response.result.data; |
| | | |
| | | callback(responseData); |
| | | } catch (e) { |
| | | this.errorMessage = e; |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | // 回显 Cloud 数据 |
| | | getCloudDataValue() { |
| | | if (this.isCloudDataList) { |
| | | return this.getCloudDataListValue(); |
| | | } |
| | | |
| | | if (this.isCloudDataTree) { |
| | | return this.getCloudDataTreeValue(); |
| | | } |
| | | }, |
| | | |
| | | // 回显 Cloud 数据 (单列) |
| | | getCloudDataListValue() { |
| | | // 根据 field's as value标识匹配 where 条件 |
| | | let where = []; |
| | | let whereField = this._getForeignKeyByField(); |
| | | if (whereField) { |
| | | where.push(`${whereField} == '${this.dataValue}'`) |
| | | } |
| | | |
| | | where = where.join(' || '); |
| | | |
| | | if (this.where) { |
| | | where = `(${this.where}) && (${where})` |
| | | } |
| | | |
| | | return this.getCommand({ |
| | | field: this._cloudDataPostField(), |
| | | where |
| | | }).then((res) => { |
| | | this.selected = res.result.data; |
| | | return res.result.data; |
| | | }); |
| | | }, |
| | | |
| | | // 回显 Cloud 数据 (树形) |
| | | getCloudDataTreeValue() { |
| | | return this.getCommand({ |
| | | field: this._cloudDataPostField(), |
| | | getTreePath: { |
| | | startWith: `${this.selfField}=='${this.dataValue}'` |
| | | } |
| | | }).then((res) => { |
| | | let treePath = []; |
| | | this._extractTreePath(res.result.data, treePath); |
| | | this.selected = treePath; |
| | | return treePath; |
| | | }); |
| | | }, |
| | | |
| | | getCommand(options = {}) { |
| | | /* eslint-disable no-undef */ |
| | | let db = uniCloud.database(this.spaceInfo) |
| | | |
| | | const action = options.action || this.action |
| | | if (action) { |
| | | db = db.action(action) |
| | | } |
| | | |
| | | const collection = options.collection || this.collection |
| | | db = db.collection(collection) |
| | | |
| | | const where = options.where || this.where |
| | | if (!(!where || !Object.keys(where).length)) { |
| | | db = db.where(where) |
| | | } |
| | | |
| | | const field = options.field || this.field |
| | | if (field) { |
| | | db = db.field(field) |
| | | } |
| | | |
| | | const orderby = options.orderby || this.orderby |
| | | if (orderby) { |
| | | db = db.orderBy(orderby) |
| | | } |
| | | |
| | | const current = options.pageCurrent !== undefined ? options.pageCurrent : this.page.current |
| | | const size = options.pageSize !== undefined ? options.pageSize : this.page.size |
| | | const getCount = options.getcount !== undefined ? options.getcount : this.getcount |
| | | const getTree = options.gettree !== undefined ? options.gettree : this.gettree |
| | | |
| | | const getOptions = { |
| | | getCount, |
| | | getTree |
| | | } |
| | | if (options.getTreePath) { |
| | | getOptions.getTreePath = options.getTreePath |
| | | } |
| | | |
| | | db = db.skip(size * (current - 1)).limit(size).get(getOptions) |
| | | |
| | | return db |
| | | }, |
| | | |
| | | _cloudDataPostField() { |
| | | let fields = [this.field]; |
| | | if (this.parentField) { |
| | | fields.push(`${this.parentField} as parent_value`); |
| | | } |
| | | return fields.join(','); |
| | | }, |
| | | |
| | | _cloudDataTreeWhere() { |
| | | let result = [] |
| | | let selected = this.selected |
| | | let parentField = this.parentField |
| | | if (parentField) { |
| | | result.push(`${parentField} == null || ${parentField} == ""`) |
| | | } |
| | | if (selected.length) { |
| | | for (var i = 0; i < selected.length - 1; i++) { |
| | | result.push(`${parentField} == '${selected[i].value}'`) |
| | | } |
| | | } |
| | | |
| | | let where = [] |
| | | if (this.where) { |
| | | where.push(`(${this.where})`) |
| | | } |
| | | |
| | | if (result.length) { |
| | | where.push(`(${result.join(' || ')})`) |
| | | } |
| | | |
| | | return where.join(' && ') |
| | | }, |
| | | |
| | | _cloudDataNodeWhere() { |
| | | let where = [] |
| | | let selected = this.selected; |
| | | if (selected.length) { |
| | | where.push(`${this.parentField} == '${selected[selected.length - 1].value}'`); |
| | | } |
| | | |
| | | where = where.join(' || '); |
| | | |
| | | if (this.where) { |
| | | return `(${this.where}) && (${where})` |
| | | } |
| | | |
| | | return where |
| | | }, |
| | | |
| | | _getWhereByForeignKey() { |
| | | let result = [] |
| | | let whereField = this._getForeignKeyByField(); |
| | | if (whereField) { |
| | | result.push(`${whereField} == '${this.dataValue}'`) |
| | | } |
| | | |
| | | if (this.where) { |
| | | return `(${this.where}) && (${result.join(' || ')})` |
| | | } |
| | | |
| | | return result.join(' || ') |
| | | }, |
| | | |
| | | _getForeignKeyByField() { |
| | | let fields = this.field.split(','); |
| | | let whereField = null; |
| | | for (let i = 0; i < fields.length; i++) { |
| | | const items = fields[i].split('as'); |
| | | if (items.length < 2) { |
| | | continue; |
| | | } |
| | | if (items[1].trim() === 'value') { |
| | | whereField = items[0].trim(); |
| | | break; |
| | | } |
| | | } |
| | | return whereField; |
| | | }, |
| | | |
| | | _updateBindData(node) { |
| | | const { |
| | | dataList, |
| | | hasNodes |
| | | } = this._filterData(this._treeData, this.selected) |
| | | |
| | | let isleaf = this._stepSearh === false && !hasNodes |
| | | |
| | | if (node) { |
| | | node.isleaf = isleaf |
| | | } |
| | | |
| | | this.dataList = dataList |
| | | this.selectedIndex = dataList.length - 1 |
| | | |
| | | if (!isleaf && this.selected.length < dataList.length) { |
| | | this.selected.push({ |
| | | value: null, |
| | | text: "请选择" |
| | | }) |
| | | } |
| | | |
| | | return { |
| | | isleaf, |
| | | hasNodes |
| | | } |
| | | }, |
| | | |
| | | _updateSelected() { |
| | | let dl = this.dataList |
| | | let sl = this.selected |
| | | let textField = this.map.text |
| | | let valueField = this.map.value |
| | | for (let i = 0; i < sl.length; i++) { |
| | | let value = sl[i].value |
| | | let dl2 = dl[i] |
| | | for (let j = 0; j < dl2.length; j++) { |
| | | let item2 = dl2[j] |
| | | if (item2[valueField] === value) { |
| | | sl[i].text = item2[textField] |
| | | break |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | |
| | | _filterData(data, paths) { |
| | | let dataList = [] |
| | | let hasNodes = true |
| | | |
| | | dataList.push(data.filter((item) => { |
| | | return (item.parent_value === null || item.parent_value === undefined || item.parent_value === '') |
| | | })) |
| | | for (let i = 0; i < paths.length; i++) { |
| | | let value = paths[i].value |
| | | let nodes = data.filter((item) => { |
| | | return item.parent_value === value |
| | | }) |
| | | |
| | | if (nodes.length) { |
| | | dataList.push(nodes) |
| | | } else { |
| | | hasNodes = false |
| | | } |
| | | } |
| | | |
| | | return { |
| | | dataList, |
| | | hasNodes |
| | | } |
| | | }, |
| | | |
| | | _extractTree(nodes, result, parent_value) { |
| | | let list = result || [] |
| | | let valueField = this.map.value |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | let node = nodes[i] |
| | | |
| | | let child = {} |
| | | for (let key in node) { |
| | | if (key !== 'children') { |
| | | child[key] = node[key] |
| | | } |
| | | } |
| | | if (parent_value !== null && parent_value !== undefined && parent_value !== '') { |
| | | child.parent_value = parent_value |
| | | } |
| | | result.push(child) |
| | | |
| | | let children = node.children |
| | | if (children) { |
| | | this._extractTree(children, result, node[valueField]) |
| | | } |
| | | } |
| | | }, |
| | | |
| | | _extractTreePath(nodes, result) { |
| | | let list = result || [] |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | let node = nodes[i] |
| | | |
| | | let child = {} |
| | | for (let key in node) { |
| | | if (key !== 'children') { |
| | | child[key] = node[key] |
| | | } |
| | | } |
| | | result.push(child) |
| | | |
| | | let children = node.children |
| | | if (children) { |
| | | this._extractTreePath(children, result) |
| | | } |
| | | } |
| | | }, |
| | | |
| | | _findNodePath(key, nodes, path = []) { |
| | | let textField = this.map.text |
| | | let valueField = this.map.value |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | let node = nodes[i] |
| | | let children = node.children |
| | | let text = node[textField] |
| | | let value = node[valueField] |
| | | |
| | | path.push({ |
| | | value, |
| | | text |
| | | }) |
| | | |
| | | if (value === key) { |
| | | return path |
| | | } |
| | | |
| | | if (children) { |
| | | const p = this._findNodePath(key, children, path) |
| | | if (p.length) { |
| | | return p |
| | | } |
| | | } |
| | | |
| | | path.pop() |
| | | } |
| | | return [] |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | export type PaginationType = { |
| | | current : number, |
| | | size : number, |
| | | count : number |
| | | } |
| | | |
| | | export type LoadMoreType = { |
| | | contentdown : string, |
| | | contentrefresh : string, |
| | | contentnomore : string |
| | | } |
| | | |
| | | export type SelectedItemType = { |
| | | name : string, |
| | | value : string, |
| | | } |
| | | |
| | | export type GetCommandOptions = { |
| | | collection ?: UTSJSONObject, |
| | | field ?: string, |
| | | orderby ?: string, |
| | | where ?: any, |
| | | pageData ?: string, |
| | | pageCurrent ?: number, |
| | | pageSize ?: number, |
| | | getCount ?: boolean, |
| | | getTree ?: any, |
| | | getTreePath ?: UTSJSONObject, |
| | | startwith ?: string, |
| | | limitlevel ?: number, |
| | | groupby ?: string, |
| | | groupField ?: string, |
| | | distinct ?: boolean, |
| | | pageIndistinct ?: boolean, |
| | | foreignKey ?: string, |
| | | loadtime ?: string, |
| | | manual ?: boolean |
| | | } |
| | | |
| | | const DefaultSelectedNode = { |
| | | text: '请选择', |
| | | value: '' |
| | | } |
| | | |
| | | export const dataPicker = defineMixin({ |
| | | props: { |
| | | localdata: { |
| | | type: Array as PropType<Array<UTSJSONObject>>, |
| | | default: [] as Array<UTSJSONObject> |
| | | }, |
| | | collection: { |
| | | type: Object, |
| | | default: '' |
| | | }, |
| | | field: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | orderby: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | where: { |
| | | type: Object, |
| | | default: '' |
| | | }, |
| | | pageData: { |
| | | type: String, |
| | | default: 'add' |
| | | }, |
| | | pageCurrent: { |
| | | type: Number, |
| | | default: 1 |
| | | }, |
| | | pageSize: { |
| | | type: Number, |
| | | default: 20 |
| | | }, |
| | | getcount: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | gettree: { |
| | | type: Object, |
| | | default: '' |
| | | }, |
| | | gettreepath: { |
| | | type: Object, |
| | | default: '' |
| | | }, |
| | | startwith: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | limitlevel: { |
| | | type: Number, |
| | | default: 10 |
| | | }, |
| | | groupby: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | groupField: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | distinct: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | pageIndistinct: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | foreignKey: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | loadtime: { |
| | | type: String, |
| | | default: 'auto' |
| | | }, |
| | | manual: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | preload: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | stepSearh: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | selfField: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | parentField: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | multiple: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | value: { |
| | | type: Object, |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: Object, |
| | | default: '' |
| | | }, |
| | | defaultProps: { |
| | | type: Object as PropType<UTSJSONObject>, |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | error: null as UniCloudError | null, |
| | | treeData: [] as Array<UTSJSONObject>, |
| | | selectedIndex: 0, |
| | | selectedNodes: [] as Array<UTSJSONObject>, |
| | | selectedPages: [] as Array<UTSJSONObject>[], |
| | | selectedValue: '', |
| | | selectedPaths: [] as Array<UTSJSONObject>, |
| | | pagination: { |
| | | current: 1, |
| | | size: 20, |
| | | count: 0 |
| | | } as PaginationType |
| | | } |
| | | }, |
| | | computed: { |
| | | mappingTextName() : string { |
| | | // TODO |
| | | return (this.defaultProps != null) ? this.defaultProps!.getString('text', 'text') : 'text' |
| | | }, |
| | | mappingValueName() : string { |
| | | // TODO |
| | | return (this.defaultProps != null) ? this.defaultProps!.getString('value', 'value') : 'value' |
| | | }, |
| | | currentDataList() : Array<UTSJSONObject> { |
| | | if (this.selectedIndex > this.selectedPages.length - 1) { |
| | | return [] as Array<UTSJSONObject> |
| | | } |
| | | return this.selectedPages[this.selectedIndex] |
| | | }, |
| | | isLocalData() : boolean { |
| | | return this.localdata.length > 0 |
| | | }, |
| | | isCloudData() : boolean { |
| | | return this._checkIsNotNull(this.collection) |
| | | }, |
| | | isCloudDataList() : boolean { |
| | | return (this.isCloudData && (this.parentField.length == 0 && this.selfField.length == 0)) |
| | | }, |
| | | isCloudDataTree() : boolean { |
| | | return (this.isCloudData && this.parentField.length > 0 && this.selfField.length > 0) |
| | | }, |
| | | dataValue() : any { |
| | | return this.hasModelValue ? this.modelValue : this.value |
| | | }, |
| | | hasCloudTreeData() : boolean { |
| | | return this.treeData.length > 0 |
| | | }, |
| | | hasModelValue() : boolean { |
| | | if (typeof this.modelValue == 'string') { |
| | | const valueString = this.modelValue as string |
| | | return (valueString.length > 0) |
| | | } else if (Array.isArray(this.modelValue)) { |
| | | const valueArray = this.modelValue as Array<string> |
| | | return (valueArray.length > 0) |
| | | } |
| | | return false |
| | | }, |
| | | hasCloudDataValue() : boolean { |
| | | if (typeof this.dataValue == 'string') { |
| | | const valueString = this.dataValue as string |
| | | return (valueString.length > 0) |
| | | } |
| | | return false |
| | | } |
| | | }, |
| | | created() { |
| | | this.pagination.current = this.pageCurrent |
| | | this.pagination.size = this.pageSize |
| | | |
| | | this.$watch( |
| | | () : any => [ |
| | | this.pageCurrent, |
| | | this.pageSize, |
| | | this.localdata, |
| | | this.value, |
| | | this.collection, |
| | | this.field, |
| | | this.getcount, |
| | | this.orderby, |
| | | this.where, |
| | | this.groupby, |
| | | this.groupField, |
| | | this.distinct |
| | | ], |
| | | (newValue : Array<any>, oldValue : Array<any>) => { |
| | | this.pagination.size = this.pageSize |
| | | if (newValue[0] !== oldValue[0]) { |
| | | this.pagination.current = this.pageCurrent |
| | | } |
| | | |
| | | this.onPropsChange() |
| | | } |
| | | ) |
| | | }, |
| | | methods: { |
| | | onPropsChange() { |
| | | this.selectedIndex = 0 |
| | | this.selectedNodes.length = 0 |
| | | this.selectedPages.length = 0 |
| | | this.selectedPaths.length = 0 |
| | | |
| | | // 加载数据 |
| | | this.$nextTick(() => { |
| | | this.loadData() |
| | | }) |
| | | }, |
| | | |
| | | onTabSelect(index : number) { |
| | | this.selectedIndex = index |
| | | }, |
| | | |
| | | onNodeClick(nodeData : UTSJSONObject) { |
| | | if (nodeData.getBoolean('disable', false)) { |
| | | return |
| | | } |
| | | |
| | | const isLeaf = this._checkIsLeafNode(nodeData) |
| | | |
| | | this._trimSelectedNodes(nodeData) |
| | | |
| | | this.$emit('nodeclick', nodeData) |
| | | |
| | | if (this.isLocalData) { |
| | | if (isLeaf || !this._checkHasChildren(nodeData)) { |
| | | this.onFinish() |
| | | } |
| | | } else if (this.isCloudDataList) { |
| | | this.onFinish() |
| | | } else if (this.isCloudDataTree) { |
| | | if (isLeaf) { |
| | | this.onFinish() |
| | | } else if (!this._checkHasChildren(nodeData)) { |
| | | // 尝试请求一次,如果没有返回数据标记为叶子节点 |
| | | this.loadCloudDataNode(nodeData) |
| | | } |
| | | } |
| | | }, |
| | | |
| | | getChangeNodes(): Array<UTSJSONObject> { |
| | | const nodes: Array<UTSJSONObject> = [] |
| | | this.selectedNodes.forEach((node : UTSJSONObject) => { |
| | | const newNode: UTSJSONObject = {} |
| | | newNode[this.mappingTextName] = node.getString(this.mappingTextName) |
| | | newNode[this.mappingValueName] = node.getString(this.mappingValueName) |
| | | nodes.push(newNode) |
| | | }) |
| | | return nodes |
| | | }, |
| | | |
| | | onFinish() { }, |
| | | |
| | | // 加载数据(自动判定环境) |
| | | loadData() { |
| | | if (this.isLocalData) { |
| | | this.loadLocalData() |
| | | } else if (this.isCloudDataList) { |
| | | this.loadCloudDataList() |
| | | } else if (this.isCloudDataTree) { |
| | | this.loadCloudDataTree() |
| | | } |
| | | }, |
| | | |
| | | // 加载本地数据 |
| | | loadLocalData() { |
| | | this.treeData = this.localdata |
| | | if (Array.isArray(this.dataValue)) { |
| | | const value = this.dataValue as Array<UTSJSONObject> |
| | | this.selectedPaths = value.slice(0) |
| | | this._pushSelectedTreeNodes(value, this.localdata) |
| | | } else { |
| | | this._pushSelectedNodes(this.localdata) |
| | | } |
| | | }, |
| | | |
| | | // 加载 Cloud 数据 (单列) |
| | | loadCloudDataList() { |
| | | this._loadCloudData(null, (data : Array<UTSJSONObject>) => { |
| | | this.treeData = data |
| | | this._pushSelectedNodes(data) |
| | | }) |
| | | }, |
| | | |
| | | // 加载 Cloud 数据 (树形) |
| | | loadCloudDataTree() { |
| | | let commandOptions = { |
| | | field: this._cloudDataPostField(), |
| | | where: this._cloudDataTreeWhere(), |
| | | getTree: true |
| | | } as GetCommandOptions |
| | | if (this._checkIsNotNull(this.gettree)) { |
| | | commandOptions.startwith = `${this.selfField}=='${this.dataValue as string}'` |
| | | } |
| | | this._loadCloudData(commandOptions, (data : Array<UTSJSONObject>) => { |
| | | this.treeData = data |
| | | if (this.selectedPaths.length > 0) { |
| | | this._pushSelectedTreeNodes(this.selectedPaths, data) |
| | | } else { |
| | | this._pushSelectedNodes(data) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // 加载 Cloud 数据 (节点) |
| | | loadCloudDataNode(nodeData : UTSJSONObject) { |
| | | const commandOptions = { |
| | | field: this._cloudDataPostField(), |
| | | where: this._cloudDataNodeWhere() |
| | | } as GetCommandOptions |
| | | this._loadCloudData(commandOptions, (data : Array<UTSJSONObject>) => { |
| | | nodeData['children'] = data |
| | | if (data.length == 0) { |
| | | nodeData['isleaf'] = true |
| | | this.onFinish() |
| | | } else { |
| | | this._pushSelectedNodes(data) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // 回显 Cloud Tree Path |
| | | loadCloudDataPath() { |
| | | if (!this.hasCloudDataValue) { |
| | | return |
| | | } |
| | | |
| | | const command : GetCommandOptions = {} |
| | | |
| | | // 单列 |
| | | if (this.isCloudDataList) { |
| | | // 根据 field's as value标识匹配 where 条件 |
| | | let where : Array<string> = []; |
| | | let whereField = this._getForeignKeyByField(); |
| | | if (whereField.length > 0) { |
| | | where.push(`${whereField} == '${this.dataValue as string}'`) |
| | | } |
| | | |
| | | let whereString = where.join(' || ') |
| | | if (this._checkIsNotNull(this.where)) { |
| | | whereString = `(${this.where}) && (${whereString})` |
| | | } |
| | | |
| | | command.field = this._cloudDataPostField() |
| | | command.where = whereString |
| | | } |
| | | |
| | | // 树形 |
| | | if (this.isCloudDataTree) { |
| | | command.field = this._cloudDataPostField() |
| | | command.getTreePath = { |
| | | startWith: `${this.selfField}=='${this.dataValue as string}'` |
| | | } |
| | | } |
| | | |
| | | this._loadCloudData(command, (data : Array<UTSJSONObject>) => { |
| | | this._extractTreePath(data, this.selectedPaths) |
| | | }) |
| | | }, |
| | | |
| | | _loadCloudData(options ?: GetCommandOptions, callback ?: ((data : Array<UTSJSONObject>) => void)) { |
| | | if (this.loading) { |
| | | return |
| | | } |
| | | this.loading = true |
| | | |
| | | this.error = null |
| | | |
| | | this._getCommand(options).then((response : UniCloudDBGetResult) => { |
| | | callback?.(response.data) |
| | | }).catch((err : any | null) => { |
| | | this.error = err as UniCloudError |
| | | }).finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | |
| | | _cloudDataPostField() : string { |
| | | let fields = [this.field]; |
| | | if (this.parentField.length > 0) { |
| | | fields.push(`${this.parentField} as parent_value`) |
| | | } |
| | | return fields.join(',') |
| | | }, |
| | | |
| | | _cloudDataTreeWhere() : string { |
| | | let result : Array<string> = [] |
| | | let selectedNodes = this.selectedNodes.length > 0 ? this.selectedNodes : this.selectedPaths |
| | | let parentField = this.parentField |
| | | if (parentField.length > 0) { |
| | | result.push(`${parentField} == null || ${parentField} == ""`) |
| | | } |
| | | if (selectedNodes.length > 0) { |
| | | for (var i = 0; i < selectedNodes.length - 1; i++) { |
| | | const parentFieldValue = selectedNodes[i].getString('value', '') |
| | | result.push(`${parentField} == '${parentFieldValue}'`) |
| | | } |
| | | } |
| | | |
| | | let where : Array<string> = [] |
| | | if (this._checkIsNotNull(this.where)) { |
| | | where.push(`(${this.where as string})`) |
| | | } |
| | | |
| | | if (result.length > 0) { |
| | | where.push(`(${result.join(' || ')})`) |
| | | } |
| | | |
| | | return where.join(' && ') |
| | | }, |
| | | |
| | | _cloudDataNodeWhere() : string { |
| | | const where : Array<string> = [] |
| | | if (this.selectedNodes.length > 0) { |
| | | const value = this.selectedNodes[this.selectedNodes.length - 1].getString('value', '') |
| | | where.push(`${this.parentField} == '${value}'`) |
| | | } |
| | | |
| | | let whereString = where.join(' || ') |
| | | if (this._checkIsNotNull(this.where)) { |
| | | return `(${this.where as string}) && (${whereString})` |
| | | } |
| | | |
| | | return whereString |
| | | }, |
| | | |
| | | _getWhereByForeignKey() : string { |
| | | let result : Array<string> = [] |
| | | let whereField = this._getForeignKeyByField(); |
| | | if (whereField.length > 0) { |
| | | result.push(`${whereField} == '${this.dataValue as string}'`) |
| | | } |
| | | |
| | | if (this._checkIsNotNull(this.where)) { |
| | | return `(${this.where}) && (${result.join(' || ')})` |
| | | } |
| | | |
| | | return result.join(' || ') |
| | | }, |
| | | |
| | | _getForeignKeyByField() : string { |
| | | const fields = this.field.split(',') |
| | | let whereField = '' |
| | | for (let i = 0; i < fields.length; i++) { |
| | | const items = fields[i].split('as') |
| | | if (items.length < 2) { |
| | | continue |
| | | } |
| | | if (items[1].trim() === 'value') { |
| | | whereField = items[0].trim() |
| | | break |
| | | } |
| | | } |
| | | return whereField |
| | | }, |
| | | |
| | | _getCommand(options ?: GetCommandOptions) : Promise<UniCloudDBGetResult> { |
| | | let db = uniCloud.databaseForJQL() |
| | | |
| | | let collection = Array.isArray(this.collection) ? db.collection(...(this.collection as Array<any>)) : db.collection(this.collection) |
| | | |
| | | let filter : UniCloudDBFilter | null = null |
| | | if (this.foreignKey.length > 0) { |
| | | filter = collection.foreignKey(this.foreignKey) |
| | | } |
| | | |
| | | const where : any = options?.where ?? this.where |
| | | if (typeof where == 'string') { |
| | | const whereString = where as string |
| | | if (whereString.length > 0) { |
| | | filter = (filter != null) ? filter.where(where) : collection.where(where) |
| | | } |
| | | } else { |
| | | filter = (filter != null) ? filter.where(where) : collection.where(where) |
| | | } |
| | | |
| | | let query : UniCloudDBQuery | null = null |
| | | if (this.field.length > 0) { |
| | | query = (filter != null) ? filter.field(this.field) : collection.field(this.field) |
| | | } |
| | | if (this.groupby.length > 0) { |
| | | if (query != null) { |
| | | query = query.groupBy(this.groupby) |
| | | } else if (filter != null) { |
| | | query = filter.groupBy(this.groupby) |
| | | } |
| | | } |
| | | if (this.groupField.length > 0) { |
| | | if (query != null) { |
| | | query = query.groupField(this.groupField) |
| | | } else if (filter != null) { |
| | | query = filter.groupField(this.groupField) |
| | | } |
| | | } |
| | | if (this.distinct == true) { |
| | | if (query != null) { |
| | | query = query.distinct(this.field) |
| | | } else if (filter != null) { |
| | | query = filter.distinct(this.field) |
| | | } |
| | | } |
| | | if (this.orderby.length > 0) { |
| | | if (query != null) { |
| | | query = query.orderBy(this.orderby) |
| | | } else if (filter != null) { |
| | | query = filter.orderBy(this.orderby) |
| | | } |
| | | } |
| | | |
| | | const size = this.pagination.size |
| | | const current = this.pagination.current |
| | | if (query != null) { |
| | | query = query.skip(size * (current - 1)).limit(size) |
| | | } else if (filter != null) { |
| | | query = filter.skip(size * (current - 1)).limit(size) |
| | | } else { |
| | | query = collection.skip(size * (current - 1)).limit(size) |
| | | } |
| | | |
| | | const getOptions = {} |
| | | const treeOptions = { |
| | | limitLevel: this.limitlevel, |
| | | startWith: this.startwith |
| | | } |
| | | if (this.getcount == true) { |
| | | getOptions['getCount'] = this.getcount |
| | | } |
| | | |
| | | const getTree : any = options?.getTree ?? this.gettree |
| | | if (typeof getTree == 'string') { |
| | | const getTreeString = getTree as string |
| | | if (getTreeString.length > 0) { |
| | | getOptions['getTree'] = treeOptions |
| | | } |
| | | } else if (typeof getTree == 'object') { |
| | | getOptions['getTree'] = treeOptions |
| | | } else { |
| | | getOptions['getTree'] = getTree |
| | | } |
| | | |
| | | const getTreePath = options?.getTreePath ?? this.gettreepath |
| | | if (typeof getTreePath == 'string') { |
| | | const getTreePathString = getTreePath as string |
| | | if (getTreePathString.length > 0) { |
| | | getOptions['getTreePath'] = getTreePath |
| | | } |
| | | } else { |
| | | getOptions['getTreePath'] = getTreePath |
| | | } |
| | | |
| | | return query.get(getOptions) |
| | | }, |
| | | |
| | | _checkIsNotNull(value : any) : boolean { |
| | | if (typeof value == 'string') { |
| | | const valueString = value as string |
| | | return (valueString.length > 0) |
| | | } else if (value instanceof UTSJSONObject) { |
| | | return true |
| | | } |
| | | return false |
| | | }, |
| | | |
| | | _checkIsLeafNode(nodeData : UTSJSONObject) : boolean { |
| | | if (this.selectedIndex >= this.limitlevel) { |
| | | return true |
| | | } |
| | | |
| | | if (nodeData.getBoolean('isleaf', false)) { |
| | | return true |
| | | } |
| | | |
| | | return false |
| | | }, |
| | | |
| | | _checkHasChildren(nodeData : UTSJSONObject) : boolean { |
| | | const children = nodeData.getArray('children') ?? ([] as Array<any>) |
| | | return children.length > 0 |
| | | }, |
| | | |
| | | _pushSelectedNodes(nodes : Array<UTSJSONObject>) { |
| | | this.selectedNodes.push(DefaultSelectedNode) |
| | | this.selectedPages.push(nodes) |
| | | this.selectedIndex = this.selectedPages.length - 1 |
| | | }, |
| | | |
| | | _trimSelectedNodes(nodeData : UTSJSONObject) { |
| | | this.selectedNodes.splice(this.selectedIndex) |
| | | this.selectedNodes.push(nodeData) |
| | | |
| | | if (this.selectedPages.length > 0) { |
| | | this.selectedPages.splice(this.selectedIndex + 1) |
| | | } |
| | | |
| | | const children = nodeData.getArray<UTSJSONObject>('children') ?? ([] as Array<UTSJSONObject>) |
| | | if (children.length > 0) { |
| | | this.selectedNodes.push(DefaultSelectedNode) |
| | | this.selectedPages.push(children) |
| | | } |
| | | |
| | | this.selectedIndex = this.selectedPages.length - 1 |
| | | }, |
| | | |
| | | _pushSelectedTreeNodes(paths : Array<UTSJSONObject>, nodes : Array<UTSJSONObject>) { |
| | | let children : Array<UTSJSONObject> = nodes |
| | | paths.forEach((node : UTSJSONObject) => { |
| | | const findNode = children.find((item : UTSJSONObject) : boolean => { |
| | | return (item.getString(this.mappingValueName) == node.getString(this.mappingValueName)) |
| | | }) |
| | | if (findNode != null) { |
| | | this.selectedPages.push(children) |
| | | this.selectedNodes.push(node) |
| | | children = findNode.getArray<UTSJSONObject>('children') ?? ([] as Array<UTSJSONObject>) |
| | | } |
| | | }) |
| | | this.selectedIndex = this.selectedPages.length - 1 |
| | | }, |
| | | |
| | | _extractTreePath(nodes : Array<UTSJSONObject>, result : Array<UTSJSONObject>) { |
| | | if (nodes.length == 0) { |
| | | return |
| | | } |
| | | |
| | | const node = nodes[0] |
| | | result.push(node) |
| | | |
| | | const children = node.getArray<UTSJSONObject>('children') |
| | | if (Array.isArray(children) && children!.length > 0) { |
| | | this._extractTreePath(children, result) |
| | | } |
| | | } |
| | | } |
| | | }) |
对比新文件 |
| | |
| | | .uni-data-pickerview { |
| | | position: relative; |
| | | flex-direction: column; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .loading-cover { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background-color: rgba(150, 150, 150, .1); |
| | | } |
| | | |
| | | .error { |
| | | background-color: #fff; |
| | | padding: 15px; |
| | | } |
| | | |
| | | .error-text { |
| | | color: #DD524D; |
| | | } |
| | | |
| | | .selected-node-list { |
| | | flex-direction: row; |
| | | flex-wrap: nowrap; |
| | | } |
| | | |
| | | .selected-node-item { |
| | | margin-left: 10px; |
| | | margin-right: 10px; |
| | | padding: 8px 10px 8px 10px; |
| | | border-bottom: 2px solid transparent; |
| | | } |
| | | |
| | | .selected-node-item-active { |
| | | color: #007aff; |
| | | border-bottom-color: #007aff; |
| | | } |
| | | |
| | | .list-view { |
| | | flex: 1; |
| | | } |
| | | |
| | | .list-item { |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | padding: 12px 15px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .item-text { |
| | | color: #333333; |
| | | } |
| | | |
| | | .item-text-disabled { |
| | | opacity: .5; |
| | | } |
| | | |
| | | .item-text-overflow { |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .check { |
| | | margin-right: 5px; |
| | | border: 2px solid #007aff; |
| | | border-left: 0; |
| | | border-top: 0; |
| | | height: 12px; |
| | | width: 6px; |
| | | transform-origin: center; |
| | | transform: rotate(45deg); |
| | | } |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-data-pickerview"> |
| | | <view v-if="error!=null" class="error"> |
| | | <text class="error-text">{{error!.errMsg}}</text> |
| | | </view> |
| | | <scroll-view v-if="!isCloudDataList" :scroll-x="true"> |
| | | <view class="selected-node-list"> |
| | | <template v-for="(item, index) in selectedNodes"> |
| | | <text class="selected-node-item" :class="{'selected-node-item-active':index==selectedIndex}" |
| | | @click="onTabSelect(index)"> |
| | | {{item[mappingTextName]}} |
| | | </text> |
| | | </template> |
| | | </view> |
| | | </scroll-view> |
| | | <list-view class="list-view" :scroll-y="true"> |
| | | <list-item class="list-item" v-for="(item, _) in currentDataList" @click="onNodeClick(item)"> |
| | | <text class="item-text" :class="{'item-text-disabled': item['disable']}">{{item[mappingTextName]}}</text> |
| | | <text class="check" v-if="item[mappingValueName] == selectedNodes[selectedIndex][mappingValueName]"></text> |
| | | </list-item> |
| | | </list-view> |
| | | <view class="loading-cover" v-if="loading"> |
| | | <slot name="pickerview-loading" :loading="loading"></slot> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { dataPicker } from "./uni-data-picker.uts" |
| | | |
| | | /** |
| | | * DataPickerview |
| | | * @description uni-data-pickerview |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3796 |
| | | * @property {Array} localdata 本地数据,参考 |
| | | * @property {Boolean} step-searh = [true|false] 是否分布查询 |
| | | * @value true 启用分布查询,仅查询当前选中节点 |
| | | * @value false 关闭分布查询,一次查询出所有数据 |
| | | * @property {String|DBFieldString} self-field 分布查询当前字段名称 |
| | | * @property {String|DBFieldString} parent-field 分布查询父字段名称 |
| | | * @property {String|DBCollectionString} collection 表名 |
| | | * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 |
| | | * @property {String} orderby 排序字段及正序倒叙设置 |
| | | * @property {String|JQLString} where 查询条件 |
| | | */ |
| | | export default { |
| | | name: 'UniDataPickerView', |
| | | emits: ['nodeclick', 'change', 'update:modelValue'], |
| | | mixins: [dataPicker], |
| | | props: { |
| | | ellipsis: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | created() { |
| | | this.loadData() |
| | | }, |
| | | methods: { |
| | | onFinish() { |
| | | this.$emit('change', this.getChangeNodes()) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | @import url("uni-data-pickerview.css"); |
| | | </style> |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-data-pickerview"> |
| | | <scroll-view v-if="!isCloudDataList" class="selected-area" scroll-x="true"> |
| | | <view class="selected-list"> |
| | | <view |
| | | class="selected-item" |
| | | v-for="(item,index) in selected" |
| | | :key="index" |
| | | :class="{ |
| | | 'selected-item-active':index == selectedIndex |
| | | }" |
| | | @click="handleSelect(index)" |
| | | > |
| | | <text>{{item.text || ''}}</text> |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | <view class="tab-c"> |
| | | <scroll-view class="list" :scroll-y="true"> |
| | | <view class="item" :class="{'is-disabled': !!item.disable}" v-for="(item, j) in dataList[selectedIndex]" :key="j" |
| | | @click="handleNodeClick(item, selectedIndex, j)"> |
| | | <text class="item-text">{{item[map.text]}}</text> |
| | | <view class="check" v-if="selected.length > selectedIndex && item[map.value] == selected[selectedIndex].value"></view> |
| | | </view> |
| | | </scroll-view> |
| | | |
| | | <view class="loading-cover" v-if="loading"> |
| | | <uni-load-more class="load-more" :contentText="loadMore" status="loading"></uni-load-more> |
| | | </view> |
| | | <view class="error-message" v-if="errorMessage"> |
| | | <text class="error-text">{{errorMessage}}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import dataPicker from "./uni-data-picker.js" |
| | | |
| | | /** |
| | | * DataPickerview |
| | | * @description uni-data-pickerview |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3796 |
| | | * @property {Array} localdata 本地数据,参考 |
| | | * @property {Boolean} step-searh = [true|false] 是否分布查询 |
| | | * @value true 启用分布查询,仅查询当前选中节点 |
| | | * @value false 关闭分布查询,一次查询出所有数据 |
| | | * @property {String|DBFieldString} self-field 分布查询当前字段名称 |
| | | * @property {String|DBFieldString} parent-field 分布查询父字段名称 |
| | | * @property {String|DBCollectionString} collection 表名 |
| | | * @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割 |
| | | * @property {String} orderby 排序字段及正序倒叙设置 |
| | | * @property {String|JQLString} where 查询条件 |
| | | */ |
| | | export default { |
| | | name: 'UniDataPickerView', |
| | | emits: ['nodeclick', 'change', 'datachange', 'update:modelValue'], |
| | | mixins: [dataPicker], |
| | | props: { |
| | | managedMode: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | ellipsis: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | created() { |
| | | if (!this.managedMode) { |
| | | this.$nextTick(() => { |
| | | this.loadData(); |
| | | }) |
| | | } |
| | | }, |
| | | methods: { |
| | | onPropsChange() { |
| | | this._treeData = []; |
| | | this.selectedIndex = 0; |
| | | this.$nextTick(() => { |
| | | this.loadData(); |
| | | }) |
| | | }, |
| | | handleSelect(index) { |
| | | this.selectedIndex = index; |
| | | }, |
| | | handleNodeClick(item, i, j) { |
| | | if (item.disable) { |
| | | return; |
| | | } |
| | | |
| | | const node = this.dataList[i][j]; |
| | | const text = node[this.map.text]; |
| | | const value = node[this.map.value]; |
| | | |
| | | if (i < this.selected.length - 1) { |
| | | this.selected.splice(i, this.selected.length - i) |
| | | this.selected.push({ |
| | | text, |
| | | value |
| | | }) |
| | | } else if (i === this.selected.length - 1) { |
| | | this.selected.splice(i, 1, { |
| | | text, |
| | | value |
| | | }) |
| | | } |
| | | |
| | | if (node.isleaf) { |
| | | this.onSelectedChange(node, node.isleaf) |
| | | return |
| | | } |
| | | |
| | | const { |
| | | isleaf, |
| | | hasNodes |
| | | } = this._updateBindData() |
| | | |
| | | // 本地数据 |
| | | if (this.isLocalData) { |
| | | this.onSelectedChange(node, (!hasNodes || isleaf)) |
| | | } else if (this.isCloudDataList) { // Cloud 数据 (单列) |
| | | this.onSelectedChange(node, true) |
| | | } else if (this.isCloudDataTree) { // Cloud 数据 (树形) |
| | | if (isleaf) { |
| | | this.onSelectedChange(node, node.isleaf) |
| | | } else if (!hasNodes) { // 请求一次服务器以确定是否为叶子节点 |
| | | this.loadCloudDataNode((data) => { |
| | | if (!data.length) { |
| | | node.isleaf = true |
| | | } else { |
| | | this._treeData.push(...data) |
| | | this._updateBindData(node) |
| | | } |
| | | this.onSelectedChange(node, node.isleaf) |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | updateData(data) { |
| | | this._treeData = data.treeData |
| | | this.selected = data.selected |
| | | if (!this._treeData.length) { |
| | | this.loadData() |
| | | } else { |
| | | //this.selected = data.selected |
| | | this._updateBindData() |
| | | } |
| | | }, |
| | | onDataChange() { |
| | | this.$emit('datachange'); |
| | | }, |
| | | onSelectedChange(node, isleaf) { |
| | | if (isleaf) { |
| | | this._dispatchEvent() |
| | | } |
| | | |
| | | if (node) { |
| | | this.$emit('nodeclick', node) |
| | | } |
| | | }, |
| | | _dispatchEvent() { |
| | | this.$emit('change', this.selected.slice(0)) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-primary: #007aff !default; |
| | | |
| | | .uni-data-pickerview { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | overflow: hidden; |
| | | height: 100%; |
| | | } |
| | | |
| | | .error-text { |
| | | color: #DD524D; |
| | | } |
| | | |
| | | .loading-cover { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: rgba(255, 255, 255, .5); |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | align-items: center; |
| | | z-index: 1001; |
| | | } |
| | | |
| | | .load-more { |
| | | /* #ifndef APP-NVUE */ |
| | | margin: auto; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .error-message { |
| | | background-color: #fff; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | padding: 15px; |
| | | opacity: .9; |
| | | z-index: 102; |
| | | } |
| | | |
| | | /* #ifdef APP-NVUE */ |
| | | .selected-area { |
| | | width: 750rpx; |
| | | } |
| | | /* #endif */ |
| | | |
| | | .selected-list { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | flex-wrap: nowrap; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | padding: 0 5px; |
| | | border-bottom: 1px solid #f8f8f8; |
| | | } |
| | | |
| | | .selected-item { |
| | | margin-left: 10px; |
| | | margin-right: 10px; |
| | | padding: 12px 0; |
| | | text-align: center; |
| | | /* #ifndef APP-NVUE */ |
| | | white-space: nowrap; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .selected-item-text-overflow { |
| | | width: 168px; |
| | | /* fix nvue */ |
| | | overflow: hidden; |
| | | /* #ifndef APP-NVUE */ |
| | | width: 6em; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | -o-text-overflow: ellipsis; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .selected-item-active { |
| | | border-bottom: 2px solid $uni-primary; |
| | | } |
| | | |
| | | .selected-item-text { |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | .tab-c { |
| | | position: relative; |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .list { |
| | | flex: 1; |
| | | } |
| | | |
| | | .item { |
| | | padding: 12px 15px; |
| | | /* border-bottom: 1px solid #f0f0f0; */ |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .is-disabled { |
| | | opacity: .5; |
| | | } |
| | | |
| | | .item-text { |
| | | /* flex: 1; */ |
| | | color: #333333; |
| | | } |
| | | |
| | | .item-text-overflow { |
| | | width: 280px; |
| | | /* fix nvue */ |
| | | overflow: hidden; |
| | | /* #ifndef APP-NVUE */ |
| | | width: 20em; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | -o-text-overflow: ellipsis; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .check { |
| | | margin-right: 5px; |
| | | border: 2px solid $uni-primary; |
| | | border-left: 0; |
| | | border-top: 0; |
| | | height: 12px; |
| | | width: 6px; |
| | | transform-origin: center; |
| | | /* #ifndef APP-NVUE */ |
| | | transition: all 0.3s; |
| | | /* #endif */ |
| | | transform: rotate(45deg); |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | { |
| | | "id": "uni-data-picker", |
| | | "displayName": "uni-data-picker 数据驱动的picker选择器", |
| | | "version": "2.0.1", |
| | | "description": "单列、多列级联选择器,常用于省市区城市选择、公司部门选择、多级分类等场景", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "picker", |
| | | "级联", |
| | | "省市区", |
| | | "" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": [ |
| | | "uni-load-more", |
| | | "uni-icons", |
| | | "uni-scss" |
| | | ], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y", |
| | | "alipay": "n" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | ## DataPicker 级联选择 |
| | | > **组件名:uni-data-picker** |
| | | > 代码块: `uDataPicker` |
| | | > 关联组件:`uni-data-pickerview`、`uni-load-more`。 |
| | | |
| | | |
| | | `<uni-data-picker>` 是一个选择类[datacom组件](https://uniapp.dcloud.net.cn/component/datacom)。 |
| | | |
| | | 支持单列、和多列级联选择。列数没有限制,如果屏幕显示不全,顶部tab区域会左右滚动。 |
| | | |
| | | 候选数据支持一次性加载完毕,也支持懒加载,比如示例图中,选择了“北京”后,动态加载北京的区县数据。 |
| | | |
| | | `<uni-data-picker>` 组件尤其适用于地址选择、分类选择等选择类。 |
| | | |
| | | `<uni-data-picker>` 支持本地数据、云端静态数据(json),uniCloud云数据库数据。 |
| | | |
| | | `<uni-data-picker>` 可以通过JQL直连uniCloud云数据库,配套[DB Schema](https://uniapp.dcloud.net.cn/uniCloud/schema),可在schema2code中自动生成前端页面,还支持服务器端校验。 |
| | | |
| | | 在uniCloud数据表中新建表“uni-id-address”和“opendb-city-china”,这2个表的schema自带foreignKey关联。在“uni-id-address”表的表结构页面使用schema2code生成前端页面,会自动生成地址管理的维护页面,自动从“opendb-city-china”表包含的中国所有省市区信息里选择地址。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-picker) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
对比新文件 |
| | |
| | | ## 1.0.8(2024-03-28) |
| | | - 修复 在vue2下:style动态绑定导致编译失败的bug |
| | | ## 1.0.7(2024-01-20) |
| | | - 修复 长文本回显超过容器的bug,超过容器部分显示省略号 |
| | | ## 1.0.6(2023-04-12) |
| | | - 修复 微信小程序点击时会改变背景颜色的 bug |
| | | ## 1.0.5(2023-02-03) |
| | | - 修复 禁用时会显示清空按钮 |
| | | ## 1.0.4(2023-02-02) |
| | | - 优化 查询条件短期内多次变更只查询最后一次变更后的结果 |
| | | - 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue |
| | | ## 1.0.3(2023-01-16) |
| | | - 修复 不关联服务空间报错的问题 |
| | | ## 1.0.2(2023-01-14) |
| | | - 新增 属性 `format` 可用于格式化显示选项内容 |
| | | ## 1.0.1(2022-12-06) |
| | | - 修复 当where变化时,数据不会自动更新的问题 |
| | | ## 0.1.9(2022-09-05) |
| | | - 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框 |
| | | ## 0.1.8(2022-08-29) |
| | | - 修复 点击的位置不准确 |
| | | ## 0.1.7(2022-08-12) |
| | | - 新增 支持 disabled 属性 |
| | | ## 0.1.6(2022-07-06) |
| | | - 修复 pc端宽度异常的bug |
| | | ## 0.1.5 |
| | | - 修复 pc端宽度异常的bug |
| | | ## 0.1.4(2022-07-05) |
| | | - 优化 显示样式 |
| | | ## 0.1.3(2022-06-02) |
| | | - 修复 localdata 赋值不生效的 bug |
| | | - 新增 支持 uni.scss 修改颜色 |
| | | - 新增 支持选项禁用(数据选项设置 disabled: true 即禁用) |
| | | ## 0.1.2(2022-05-08) |
| | | - 修复 当 value 为 0 时选择不生效的 bug |
| | | ## 0.1.1(2022-05-07) |
| | | - 新增 记住上次的选项(仅 collection 存在时有效) |
| | | ## 0.1.0(2022-04-22) |
| | | - 初始化 |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-stat__select"> |
| | | <span v-if="label" class="uni-label-text hide-on-phone">{{label + ':'}}</span> |
| | | <view class="uni-stat-box" :class="{'uni-stat__actived': current}"> |
| | | <view class="uni-select" :class="{'uni-select--disabled':disabled}"> |
| | | <view class="uni-select__input-box" @click="toggleSelector"> |
| | | <view v-if="current" class="uni-select__input-text">{{textShow}}</view> |
| | | <view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view> |
| | | <view v-if="current && clear && !disabled" @click.stop="clearVal"> |
| | | <uni-icons type="clear" color="#c0c4cc" size="24" /> |
| | | </view> |
| | | <view v-else> |
| | | <uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" /> |
| | | </view> |
| | | </view> |
| | | <view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" /> |
| | | <view class="uni-select__selector" :style="getOffsetByPlacement" v-if="showSelector"> |
| | | <view :class="placement=='bottom'?'uni-popper__arrow_bottom':'uni-popper__arrow_top'"></view> |
| | | <scroll-view scroll-y="true" class="uni-select__selector-scroll"> |
| | | <view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0"> |
| | | <text>{{emptyTips}}</text> |
| | | </view> |
| | | <view v-else class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index" |
| | | @click="change(item)"> |
| | | <text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | /** |
| | | * DataChecklist 数据选择器 |
| | | * @description 通过数据渲染的下拉框组件 |
| | | * @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select |
| | | * @property {String} value 默认值 |
| | | * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}] |
| | | * @property {Boolean} clear 是否可以清空已选项 |
| | | * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效 |
| | | * @property {String} label 左侧标题 |
| | | * @property {String} placeholder 输入框的提示文字 |
| | | * @property {Boolean} disabled 是否禁用 |
| | | * @property {String} placement 弹出位置 |
| | | * @value top 顶部弹出 |
| | | * @value bottom 底部弹出(default) |
| | | * @event {Function} change 选中发生变化触发 |
| | | */ |
| | | |
| | | export default { |
| | | name: "uni-data-select", |
| | | mixins: [uniCloud.mixinDatacom || {}], |
| | | props: { |
| | | localdata: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | label: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '请选择' |
| | | }, |
| | | emptyTips: { |
| | | type: String, |
| | | default: '无选项' |
| | | }, |
| | | clear: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | defItem: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // 格式化输出 用法 field="_id as value, version as text, uni_platform as label" format="{label} - {text}" |
| | | format: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | placement: { |
| | | type: String, |
| | | default: 'bottom' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | showSelector: false, |
| | | current: '', |
| | | mixinDatacomResData: [], |
| | | apps: [], |
| | | channels: [], |
| | | cacheKey: "uni-data-select-lastSelectedValue", |
| | | }; |
| | | }, |
| | | created() { |
| | | this.debounceGet = this.debounce(() => { |
| | | this.query(); |
| | | }, 300); |
| | | if (this.collection && !this.localdata.length) { |
| | | this.debounceGet(); |
| | | } |
| | | }, |
| | | computed: { |
| | | typePlaceholder() { |
| | | const text = { |
| | | 'opendb-stat-app-versions': '版本', |
| | | 'opendb-app-channels': '渠道', |
| | | 'opendb-app-list': '应用' |
| | | } |
| | | const common = this.placeholder |
| | | const placeholder = text[this.collection] |
| | | return placeholder ? |
| | | common + placeholder : |
| | | common |
| | | }, |
| | | valueCom() { |
| | | // #ifdef VUE3 |
| | | return this.modelValue; |
| | | // #endif |
| | | // #ifndef VUE3 |
| | | return this.value; |
| | | // #endif |
| | | }, |
| | | textShow() { |
| | | // 长文本显示 |
| | | let text = this.current; |
| | | if (text.length > 10) { |
| | | return text.slice(0, 25) + '...'; |
| | | } |
| | | return text; |
| | | }, |
| | | getOffsetByPlacement() { |
| | | switch (this.placement) { |
| | | case 'top': |
| | | return "bottom:calc(100% + 12px);"; |
| | | case 'bottom': |
| | | return "top:calc(100% + 12px);"; |
| | | } |
| | | } |
| | | }, |
| | | |
| | | watch: { |
| | | localdata: { |
| | | immediate: true, |
| | | handler(val, old) { |
| | | if (Array.isArray(val) && old !== val) { |
| | | this.mixinDatacomResData = val |
| | | } |
| | | } |
| | | }, |
| | | valueCom(val, old) { |
| | | this.initDefVal() |
| | | }, |
| | | mixinDatacomResData: { |
| | | immediate: true, |
| | | handler(val) { |
| | | if (val.length) { |
| | | this.initDefVal() |
| | | } |
| | | } |
| | | }, |
| | | |
| | | }, |
| | | methods: { |
| | | debounce(fn, time = 100) { |
| | | let timer = null |
| | | return function(...args) { |
| | | if (timer) clearTimeout(timer) |
| | | timer = setTimeout(() => { |
| | | fn.apply(this, args) |
| | | }, time) |
| | | } |
| | | }, |
| | | // 执行数据库查询 |
| | | query() { |
| | | this.mixinDatacomEasyGet(); |
| | | }, |
| | | // 监听查询条件变更事件 |
| | | onMixinDatacomPropsChange() { |
| | | if (this.collection) { |
| | | this.debounceGet(); |
| | | } |
| | | }, |
| | | initDefVal() { |
| | | let defValue = '' |
| | | if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) { |
| | | defValue = this.valueCom |
| | | } else { |
| | | let strogeValue |
| | | if (this.collection) { |
| | | strogeValue = this.getCache() |
| | | } |
| | | if (strogeValue || strogeValue === 0) { |
| | | defValue = strogeValue |
| | | } else { |
| | | let defItem = '' |
| | | if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) { |
| | | defItem = this.mixinDatacomResData[this.defItem - 1].value |
| | | } |
| | | defValue = defItem |
| | | } |
| | | if (defValue || defValue === 0) { |
| | | this.emit(defValue) |
| | | } |
| | | } |
| | | const def = this.mixinDatacomResData.find(item => item.value === defValue) |
| | | this.current = def ? this.formatItemName(def) : '' |
| | | }, |
| | | |
| | | /** |
| | | * @param {[String, Number]} value |
| | | * 判断用户给的 value 是否同时为禁用状态 |
| | | */ |
| | | isDisabled(value) { |
| | | let isDisabled = false; |
| | | |
| | | this.mixinDatacomResData.forEach(item => { |
| | | if (item.value === value) { |
| | | isDisabled = item.disable |
| | | } |
| | | }) |
| | | |
| | | return isDisabled; |
| | | }, |
| | | |
| | | clearVal() { |
| | | this.emit('') |
| | | if (this.collection) { |
| | | this.removeCache() |
| | | } |
| | | }, |
| | | change(item) { |
| | | if (!item.disable) { |
| | | this.showSelector = false |
| | | this.current = this.formatItemName(item) |
| | | this.emit(item.value) |
| | | } |
| | | }, |
| | | emit(val) { |
| | | this.$emit('input', val) |
| | | this.$emit('update:modelValue', val) |
| | | this.$emit('change', val) |
| | | if (this.collection) { |
| | | this.setCache(val); |
| | | } |
| | | }, |
| | | toggleSelector() { |
| | | if (this.disabled) { |
| | | return |
| | | } |
| | | |
| | | this.showSelector = !this.showSelector |
| | | }, |
| | | formatItemName(item) { |
| | | let { |
| | | text, |
| | | value, |
| | | channel_code |
| | | } = item |
| | | channel_code = channel_code ? `(${channel_code})` : '' |
| | | |
| | | if (this.format) { |
| | | // 格式化输出 |
| | | let str = ""; |
| | | str = this.format; |
| | | for (let key in item) { |
| | | str = str.replace(new RegExp(`{${key}}`, "g"), item[key]); |
| | | } |
| | | return str; |
| | | } else { |
| | | return this.collection.indexOf('app-list') > 0 ? |
| | | `${text}(${value})` : |
| | | ( |
| | | text ? |
| | | text : |
| | | `未命名${channel_code}` |
| | | ) |
| | | } |
| | | }, |
| | | // 获取当前加载的数据 |
| | | getLoadData() { |
| | | return this.mixinDatacomResData; |
| | | }, |
| | | // 获取当前缓存key |
| | | getCurrentCacheKey() { |
| | | return this.collection; |
| | | }, |
| | | // 获取缓存 |
| | | getCache(name = this.getCurrentCacheKey()) { |
| | | let cacheData = uni.getStorageSync(this.cacheKey) || {}; |
| | | return cacheData[name]; |
| | | }, |
| | | // 设置缓存 |
| | | setCache(value, name = this.getCurrentCacheKey()) { |
| | | let cacheData = uni.getStorageSync(this.cacheKey) || {}; |
| | | cacheData[name] = value; |
| | | uni.setStorageSync(this.cacheKey, cacheData); |
| | | }, |
| | | // 删除缓存 |
| | | removeCache(name = this.getCurrentCacheKey()) { |
| | | let cacheData = uni.getStorageSync(this.cacheKey) || {}; |
| | | delete cacheData[name]; |
| | | uni.setStorageSync(this.cacheKey, cacheData); |
| | | }, |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-base-color: #6a6a6a !default; |
| | | $uni-main-color: #333 !default; |
| | | $uni-secondary-color: #909399 !default; |
| | | $uni-border-3: #e5e5e5; |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | @media screen and (max-width: 500px) { |
| | | .hide-on-phone { |
| | | display: none; |
| | | } |
| | | } |
| | | |
| | | /* #endif */ |
| | | .uni-stat__select { |
| | | display: flex; |
| | | align-items: center; |
| | | // padding: 15px; |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | width: 100%; |
| | | flex: 1; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .uni-stat-box { |
| | | width: 100%; |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-stat__actived { |
| | | width: 100%; |
| | | flex: 1; |
| | | // outline: 1px solid #2979ff; |
| | | } |
| | | |
| | | .uni-label-text { |
| | | font-size: 14px; |
| | | font-weight: bold; |
| | | color: $uni-base-color; |
| | | margin: auto 0; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .uni-select { |
| | | font-size: 14px; |
| | | border: 1px solid $uni-border-3; |
| | | box-sizing: border-box; |
| | | border-radius: 4px; |
| | | padding: 0 5px; |
| | | padding-left: 10px; |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | user-select: none; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | border-bottom: solid 1px $uni-border-3; |
| | | width: 100%; |
| | | flex: 1; |
| | | height: 35px; |
| | | |
| | | &--disabled { |
| | | background-color: #f5f7fa; |
| | | cursor: not-allowed; |
| | | } |
| | | } |
| | | |
| | | .uni-select__label { |
| | | font-size: 16px; |
| | | // line-height: 22px; |
| | | height: 35px; |
| | | padding-right: 10px; |
| | | color: $uni-secondary-color; |
| | | } |
| | | |
| | | .uni-select__input-box { |
| | | height: 35px; |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex: 1; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-select__input { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | height: 22px; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .uni-select__input-plac { |
| | | font-size: 14px; |
| | | color: $uni-secondary-color; |
| | | } |
| | | |
| | | .uni-select__selector { |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | position: absolute; |
| | | left: 0; |
| | | width: 100%; |
| | | background-color: #FFFFFF; |
| | | border: 1px solid #EBEEF5; |
| | | border-radius: 6px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | z-index: 3; |
| | | padding: 4px 0; |
| | | } |
| | | |
| | | .uni-select__selector-scroll { |
| | | /* #ifndef APP-NVUE */ |
| | | max-height: 200px; |
| | | box-sizing: border-box; |
| | | /* #endif */ |
| | | } |
| | | |
| | | /* #ifdef H5 */ |
| | | @media (min-width: 768px) { |
| | | .uni-select__selector-scroll { |
| | | max-height: 600px; |
| | | } |
| | | } |
| | | |
| | | /* #endif */ |
| | | |
| | | .uni-select__selector-empty, |
| | | .uni-select__selector-item { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | line-height: 35px; |
| | | font-size: 14px; |
| | | text-align: center; |
| | | /* border-bottom: solid 1px $uni-border-3; */ |
| | | padding: 0px 10px; |
| | | } |
| | | |
| | | .uni-select__selector-item:hover { |
| | | background-color: #f9f9f9; |
| | | } |
| | | |
| | | .uni-select__selector-empty:last-child, |
| | | .uni-select__selector-item:last-child { |
| | | /* #ifndef APP-NVUE */ |
| | | border-bottom: none; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-select__selector__disabled { |
| | | opacity: 0.4; |
| | | cursor: default; |
| | | } |
| | | |
| | | /* picker 弹出层通用的指示小三角 */ |
| | | .uni-popper__arrow_bottom, |
| | | .uni-popper__arrow_bottom::after, |
| | | .uni-popper__arrow_top, |
| | | .uni-popper__arrow_top::after, |
| | | { |
| | | position: absolute; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | border-color: transparent; |
| | | border-style: solid; |
| | | border-width: 6px; |
| | | } |
| | | |
| | | .uni-popper__arrow_bottom { |
| | | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
| | | top: -6px; |
| | | left: 10%; |
| | | margin-right: 3px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #EBEEF5; |
| | | } |
| | | |
| | | .uni-popper__arrow_bottom::after { |
| | | content: " "; |
| | | top: 1px; |
| | | margin-left: -6px; |
| | | border-top-width: 0; |
| | | border-bottom-color: #fff; |
| | | } |
| | | |
| | | .uni-popper__arrow_top { |
| | | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
| | | bottom: -6px; |
| | | left: 10%; |
| | | margin-right: 3px; |
| | | border-bottom-width: 0; |
| | | border-top-color: #EBEEF5; |
| | | } |
| | | |
| | | .uni-popper__arrow_top::after { |
| | | content: " "; |
| | | bottom: 1px; |
| | | margin-left: -6px; |
| | | border-bottom-width: 0; |
| | | border-top-color: #fff; |
| | | } |
| | | |
| | | |
| | | .uni-select__input-text { |
| | | // width: 280px; |
| | | width: 100%; |
| | | color: $uni-main-color; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | -o-text-overflow: ellipsis; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .uni-select__input-placeholder { |
| | | color: $uni-base-color; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .uni-select--mask { |
| | | position: fixed; |
| | | top: 0; |
| | | bottom: 0; |
| | | right: 0; |
| | | left: 0; |
| | | z-index: 2; |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | { |
| | | "id": "uni-data-select", |
| | | "displayName": "uni-data-select 下拉框选择器", |
| | | "version": "1.0.8", |
| | | "description": "通过数据驱动的下拉框选择器", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "select", |
| | | "uni-data-select", |
| | | "下拉框", |
| | | "下拉选" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "^3.1.1" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
| | | "type": "component-vue" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-load-more"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y", |
| | | "alipay": "n" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "u", |
| | | "app-nvue": "n" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "u", |
| | | "百度": "u", |
| | | "字节跳动": "u", |
| | | "QQ": "u", |
| | | "京东": "u" |
| | | }, |
| | | "快应用": { |
| | | "华为": "u", |
| | | "联盟": "u" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | ## DataSelect 下拉框选择器 |
| | | > **组件名:uni-data-select** |
| | | > 代码块: `uDataSelect` |
| | | |
| | | 当选项过多时,使用下拉菜单展示并选择内容 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-select) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
对比新文件 |
| | |
| | | ## 1.0.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-dateformat](https://uniapp.dcloud.io/component/uniui/uni-dateformat) |
| | | ## 0.0.5(2021-07-08) |
| | | - 调整 默认时间不再是当前时间,而是显示'-'字符 |
| | | ## 0.0.4(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 0.0.3(2021-02-04) |
| | | - 调整为uni_modules目录规范 |
| | | - 修复 iOS 平台日期格式化出错的问题 |
对比新文件 |
| | |
| | | // yyyy-MM-dd hh:mm:ss.SSS 所有支持的类型 |
| | | function pad(str, length = 2) { |
| | | str += '' |
| | | while (str.length < length) { |
| | | str = '0' + str |
| | | } |
| | | return str.slice(-length) |
| | | } |
| | | |
| | | const parser = { |
| | | yyyy: (dateObj) => { |
| | | return pad(dateObj.year, 4) |
| | | }, |
| | | yy: (dateObj) => { |
| | | return pad(dateObj.year) |
| | | }, |
| | | MM: (dateObj) => { |
| | | return pad(dateObj.month) |
| | | }, |
| | | M: (dateObj) => { |
| | | return dateObj.month |
| | | }, |
| | | dd: (dateObj) => { |
| | | return pad(dateObj.day) |
| | | }, |
| | | d: (dateObj) => { |
| | | return dateObj.day |
| | | }, |
| | | hh: (dateObj) => { |
| | | return pad(dateObj.hour) |
| | | }, |
| | | h: (dateObj) => { |
| | | return dateObj.hour |
| | | }, |
| | | mm: (dateObj) => { |
| | | return pad(dateObj.minute) |
| | | }, |
| | | m: (dateObj) => { |
| | | return dateObj.minute |
| | | }, |
| | | ss: (dateObj) => { |
| | | return pad(dateObj.second) |
| | | }, |
| | | s: (dateObj) => { |
| | | return dateObj.second |
| | | }, |
| | | SSS: (dateObj) => { |
| | | return pad(dateObj.millisecond, 3) |
| | | }, |
| | | S: (dateObj) => { |
| | | return dateObj.millisecond |
| | | }, |
| | | } |
| | | |
| | | // 这都n年了iOS依然不认识2020-12-12,需要转换为2020/12/12 |
| | | function getDate(time) { |
| | | if (time instanceof Date) { |
| | | return time |
| | | } |
| | | switch (typeof time) { |
| | | case 'string': |
| | | { |
| | | // 2020-12-12T12:12:12.000Z、2020-12-12T12:12:12.000 |
| | | if (time.indexOf('T') > -1) { |
| | | return new Date(time) |
| | | } |
| | | return new Date(time.replace(/-/g, '/')) |
| | | } |
| | | default: |
| | | return new Date(time) |
| | | } |
| | | } |
| | | |
| | | export function formatDate(date, format = 'yyyy/MM/dd hh:mm:ss') { |
| | | if (!date && date !== 0) { |
| | | return '' |
| | | } |
| | | date = getDate(date) |
| | | const dateObj = { |
| | | year: date.getFullYear(), |
| | | month: date.getMonth() + 1, |
| | | day: date.getDate(), |
| | | hour: date.getHours(), |
| | | minute: date.getMinutes(), |
| | | second: date.getSeconds(), |
| | | millisecond: date.getMilliseconds() |
| | | } |
| | | const tokenRegExp = /yyyy|yy|MM|M|dd|d|hh|h|mm|m|ss|s|SSS|SS|S/ |
| | | let flag = true |
| | | let result = format |
| | | while (flag) { |
| | | flag = false |
| | | result = result.replace(tokenRegExp, function(matched) { |
| | | flag = true |
| | | return parser[matched](dateObj) |
| | | }) |
| | | } |
| | | return result |
| | | } |
| | | |
| | | export function friendlyDate(time, { |
| | | locale = 'zh', |
| | | threshold = [60000, 3600000], |
| | | format = 'yyyy/MM/dd hh:mm:ss' |
| | | }) { |
| | | if (time === '-') { |
| | | return time |
| | | } |
| | | if (!time && time !== 0) { |
| | | return '' |
| | | } |
| | | const localeText = { |
| | | zh: { |
| | | year: '年', |
| | | month: '月', |
| | | day: '天', |
| | | hour: '小时', |
| | | minute: '分钟', |
| | | second: '秒', |
| | | ago: '前', |
| | | later: '后', |
| | | justNow: '刚刚', |
| | | soon: '马上', |
| | | template: '{num}{unit}{suffix}' |
| | | }, |
| | | en: { |
| | | year: 'year', |
| | | month: 'month', |
| | | day: 'day', |
| | | hour: 'hour', |
| | | minute: 'minute', |
| | | second: 'second', |
| | | ago: 'ago', |
| | | later: 'later', |
| | | justNow: 'just now', |
| | | soon: 'soon', |
| | | template: '{num} {unit} {suffix}' |
| | | } |
| | | } |
| | | const text = localeText[locale] || localeText.zh |
| | | let date = getDate(time) |
| | | let ms = date.getTime() - Date.now() |
| | | let absMs = Math.abs(ms) |
| | | if (absMs < threshold[0]) { |
| | | return ms < 0 ? text.justNow : text.soon |
| | | } |
| | | if (absMs >= threshold[1]) { |
| | | return formatDate(date, format) |
| | | } |
| | | let num |
| | | let unit |
| | | let suffix = text.later |
| | | if (ms < 0) { |
| | | suffix = text.ago |
| | | ms = -ms |
| | | } |
| | | const seconds = Math.floor((ms) / 1000) |
| | | const minutes = Math.floor(seconds / 60) |
| | | const hours = Math.floor(minutes / 60) |
| | | const days = Math.floor(hours / 24) |
| | | const months = Math.floor(days / 30) |
| | | const years = Math.floor(months / 12) |
| | | switch (true) { |
| | | case years > 0: |
| | | num = years |
| | | unit = text.year |
| | | break |
| | | case months > 0: |
| | | num = months |
| | | unit = text.month |
| | | break |
| | | case days > 0: |
| | | num = days |
| | | unit = text.day |
| | | break |
| | | case hours > 0: |
| | | num = hours |
| | | unit = text.hour |
| | | break |
| | | case minutes > 0: |
| | | num = minutes |
| | | unit = text.minute |
| | | break |
| | | default: |
| | | num = seconds |
| | | unit = text.second |
| | | break |
| | | } |
| | | |
| | | if (locale === 'en') { |
| | | if (num === 1) { |
| | | num = 'a' |
| | | } else { |
| | | unit += 's' |
| | | } |
| | | } |
| | | |
| | | return text.template.replace(/{\s*num\s*}/g, num + '').replace(/{\s*unit\s*}/g, unit).replace(/{\s*suffix\s*}/g, |
| | | suffix) |
| | | } |
对比新文件 |
| | |
| | | <template> |
| | | <text>{{dateShow}}</text> |
| | | </template> |
| | | |
| | | <script> |
| | | import {friendlyDate} from './date-format.js' |
| | | /** |
| | | * Dateformat 日期格式化 |
| | | * @description 日期格式化组件 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3279 |
| | | * @property {Object|String|Number} date 日期对象/日期字符串/时间戳 |
| | | * @property {String} locale 格式化使用的语言 |
| | | * @value zh 中文 |
| | | * @value en 英文 |
| | | * @property {Array} threshold 应用不同类型格式化的阈值 |
| | | * @property {String} format 输出日期字符串时的格式 |
| | | */ |
| | | export default { |
| | | name: 'uniDateformat', |
| | | props: { |
| | | date: { |
| | | type: [Object, String, Number], |
| | | default () { |
| | | return '-' |
| | | } |
| | | }, |
| | | locale: { |
| | | type: String, |
| | | default: 'zh', |
| | | }, |
| | | threshold: { |
| | | type: Array, |
| | | default () { |
| | | return [0, 0] |
| | | } |
| | | }, |
| | | format: { |
| | | type: String, |
| | | default: 'yyyy/MM/dd hh:mm:ss' |
| | | }, |
| | | // refreshRate使用不当可能导致性能问题,谨慎使用 |
| | | refreshRate: { |
| | | type: [Number, String], |
| | | default: 0 |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | refreshMark: 0 |
| | | } |
| | | }, |
| | | computed: { |
| | | dateShow() { |
| | | this.refreshMark |
| | | return friendlyDate(this.date, { |
| | | locale: this.locale, |
| | | threshold: this.threshold, |
| | | format: this.format |
| | | }) |
| | | } |
| | | }, |
| | | watch: { |
| | | refreshRate: { |
| | | handler() { |
| | | this.setAutoRefresh() |
| | | }, |
| | | immediate: true |
| | | } |
| | | }, |
| | | methods: { |
| | | refresh() { |
| | | this.refreshMark++ |
| | | }, |
| | | setAutoRefresh() { |
| | | clearInterval(this.refreshInterval) |
| | | if (this.refreshRate) { |
| | | this.refreshInterval = setInterval(() => { |
| | | this.refresh() |
| | | }, parseInt(this.refreshRate)) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | |
| | | </style> |
对比新文件 |
| | |
| | | { |
| | | "id": "uni-dateformat", |
| | | "displayName": "uni-dateformat 日期格式化", |
| | | "version": "1.0.0", |
| | | "description": "日期格式化组件,可以将日期格式化为1分钟前、刚刚等形式", |
| | | "keywords": [ |
| | | "uni-ui", |
| | | "uniui", |
| | | "日期格式化", |
| | | "时间格式化", |
| | | "格式化时间", |
| | | "" |
| | | ], |
| | | "repository": "https://github.com/dcloudio/uni-ui", |
| | | "engines": { |
| | | "HBuilderX": "" |
| | | }, |
| | | "directories": { |
| | | "example": "../../temps/example_temps" |
| | | }, |
| | | "dcloudext": { |
| | | "category": [ |
| | | "前端组件", |
| | | "通用组件" |
| | | ], |
| | | "sale": { |
| | | "regular": { |
| | | "price": "0.00" |
| | | }, |
| | | "sourcecode": { |
| | | "price": "0.00" |
| | | } |
| | | }, |
| | | "contact": { |
| | | "qq": "" |
| | | }, |
| | | "declaration": { |
| | | "ads": "无", |
| | | "data": "无", |
| | | "permissions": "无" |
| | | }, |
| | | "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
| | | }, |
| | | "uni_modules": { |
| | | "dependencies": ["uni-scss"], |
| | | "encrypt": [], |
| | | "platforms": { |
| | | "cloud": { |
| | | "tcb": "y", |
| | | "aliyun": "y" |
| | | }, |
| | | "client": { |
| | | "App": { |
| | | "app-vue": "y", |
| | | "app-nvue": "y" |
| | | }, |
| | | "H5-mobile": { |
| | | "Safari": "y", |
| | | "Android Browser": "y", |
| | | "微信浏览器(Android)": "y", |
| | | "QQ浏览器(Android)": "y" |
| | | }, |
| | | "H5-pc": { |
| | | "Chrome": "y", |
| | | "IE": "y", |
| | | "Edge": "y", |
| | | "Firefox": "y", |
| | | "Safari": "y" |
| | | }, |
| | | "小程序": { |
| | | "微信": "y", |
| | | "阿里": "y", |
| | | "百度": "y", |
| | | "字节跳动": "y", |
| | | "QQ": "y" |
| | | }, |
| | | "快应用": { |
| | | "华为": "y", |
| | | "联盟": "y" |
| | | }, |
| | | "Vue": { |
| | | "vue2": "y", |
| | | "vue3": "y" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | |
| | | |
| | | ### DateFormat 日期格式化 |
| | | > **组件名:uni-dateformat** |
| | | > 代码块: `uDateformat` |
| | | |
| | | |
| | | 日期格式化组件。 |
| | | |
| | | ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-dateformat) |
| | | #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
对比新文件 |
| | |
| | | ## 2.2.38(2024-10-15) |
| | | - 修复 微信小程序中的getSystemInfo警告 |
| | | ## 2.2.37(2024-10-12) |
| | | - 修复 微信小程序中的getSystemInfo警告 |
| | | ## 2.2.36(2024-10-12) |
| | | - 修复 微信小程序中的getSystemInfo警告 |
| | | ## 2.2.35(2024-09-21) |
| | | - 修复 没有选中日期时点击确定直接报错的Bug [详情](https://ask.dcloud.net.cn/question/198168) |
| | | ## 2.2.34(2024-04-24) |
| | | - 新增 日期点击事件,在点击日期时会触发该事件。 |
| | | ## 2.2.33(2024-04-15) |
| | | - 修复 抖音小程序事件传递失效bug |
| | | ## 2.2.32(2024-02-20) |
| | | - 修复 日历的close事件触发异常的bug [详情](https://github.com/dcloudio/uni-ui/issues/844) |
| | | ## 2.2.31(2024-02-20) |
| | | - 修复 h5平台 右边日历的月份默认+1的bug [详情](https://github.com/dcloudio/uni-ui/issues/841) |
| | | ## 2.2.30(2024-01-31) |
| | | - 修复 隐藏“秒”时,在IOS15及以下版本时出现 结束时间在开始时间之前 的bug [详情](https://github.com/dcloudio/uni-ui/issues/788) |
| | | ## 2.2.29(2024-01-20) |
| | | - 新增 show事件,弹窗弹出时触发该事件 [详情](https://github.com/dcloudio/uni-app/issues/4694) |
| | | ## 2.2.28(2024-01-18) |
| | | - 去除 noChange事件,当进行日期范围选择时,若只选了一天,则开始结束日期都为同一天 [详情](https://github.com/dcloudio/uni-ui/issues/815) |
| | | ## 2.2.27(2024-01-10) |
| | | - 优化 增加noChange事件,当进行日期范围选择时,若有空值,则触发该事件 [详情](https://github.com/dcloudio/uni-ui/issues/815) |
| | | ## 2.2.26(2024-01-08) |
| | | - 修复 字节小程序时间选择范围器失效问题 [详情](https://github.com/dcloudio/uni-ui/issues/834) |
| | | ## 2.2.25(2023-10-18) |
| | | - 修复 PC端初次修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737) |
| | | ## 2.2.24(2023-06-02) |
| | | - 修复 部分情况修改时间,开始、结束时间显示异常的Bug [详情](https://ask.dcloud.net.cn/question/171146) |
| | | - 优化 当前月可以选择上月、下月的日期的Bug |
| | | ## 2.2.23(2023-05-02) |
| | | - 修复 部分情况修改时间,开始时间未更新的Bug [详情](https://github.com/dcloudio/uni-ui/issues/737) |
| | | - 修复 部分平台及设备第一次点击无法显示弹框的Bug |
| | | - 修复 ios 日期格式未补零显示及使用异常的Bug [详情](https://ask.dcloud.net.cn/question/162979) |
| | | ## 2.2.22(2023-03-30) |
| | | - 修复 日历 picker 修改年月后,自动选中当月1日的Bug [详情](https://ask.dcloud.net.cn/question/165937) |
| | | - 修复 小程序端 低版本 ios NaN的Bug [详情](https://ask.dcloud.net.cn/question/162979) |
| | | ## 2.2.21(2023-02-20) |
| | | - 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362) |
| | | ## 2.2.20(2023-02-17) |
| | | - 优化 值为空依然选中当天问题 |
| | | - 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间 |
| | | - 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间 |
| | | - 优化 字节小程序日期时间范围选择,底部日期换行的Bug |
| | | ## 2.2.19(2023-02-09) |
| | | - 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686) |
| | | ## 2.2.18(2023-02-08) |
| | | - 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684) |
| | | - 优化 PC端输入日期格式错误时返回当前日期时间 |
| | | - 优化 PC端输入日期时间超出 start、end 限制的Bug |
| | | - 优化 移动端日期时间范围用法时间展示不完整问题 |
| | | ## 2.2.17(2023-02-04) |
| | | - 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679) |
| | | - 修复 vue3 time-picker 无法显示绑定时分秒的Bug |
| | | ## 2.2.16(2023-02-02) |
| | | - 修复 字节小程序报错的Bug |
| | | ## 2.2.15(2023-02-02) |
| | | - 修复 某些情况切换月份错误的Bug |
| | | ## 2.2.14(2023-01-30) |
| | | - 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/162033) |
| | | ## 2.2.13(2023-01-10) |
| | | - 修复 多次加载组件造成内存占用的Bug |
| | | ## 2.2.12(2022-12-01) |
| | | - 修复 vue3 下 i18n 国际化初始值不正确的Bug |
| | | ## 2.2.11(2022-09-19) |
| | | - 修复 支付宝小程序样式错乱的Bug [详情](https://github.com/dcloudio/uni-app/issues/3861) |
| | | ## 2.2.10(2022-09-19) |
| | | - 修复 反向选择日期范围,日期显示异常的Bug [详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false) |
| | | ## 2.2.9(2022-09-16) |
| | | - 可以使用 uni-scss 控制主题色 |
| | | ## 2.2.8(2022-09-08) |
| | | - 修复 close事件无效的Bug |
| | | ## 2.2.7(2022-09-05) |
| | | - 修复 移动端 maskClick 无效的Bug [详情](https://ask.dcloud.net.cn/question/140824) |
| | | ## 2.2.6(2022-06-30) |
| | | - 优化 组件样式,调整了组件图标大小、高度、颜色等,与uni-ui风格保持一致 |
| | | ## 2.2.5(2022-06-24) |
| | | - 修复 日历顶部年月及底部确认未国际化的Bug |
| | | ## 2.2.4(2022-03-31) |
| | | - 修复 Vue3 下动态赋值,单选类型未响应的Bug |
| | | ## 2.2.3(2022-03-28) |
| | | - 修复 Vue3 下动态赋值未响应的Bug |
| | | ## 2.2.2(2021-12-10) |
| | | - 修复 clear-icon 属性在小程序平台不生效的Bug |
| | | ## 2.2.1(2021-12-10) |
| | | - 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的Bug |
| | | ## 2.2.0(2021-11-19) |
| | | - 优化 组件UI,并提供设计资源 [详情](https://uniapp.dcloud.io/component/uniui/resource) |
| | | - 文档迁移 [https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker) |
| | | ## 2.1.5(2021-11-09) |
| | | - 新增 提供组件设计资源,组件样式调整 |
| | | ## 2.1.4(2021-09-10) |
| | | - 修复 hide-second 在移动端的Bug |
| | | - 修复 单选赋默认值时,赋值日期未高亮的Bug |
| | | - 修复 赋默认值时,移动端未正确显示时间的Bug |
| | | ## 2.1.3(2021-09-09) |
| | | - 新增 hide-second 属性,支持只使用时分,隐藏秒 |
| | | ## 2.1.2(2021-09-03) |
| | | - 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次 |
| | | - 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法 |
| | | - 优化 调整字号大小,美化日历界面 |
| | | - 修复 因国际化导致的 placeholder 失效的Bug |
| | | ## 2.1.1(2021-08-24) |
| | | - 新增 支持国际化 |
| | | - 优化 范围选择器在 pc 端过宽的问题 |
| | | ## 2.1.0(2021-08-09) |
| | | - 新增 适配 vue3 |
| | | ## 2.0.19(2021-08-09) |
| | | - 新增 支持作为 uni-forms 子组件相关功能 |
| | | - 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的Bug |
| | | ## 2.0.18(2021-08-05) |
| | | - 修复 type 属性动态赋值无效的Bug |
| | | - 修复 ‘确认’按钮被 tabbar 遮盖 bug |
| | | - 修复 组件未赋值时范围选左、右日历相同的Bug |
| | | ## 2.0.17(2021-08-04) |
| | | - 修复 范围选未正确显示当前值的Bug |
| | | - 修复 h5 平台(移动端)报错 'cale' of undefined 的Bug |
| | | ## 2.0.16(2021-07-21) |
| | | - 新增 return-type 属性支持返回 date 日期对象 |
| | | ## 2.0.15(2021-07-14) |
| | | - 修复 单选日期类型,初始赋值后不在当前日历的Bug |
| | | - 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效) |
| | | - 优化 移动端移除显示框的清空按钮,无实际用途 |
| | | ## 2.0.14(2021-07-14) |
| | | - 修复 组件赋值为空,界面未更新的Bug |
| | | - 修复 start 和 end 不能动态赋值的Bug |
| | | - 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的Bug |
| | | ## 2.0.13(2021-07-08) |
| | | - 修复 范围选择不能动态赋值的Bug |
| | | ## 2.0.12(2021-07-08) |
| | | - 修复 范围选择的初始时间在一个月内时,造成无法选择的bug |
| | | ## 2.0.11(2021-07-08) |
| | | - 优化 弹出层在超出视窗边缘定位不准确的问题 |
| | | ## 2.0.10(2021-07-08) |
| | | - 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的Bug |
| | | - 优化 弹出层在超出视窗边缘被遮盖的问题 |
| | | ## 2.0.9(2021-07-07) |
| | | - 新增 maskClick 事件 |
| | | - 修复 特殊情况日历 rpx 布局错误的Bug,rpx -> px |
| | | - 修复 范围选择时清空返回值不合理的bug,['', ''] -> [] |
| | | ## 2.0.8(2021-07-07) |
| | | - 新增 日期时间显示框支持插槽 |
| | | ## 2.0.7(2021-07-01) |
| | | - 优化 添加 uni-icons 依赖 |
| | | ## 2.0.6(2021-05-22) |
| | | - 修复 图标在小程序上不显示的Bug |
| | | - 优化 重命名引用组件,避免潜在组件命名冲突 |
| | | ## 2.0.5(2021-05-20) |
| | | - 优化 代码目录扁平化 |
| | | ## 2.0.4(2021-05-12) |
| | | - 新增 组件示例地址 |
| | | ## 2.0.3(2021-05-10) |
| | | - 修复 ios 下不识别 '-' 日期格式的Bug |
| | | - 优化 pc 下弹出层添加边框和阴影 |
| | | ## 2.0.2(2021-05-08) |
| | | - 修复 在 admin 中获取弹出层定位错误的bug |
| | | ## 2.0.1(2021-05-08) |
| | | - 修复 type 属性向下兼容,默认值从 date 变更为 datetime |
| | | ## 2.0.0(2021-04-30) |
| | | - 支持日历形式的日期+时间的范围选择 |
| | | > 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker) |
| | | ## 1.0.6(2021-03-18) |
| | | - 新增 hide-second 属性,时间支持仅选择时、分 |
| | | - 修复 选择跟显示的日期不一样的Bug |
| | | - 修复 chang事件触发2次的Bug |
| | | - 修复 分、秒 end 范围错误的Bug |
| | | - 优化 更好的 nvue 适配 |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-calendar-item__weeks-box" :class="{ |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | 'uni-calendar-item--before-checked-x':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked-x':weeks.afterMultiple, |
| | | }" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)"> |
| | | <view class="uni-calendar-item__weeks-box-item" :class="{ |
| | | 'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover), |
| | | 'uni-calendar-item--checked-range-text': checkHover, |
| | | 'uni-calendar-item--before-checked':weeks.beforeMultiple, |
| | | 'uni-calendar-item--multiple': weeks.multiple, |
| | | 'uni-calendar-item--after-checked':weeks.afterMultiple, |
| | | 'uni-calendar-item--disable':weeks.disable, |
| | | }"> |
| | | <text v-if="selected && weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text> |
| | | <text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text> |
| | | </view> |
| | | <view :class="{'uni-calendar-item--today': weeks.isToday}"></view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | props: { |
| | | weeks: { |
| | | type: Object, |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | calendar: { |
| | | type: Object, |
| | | default: () => { |
| | | return {} |
| | | } |
| | | }, |
| | | selected: { |
| | | type: Array, |
| | | default: () => { |
| | | return [] |
| | | } |
| | | }, |
| | | checkHover: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | methods: { |
| | | choiceDate(weeks) { |
| | | this.$emit('change', weeks) |
| | | }, |
| | | handleMousemove(weeks) { |
| | | this.$emit('handleMouse', weeks) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | $uni-primary: #007aff !default; |
| | | |
| | | .uni-calendar-item__weeks-box { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin: 1px 0; |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box-text { |
| | | font-size: 14px; |
| | | // font-family: Lato-Bold, Lato; |
| | | font-weight: bold; |
| | | color: darken($color: $uni-primary, $amount: 40%); |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box-item { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: 40px; |
| | | height: 40px; |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | |
| | | .uni-calendar-item__weeks-box-circle { |
| | | position: absolute; |
| | | top: 5px; |
| | | right: 5px; |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 8px; |
| | | background-color: #dd524d; |
| | | |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box .uni-calendar-item--disable { |
| | | cursor: default; |
| | | } |
| | | |
| | | .uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable { |
| | | color: #D1D1D1; |
| | | } |
| | | |
| | | .uni-calendar-item--today { |
| | | position: absolute; |
| | | top: 10px; |
| | | right: 17%; |
| | | background-color: #dd524d; |
| | | width:6px; |
| | | height: 6px; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .uni-calendar-item--extra { |
| | | color: #dd524d; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .uni-calendar-item__weeks-box .uni-calendar-item--checked { |
| | | background-color: $uni-primary; |
| | | border-radius: 50%; |
| | | box-sizing: border-box; |
| | | border: 3px solid #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--checked .uni-calendar-item--checked-text { |
| | | color: #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--multiple .uni-calendar-item--checked-range-text { |
| | | color: #333; |
| | | } |
| | | |
| | | .uni-calendar-item--multiple { |
| | | background-color: #F6F7FC; |
| | | // color: #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--multiple .uni-calendar-item--before-checked, |
| | | .uni-calendar-item--multiple .uni-calendar-item--after-checked { |
| | | background-color: $uni-primary; |
| | | border-radius: 50%; |
| | | box-sizing: border-box; |
| | | border: 3px solid #F6F7FC; |
| | | } |
| | | |
| | | .uni-calendar-item--before-checked .uni-calendar-item--checked-text, |
| | | .uni-calendar-item--after-checked .uni-calendar-item--checked-text { |
| | | color: #fff; |
| | | } |
| | | |
| | | .uni-calendar-item--before-checked-x { |
| | | border-top-left-radius: 50px; |
| | | border-bottom-left-radius: 50px; |
| | | box-sizing: border-box; |
| | | background-color: #F6F7FC; |
| | | } |
| | | |
| | | .uni-calendar-item--after-checked-x { |
| | | border-top-right-radius: 50px; |
| | | border-bottom-right-radius: 50px; |
| | | background-color: #F6F7FC; |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-calendar" @mouseleave="leaveCale"> |
| | | |
| | | <view v-if="!insert && show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" |
| | | @click="maskClick"></view> |
| | | |
| | | <view v-if="insert || show" class="uni-calendar__content" |
| | | :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}"> |
| | | <view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}"> |
| | | |
| | | <view class="uni-calendar__header-btn-box" @click.stop="changeMonth('pre')"> |
| | | <view class="uni-calendar__header-btn uni-calendar--left"></view> |
| | | </view> |
| | | |
| | | <picker mode="date" :value="date" fields="month" @change="bindDateChange"> |
| | | <text |
| | | class="uni-calendar__header-text">{{ (nowDate.year||'') + yearText + ( nowDate.month||'') + monthText}}</text> |
| | | </picker> |
| | | |
| | | <view class="uni-calendar__header-btn-box" @click.stop="changeMonth('next')"> |
| | | <view class="uni-calendar__header-btn uni-calendar--right"></view> |
| | | </view> |
| | | |
| | | <view v-if="!insert" class="dialog-close" @click="maskClick"> |
| | | <view class="dialog-close-plus" data-id="close"></view> |
| | | <view class="dialog-close-plus dialog-close-rotate" data-id="close"></view> |
| | | </view> |
| | | </view> |
| | | <view class="uni-calendar__box"> |
| | | |
| | | <view v-if="showMonth" class="uni-calendar__box-bg"> |
| | | <text class="uni-calendar__box-bg-text">{{nowDate.month}}</text> |
| | | </view> |
| | | |
| | | <view class="uni-calendar__weeks" style="padding-bottom: 7px;"> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{SUNText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{MONText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{TUEText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{WEDText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{THUText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{FRIText}}</text> |
| | | </view> |
| | | <view class="uni-calendar__weeks-day"> |
| | | <text class="uni-calendar__weeks-day-text">{{SATText}}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex"> |
| | | <view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex"> |
| | | <calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" |
| | | :checkHover="range" @change="choiceDate" @handleMouse="handleMouse"> |
| | | </calendar-item> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="!insert && !range && hasTime" class="uni-date-changed uni-calendar--fixed-top" |
| | | style="padding: 0 80px;"> |
| | | <view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view> |
| | | <time-picker type="time" :start="timepickerStartTime" :end="timepickerEndTime" v-model="time" |
| | | :disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style"> |
| | | </time-picker> |
| | | </view> |
| | | |
| | | <view v-if="!insert && range && hasTime" class="uni-date-changed uni-calendar--fixed-top"> |
| | | <view class="uni-date-changed--time-start"> |
| | | <view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}} |
| | | </view> |
| | | <time-picker type="time" :start="timepickerStartTime" v-model="timeRange.startTime" :border="false" |
| | | :hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style"> |
| | | </time-picker> |
| | | </view> |
| | | <view style="line-height: 50px;"> |
| | | <uni-icons type="arrowthinright" color="#999"></uni-icons> |
| | | </view> |
| | | <view class="uni-date-changed--time-end"> |
| | | <view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view> |
| | | <time-picker type="time" :end="timepickerEndTime" v-model="timeRange.endTime" :border="false" |
| | | :hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style"> |
| | | </time-picker> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="!insert" class="uni-date-changed uni-date-btn--ok"> |
| | | <view class="uni-datetime-picker--btn" @click="confirm">{{confirmText}}</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { |
| | | Calendar, |
| | | getDate, |
| | | getTime |
| | | } from './util.js'; |
| | | import calendarItem from './calendar-item.vue' |
| | | import timePicker from './time-picker.vue' |
| | | |
| | | import { |
| | | initVueI18n |
| | | } from '@dcloudio/uni-i18n' |
| | | import i18nMessages from './i18n/index.js' |
| | | const { |
| | | t |
| | | } = initVueI18n(i18nMessages) |
| | | |
| | | /** |
| | | * Calendar 日历 |
| | | * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=56 |
| | | * @property {String} date 自定义当前时间,默认为今天 |
| | | * @property {String} startDate 日期选择范围-开始日期 |
| | | * @property {String} endDate 日期选择范围-结束日期 |
| | | * @property {Boolean} range 范围选择 |
| | | * @property {Boolean} insert = [true|false] 插入模式,默认为false |
| | | * @value true 弹窗模式 |
| | | * @value false 插入模式 |
| | | * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容 |
| | | * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
| | | * @property {Boolean} showMonth 是否选择月份为背景 |
| | | * @property {[String} defaultValue 选择器打开时默认显示的时间 |
| | | * @event {Function} change 日期改变,`insert :ture` 时生效 |
| | | * @event {Function} confirm 确认选择`insert :false` 时生效 |
| | | * @event {Function} monthSwitch 切换月份时触发 |
| | | * @example <uni-calendar :insert="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" /> |
| | | */ |
| | | export default { |
| | | components: { |
| | | calendarItem, |
| | | timePicker |
| | | }, |
| | | |
| | | options: { |
| | | // #ifdef MP-TOUTIAO |
| | | virtualHost: false, |
| | | // #endif |
| | | // #ifndef MP-TOUTIAO |
| | | virtualHost: true |
| | | // #endif |
| | | }, |
| | | props: { |
| | | date: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | defTime: { |
| | | type: [String, Object], |
| | | default: '' |
| | | }, |
| | | selectableTimes: { |
| | | type: [Object], |
| | | default () { |
| | | return {} |
| | | } |
| | | }, |
| | | selected: { |
| | | type: Array, |
| | | default () { |
| | | return [] |
| | | } |
| | | }, |
| | | startDate: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | endDate: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | startPlaceholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | endPlaceholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | range: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | hasTime: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | insert: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | showMonth: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | clearDate: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | checkHover: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | hideSecond: { |
| | | type: [Boolean], |
| | | default: false |
| | | }, |
| | | pleStatus: { |
| | | type: Object, |
| | | default () { |
| | | return { |
| | | before: '', |
| | | after: '', |
| | | data: [], |
| | | fulldate: '' |
| | | } |
| | | } |
| | | }, |
| | | defaultValue: { |
| | | type: [String, Object, Array], |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | show: false, |
| | | weeks: [], |
| | | calendar: {}, |
| | | nowDate: {}, |
| | | aniMaskShow: false, |
| | | firstEnter: true, |
| | | time: '', |
| | | timeRange: { |
| | | startTime: '', |
| | | endTime: '' |
| | | }, |
| | | tempSingleDate: '', |
| | | tempRange: { |
| | | before: '', |
| | | after: '' |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | date: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (!this.range) { |
| | | this.tempSingleDate = newVal |
| | | setTimeout(() => { |
| | | this.init(newVal) |
| | | }, 100) |
| | | } |
| | | } |
| | | }, |
| | | defTime: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (!this.range) { |
| | | this.time = newVal |
| | | } else { |
| | | this.timeRange.startTime = newVal.start |
| | | this.timeRange.endTime = newVal.end |
| | | } |
| | | } |
| | | }, |
| | | startDate(val) { |
| | | // 字节小程序 watch 早于 created |
| | | if (!this.cale) { |
| | | return |
| | | } |
| | | this.cale.setStartDate(val) |
| | | this.cale.setDate(this.nowDate.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | }, |
| | | endDate(val) { |
| | | // 字节小程序 watch 早于 created |
| | | if (!this.cale) { |
| | | return |
| | | } |
| | | this.cale.setEndDate(val) |
| | | this.cale.setDate(this.nowDate.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | }, |
| | | selected(newVal) { |
| | | // 字节小程序 watch 早于 created |
| | | if (!this.cale) { |
| | | return |
| | | } |
| | | this.cale.setSelectInfo(this.nowDate.fullDate, newVal) |
| | | this.weeks = this.cale.weeks |
| | | }, |
| | | pleStatus: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | const { |
| | | before, |
| | | after, |
| | | fulldate, |
| | | which |
| | | } = newVal |
| | | this.tempRange.before = before |
| | | this.tempRange.after = after |
| | | setTimeout(() => { |
| | | if (fulldate) { |
| | | this.cale.setHoverMultiple(fulldate) |
| | | if (before && after) { |
| | | this.cale.lastHover = true |
| | | if (this.rangeWithinMonth(after, before)) return |
| | | this.setDate(before) |
| | | } else { |
| | | this.cale.setMultiple(fulldate) |
| | | this.setDate(this.nowDate.fullDate) |
| | | this.calendar.fullDate = '' |
| | | this.cale.lastHover = false |
| | | } |
| | | } else { |
| | | // 字节小程序 watch 早于 created |
| | | if (!this.cale) { |
| | | return |
| | | } |
| | | |
| | | this.cale.setDefaultMultiple(before, after) |
| | | if (which === 'left' && before) { |
| | | this.setDate(before) |
| | | this.weeks = this.cale.weeks |
| | | } else if (after) { |
| | | this.setDate(after) |
| | | this.weeks = this.cale.weeks |
| | | } |
| | | this.cale.lastHover = true |
| | | } |
| | | }, 16) |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | timepickerStartTime() { |
| | | const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate |
| | | return activeDate === this.startDate ? this.selectableTimes.start : '' |
| | | }, |
| | | timepickerEndTime() { |
| | | const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate |
| | | return activeDate === this.endDate ? this.selectableTimes.end : '' |
| | | }, |
| | | /** |
| | | * for i18n |
| | | */ |
| | | selectDateText() { |
| | | return t("uni-datetime-picker.selectDate") |
| | | }, |
| | | startDateText() { |
| | | return this.startPlaceholder || t("uni-datetime-picker.startDate") |
| | | }, |
| | | endDateText() { |
| | | return this.endPlaceholder || t("uni-datetime-picker.endDate") |
| | | }, |
| | | okText() { |
| | | return t("uni-datetime-picker.ok") |
| | | }, |
| | | yearText() { |
| | | return t("uni-datetime-picker.year") |
| | | }, |
| | | monthText() { |
| | | return t("uni-datetime-picker.month") |
| | | }, |
| | | MONText() { |
| | | return t("uni-calender.MON") |
| | | }, |
| | | TUEText() { |
| | | return t("uni-calender.TUE") |
| | | }, |
| | | WEDText() { |
| | | return t("uni-calender.WED") |
| | | }, |
| | | THUText() { |
| | | return t("uni-calender.THU") |
| | | }, |
| | | FRIText() { |
| | | return t("uni-calender.FRI") |
| | | }, |
| | | SATText() { |
| | | return t("uni-calender.SAT") |
| | | }, |
| | | SUNText() { |
| | | return t("uni-calender.SUN") |
| | | }, |
| | | confirmText() { |
| | | return t("uni-calender.confirm") |
| | | }, |
| | | }, |
| | | created() { |
| | | // 获取日历方法实例 |
| | | this.cale = new Calendar({ |
| | | selected: this.selected, |
| | | startDate: this.startDate, |
| | | endDate: this.endDate, |
| | | range: this.range, |
| | | }) |
| | | // 选中某一天 |
| | | this.init(this.date) |
| | | }, |
| | | methods: { |
| | | leaveCale() { |
| | | this.firstEnter = true |
| | | }, |
| | | handleMouse(weeks) { |
| | | if (weeks.disable) return |
| | | if (this.cale.lastHover) return |
| | | let { |
| | | before, |
| | | after |
| | | } = this.cale.multipleStatus |
| | | if (!before) return |
| | | this.calendar = weeks |
| | | // 设置范围选 |
| | | this.cale.setHoverMultiple(this.calendar.fullDate) |
| | | this.weeks = this.cale.weeks |
| | | // hover时,进入一个日历,更新另一个 |
| | | if (this.firstEnter) { |
| | | this.$emit('firstEnterCale', this.cale.multipleStatus) |
| | | this.firstEnter = false |
| | | } |
| | | }, |
| | | rangeWithinMonth(A, B) { |
| | | const [yearA, monthA] = A.split('-') |
| | | const [yearB, monthB] = B.split('-') |
| | | return yearA === yearB && monthA === monthB |
| | | }, |
| | | // 蒙版点击事件 |
| | | maskClick() { |
| | | this.close() |
| | | this.$emit('maskClose') |
| | | }, |
| | | |
| | | clearCalender() { |
| | | if (this.range) { |
| | | this.timeRange.startTime = '' |
| | | this.timeRange.endTime = '' |
| | | this.tempRange.before = '' |
| | | this.tempRange.after = '' |
| | | this.cale.multipleStatus.before = '' |
| | | this.cale.multipleStatus.after = '' |
| | | this.cale.multipleStatus.data = [] |
| | | this.cale.lastHover = false |
| | | } else { |
| | | this.time = '' |
| | | this.tempSingleDate = '' |
| | | } |
| | | this.calendar.fullDate = '' |
| | | this.setDate(new Date()) |
| | | }, |
| | | |
| | | bindDateChange(e) { |
| | | const value = e.detail.value + '-1' |
| | | this.setDate(value) |
| | | }, |
| | | /** |
| | | * 初始化日期显示 |
| | | * @param {Object} date |
| | | */ |
| | | init(date) { |
| | | // 字节小程序 watch 早于 created |
| | | if (!this.cale) { |
| | | return |
| | | } |
| | | this.cale.setDate(date || new Date()) |
| | | this.weeks = this.cale.weeks |
| | | this.nowDate = this.cale.getInfo(date) |
| | | this.calendar = { |
| | | ...this.nowDate |
| | | } |
| | | if (!date) { |
| | | // 优化date为空默认不选中今天 |
| | | this.calendar.fullDate = '' |
| | | if (this.defaultValue && !this.range) { |
| | | // 暂时只支持移动端非范围选择 |
| | | const defaultDate = new Date(this.defaultValue) |
| | | const fullDate = getDate(defaultDate) |
| | | const year = defaultDate.getFullYear() |
| | | const month = defaultDate.getMonth() + 1 |
| | | const date = defaultDate.getDate() |
| | | const day = defaultDate.getDay() |
| | | this.calendar = { |
| | | fullDate, |
| | | year, |
| | | month, |
| | | date, |
| | | day |
| | | }, |
| | | this.tempSingleDate = fullDate |
| | | this.time = getTime(defaultDate, this.hideSecond) |
| | | } |
| | | } |
| | | }, |
| | | /** |
| | | * 打开日历弹窗 |
| | | */ |
| | | open() { |
| | | // 弹窗模式并且清理数据 |
| | | if (this.clearDate && !this.insert) { |
| | | this.cale.cleanMultipleStatus() |
| | | this.init(this.date) |
| | | } |
| | | this.show = true |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | this.aniMaskShow = true |
| | | }, 50) |
| | | }) |
| | | }, |
| | | /** |
| | | * 关闭日历弹窗 |
| | | */ |
| | | close() { |
| | | this.aniMaskShow = false |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | this.show = false |
| | | this.$emit('close') |
| | | }, 300) |
| | | }) |
| | | }, |
| | | /** |
| | | * 确认按钮 |
| | | */ |
| | | confirm() { |
| | | this.setEmit('confirm') |
| | | this.close() |
| | | }, |
| | | /** |
| | | * 变化触发 |
| | | */ |
| | | change(isSingleChange) { |
| | | if (!this.insert && !isSingleChange) return |
| | | this.setEmit('change') |
| | | }, |
| | | /** |
| | | * 选择月份触发 |
| | | */ |
| | | monthSwitch() { |
| | | let { |
| | | year, |
| | | month |
| | | } = this.nowDate |
| | | this.$emit('monthSwitch', { |
| | | year, |
| | | month: Number(month) |
| | | }) |
| | | }, |
| | | /** |
| | | * 派发事件 |
| | | * @param {Object} name |
| | | */ |
| | | setEmit(name) { |
| | | if (!this.range) { |
| | | if (!this.calendar.fullDate) { |
| | | this.calendar = this.cale.getInfo(new Date()) |
| | | this.tempSingleDate = this.calendar.fullDate |
| | | } |
| | | if (this.hasTime && !this.time) { |
| | | this.time = getTime(new Date(), this.hideSecond) |
| | | } |
| | | } |
| | | let { |
| | | year, |
| | | month, |
| | | date, |
| | | fullDate, |
| | | extraInfo |
| | | } = this.calendar |
| | | this.$emit(name, { |
| | | range: this.cale.multipleStatus, |
| | | year, |
| | | month, |
| | | date, |
| | | time: this.time, |
| | | timeRange: this.timeRange, |
| | | fulldate: fullDate, |
| | | extraInfo: extraInfo || {} |
| | | }) |
| | | }, |
| | | /** |
| | | * 选择天触发 |
| | | * @param {Object} weeks |
| | | */ |
| | | choiceDate(weeks) { |
| | | if (weeks.disable) return |
| | | this.calendar = weeks |
| | | this.calendar.userChecked = true |
| | | // 设置多选 |
| | | this.cale.setMultiple(this.calendar.fullDate, true) |
| | | this.weeks = this.cale.weeks |
| | | this.tempSingleDate = this.calendar.fullDate |
| | | const beforeDate = new Date(this.cale.multipleStatus.before).getTime() |
| | | const afterDate = new Date(this.cale.multipleStatus.after).getTime() |
| | | if (beforeDate > afterDate && afterDate) { |
| | | this.tempRange.before = this.cale.multipleStatus.after |
| | | this.tempRange.after = this.cale.multipleStatus.before |
| | | } else { |
| | | this.tempRange.before = this.cale.multipleStatus.before |
| | | this.tempRange.after = this.cale.multipleStatus.after |
| | | } |
| | | this.change(true) |
| | | }, |
| | | changeMonth(type) { |
| | | let newDate |
| | | if (type === 'pre') { |
| | | newDate = this.cale.getPreMonthObj(this.nowDate.fullDate).fullDate |
| | | } else if (type === 'next') { |
| | | newDate = this.cale.getNextMonthObj(this.nowDate.fullDate).fullDate |
| | | } |
| | | |
| | | this.setDate(newDate) |
| | | this.monthSwitch() |
| | | }, |
| | | /** |
| | | * 设置日期 |
| | | * @param {Object} date |
| | | */ |
| | | setDate(date) { |
| | | this.cale.setDate(date) |
| | | this.weeks = this.cale.weeks |
| | | this.nowDate = this.cale.getInfo(date) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-primary: #007aff !default; |
| | | |
| | | .uni-calendar { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .uni-calendar__mask { |
| | | position: fixed; |
| | | bottom: 0; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background-color: rgba(0, 0, 0, 0.4); |
| | | transition-property: opacity; |
| | | transition-duration: 0.3s; |
| | | opacity: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | z-index: 99; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-calendar--mask-show { |
| | | opacity: 1 |
| | | } |
| | | |
| | | .uni-calendar--fixed { |
| | | position: fixed; |
| | | bottom: calc(var(--window-bottom)); |
| | | left: 0; |
| | | right: 0; |
| | | transition-property: transform; |
| | | transition-duration: 0.3s; |
| | | transform: translateY(460px); |
| | | /* #ifndef APP-NVUE */ |
| | | z-index: 99; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-calendar--ani-show { |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | .uni-calendar__content { |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .uni-calendar__content-mobile { |
| | | border-top-left-radius: 10px; |
| | | border-top-right-radius: 10px; |
| | | box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .uni-calendar__header { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 50px; |
| | | } |
| | | |
| | | .uni-calendar__header-mobile { |
| | | padding: 10px; |
| | | padding-bottom: 0; |
| | | } |
| | | |
| | | .uni-calendar--fixed-top { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | border-top-color: rgba(0, 0, 0, 0.4); |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar--fixed-width { |
| | | width: 50px; |
| | | } |
| | | |
| | | .uni-calendar__backtoday { |
| | | position: absolute; |
| | | right: 0; |
| | | top: 25rpx; |
| | | padding: 0 5px; |
| | | padding-left: 10px; |
| | | height: 25px; |
| | | line-height: 25px; |
| | | font-size: 12px; |
| | | border-top-left-radius: 25px; |
| | | border-bottom-left-radius: 25px; |
| | | color: #fff; |
| | | background-color: #f1f1f1; |
| | | } |
| | | |
| | | .uni-calendar__header-text { |
| | | text-align: center; |
| | | width: 100px; |
| | | font-size: 15px; |
| | | color: #666; |
| | | } |
| | | |
| | | .uni-calendar__button-text { |
| | | text-align: center; |
| | | width: 100px; |
| | | font-size: 14px; |
| | | color: $uni-primary; |
| | | /* #ifndef APP-NVUE */ |
| | | letter-spacing: 3px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-calendar__header-btn-box { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 50px; |
| | | height: 50px; |
| | | } |
| | | |
| | | .uni-calendar__header-btn { |
| | | width: 9px; |
| | | height: 9px; |
| | | border-left-color: #808080; |
| | | border-left-style: solid; |
| | | border-left-width: 1px; |
| | | border-top-color: #555555; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar--left { |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .uni-calendar--right { |
| | | transform: rotate(135deg); |
| | | } |
| | | |
| | | |
| | | .uni-calendar__weeks { |
| | | position: relative; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-calendar__weeks-item { |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-calendar__weeks-day { |
| | | flex: 1; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 40px; |
| | | border-bottom-color: #F5F5F5; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | |
| | | .uni-calendar__weeks-day-text { |
| | | font-size: 12px; |
| | | color: #B2B2B2; |
| | | } |
| | | |
| | | .uni-calendar__box { |
| | | position: relative; |
| | | // padding: 0 10px; |
| | | padding-bottom: 7px; |
| | | } |
| | | |
| | | .uni-calendar__box-bg { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | } |
| | | |
| | | .uni-calendar__box-bg-text { |
| | | font-size: 200px; |
| | | font-weight: bold; |
| | | color: #999; |
| | | opacity: 0.1; |
| | | text-align: center; |
| | | /* #ifndef APP-NVUE */ |
| | | line-height: 1; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-date-changed { |
| | | padding: 0 10px; |
| | | // line-height: 50px; |
| | | text-align: center; |
| | | color: #333; |
| | | border-top-color: #DCDCDC; |
| | | ; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-date-btn--ok { |
| | | padding: 20px 15px; |
| | | } |
| | | |
| | | .uni-date-changed--time-start { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-date-changed--time-end { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | align-items: center; |
| | | } |
| | | |
| | | .uni-date-changed--time-date { |
| | | color: #999; |
| | | line-height: 50px; |
| | | /* #ifdef MP-TOUTIAO */ |
| | | font-size: 16px; |
| | | /* #endif */ |
| | | margin-right: 5px; |
| | | // opacity: 0.6; |
| | | } |
| | | |
| | | .time-picker-style { |
| | | // width: 62px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | justify-content: center; |
| | | align-items: center |
| | | } |
| | | |
| | | .mr-10 { |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .dialog-close { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | align-items: center; |
| | | padding: 0 25px; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .dialog-close-plus { |
| | | width: 16px; |
| | | height: 2px; |
| | | background-color: #737987; |
| | | border-radius: 2px; |
| | | transform: rotate(45deg); |
| | | } |
| | | |
| | | .dialog-close-rotate { |
| | | position: absolute; |
| | | transform: rotate(-45deg); |
| | | } |
| | | |
| | | .uni-datetime-picker--btn { |
| | | border-radius: 100px; |
| | | height: 40px; |
| | | line-height: 40px; |
| | | background-color: $uni-primary; |
| | | color: #fff; |
| | | font-size: 16px; |
| | | letter-spacing: 2px; |
| | | } |
| | | |
| | | /* #ifndef APP-NVUE */ |
| | | .uni-datetime-picker--btn:active { |
| | | opacity: 0.7; |
| | | } |
| | | |
| | | /* #endif */ |
| | | </style> |
对比新文件 |
| | |
| | | { |
| | | "uni-datetime-picker.selectDate": "select date", |
| | | "uni-datetime-picker.selectTime": "select time", |
| | | "uni-datetime-picker.selectDateTime": "select date and time", |
| | | "uni-datetime-picker.startDate": "start date", |
| | | "uni-datetime-picker.endDate": "end date", |
| | | "uni-datetime-picker.startTime": "start time", |
| | | "uni-datetime-picker.endTime": "end time", |
| | | "uni-datetime-picker.ok": "ok", |
| | | "uni-datetime-picker.clear": "clear", |
| | | "uni-datetime-picker.cancel": "cancel", |
| | | "uni-datetime-picker.year": "-", |
| | | "uni-datetime-picker.month": "", |
| | | "uni-calender.MON": "MON", |
| | | "uni-calender.TUE": "TUE", |
| | | "uni-calender.WED": "WED", |
| | | "uni-calender.THU": "THU", |
| | | "uni-calender.FRI": "FRI", |
| | | "uni-calender.SAT": "SAT", |
| | | "uni-calender.SUN": "SUN", |
| | | "uni-calender.confirm": "confirm" |
| | | } |
对比新文件 |
| | |
| | | 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 |
| | | } |
对比新文件 |
| | |
| | | { |
| | | "uni-datetime-picker.selectDate": "选择日期", |
| | | "uni-datetime-picker.selectTime": "选择时间", |
| | | "uni-datetime-picker.selectDateTime": "选择日期时间", |
| | | "uni-datetime-picker.startDate": "开始日期", |
| | | "uni-datetime-picker.endDate": "结束日期", |
| | | "uni-datetime-picker.startTime": "开始时间", |
| | | "uni-datetime-picker.endTime": "结束时间", |
| | | "uni-datetime-picker.ok": "确定", |
| | | "uni-datetime-picker.clear": "清除", |
| | | "uni-datetime-picker.cancel": "取消", |
| | | "uni-datetime-picker.year": "年", |
| | | "uni-datetime-picker.month": "月", |
| | | "uni-calender.SUN": "日", |
| | | "uni-calender.MON": "一", |
| | | "uni-calender.TUE": "二", |
| | | "uni-calender.WED": "三", |
| | | "uni-calender.THU": "四", |
| | | "uni-calender.FRI": "五", |
| | | "uni-calender.SAT": "六", |
| | | "uni-calender.confirm": "确认" |
| | | } |
对比新文件 |
| | |
| | | { |
| | | "uni-datetime-picker.selectDate": "選擇日期", |
| | | "uni-datetime-picker.selectTime": "選擇時間", |
| | | "uni-datetime-picker.selectDateTime": "選擇日期時間", |
| | | "uni-datetime-picker.startDate": "開始日期", |
| | | "uni-datetime-picker.endDate": "結束日期", |
| | | "uni-datetime-picker.startTime": "開始时间", |
| | | "uni-datetime-picker.endTime": "結束时间", |
| | | "uni-datetime-picker.ok": "確定", |
| | | "uni-datetime-picker.clear": "清除", |
| | | "uni-datetime-picker.cancel": "取消", |
| | | "uni-datetime-picker.year": "年", |
| | | "uni-datetime-picker.month": "月", |
| | | "uni-calender.SUN": "日", |
| | | "uni-calender.MON": "一", |
| | | "uni-calender.TUE": "二", |
| | | "uni-calender.WED": "三", |
| | | "uni-calender.THU": "四", |
| | | "uni-calender.FRI": "五", |
| | | "uni-calender.SAT": "六", |
| | | "uni-calender.confirm": "確認" |
| | | } |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-datetime-picker"> |
| | | <view @click="initTimePicker"> |
| | | <slot> |
| | | <view class="uni-datetime-picker-timebox-pointer" |
| | | :class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}"> |
| | | <text class="uni-datetime-picker-text">{{time}}</text> |
| | | <view v-if="!time" class="uni-datetime-picker-time"> |
| | | <text class="uni-datetime-picker-text">{{selectTimeText}}</text> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | <view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view> |
| | | <view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']" |
| | | :style="fixNvueBug"> |
| | | <view class="uni-title"> |
| | | <text class="uni-datetime-picker-text">{{selectTimeText}}</text> |
| | | </view> |
| | | <view v-if="dateShow" class="uni-datetime-picker__container-box"> |
| | | <picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd" |
| | | @change="bindDateChange"> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | </picker-view> |
| | | <!-- 兼容 nvue 不支持伪类 --> |
| | | <text class="uni-datetime-picker-sign sign-left">-</text> |
| | | <text class="uni-datetime-picker-sign sign-right">-</text> |
| | | </view> |
| | | <view v-if="timeShow" class="uni-datetime-picker__container-box"> |
| | | <picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']" |
| | | :indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange"> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | <picker-view-column v-if="!hideSecond"> |
| | | <view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index"> |
| | | <text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text> |
| | | </view> |
| | | </picker-view-column> |
| | | </picker-view> |
| | | <!-- 兼容 nvue 不支持伪类 --> |
| | | <text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text> |
| | | <text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text> |
| | | </view> |
| | | <view class="uni-datetime-picker-btn"> |
| | | <view @click="clearTime"> |
| | | <text class="uni-datetime-picker-btn-text">{{clearText}}</text> |
| | | </view> |
| | | <view class="uni-datetime-picker-btn-group"> |
| | | <view class="uni-datetime-picker-cancel" @click="tiggerTimePicker"> |
| | | <text class="uni-datetime-picker-btn-text">{{cancelText}}</text> |
| | | </view> |
| | | <view @click="setTime"> |
| | | <text class="uni-datetime-picker-btn-text">{{okText}}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { |
| | | initVueI18n |
| | | } from '@dcloudio/uni-i18n' |
| | | import i18nMessages from './i18n/index.js' |
| | | const { |
| | | t |
| | | } = initVueI18n(i18nMessages) |
| | | import { |
| | | fixIosDateFormat |
| | | } from './util' |
| | | |
| | | /** |
| | | * DatetimePicker 时间选择器 |
| | | * @description 可以同时选择日期和时间的选择器 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx |
| | | * @property {String} type = [datetime | date | time] 显示模式 |
| | | * @property {Boolean} multiple = [true|false] 是否多选 |
| | | * @property {String|Number} value 默认值 |
| | | * @property {String|Number} start 起始日期或时间 |
| | | * @property {String|Number} end 起始日期或时间 |
| | | * @property {String} return-type = [timestamp | string] |
| | | * @event {Function} change 选中发生变化触发 |
| | | */ |
| | | |
| | | export default { |
| | | name: 'UniDatetimePicker', |
| | | data() { |
| | | return { |
| | | indicatorStyle: `height: 50px;`, |
| | | visible: false, |
| | | fixNvueBug: {}, |
| | | dateShow: true, |
| | | timeShow: true, |
| | | title: '日期和时间', |
| | | // 输入框当前时间 |
| | | time: '', |
| | | // 当前的年月日时分秒 |
| | | year: 1920, |
| | | month: 0, |
| | | day: 0, |
| | | hour: 0, |
| | | minute: 0, |
| | | second: 0, |
| | | // 起始时间 |
| | | startYear: 1920, |
| | | startMonth: 1, |
| | | startDay: 1, |
| | | startHour: 0, |
| | | startMinute: 0, |
| | | startSecond: 0, |
| | | // 结束时间 |
| | | endYear: 2120, |
| | | endMonth: 12, |
| | | endDay: 31, |
| | | endHour: 23, |
| | | endMinute: 59, |
| | | endSecond: 59, |
| | | } |
| | | }, |
| | | options: { |
| | | // #ifdef MP-TOUTIAO |
| | | virtualHost: false, |
| | | // #endif |
| | | // #ifndef MP-TOUTIAO |
| | | virtualHost: true |
| | | // #endif |
| | | }, |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: 'datetime' |
| | | }, |
| | | value: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Number], |
| | | default: '' |
| | | }, |
| | | start: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | end: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | returnType: { |
| | | type: String, |
| | | default: 'string' |
| | | }, |
| | | disabled: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | }, |
| | | border: { |
| | | type: [Boolean, String], |
| | | default: true |
| | | }, |
| | | hideSecond: { |
| | | type: [Boolean, String], |
| | | default: false |
| | | } |
| | | }, |
| | | watch: { |
| | | // #ifndef VUE3 |
| | | value: { |
| | | handler(newVal) { |
| | | if (newVal) { |
| | | this.parseValue(fixIosDateFormat(newVal)) |
| | | this.initTime(false) |
| | | } else { |
| | | this.time = '' |
| | | this.parseValue(Date.now()) |
| | | } |
| | | }, |
| | | immediate: true |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | modelValue: { |
| | | handler(newVal) { |
| | | if (newVal) { |
| | | this.parseValue(fixIosDateFormat(newVal)) |
| | | this.initTime(false) |
| | | } else { |
| | | this.time = '' |
| | | this.parseValue(Date.now()) |
| | | } |
| | | }, |
| | | immediate: true |
| | | }, |
| | | // #endif |
| | | type: { |
| | | handler(newValue) { |
| | | if (newValue === 'date') { |
| | | this.dateShow = true |
| | | this.timeShow = false |
| | | this.title = '日期' |
| | | } else if (newValue === 'time') { |
| | | this.dateShow = false |
| | | this.timeShow = true |
| | | this.title = '时间' |
| | | } else { |
| | | this.dateShow = true |
| | | this.timeShow = true |
| | | this.title = '日期和时间' |
| | | } |
| | | }, |
| | | immediate: true |
| | | }, |
| | | start: { |
| | | handler(newVal) { |
| | | this.parseDatetimeRange(fixIosDateFormat(newVal), 'start') |
| | | }, |
| | | immediate: true |
| | | }, |
| | | end: { |
| | | handler(newVal) { |
| | | this.parseDatetimeRange(fixIosDateFormat(newVal), 'end') |
| | | }, |
| | | immediate: true |
| | | }, |
| | | |
| | | // 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项 |
| | | months(newVal) { |
| | | this.checkValue('month', this.month, newVal) |
| | | }, |
| | | days(newVal) { |
| | | this.checkValue('day', this.day, newVal) |
| | | }, |
| | | hours(newVal) { |
| | | this.checkValue('hour', this.hour, newVal) |
| | | }, |
| | | minutes(newVal) { |
| | | this.checkValue('minute', this.minute, newVal) |
| | | }, |
| | | seconds(newVal) { |
| | | this.checkValue('second', this.second, newVal) |
| | | } |
| | | }, |
| | | computed: { |
| | | // 当前年、月、日、时、分、秒选择范围 |
| | | years() { |
| | | return this.getCurrentRange('year') |
| | | }, |
| | | |
| | | months() { |
| | | return this.getCurrentRange('month') |
| | | }, |
| | | |
| | | days() { |
| | | return this.getCurrentRange('day') |
| | | }, |
| | | |
| | | hours() { |
| | | return this.getCurrentRange('hour') |
| | | }, |
| | | |
| | | minutes() { |
| | | return this.getCurrentRange('minute') |
| | | }, |
| | | |
| | | seconds() { |
| | | return this.getCurrentRange('second') |
| | | }, |
| | | |
| | | // picker 当前值数组 |
| | | ymd() { |
| | | return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay] |
| | | }, |
| | | hms() { |
| | | return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond] |
| | | }, |
| | | |
| | | // 当前 date 是 start |
| | | currentDateIsStart() { |
| | | return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay |
| | | }, |
| | | |
| | | // 当前 date 是 end |
| | | currentDateIsEnd() { |
| | | return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay |
| | | }, |
| | | |
| | | // 当前年、月、日、时、分、秒的最小值和最大值 |
| | | minYear() { |
| | | return this.startYear |
| | | }, |
| | | maxYear() { |
| | | return this.endYear |
| | | }, |
| | | minMonth() { |
| | | if (this.year === this.startYear) { |
| | | return this.startMonth |
| | | } else { |
| | | return 1 |
| | | } |
| | | }, |
| | | maxMonth() { |
| | | if (this.year === this.endYear) { |
| | | return this.endMonth |
| | | } else { |
| | | return 12 |
| | | } |
| | | }, |
| | | minDay() { |
| | | if (this.year === this.startYear && this.month === this.startMonth) { |
| | | return this.startDay |
| | | } else { |
| | | return 1 |
| | | } |
| | | }, |
| | | maxDay() { |
| | | if (this.year === this.endYear && this.month === this.endMonth) { |
| | | return this.endDay |
| | | } else { |
| | | return this.daysInMonth(this.year, this.month) |
| | | } |
| | | }, |
| | | minHour() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsStart) { |
| | | return this.startHour |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | return this.startHour |
| | | } |
| | | }, |
| | | maxHour() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsEnd) { |
| | | return this.endHour |
| | | } else { |
| | | return 23 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | return this.endHour |
| | | } |
| | | }, |
| | | minMinute() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsStart && this.hour === this.startHour) { |
| | | return this.startMinute |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.startHour) { |
| | | return this.startMinute |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | }, |
| | | maxMinute() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsEnd && this.hour === this.endHour) { |
| | | return this.endMinute |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.endHour) { |
| | | return this.endMinute |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | }, |
| | | minSecond() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) { |
| | | return this.startSecond |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.startHour && this.minute === this.startMinute) { |
| | | return this.startSecond |
| | | } else { |
| | | return 0 |
| | | } |
| | | } |
| | | }, |
| | | maxSecond() { |
| | | if (this.type === 'datetime') { |
| | | if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) { |
| | | return this.endSecond |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | if (this.type === 'time') { |
| | | if (this.hour === this.endHour && this.minute === this.endMinute) { |
| | | return this.endSecond |
| | | } else { |
| | | return 59 |
| | | } |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * for i18n |
| | | */ |
| | | selectTimeText() { |
| | | return t("uni-datetime-picker.selectTime") |
| | | }, |
| | | okText() { |
| | | return t("uni-datetime-picker.ok") |
| | | }, |
| | | clearText() { |
| | | return t("uni-datetime-picker.clear") |
| | | }, |
| | | cancelText() { |
| | | return t("uni-datetime-picker.cancel") |
| | | } |
| | | }, |
| | | |
| | | mounted() { |
| | | // #ifdef APP-NVUE |
| | | const res = uni.getSystemInfoSync(); |
| | | this.fixNvueBug = { |
| | | top: res.windowHeight / 2, |
| | | left: res.windowWidth / 2 |
| | | } |
| | | // #endif |
| | | }, |
| | | |
| | | methods: { |
| | | /** |
| | | * @param {Object} item |
| | | * 小于 10 在前面加个 0 |
| | | */ |
| | | |
| | | lessThanTen(item) { |
| | | return item < 10 ? '0' + item : item |
| | | }, |
| | | |
| | | /** |
| | | * 解析时分秒字符串,例如:00:00:00 |
| | | * @param {String} timeString |
| | | */ |
| | | parseTimeType(timeString) { |
| | | if (timeString) { |
| | | let timeArr = timeString.split(':') |
| | | this.hour = Number(timeArr[0]) |
| | | this.minute = Number(timeArr[1]) |
| | | this.second = Number(timeArr[2]) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000 |
| | | * @param {String | Number} datetime |
| | | */ |
| | | initPickerValue(datetime) { |
| | | let defaultValue = null |
| | | if (datetime) { |
| | | defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end) |
| | | } else { |
| | | defaultValue = Date.now() |
| | | defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end) |
| | | } |
| | | this.parseValue(defaultValue) |
| | | }, |
| | | |
| | | /** |
| | | * 初始值规则: |
| | | * - 用户设置初始值 value |
| | | * - 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start |
| | | * - 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start |
| | | * - 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end |
| | | * - 无起始终止时间,则初始值为 value |
| | | * - 无初始值 value,则初始值为当前本地时间 Date.now() |
| | | * @param {Object} value |
| | | * @param {Object} dateBase |
| | | */ |
| | | compareValueWithStartAndEnd(value, start, end) { |
| | | let winner = null |
| | | value = this.superTimeStamp(value) |
| | | start = this.superTimeStamp(start) |
| | | end = this.superTimeStamp(end) |
| | | |
| | | if (start && end) { |
| | | if (value < start) { |
| | | winner = new Date(start) |
| | | } else if (value > end) { |
| | | winner = new Date(end) |
| | | } else { |
| | | winner = new Date(value) |
| | | } |
| | | } else if (start && !end) { |
| | | winner = start <= value ? new Date(value) : new Date(start) |
| | | } else if (!start && end) { |
| | | winner = value <= end ? new Date(value) : new Date(end) |
| | | } else { |
| | | winner = new Date(value) |
| | | } |
| | | |
| | | return winner |
| | | }, |
| | | |
| | | /** |
| | | * 转换为可比较的时间戳,接受日期、时分秒、时间戳 |
| | | * @param {Object} value |
| | | */ |
| | | superTimeStamp(value) { |
| | | let dateBase = '' |
| | | if (this.type === 'time' && value && typeof value === 'string') { |
| | | const now = new Date() |
| | | const year = now.getFullYear() |
| | | const month = now.getMonth() + 1 |
| | | const day = now.getDate() |
| | | dateBase = year + '/' + month + '/' + day + ' ' |
| | | } |
| | | if (Number(value)) { |
| | | value = parseInt(value) |
| | | dateBase = 0 |
| | | } |
| | | return this.createTimeStamp(dateBase + value) |
| | | }, |
| | | |
| | | /** |
| | | * 解析默认值 value,字符串、时间戳 |
| | | * @param {Object} defaultTime |
| | | */ |
| | | parseValue(value) { |
| | | if (!value) { |
| | | return |
| | | } |
| | | if (this.type === 'time' && typeof value === "string") { |
| | | this.parseTimeType(value) |
| | | } else { |
| | | let defaultDate = null |
| | | defaultDate = new Date(value) |
| | | if (this.type !== 'time') { |
| | | this.year = defaultDate.getFullYear() |
| | | this.month = defaultDate.getMonth() + 1 |
| | | this.day = defaultDate.getDate() |
| | | } |
| | | if (this.type !== 'date') { |
| | | this.hour = defaultDate.getHours() |
| | | this.minute = defaultDate.getMinutes() |
| | | this.second = defaultDate.getSeconds() |
| | | } |
| | | } |
| | | if (this.hideSecond) { |
| | | this.second = 0 |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 解析可选择时间范围 start、end,年月日字符串、时间戳 |
| | | * @param {Object} defaultTime |
| | | */ |
| | | parseDatetimeRange(point, pointType) { |
| | | // 时间为空,则重置为初始值 |
| | | if (!point) { |
| | | if (pointType === 'start') { |
| | | this.startYear = 1920 |
| | | this.startMonth = 1 |
| | | this.startDay = 1 |
| | | this.startHour = 0 |
| | | this.startMinute = 0 |
| | | this.startSecond = 0 |
| | | } |
| | | if (pointType === 'end') { |
| | | this.endYear = 2120 |
| | | this.endMonth = 12 |
| | | this.endDay = 31 |
| | | this.endHour = 23 |
| | | this.endMinute = 59 |
| | | this.endSecond = 59 |
| | | } |
| | | return |
| | | } |
| | | if (this.type === 'time') { |
| | | const pointArr = point.split(':') |
| | | this[pointType + 'Hour'] = Number(pointArr[0]) |
| | | this[pointType + 'Minute'] = Number(pointArr[1]) |
| | | this[pointType + 'Second'] = Number(pointArr[2]) |
| | | } else { |
| | | if (!point) { |
| | | pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60 |
| | | return |
| | | } |
| | | if (Number(point)) { |
| | | point = parseInt(point) |
| | | } |
| | | // datetime 的 end 没有时分秒, 则不限制 |
| | | const hasTime = /[0-9]:[0-9]/ |
| | | if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test( |
| | | point)) { |
| | | point = point + ' 23:59:59' |
| | | } |
| | | const pointDate = new Date(point) |
| | | this[pointType + 'Year'] = pointDate.getFullYear() |
| | | this[pointType + 'Month'] = pointDate.getMonth() + 1 |
| | | this[pointType + 'Day'] = pointDate.getDate() |
| | | if (this.type === 'datetime') { |
| | | this[pointType + 'Hour'] = pointDate.getHours() |
| | | this[pointType + 'Minute'] = pointDate.getMinutes() |
| | | this[pointType + 'Second'] = pointDate.getSeconds() |
| | | } |
| | | } |
| | | }, |
| | | |
| | | // 获取 年、月、日、时、分、秒 当前可选范围 |
| | | getCurrentRange(value) { |
| | | const range = [] |
| | | for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) { |
| | | range.push(i) |
| | | } |
| | | return range |
| | | }, |
| | | |
| | | // 字符串首字母大写 |
| | | capitalize(str) { |
| | | return str.charAt(0).toUpperCase() + str.slice(1) |
| | | }, |
| | | |
| | | // 检查当前值是否在范围内,不在则当前值重置为可选范围第一项 |
| | | checkValue(name, value, values) { |
| | | if (values.indexOf(value) === -1) { |
| | | this[name] = values[0] |
| | | } |
| | | }, |
| | | |
| | | // 每个月的实际天数 |
| | | daysInMonth(year, month) { // Use 1 for January, 2 for February, etc. |
| | | return new Date(year, month, 0).getDate(); |
| | | }, |
| | | |
| | | /** |
| | | * 生成时间戳 |
| | | * @param {Object} time |
| | | */ |
| | | createTimeStamp(time) { |
| | | if (!time) return |
| | | if (typeof time === "number") { |
| | | return time |
| | | } else { |
| | | time = time.replace(/-/g, '/') |
| | | if (this.type === 'date') { |
| | | time = time + ' ' + '00:00:00' |
| | | } |
| | | return Date.parse(time) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 生成日期或时间的字符串 |
| | | */ |
| | | createDomSting() { |
| | | const yymmdd = this.year + |
| | | '-' + |
| | | this.lessThanTen(this.month) + |
| | | '-' + |
| | | this.lessThanTen(this.day) |
| | | |
| | | let hhmmss = this.lessThanTen(this.hour) + |
| | | ':' + |
| | | this.lessThanTen(this.minute) |
| | | |
| | | if (!this.hideSecond) { |
| | | hhmmss = hhmmss + ':' + this.lessThanTen(this.second) |
| | | } |
| | | |
| | | if (this.type === 'date') { |
| | | return yymmdd |
| | | } else if (this.type === 'time') { |
| | | return hhmmss |
| | | } else { |
| | | return yymmdd + ' ' + hhmmss |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 初始化返回值,并抛出 change 事件 |
| | | */ |
| | | initTime(emit = true) { |
| | | this.time = this.createDomSting() |
| | | if (!emit) return |
| | | if (this.returnType === 'timestamp' && this.type !== 'time') { |
| | | this.$emit('change', this.createTimeStamp(this.time)) |
| | | this.$emit('input', this.createTimeStamp(this.time)) |
| | | this.$emit('update:modelValue', this.createTimeStamp(this.time)) |
| | | } else { |
| | | this.$emit('change', this.time) |
| | | this.$emit('input', this.time) |
| | | this.$emit('update:modelValue', this.time) |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * 用户选择日期或时间更新 data |
| | | * @param {Object} e |
| | | */ |
| | | bindDateChange(e) { |
| | | const val = e.detail.value |
| | | this.year = this.years[val[0]] |
| | | this.month = this.months[val[1]] |
| | | this.day = this.days[val[2]] |
| | | }, |
| | | bindTimeChange(e) { |
| | | const val = e.detail.value |
| | | this.hour = this.hours[val[0]] |
| | | this.minute = this.minutes[val[1]] |
| | | this.second = this.seconds[val[2]] |
| | | }, |
| | | |
| | | /** |
| | | * 初始化弹出层 |
| | | */ |
| | | initTimePicker() { |
| | | if (this.disabled) return |
| | | const value = fixIosDateFormat(this.time) |
| | | this.initPickerValue(value) |
| | | this.visible = !this.visible |
| | | }, |
| | | |
| | | /** |
| | | * 触发或关闭弹框 |
| | | */ |
| | | tiggerTimePicker(e) { |
| | | this.visible = !this.visible |
| | | }, |
| | | |
| | | /** |
| | | * 用户点击“清空”按钮,清空当前值 |
| | | */ |
| | | clearTime() { |
| | | this.time = '' |
| | | this.$emit('change', this.time) |
| | | this.$emit('input', this.time) |
| | | this.$emit('update:modelValue', this.time) |
| | | this.tiggerTimePicker() |
| | | }, |
| | | |
| | | /** |
| | | * 用户点击“确定”按钮 |
| | | */ |
| | | setTime() { |
| | | this.initTime() |
| | | this.tiggerTimePicker() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-primary: #007aff !default; |
| | | |
| | | .uni-datetime-picker { |
| | | /* #ifndef APP-NVUE */ |
| | | /* width: 100%; */ |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-view { |
| | | height: 130px; |
| | | width: 270px; |
| | | /* #ifndef APP-NVUE */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-item { |
| | | height: 50px; |
| | | line-height: 50px; |
| | | text-align: center; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn { |
| | | margin-top: 60px; |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn-text { |
| | | font-size: 14px; |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | .uni-datetime-picker-btn-group { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .uni-datetime-picker-cancel { |
| | | margin-right: 30px; |
| | | } |
| | | |
| | | .uni-datetime-picker-mask { |
| | | position: fixed; |
| | | bottom: 0px; |
| | | top: 0px; |
| | | left: 0px; |
| | | right: 0px; |
| | | background-color: rgba(0, 0, 0, 0.4); |
| | | transition-duration: 0.3s; |
| | | z-index: 998; |
| | | } |
| | | |
| | | .uni-datetime-picker-popup { |
| | | border-radius: 8px; |
| | | padding: 30px; |
| | | width: 270px; |
| | | /* #ifdef APP-NVUE */ |
| | | height: 500px; |
| | | /* #endif */ |
| | | /* #ifdef APP-NVUE */ |
| | | width: 330px; |
| | | /* #endif */ |
| | | background-color: #fff; |
| | | position: fixed; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%); |
| | | transition-duration: 0.3s; |
| | | z-index: 999; |
| | | } |
| | | |
| | | .fix-nvue-height { |
| | | /* #ifdef APP-NVUE */ |
| | | height: 330px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-time { |
| | | color: grey; |
| | | } |
| | | |
| | | .uni-datetime-picker-column { |
| | | height: 50px; |
| | | } |
| | | |
| | | .uni-datetime-picker-timebox { |
| | | |
| | | border: 1px solid #E5E5E5; |
| | | border-radius: 5px; |
| | | padding: 7px 10px; |
| | | /* #ifndef APP-NVUE */ |
| | | box-sizing: border-box; |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-timebox-pointer { |
| | | /* #ifndef APP-NVUE */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | |
| | | .uni-datetime-picker-disabled { |
| | | opacity: 0.4; |
| | | /* #ifdef H5 */ |
| | | cursor: not-allowed !important; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-datetime-picker-text { |
| | | font-size: 14px; |
| | | line-height: 50px |
| | | } |
| | | |
| | | .uni-datetime-picker-sign { |
| | | position: absolute; |
| | | top: 53px; |
| | | /* 减掉 10px 的元素高度,兼容nvue */ |
| | | color: #999; |
| | | /* #ifdef APP-NVUE */ |
| | | font-size: 16px; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .sign-left { |
| | | left: 86px; |
| | | } |
| | | |
| | | .sign-right { |
| | | right: 86px; |
| | | } |
| | | |
| | | .sign-center { |
| | | left: 135px; |
| | | } |
| | | |
| | | .uni-datetime-picker__container-box { |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-top: 40px; |
| | | } |
| | | |
| | | .time-hide-second { |
| | | width: 180px; |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | <template> |
| | | <view class="uni-date"> |
| | | <view class="uni-date-editor" @click="show"> |
| | | <slot> |
| | | <view class="uni-date-editor--x" |
| | | :class="{'uni-date-editor--x__disabled': disabled,'uni-date-x--border': border}"> |
| | | <view v-if="!isRange" class="uni-date-x uni-date-single"> |
| | | <uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons> |
| | | <view class="uni-date__x-input">{{ displayValue || singlePlaceholderText }}</view> |
| | | </view> |
| | | |
| | | <view v-else class="uni-date-x uni-date-range"> |
| | | <uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons> |
| | | <view class="uni-date__x-input text-center">{{ displayRangeValue.startDate || startPlaceholderText }}</view> |
| | | |
| | | <view class="range-separator">{{rangeSeparator}}</view> |
| | | |
| | | <view class="uni-date__x-input text-center">{{ displayRangeValue.endDate || endPlaceholderText }}</view> |
| | | </view> |
| | | |
| | | <view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear"> |
| | | <uni-icons type="clear" color="#c0c4cc" size="22"></uni-icons> |
| | | </view> |
| | | </view> |
| | | </slot> |
| | | </view> |
| | | |
| | | <view v-show="pickerVisible" class="uni-date-mask--pc" @click="close"></view> |
| | | |
| | | <view v-if="!isPhone" v-show="pickerVisible" ref="datePicker" class="uni-date-picker__container"> |
| | | <view v-if="!isRange" class="uni-date-single--x" :style="pickerPositionStyle"> |
| | | <view class="uni-popper__arrow"></view> |
| | | |
| | | <view v-if="hasTime" class="uni-date-changed popup-x-header"> |
| | | <input class="uni-date__input text-center" type="text" v-model="inputDate" :placeholder="selectDateText" /> |
| | | |
| | | <time-picker type="time" v-model="pickerTime" :border="false" :disabled="!inputDate" |
| | | :start="timepickerStartTime" :end="timepickerEndTime" :hideSecond="hideSecond" style="width: 100%;"> |
| | | <input class="uni-date__input text-center" type="text" v-model="pickerTime" :placeholder="selectTimeText" |
| | | :disabled="!inputDate" /> |
| | | </time-picker> |
| | | </view> |
| | | |
| | | <Calendar ref="pcSingle" :showMonth="false" :start-date="calendarRange.startDate" |
| | | :end-date="calendarRange.endDate" :date="calendarDate" @change="singleChange" :default-value="defaultValue" |
| | | style="padding: 0 8px;" /> |
| | | |
| | | <view v-if="hasTime" class="popup-x-footer"> |
| | | <text class="confirm-text" @click="confirmSingleChange">{{okText}}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="uni-date-range--x" :style="pickerPositionStyle"> |
| | | <view class="uni-popper__arrow"></view> |
| | | <view v-if="hasTime" class="popup-x-header uni-date-changed"> |
| | | <view class="popup-x-header--datetime"> |
| | | <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate" |
| | | :placeholder="startDateText" /> |
| | | |
| | | <time-picker type="time" v-model="tempRange.startTime" :start="timepickerStartTime" :border="false" |
| | | :disabled="!tempRange.startDate" :hideSecond="hideSecond"> |
| | | <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startTime" |
| | | :placeholder="startTimeText" :disabled="!tempRange.startDate" /> |
| | | </time-picker> |
| | | </view> |
| | | |
| | | <uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons> |
| | | |
| | | <view class="popup-x-header--datetime"> |
| | | <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate" |
| | | :placeholder="endDateText" /> |
| | | |
| | | <time-picker type="time" v-model="tempRange.endTime" :end="timepickerEndTime" :border="false" |
| | | :disabled="!tempRange.endDate" :hideSecond="hideSecond"> |
| | | <input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime" |
| | | :placeholder="endTimeText" :disabled="!tempRange.endDate" /> |
| | | </time-picker> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="popup-x-body"> |
| | | <Calendar ref="left" :showMonth="false" :start-date="calendarRange.startDate" |
| | | :end-date="calendarRange.endDate" :range="true" :pleStatus="endMultipleStatus" @change="leftChange" |
| | | @firstEnterCale="updateRightCale" style="padding: 0 8px;"/> |
| | | <Calendar ref="right" :showMonth="false" :start-date="calendarRange.startDate" |
| | | :end-date="calendarRange.endDate" :range="true" @change="rightChange" :pleStatus="startMultipleStatus" |
| | | @firstEnterCale="updateLeftCale" style="padding: 0 8px;border-left: 1px solid #F1F1F1;" /> |
| | | </view> |
| | | |
| | | <view v-if="hasTime" class="popup-x-footer"> |
| | | <text @click="clear">{{clearText}}</text> |
| | | <text class="confirm-text" @click="confirmRangeChange">{{okText}}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <Calendar v-if="isPhone" ref="mobile" :clearDate="false" :date="calendarDate" :defTime="mobileCalendarTime" |
| | | :start-date="calendarRange.startDate" :end-date="calendarRange.endDate" :selectableTimes="mobSelectableTime" |
| | | :startPlaceholder="startPlaceholder" :endPlaceholder="endPlaceholder" :default-value="defaultValue" |
| | | :pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :hasTime="hasTime" :insert="false" |
| | | :hideSecond="hideSecond" @confirm="mobileChange" @maskClose="close" @change="calendarClick"/> |
| | | </view> |
| | | </template> |
| | | <script> |
| | | /** |
| | | * DatetimePicker 时间选择器 |
| | | * @description 同时支持 PC 和移动端使用日历选择日期和日期范围 |
| | | * @tutorial https://ext.dcloud.net.cn/plugin?id=3962 |
| | | * @property {String} type 选择器类型 |
| | | * @property {String|Number|Array|Date} value 绑定值 |
| | | * @property {String} placeholder 单选择时的占位内容 |
| | | * @property {String} start 起始时间 |
| | | * @property {String} end 终止时间 |
| | | * @property {String} start-placeholder 范围选择时开始日期的占位内容 |
| | | * @property {String} end-placeholder 范围选择时结束日期的占位内容 |
| | | * @property {String} range-separator 选择范围时的分隔符 |
| | | * @property {Boolean} border = [true|false] 是否有边框 |
| | | * @property {Boolean} disabled = [true|false] 是否禁用 |
| | | * @property {Boolean} clearIcon = [true|false] 是否显示清除按钮(仅PC端适用) |
| | | * @property {[String} defaultValue 选择器打开时默认显示的时间 |
| | | * @event {Function} change 确定日期时触发的事件 |
| | | * @event {Function} maskClick 点击遮罩层触发的事件 |
| | | * @event {Function} show 打开弹出层 |
| | | * @event {Function} close 关闭弹出层 |
| | | * @event {Function} clear 清除上次选中的状态和值 |
| | | **/ |
| | | import Calendar from './calendar.vue' |
| | | import TimePicker from './time-picker.vue' |
| | | import { |
| | | initVueI18n |
| | | } from '@dcloudio/uni-i18n' |
| | | import i18nMessages from './i18n/index.js' |
| | | import { |
| | | getDateTime, |
| | | getDate, |
| | | getTime, |
| | | getDefaultSecond, |
| | | dateCompare, |
| | | checkDate, |
| | | fixIosDateFormat |
| | | } from './util' |
| | | |
| | | export default { |
| | | name: 'UniDatetimePicker', |
| | | |
| | | options: { |
| | | // #ifdef MP-TOUTIAO |
| | | virtualHost: false, |
| | | // #endif |
| | | // #ifndef MP-TOUTIAO |
| | | virtualHost: true |
| | | // #endif |
| | | }, |
| | | components: { |
| | | Calendar, |
| | | TimePicker |
| | | }, |
| | | data() { |
| | | return { |
| | | isRange: false, |
| | | hasTime: false, |
| | | displayValue: '', |
| | | inputDate: '', |
| | | calendarDate: '', |
| | | pickerTime: '', |
| | | calendarRange: { |
| | | startDate: '', |
| | | startTime: '', |
| | | endDate: '', |
| | | endTime: '' |
| | | }, |
| | | displayRangeValue: { |
| | | startDate: '', |
| | | endDate: '', |
| | | }, |
| | | tempRange: { |
| | | startDate: '', |
| | | startTime: '', |
| | | endDate: '', |
| | | endTime: '' |
| | | }, |
| | | // 左右日历同步数据 |
| | | startMultipleStatus: { |
| | | before: '', |
| | | after: '', |
| | | data: [], |
| | | fulldate: '' |
| | | }, |
| | | endMultipleStatus: { |
| | | before: '', |
| | | after: '', |
| | | data: [], |
| | | fulldate: '' |
| | | }, |
| | | pickerVisible: false, |
| | | pickerPositionStyle: null, |
| | | isEmitValue: false, |
| | | isPhone: false, |
| | | isFirstShow: true, |
| | | i18nT: () => {} |
| | | } |
| | | }, |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: 'datetime' |
| | | }, |
| | | value: { |
| | | type: [String, Number, Array, Date], |
| | | default: '' |
| | | }, |
| | | modelValue: { |
| | | type: [String, Number, Array, Date], |
| | | default: '' |
| | | }, |
| | | start: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | end: { |
| | | type: [Number, String], |
| | | default: '' |
| | | }, |
| | | returnType: { |
| | | type: String, |
| | | default: 'string' |
| | | }, |
| | | placeholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | startPlaceholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | endPlaceholder: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | rangeSeparator: { |
| | | type: String, |
| | | default: '-' |
| | | }, |
| | | border: { |
| | | type: [Boolean], |
| | | default: true |
| | | }, |
| | | disabled: { |
| | | type: [Boolean], |
| | | default: false |
| | | }, |
| | | clearIcon: { |
| | | type: [Boolean], |
| | | default: true |
| | | }, |
| | | hideSecond: { |
| | | type: [Boolean], |
| | | default: false |
| | | }, |
| | | defaultValue: { |
| | | type: [String, Object, Array], |
| | | default: '' |
| | | } |
| | | }, |
| | | watch: { |
| | | type: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | this.hasTime = newVal.indexOf('time') !== -1 |
| | | this.isRange = newVal.indexOf('range') !== -1 |
| | | } |
| | | }, |
| | | // #ifndef VUE3 |
| | | value: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (this.isEmitValue) { |
| | | this.isEmitValue = false |
| | | return |
| | | } |
| | | this.initPicker(newVal) |
| | | } |
| | | }, |
| | | // #endif |
| | | // #ifdef VUE3 |
| | | modelValue: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (this.isEmitValue) { |
| | | this.isEmitValue = false |
| | | return |
| | | } |
| | | this.initPicker(newVal) |
| | | } |
| | | }, |
| | | // #endif |
| | | start: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (!newVal) return |
| | | this.calendarRange.startDate = getDate(newVal) |
| | | if (this.hasTime) { |
| | | this.calendarRange.startTime = getTime(newVal) |
| | | } |
| | | } |
| | | }, |
| | | end: { |
| | | immediate: true, |
| | | handler(newVal) { |
| | | if (!newVal) return |
| | | this.calendarRange.endDate = getDate(newVal) |
| | | if (this.hasTime) { |
| | | this.calendarRange.endTime = getTime(newVal, this.hideSecond) |
| | | } |
| | | } |
| | | }, |
| | | }, |
| | | computed: { |
| | | timepickerStartTime() { |
| | | const activeDate = this.isRange ? this.tempRange.startDate : this.inputDate |
| | | return activeDate === this.calendarRange.startDate ? this.calendarRange.startTime : '' |
| | | }, |
| | | timepickerEndTime() { |
| | | const activeDate = this.isRange ? this.tempRange.endDate : this.inputDate |
| | | return activeDate === this.calendarRange.endDate ? this.calendarRange.endTime : '' |
| | | }, |
| | | mobileCalendarTime() { |
| | | const timeRange = { |
| | | start: this.tempRange.startTime, |
| | | end: this.tempRange.endTime |
| | | } |
| | | return this.isRange ? timeRange : this.pickerTime |
| | | }, |
| | | mobSelectableTime() { |
| | | return { |
| | | start: this.calendarRange.startTime, |
| | | end: this.calendarRange.endTime |
| | | } |
| | | }, |
| | | datePopupWidth() { |
| | | // todo |
| | | return this.isRange ? 653 : 301 |
| | | }, |
| | | |
| | | /** |
| | | * for i18n |
| | | */ |
| | | singlePlaceholderText() { |
| | | return this.placeholder || (this.type === 'date' ? this.selectDateText : this.selectDateTimeText) |
| | | }, |
| | | startPlaceholderText() { |
| | | return this.startPlaceholder || this.startDateText |
| | | }, |
| | | endPlaceholderText() { |
| | | return this.endPlaceholder || this.endDateText |
| | | }, |
| | | selectDateText() { |
| | | return this.i18nT("uni-datetime-picker.selectDate") |
| | | }, |
| | | selectDateTimeText() { |
| | | return this.i18nT("uni-datetime-picker.selectDateTime") |
| | | }, |
| | | selectTimeText() { |
| | | return this.i18nT("uni-datetime-picker.selectTime") |
| | | }, |
| | | startDateText() { |
| | | return this.startPlaceholder || this.i18nT("uni-datetime-picker.startDate") |
| | | }, |
| | | startTimeText() { |
| | | return this.i18nT("uni-datetime-picker.startTime") |
| | | }, |
| | | endDateText() { |
| | | return this.endPlaceholder || this.i18nT("uni-datetime-picker.endDate") |
| | | }, |
| | | endTimeText() { |
| | | return this.i18nT("uni-datetime-picker.endTime") |
| | | }, |
| | | okText() { |
| | | return this.i18nT("uni-datetime-picker.ok") |
| | | }, |
| | | clearText() { |
| | | return this.i18nT("uni-datetime-picker.clear") |
| | | }, |
| | | showClearIcon() { |
| | | return this.clearIcon && !this.disabled && (this.displayValue || (this.displayRangeValue.startDate && this |
| | | .displayRangeValue.endDate)) |
| | | } |
| | | }, |
| | | created() { |
| | | this.initI18nT() |
| | | this.platform() |
| | | }, |
| | | methods: { |
| | | initI18nT() { |
| | | const vueI18n = initVueI18n(i18nMessages) |
| | | this.i18nT = vueI18n.t |
| | | }, |
| | | initPicker(newVal) { |
| | | if ((!newVal && !this.defaultValue) || Array.isArray(newVal) && !newVal.length) { |
| | | this.$nextTick(() => { |
| | | this.clear(false) |
| | | }) |
| | | return |
| | | } |
| | | |
| | | if (!Array.isArray(newVal) && !this.isRange) { |
| | | if (newVal) { |
| | | this.displayValue = this.inputDate = this.calendarDate = getDate(newVal) |
| | | if (this.hasTime) { |
| | | this.pickerTime = getTime(newVal, this.hideSecond) |
| | | this.displayValue = `${this.displayValue} ${this.pickerTime}` |
| | | } |
| | | } else if (this.defaultValue) { |
| | | this.inputDate = this.calendarDate = getDate(this.defaultValue) |
| | | if (this.hasTime) { |
| | | this.pickerTime = getTime(this.defaultValue, this.hideSecond) |
| | | } |
| | | } |
| | | } else { |
| | | const [before, after] = newVal |
| | | if (!before && !after) return |
| | | const beforeDate = getDate(before) |
| | | const beforeTime = getTime(before, this.hideSecond) |
| | | |
| | | const afterDate = getDate(after) |
| | | const afterTime = getTime(after, this.hideSecond) |
| | | const startDate = beforeDate |
| | | const endDate = afterDate |
| | | this.displayRangeValue.startDate = this.tempRange.startDate = startDate |
| | | this.displayRangeValue.endDate = this.tempRange.endDate = endDate |
| | | |
| | | if (this.hasTime) { |
| | | this.displayRangeValue.startDate = `${beforeDate} ${beforeTime}` |
| | | this.displayRangeValue.endDate = `${afterDate} ${afterTime}` |
| | | this.tempRange.startTime = beforeTime |
| | | this.tempRange.endTime = afterTime |
| | | } |
| | | const defaultRange = { |
| | | before: beforeDate, |
| | | after: afterDate |
| | | } |
| | | this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, { |
| | | which: 'right' |
| | | }) |
| | | this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, { |
| | | which: 'left' |
| | | }) |
| | | } |
| | | }, |
| | | updateLeftCale(e) { |
| | | const left = this.$refs.left |
| | | // 设置范围选 |
| | | left.cale.setHoverMultiple(e.after) |
| | | left.setDate(this.$refs.left.nowDate.fullDate) |
| | | }, |
| | | updateRightCale(e) { |
| | | const right = this.$refs.right |
| | | // 设置范围选 |
| | | right.cale.setHoverMultiple(e.after) |
| | | right.setDate(this.$refs.right.nowDate.fullDate) |
| | | }, |
| | | platform() { |
| | | if (typeof navigator !== "undefined") { |
| | | this.isPhone = navigator.userAgent.toLowerCase().indexOf('mobile') !== -1 |
| | | return |
| | | } |
| | | // #ifdef MP-WEIXIN |
| | | const { |
| | | windowWidth |
| | | } = uni.getWindowInfo() |
| | | // #endif |
| | | // #ifndef MP-WEIXIN |
| | | const { |
| | | windowWidth |
| | | } = uni.getSystemInfoSync() |
| | | // #endif |
| | | this.isPhone = windowWidth <= 500 |
| | | this.windowWidth = windowWidth |
| | | }, |
| | | show() { |
| | | this.$emit("show") |
| | | if (this.disabled) { |
| | | return |
| | | } |
| | | this.platform() |
| | | if (this.isPhone) { |
| | | setTimeout(() => { |
| | | this.$refs.mobile.open() |
| | | }, 0); |
| | | return |
| | | } |
| | | this.pickerPositionStyle = { |
| | | top: '10px' |
| | | } |
| | | const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor") |
| | | dateEditor.boundingClientRect(rect => { |
| | | if (this.windowWidth - rect.left < this.datePopupWidth) { |
| | | this.pickerPositionStyle.right = 0 |
| | | } |
| | | }).exec() |
| | | setTimeout(() => { |
| | | this.pickerVisible = !this.pickerVisible |
| | | if (!this.isPhone && this.isRange && this.isFirstShow) { |
| | | this.isFirstShow = false |
| | | const { |
| | | startDate, |
| | | endDate |
| | | } = this.calendarRange |
| | | if (startDate && endDate) { |
| | | if (this.diffDate(startDate, endDate) < 30) { |
| | | this.$refs.right.changeMonth('pre') |
| | | } |
| | | } else { |
| | | // this.$refs.right.changeMonth('next') |
| | | if (this.isPhone) { |
| | | this.$refs.right.cale.lastHover = false; |
| | | } |
| | | } |
| | | } |
| | | |
| | | }, 50) |
| | | }, |
| | | close() { |
| | | setTimeout(() => { |
| | | this.pickerVisible = false |
| | | this.$emit('maskClick', this.value) |
| | | this.$refs.mobile && this.$refs.mobile.close() |
| | | }, 20) |
| | | }, |
| | | setEmit(value) { |
| | | if (this.returnType === "timestamp" || this.returnType === "date") { |
| | | if (!Array.isArray(value)) { |
| | | if (!this.hasTime) { |
| | | value = value + ' ' + '00:00:00' |
| | | } |
| | | value = this.createTimestamp(value) |
| | | if (this.returnType === "date") { |
| | | value = new Date(value) |
| | | } |
| | | } else { |
| | | if (!this.hasTime) { |
| | | value[0] = value[0] + ' ' + '00:00:00' |
| | | value[1] = value[1] + ' ' + '00:00:00' |
| | | } |
| | | value[0] = this.createTimestamp(value[0]) |
| | | value[1] = this.createTimestamp(value[1]) |
| | | if (this.returnType === "date") { |
| | | value[0] = new Date(value[0]) |
| | | value[1] = new Date(value[1]) |
| | | } |
| | | } |
| | | } |
| | | |
| | | this.$emit('update:modelValue', value) |
| | | this.$emit('input', value) |
| | | this.$emit('change', value) |
| | | this.isEmitValue = true |
| | | }, |
| | | createTimestamp(date) { |
| | | date = fixIosDateFormat(date) |
| | | return Date.parse(new Date(date)) |
| | | }, |
| | | singleChange(e) { |
| | | this.calendarDate = this.inputDate = e.fulldate |
| | | if (this.hasTime) return |
| | | this.confirmSingleChange() |
| | | }, |
| | | confirmSingleChange() { |
| | | if (!checkDate(this.inputDate)) { |
| | | const now = new Date() |
| | | this.calendarDate = this.inputDate = getDate(now) |
| | | this.pickerTime = getTime(now, this.hideSecond) |
| | | } |
| | | |
| | | let startLaterInputDate = false |
| | | let startDate, startTime |
| | | if (this.start) { |
| | | let startString = this.start |
| | | if (typeof this.start === 'number') { |
| | | startString = getDateTime(this.start, this.hideSecond) |
| | | } |
| | | [startDate, startTime] = startString.split(' ') |
| | | if (this.start && !dateCompare(startDate, this.inputDate)) { |
| | | startLaterInputDate = true |
| | | this.inputDate = startDate |
| | | } |
| | | } |
| | | |
| | | let endEarlierInputDate = false |
| | | let endDate, endTime |
| | | if (this.end) { |
| | | let endString = this.end |
| | | if (typeof this.end === 'number') { |
| | | endString = getDateTime(this.end, this.hideSecond) |
| | | } |
| | | [endDate, endTime] = endString.split(' ') |
| | | if (this.end && !dateCompare(this.inputDate, endDate)) { |
| | | endEarlierInputDate = true |
| | | this.inputDate = endDate |
| | | } |
| | | } |
| | | if (this.hasTime) { |
| | | if (startLaterInputDate) { |
| | | this.pickerTime = startTime || getDefaultSecond(this.hideSecond) |
| | | } |
| | | if (endEarlierInputDate) { |
| | | this.pickerTime = endTime || getDefaultSecond(this.hideSecond) |
| | | } |
| | | if (!this.pickerTime) { |
| | | this.pickerTime = getTime(Date.now(), this.hideSecond) |
| | | } |
| | | this.displayValue = `${this.inputDate} ${this.pickerTime}` |
| | | } else { |
| | | this.displayValue = this.inputDate |
| | | } |
| | | this.setEmit(this.displayValue) |
| | | this.pickerVisible = false |
| | | }, |
| | | leftChange(e) { |
| | | const { |
| | | before, |
| | | after |
| | | } = e.range |
| | | this.rangeChange(before, after) |
| | | const obj = { |
| | | before: e.range.before, |
| | | after: e.range.after, |
| | | data: e.range.data, |
| | | fulldate: e.fulldate |
| | | } |
| | | this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj) |
| | | this.$emit('calendarClick', e) |
| | | }, |
| | | rightChange(e) { |
| | | const { |
| | | before, |
| | | after |
| | | } = e.range |
| | | this.rangeChange(before, after) |
| | | const obj = { |
| | | before: e.range.before, |
| | | after: e.range.after, |
| | | data: e.range.data, |
| | | fulldate: e.fulldate |
| | | } |
| | | this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj) |
| | | this.$emit('calendarClick', e) |
| | | }, |
| | | mobileChange(e) { |
| | | if (this.isRange) { |
| | | const { |
| | | before, |
| | | after |
| | | } = e.range |
| | | if (!before) { |
| | | return; |
| | | } |
| | | |
| | | this.handleStartAndEnd(before, after, true) |
| | | if (this.hasTime) { |
| | | const { |
| | | startTime, |
| | | endTime |
| | | } = e.timeRange |
| | | this.tempRange.startTime = startTime |
| | | this.tempRange.endTime = endTime |
| | | } |
| | | this.confirmRangeChange() |
| | | } else { |
| | | if (this.hasTime) { |
| | | this.displayValue = e.fulldate + ' ' + e.time |
| | | } else { |
| | | this.displayValue = e.fulldate |
| | | } |
| | | this.setEmit(this.displayValue) |
| | | } |
| | | this.$refs.mobile.close() |
| | | }, |
| | | rangeChange(before, after) { |
| | | if (!(before && after)) return |
| | | this.handleStartAndEnd(before, after, true) |
| | | if (this.hasTime) return |
| | | this.confirmRangeChange() |
| | | }, |
| | | confirmRangeChange() { |
| | | if (!this.tempRange.startDate || !this.tempRange.endDate) { |
| | | this.pickerVisible = false |
| | | return |
| | | } |
| | | if (!checkDate(this.tempRange.startDate)) { |
| | | this.tempRange.startDate = getDate(Date.now()) |
| | | } |
| | | if (!checkDate(this.tempRange.endDate)) { |
| | | this.tempRange.endDate = getDate(Date.now()) |
| | | } |
| | | |
| | | let start, end |
| | | |
| | | let startDateLaterRangeStartDate = false |
| | | let startDateLaterRangeEndDate = false |
| | | let startDate, startTime |
| | | if (this.start) { |
| | | let startString = this.start |
| | | if (typeof this.start === 'number') { |
| | | startString = getDateTime(this.start, this.hideSecond) |
| | | } |
| | | [startDate, startTime] = startString.split(' ') |
| | | if (this.start && !dateCompare(this.start, `${this.tempRange.startDate} ${this.tempRange.startTime}`)) { |
| | | startDateLaterRangeStartDate = true |
| | | this.tempRange.startDate = startDate |
| | | } |
| | | if (this.start && !dateCompare(this.start, `${this.tempRange.endDate} ${this.tempRange.endTime}`)) { |
| | | startDateLaterRangeEndDate = true |
| | | this.tempRange.endDate = startDate |
| | | } |
| | | } |
| | | let endDateEarlierRangeStartDate = false |
| | | let endDateEarlierRangeEndDate = false |
| | | let endDate, endTime |
| | | if (this.end) { |
| | | let endString = this.end |
| | | if (typeof this.end === 'number') { |
| | | endString = getDateTime(this.end, this.hideSecond) |
| | | } |
| | | [endDate, endTime] = endString.split(' ') |
| | | |
| | | if (this.end && !dateCompare(`${this.tempRange.startDate} ${this.tempRange.startTime}`, this.end)) { |
| | | endDateEarlierRangeStartDate = true |
| | | this.tempRange.startDate = endDate |
| | | } |
| | | if (this.end && !dateCompare(`${this.tempRange.endDate} ${this.tempRange.endTime}`, this.end)) { |
| | | endDateEarlierRangeEndDate = true |
| | | this.tempRange.endDate = endDate |
| | | } |
| | | } |
| | | if (!this.hasTime) { |
| | | start = this.displayRangeValue.startDate = this.tempRange.startDate |
| | | end = this.displayRangeValue.endDate = this.tempRange.endDate |
| | | } else { |
| | | if (startDateLaterRangeStartDate) { |
| | | this.tempRange.startTime = startTime || getDefaultSecond(this.hideSecond) |
| | | } else if (endDateEarlierRangeStartDate) { |
| | | this.tempRange.startTime = endTime || getDefaultSecond(this.hideSecond) |
| | | } |
| | | if (!this.tempRange.startTime) { |
| | | this.tempRange.startTime = getTime(Date.now(), this.hideSecond) |
| | | } |
| | | |
| | | if (startDateLaterRangeEndDate) { |
| | | this.tempRange.endTime = startTime || getDefaultSecond(this.hideSecond) |
| | | } else if (endDateEarlierRangeEndDate) { |
| | | this.tempRange.endTime = endTime || getDefaultSecond(this.hideSecond) |
| | | } |
| | | if (!this.tempRange.endTime) { |
| | | this.tempRange.endTime = getTime(Date.now(), this.hideSecond) |
| | | } |
| | | start = this.displayRangeValue.startDate = `${this.tempRange.startDate} ${this.tempRange.startTime}` |
| | | end = this.displayRangeValue.endDate = `${this.tempRange.endDate} ${this.tempRange.endTime}` |
| | | } |
| | | if (!dateCompare(start, end)) { |
| | | [start, end] = [end, start] |
| | | } |
| | | this.displayRangeValue.startDate = start |
| | | this.displayRangeValue.endDate = end |
| | | const displayRange = [start, end] |
| | | this.setEmit(displayRange) |
| | | this.pickerVisible = false |
| | | }, |
| | | handleStartAndEnd(before, after, temp = false) { |
| | | if (!before) return |
| | | if (!after) after = before; |
| | | const type = temp ? 'tempRange' : 'range' |
| | | const isStartEarlierEnd = dateCompare(before, after) |
| | | this[type].startDate = isStartEarlierEnd ? before : after |
| | | this[type].endDate = isStartEarlierEnd ? after : before |
| | | }, |
| | | /** |
| | | * 比较时间大小 |
| | | */ |
| | | dateCompare(startDate, endDate) { |
| | | // 计算截止时间 |
| | | startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
| | | // 计算详细项的截止时间 |
| | | endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
| | | return startDate <= endDate |
| | | }, |
| | | |
| | | /** |
| | | * 比较时间差 |
| | | */ |
| | | diffDate(startDate, endDate) { |
| | | // 计算截止时间 |
| | | startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
| | | // 计算详细项的截止时间 |
| | | endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
| | | const diff = (endDate - startDate) / (24 * 60 * 60 * 1000) |
| | | return Math.abs(diff) |
| | | }, |
| | | |
| | | clear(needEmit = true) { |
| | | if (!this.isRange) { |
| | | this.displayValue = '' |
| | | this.inputDate = '' |
| | | this.pickerTime = '' |
| | | if (this.isPhone) { |
| | | this.$refs.mobile && this.$refs.mobile.clearCalender() |
| | | } else { |
| | | this.$refs.pcSingle && this.$refs.pcSingle.clearCalender() |
| | | } |
| | | if (needEmit) { |
| | | this.$emit('change', '') |
| | | this.$emit('input', '') |
| | | this.$emit('update:modelValue', '') |
| | | } |
| | | } else { |
| | | this.displayRangeValue.startDate = '' |
| | | this.displayRangeValue.endDate = '' |
| | | this.tempRange.startDate = '' |
| | | this.tempRange.startTime = '' |
| | | this.tempRange.endDate = '' |
| | | this.tempRange.endTime = '' |
| | | if (this.isPhone) { |
| | | this.$refs.mobile && this.$refs.mobile.clearCalender() |
| | | } else { |
| | | this.$refs.left && this.$refs.left.clearCalender() |
| | | this.$refs.right && this.$refs.right.clearCalender() |
| | | this.$refs.right && this.$refs.right.changeMonth('next') |
| | | } |
| | | if (needEmit) { |
| | | this.$emit('change', []) |
| | | this.$emit('input', []) |
| | | this.$emit('update:modelValue', []) |
| | | } |
| | | } |
| | | }, |
| | | |
| | | calendarClick(e) { |
| | | this.$emit('calendarClick', e) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | $uni-primary: #007aff !default; |
| | | |
| | | .uni-date { |
| | | width: 100%; |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-date-x { |
| | | display: flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border-radius: 4px; |
| | | background-color: #fff; |
| | | color: #666; |
| | | font-size: 14px; |
| | | flex: 1; |
| | | |
| | | .icon-calendar { |
| | | padding-left: 3px; |
| | | } |
| | | |
| | | .range-separator { |
| | | height: 35px; |
| | | /* #ifndef MP */ |
| | | padding: 0 2px; |
| | | /* #endif */ |
| | | line-height: 35px; |
| | | } |
| | | } |
| | | |
| | | .uni-date-x--border { |
| | | box-sizing: border-box; |
| | | border-radius: 4px; |
| | | border: 1px solid #e5e5e5; |
| | | } |
| | | |
| | | .uni-date-editor--x { |
| | | display: flex; |
| | | align-items: center; |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-date-editor--x .uni-date__icon-clear { |
| | | padding-right: 3px; |
| | | display: flex; |
| | | align-items: center; |
| | | /* #ifdef H5 */ |
| | | cursor: pointer; |
| | | /* #endif */ |
| | | } |
| | | |
| | | .uni-date__x-input { |
| | | width: auto; |
| | | height: 35px; |
| | | /* #ifndef MP */ |
| | | padding-left: 5px; |
| | | /* #endif */ |
| | | position: relative; |
| | | flex: 1; |
| | | line-height: 35px; |
| | | font-size: 14px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .text-center { |
| | | text-align: center; |
| | | } |
| | | |
| | | .uni-date__input { |
| | | height: 40px; |
| | | width: 100%; |
| | | line-height: 40px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .uni-date-range__input { |
| | | text-align: center; |
| | | max-width: 142px; |
| | | } |
| | | |
| | | .uni-date-picker__container { |
| | | position: relative; |
| | | } |
| | | |
| | | .uni-date-mask--pc { |
| | | position: fixed; |
| | | bottom: 0px; |
| | | top: 0px; |
| | | left: 0px; |
| | | right: 0px; |
| | | background-color: rgba(0, 0, 0, 0); |
| | | transition-duration: 0.3s; |
| | | z-index: 996; |
| | | } |
| | | |
| | | .uni-date-single--x { |
| | | background-color: #fff; |
| | | position: absolute; |
| | | top: 0; |
| | | z-index: 999; |
| | | border: 1px solid #EBEEF5; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .uni-date-range--x { |
| | | background-color: #fff; |
| | | position: absolute; |
| | | top: 0; |
| | | z-index: 999; |
| | | border: 1px solid #EBEEF5; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .uni-date-editor--x__disabled { |
| | | opacity: 0.4; |
| | | cursor: default; |
| | | } |
| | | |
| | | .uni-date-editor--logo { |
| | | width: 16px; |
| | | height: 16px; |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | /* 添加时间 */ |
| | | .popup-x-header { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .popup-x-header--datetime { |
| | | /* #ifndef APP-NVUE */ |
| | | display: flex; |
| | | /* #endif */ |
| | | flex-direction: row; |
| | | flex: 1; |
| | | } |
| | | |
| | | .popup-x-body { |
| | | display: flex; |
| | | } |
| | | |
| | | .popup-x-footer { |
| | | padding: 0 15px; |
| | | border-top-color: #F1F1F1; |
| | | border-top-style: solid; |
| | | border-top-width: 1px; |
| | | line-height: 40px; |
| | | text-align: right; |
| | | color: #666; |
| | | } |
| | | |
| | | .popup-x-footer text:hover { |
| | | color: $uni-primary; |
| | | cursor: pointer; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .popup-x-footer .confirm-text { |
| | | margin-left: 20px; |
| | | color: $uni-primary; |
| | | } |
| | | |
| | | .uni-date-changed { |
| | | text-align: center; |
| | | color: #333; |
| | | border-bottom-color: #F1F1F1; |
| | | border-bottom-style: solid; |
| | | border-bottom-width: 1px; |
| | | } |
| | | |
| | | .uni-date-changed--time text { |
| | | height: 50px; |
| | | line-height: 50px; |
| | | } |
| | | |
| | | .uni-date-changed .uni-date-changed--time { |
| | | flex: 1; |
| | | } |
| | | |
| | | .uni-date-changed--time-date { |
| | | color: #333; |
| | | opacity: 0.6; |
| | | } |
| | | |
| | | .mr-50 { |
| | | margin-right: 50px; |
| | | } |
| | | |
| | | /* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */ |
| | | .uni-popper__arrow, |
| | | .uni-popper__arrow::after { |
| | | position: absolute; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | border: 6px solid transparent; |
| | | border-top-width: 0; |
| | | } |
| | | |
| | | .uni-popper__arrow { |
| | | filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); |
| | | top: -6px; |
| | | left: 10%; |
| | | margin-right: 3px; |
| | | border-bottom-color: #EBEEF5; |
| | | } |
| | | |
| | | .uni-popper__arrow::after { |
| | | content: " "; |
| | | top: 1px; |
| | | margin-left: -6px; |
| | | border-bottom-color: #fff; |
| | | } |
| | | </style> |
对比新文件 |
| | |
| | | class Calendar { |
| | | constructor({ |
| | | selected, |
| | | startDate, |
| | | endDate, |
| | | range, |
| | | } = {}) { |
| | | // 当前日期 |
| | | this.date = this.getDateObj(new Date()) // 当前初入日期 |
| | | // 打点信息 |
| | | this.selected = selected || []; |
| | | // 起始时间 |
| | | this.startDate = startDate |
| | | // 终止时间 |
| | | this.endDate = endDate |
| | | // 是否范围选择 |
| | | this.range = range |
| | | // 多选状态 |
| | | this.cleanMultipleStatus() |
| | | // 每周日期 |
| | | this.weeks = {} |
| | | this.lastHover = false |
| | | } |
| | | /** |
| | | * 设置日期 |
| | | * @param {Object} date |
| | | */ |
| | | setDate(date) { |
| | | const selectDate = this.getDateObj(date) |
| | | this.getWeeks(selectDate.fullDate) |
| | | } |
| | | |
| | | /** |
| | | * 清理多选状态 |
| | | */ |
| | | cleanMultipleStatus() { |
| | | this.multipleStatus = { |
| | | before: '', |
| | | after: '', |
| | | data: [] |
| | | } |
| | | } |
| | | |
| | | setStartDate(startDate) { |
| | | this.startDate = startDate |
| | | } |
| | | |
| | | setEndDate(endDate) { |
| | | this.endDate = endDate |
| | | } |
| | | |
| | | getPreMonthObj(date) { |
| | | date = fixIosDateFormat(date) |
| | | date = new Date(date) |
| | | |
| | | const oldMonth = date.getMonth() |
| | | date.setMonth(oldMonth - 1) |
| | | const newMonth = date.getMonth() |
| | | if (oldMonth !== 0 && newMonth - oldMonth === 0) { |
| | | date.setMonth(newMonth - 1) |
| | | } |
| | | return this.getDateObj(date) |
| | | } |
| | | getNextMonthObj(date) { |
| | | date = fixIosDateFormat(date) |
| | | date = new Date(date) |
| | | |
| | | const oldMonth = date.getMonth() |
| | | date.setMonth(oldMonth + 1) |
| | | const newMonth = date.getMonth() |
| | | if (newMonth - oldMonth > 1) { |
| | | date.setMonth(newMonth - 1) |
| | | } |
| | | return this.getDateObj(date) |
| | | } |
| | | |
| | | /** |
| | | * 获取指定格式Date对象 |
| | | */ |
| | | getDateObj(date) { |
| | | date = fixIosDateFormat(date) |
| | | date = new Date(date) |
| | | |
| | | return { |
| | | fullDate: getDate(date), |
| | | year: date.getFullYear(), |
| | | month: addZero(date.getMonth() + 1), |
| | | date: addZero(date.getDate()), |
| | | day: date.getDay() |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取上一个月日期集合 |
| | | */ |
| | | getPreMonthDays(amount, dateObj) { |
| | | const result = [] |
| | | for (let i = amount - 1; i >= 0; i--) { |
| | | const month = dateObj.month - 1 |
| | | result.push({ |
| | | date: new Date(dateObj.year, month, -i).getDate(), |
| | | month, |
| | | disable: true |
| | | }) |
| | | } |
| | | return result |
| | | } |
| | | /** |
| | | * 获取本月日期集合 |
| | | */ |
| | | getCurrentMonthDays(amount, dateObj) { |
| | | const result = [] |
| | | const fullDate = this.date.fullDate |
| | | for (let i = 1; i <= amount; i++) { |
| | | const currentDate = `${dateObj.year}-${dateObj.month}-${addZero(i)}` |
| | | const isToday = fullDate === currentDate |
| | | // 获取打点信息 |
| | | const info = this.selected && this.selected.find((item) => { |
| | | if (this.dateEqual(currentDate, item.date)) { |
| | | return item |
| | | } |
| | | }) |
| | | |
| | | // 日期禁用 |
| | | let disableBefore = true |
| | | let disableAfter = true |
| | | if (this.startDate) { |
| | | disableBefore = dateCompare(this.startDate, currentDate) |
| | | } |
| | | |
| | | if (this.endDate) { |
| | | disableAfter = dateCompare(currentDate, this.endDate) |
| | | } |
| | | |
| | | let multiples = this.multipleStatus.data |
| | | let multiplesStatus = -1 |
| | | if (this.range && multiples) { |
| | | multiplesStatus = multiples.findIndex((item) => { |
| | | return this.dateEqual(item, currentDate) |
| | | }) |
| | | } |
| | | const checked = multiplesStatus !== -1 |
| | | |
| | | result.push({ |
| | | fullDate: currentDate, |
| | | year: dateObj.year, |
| | | date: i, |
| | | multiple: this.range ? checked : false, |
| | | beforeMultiple: this.isLogicBefore(currentDate, this.multipleStatus.before, this.multipleStatus.after), |
| | | afterMultiple: this.isLogicAfter(currentDate, this.multipleStatus.before, this.multipleStatus.after), |
| | | month: dateObj.month, |
| | | disable: (this.startDate && !dateCompare(this.startDate, currentDate)) || (this.endDate && !dateCompare( |
| | | currentDate, this.endDate)), |
| | | isToday, |
| | | userChecked: false, |
| | | extraInfo: info |
| | | }) |
| | | } |
| | | return result |
| | | } |
| | | /** |
| | | * 获取下一个月日期集合 |
| | | */ |
| | | _getNextMonthDays(amount, dateObj) { |
| | | const result = [] |
| | | const month = dateObj.month + 1 |
| | | for (let i = 1; i <= amount; i++) { |
| | | result.push({ |
| | | date: i, |
| | | month, |
| | | disable: true |
| | | }) |
| | | } |
| | | return result |
| | | } |
| | | |
| | | /** |
| | | * 获取当前日期详情 |
| | | * @param {Object} date |
| | | */ |
| | | getInfo(date) { |
| | | if (!date) { |
| | | date = new Date() |
| | | } |
| | | const res = this.calendar.find(item => item.fullDate === this.getDateObj(date).fullDate) |
| | | return res ? res : this.getDateObj(date) |
| | | } |
| | | |
| | | /** |
| | | * 比较时间是否相等 |
| | | */ |
| | | dateEqual(before, after) { |
| | | before = new Date(fixIosDateFormat(before)) |
| | | after = new Date(fixIosDateFormat(after)) |
| | | return before.valueOf() === after.valueOf() |
| | | } |
| | | |
| | | /** |
| | | * 比较真实起始日期 |
| | | */ |
| | | |
| | | isLogicBefore(currentDate, before, after) { |
| | | let logicBefore = before |
| | | if (before && after) { |
| | | logicBefore = dateCompare(before, after) ? before : after |
| | | } |
| | | return this.dateEqual(logicBefore, currentDate) |
| | | } |
| | | |
| | | isLogicAfter(currentDate, before, after) { |
| | | let logicAfter = after |
| | | if (before && after) { |
| | | logicAfter = dateCompare(before, after) ? after : before |
| | | } |
| | | return this.dateEqual(logicAfter, currentDate) |
| | | } |
| | | |
| | | /** |
| | | * 获取日期范围内所有日期 |
| | | * @param {Object} begin |
| | | * @param {Object} end |
| | | */ |
| | | geDateAll(begin, end) { |
| | | var arr = [] |
| | | var ab = begin.split('-') |
| | | var ae = end.split('-') |
| | | var db = new Date() |
| | | db.setFullYear(ab[0], ab[1] - 1, ab[2]) |
| | | var de = new Date() |
| | | de.setFullYear(ae[0], ae[1] - 1, ae[2]) |
| | | var unixDb = db.getTime() - 24 * 60 * 60 * 1000 |
| | | var unixDe = de.getTime() - 24 * 60 * 60 * 1000 |
| | | for (var k = unixDb; k <= unixDe;) { |
| | | k = k + 24 * 60 * 60 * 1000 |
| | | arr.push(this.getDateObj(new Date(parseInt(k))).fullDate) |
| | | } |
| | | return arr |
| | | } |
| | | |
| | | /** |
| | | * 获取多选状态 |
| | | */ |
| | | setMultiple(fullDate) { |
| | | if (!this.range) return |
| | | |
| | | let { |
| | | before, |
| | | after |
| | | } = this.multipleStatus |
| | | if (before && after) { |
| | | if (!this.lastHover) { |
| | | this.lastHover = true |
| | | return |
| | | } |
| | | this.multipleStatus.before = fullDate |
| | | this.multipleStatus.after = '' |
| | | this.multipleStatus.data = [] |
| | | this.multipleStatus.fulldate = '' |
| | | this.lastHover = false |
| | | } else { |
| | | if (!before) { |
| | | this.multipleStatus.before = fullDate |
| | | this.multipleStatus.after = undefined; |
| | | this.lastHover = false |
| | | } else { |
| | | this.multipleStatus.after = fullDate |
| | | if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus |
| | | .after); |
| | | } else { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus |
| | | .before); |
| | | } |
| | | this.lastHover = true |
| | | } |
| | | } |
| | | this.getWeeks(fullDate) |
| | | } |
| | | |
| | | /** |
| | | * 鼠标 hover 更新多选状态 |
| | | */ |
| | | setHoverMultiple(fullDate) { |
| | | //抖音小程序点击会触发hover事件,需要避免一下 |
| | | // #ifndef MP-TOUTIAO |
| | | if (!this.range || this.lastHover) return |
| | | const { |
| | | before |
| | | } = this.multipleStatus |
| | | |
| | | if (!before) { |
| | | this.multipleStatus.before = fullDate |
| | | } else { |
| | | this.multipleStatus.after = fullDate |
| | | if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after); |
| | | } else { |
| | | this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before); |
| | | } |
| | | } |
| | | this.getWeeks(fullDate) |
| | | // #endif |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 更新默认值多选状态 |
| | | */ |
| | | setDefaultMultiple(before, after) { |
| | | this.multipleStatus.before = before |
| | | this.multipleStatus.after = after |
| | | if (before && after) { |
| | | if (dateCompare(before, after)) { |
| | | this.multipleStatus.data = this.geDateAll(before, after); |
| | | this.getWeeks(after) |
| | | } else { |
| | | this.multipleStatus.data = this.geDateAll(after, before); |
| | | this.getWeeks(before) |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取每周数据 |
| | | * @param {Object} dateData |
| | | */ |
| | | getWeeks(dateData) { |
| | | const { |
| | | year, |
| | | month, |
| | | } = this.getDateObj(dateData) |
| | | |
| | | const preMonthDayAmount = new Date(year, month - 1, 1).getDay() |
| | | const preMonthDays = this.getPreMonthDays(preMonthDayAmount, this.getDateObj(dateData)) |
| | | |
| | | const currentMonthDayAmount = new Date(year, month, 0).getDate() |
| | | const currentMonthDays = this.getCurrentMonthDays(currentMonthDayAmount, this.getDateObj(dateData)) |
| | | |
| | | const nextMonthDayAmount = 42 - preMonthDayAmount - currentMonthDayAmount |
| | | const nextMonthDays = this._getNextMonthDays(nextMonthDayAmount, this.getDateObj(dateData)) |
| | | |
| | | const calendarDays = [...preMonthDays, ...currentMonthDays, ...nextMonthDays] |
| | | |
| | | const weeks = new Array(6) |
| | | for (let i = 0; i < calendarDays.length; i++) { |
| | | const index = Math.floor(i / 7) |
| | | if (!weeks[index]) { |
| | | weeks[index] = new Array(7) |
| | | } |
| | | weeks[index][i % 7] = calendarDays[i] |
| | | } |
| | | |
| | | this.calendar = calendarDays |
| | | this.weeks = weeks |
| | | } |
| | | } |
| | | |
| | | function getDateTime(date, hideSecond) { |
| | | return `${getDate(date)} ${getTime(date, hideSecond)}` |
| | | } |
| | | |
| | | function getDate(date) { |
| | | date = fixIosDateFormat(date) |
| | | date = new Date(date) |
| | | const year = date.getFullYear() |
| | | const month = date.getMonth() + 1 |
| | | const day = date.getDate() |
| | | return `${year}-${addZero(month)}-${addZero(day)}` |
| | | } |
| | | |
| | | function getTime(date, hideSecond) { |
| | | date = fixIosDateFormat(date) |
| | | date = new Date(date) |
| | | const hour = date.getHours() |
| | | const minute = date.getMinutes() |
| | | const second = date.getSeconds() |
| | | return hideSecond ? `${addZero(hour)}:${addZero(minute)}` : `${addZero(hour)}:${addZero(minute)}:${addZero(second)}` |
| | | } |
| | | |
| | | function addZero(num) { |
| | | if (num < 10) { |
| | | num = `0${num}` |
| | | } |
| | | return num |
| | | } |
| | | |
| | | function getDefaultSecond(hideSecond) { |
| | | return hideSecond ? '00:00' : '00:00:00' |
| | | } |
| | | |
| | | function dateCompare(startDate, endDate) { |
| | | startDate = new Date(fixIosDateFormat(startDate)) |
| | | endDate = new Date(fixIosDateFormat(endDate)) |
| | | return startDate <= endDate |
| | | } |
| | | |
| | | function checkDate(date) { |
| | | const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g |
| | | return date.match(dateReg) |
| | | } |
| | | //ios低版本15及以下,无法匹配 没有 ’秒‘ 时的情况,所以需要在末尾 秒 加上 问号 |
| | | const dateTimeReg = /^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])( [0-5]?[0-9]:[0-5]?[0-9](:[0-5]?[0-9])?)?$/; |
| | | |
| | | function fixIosDateFormat(value) { |
| | | if (typeof value === 'string' && dateTimeReg.test(value)) { |
| | | value = value.replace(/-/g, '/') |
| | | } |
| | | return value |
| | | } |
| | | |
| | | export { |
| | | Calendar, |
| | | getDateTime, |
| | | getDate, |
| | | getTime, |
| | | addZero, |
| | | getDefaultSecond, |
| | | dateCompare, |
| | | checkDate, |
| | | fixIosDateFormat |
| | | } |
uni_modules/uni-datetime-picker/package.json
uni_modules/uni-datetime-picker/readme.md
uni_modules/uni-drawer/changelog.md
uni_modules/uni-drawer/components/uni-drawer/keypress.js
uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue
uni_modules/uni-drawer/package.json
uni_modules/uni-drawer/readme.md
uni_modules/uni-easyinput/changelog.md
uni_modules/uni-easyinput/components/uni-easyinput/common.js
uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
uni_modules/uni-easyinput/package.json
uni_modules/uni-easyinput/readme.md
uni_modules/uni-fab/changelog.md
uni_modules/uni-fab/components/uni-fab/uni-fab.vue
uni_modules/uni-fab/package.json
uni_modules/uni-fab/readme.md
uni_modules/uni-fav/changelog.md
uni_modules/uni-fav/components/uni-fav/i18n/en.json
uni_modules/uni-fav/components/uni-fav/i18n/index.js
uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json
uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json
uni_modules/uni-fav/components/uni-fav/uni-fav.vue
uni_modules/uni-fav/package.json
uni_modules/uni-fav/readme.md
uni_modules/uni-file-picker/changelog.md
uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js
uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue
uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue
uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue
uni_modules/uni-file-picker/components/uni-file-picker/utils.js
uni_modules/uni-file-picker/package.json
uni_modules/uni-file-picker/readme.md
uni_modules/uni-forms/changelog.md
uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue
uni_modules/uni-forms/components/uni-forms/uni-forms.vue
uni_modules/uni-forms/components/uni-forms/utils.js
uni_modules/uni-forms/components/uni-forms/validate.js
uni_modules/uni-forms/package.json
uni_modules/uni-forms/readme.md
uni_modules/uni-goods-nav/changelog.md
uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json
uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js
uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json
uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json
uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue
uni_modules/uni-goods-nav/package.json
uni_modules/uni-goods-nav/readme.md
uni_modules/uni-grid/changelog.md
uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue
uni_modules/uni-grid/components/uni-grid/uni-grid.vue
uni_modules/uni-grid/package.json
uni_modules/uni-grid/readme.md
uni_modules/uni-group/changelog.md
uni_modules/uni-group/components/uni-group/uni-group.vue
uni_modules/uni-group/package.json
uni_modules/uni-group/readme.md
uni_modules/uni-icons/changelog.md
uni_modules/uni-icons/components/uni-icons/uni-icons.uvue
uni_modules/uni-icons/components/uni-icons/uni-icons.vue
uni_modules/uni-icons/components/uni-icons/uniicons.css
uni_modules/uni-icons/components/uni-icons/uniicons.ttf
uni_modules/uni-icons/components/uni-icons/uniicons_file.ts
uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js
uni_modules/uni-icons/package.json
uni_modules/uni-icons/readme.md
uni_modules/uni-indexed-list/changelog.md
uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue
uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue
uni_modules/uni-indexed-list/package.json
uni_modules/uni-indexed-list/readme.md
uni_modules/uni-link/changelog.md
uni_modules/uni-link/components/uni-link/uni-link.vue
uni_modules/uni-link/package.json
uni_modules/uni-link/readme.md
uni_modules/uni-list/changelog.md
uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue
uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss
uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue
uni_modules/uni-list/components/uni-list-item/uni-list-item.vue
uni_modules/uni-list/components/uni-list/uni-list.vue
uni_modules/uni-list/components/uni-list/uni-refresh.vue
uni_modules/uni-list/components/uni-list/uni-refresh.wxs
uni_modules/uni-list/package.json
uni_modules/uni-list/readme.md
uni_modules/uni-load-more/changelog.md
uni_modules/uni-load-more/components/uni-load-more/i18n/en.json
uni_modules/uni-load-more/components/uni-load-more/i18n/index.js
uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json
uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json
uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue
uni_modules/uni-load-more/package.json
uni_modules/uni-load-more/readme.md
uni_modules/uni-nav-bar/changelog.md
uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue
uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue
uni_modules/uni-nav-bar/package.json
uni_modules/uni-nav-bar/readme.md
uni_modules/uni-notice-bar/changelog.md
uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue
uni_modules/uni-notice-bar/package.json
uni_modules/uni-notice-bar/readme.md
uni_modules/uni-number-box/changelog.md
uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue
uni_modules/uni-number-box/package.json
uni_modules/uni-number-box/readme.md
uni_modules/uni-pagination/changelog.md
uni_modules/uni-pagination/components/uni-pagination/i18n/en.json
uni_modules/uni-pagination/components/uni-pagination/i18n/es.json
uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json
uni_modules/uni-pagination/components/uni-pagination/i18n/index.js
uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json
uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json
uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue
uni_modules/uni-pagination/package.json
uni_modules/uni-pagination/readme.md
uni_modules/uni-popup/changelog.md
uni_modules/uni-popup/components/uni-popup-dialog/keypress.js
uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue
uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue
uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue
uni_modules/uni-popup/components/uni-popup/i18n/en.json
uni_modules/uni-popup/components/uni-popup/i18n/index.js
uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json
uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json
uni_modules/uni-popup/components/uni-popup/keypress.js
uni_modules/uni-popup/components/uni-popup/popup.js
uni_modules/uni-popup/components/uni-popup/uni-popup.uvue
uni_modules/uni-popup/components/uni-popup/uni-popup.vue
uni_modules/uni-popup/package.json
uni_modules/uni-popup/readme.md
uni_modules/uni-rate/changelog.md
uni_modules/uni-rate/components/uni-rate/uni-rate.vue
uni_modules/uni-rate/package.json
uni_modules/uni-rate/readme.md
uni_modules/uni-row/changelog.md
uni_modules/uni-row/components/uni-col/uni-col.vue
uni_modules/uni-row/components/uni-row/uni-row.vue
uni_modules/uni-row/package.json
uni_modules/uni-row/readme.md
uni_modules/uni-scss/changelog.md
uni_modules/uni-scss/index.scss
uni_modules/uni-scss/package.json
uni_modules/uni-scss/readme.md
uni_modules/uni-scss/styles/index.scss
uni_modules/uni-scss/styles/setting/_border.scss
uni_modules/uni-scss/styles/setting/_color.scss
uni_modules/uni-scss/styles/setting/_radius.scss
uni_modules/uni-scss/styles/setting/_space.scss
uni_modules/uni-scss/styles/setting/_styles.scss
uni_modules/uni-scss/styles/setting/_text.scss
uni_modules/uni-scss/styles/setting/_variables.scss
uni_modules/uni-scss/styles/tools/functions.scss
uni_modules/uni-scss/theme.scss
uni_modules/uni-scss/variables.scss
uni_modules/uni-search-bar/changelog.md
uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json
uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js
uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json
uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json
uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue
uni_modules/uni-search-bar/package.json
uni_modules/uni-search-bar/readme.md
uni_modules/uni-section/changelog.md
uni_modules/uni-section/components/uni-section/uni-section.vue
uni_modules/uni-section/package.json
uni_modules/uni-section/readme.md
uni_modules/uni-segmented-control/changelog.md
uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue
uni_modules/uni-segmented-control/package.json
uni_modules/uni-segmented-control/readme.md
uni_modules/uni-steps/changelog.md
uni_modules/uni-steps/components/uni-steps/uni-steps.vue
uni_modules/uni-steps/package.json
uni_modules/uni-steps/readme.md
uni_modules/uni-swipe-action/changelog.md
uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js
uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js
uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js
uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js
uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js
uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js
uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue
uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs
uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue
uni_modules/uni-swipe-action/package.json
uni_modules/uni-swipe-action/readme.md
uni_modules/uni-swiper-dot/changelog.md
uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue
uni_modules/uni-swiper-dot/package.json
uni_modules/uni-swiper-dot/readme.md
uni_modules/uni-table/changelog.md
uni_modules/uni-table/components/uni-table/uni-table.vue
uni_modules/uni-table/components/uni-tbody/uni-tbody.vue
uni_modules/uni-table/components/uni-td/uni-td.vue
uni_modules/uni-table/components/uni-th/filter-dropdown.vue
uni_modules/uni-table/components/uni-th/uni-th.vue
uni_modules/uni-table/components/uni-thead/uni-thead.vue
uni_modules/uni-table/components/uni-tr/table-checkbox.vue
uni_modules/uni-table/components/uni-tr/uni-tr.vue
uni_modules/uni-table/i18n/en.json
uni_modules/uni-table/i18n/es.json
uni_modules/uni-table/i18n/fr.json
uni_modules/uni-table/i18n/index.js
uni_modules/uni-table/i18n/zh-Hans.json
uni_modules/uni-table/i18n/zh-Hant.json
uni_modules/uni-table/package.json
uni_modules/uni-table/readme.md
uni_modules/uni-tag/changelog.md
uni_modules/uni-tag/components/uni-tag/uni-tag.vue
uni_modules/uni-tag/package.json
uni_modules/uni-tag/readme.md
uni_modules/uni-title/changelog.md
uni_modules/uni-title/components/uni-title/uni-title.vue
uni_modules/uni-title/package.json
uni_modules/uni-title/readme.md
uni_modules/uni-tooltip/changelog.md
uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue
uni_modules/uni-tooltip/package.json
uni_modules/uni-tooltip/readme.md
uni_modules/uni-transition/changelog.md
uni_modules/uni-transition/components/uni-transition/createAnimation.js
uni_modules/uni-transition/components/uni-transition/uni-transition.vue
uni_modules/uni-transition/package.json
uni_modules/uni-transition/readme.md
uni_modules/uni-ui/changelog.md
uni_modules/uni-ui/components/uni-ui/uni-ui.vue
uni_modules/uni-ui/package.json
uni_modules/uni-ui/readme.md
unpackage/dist/cache/.vite/deps/_metadata.json (已删除)
unpackage/dist/cache/.vite/deps/package.json (已删除)
unpackage/dist/dev/mp-weixin/api/index.js (已删除)
unpackage/dist/dev/mp-weixin/app.js (已删除)
unpackage/dist/dev/mp-weixin/app.json (已删除)
unpackage/dist/dev/mp-weixin/app.wxss (已删除)
unpackage/dist/dev/mp-weixin/common/assets.js (已删除)
unpackage/dist/dev/mp-weixin/common/vendor.js (已删除)
unpackage/dist/dev/mp-weixin/components/navbar/navbar.js (已删除)
unpackage/dist/dev/mp-weixin/components/navbar/navbar.json (已删除)
unpackage/dist/dev/mp-weixin/components/navbar/navbar.wxml (已删除)
unpackage/dist/dev/mp-weixin/components/navbar/navbar.wxss (已删除)
unpackage/dist/dev/mp-weixin/config/baseUrl.js (已删除)
unpackage/dist/dev/mp-weixin/pages/addCard/index.js (已删除)
unpackage/dist/dev/mp-weixin/pages/addCard/index.json (已删除)
unpackage/dist/dev/mp-weixin/pages/addCard/index.wxml (已删除)
unpackage/dist/dev/mp-weixin/pages/addCard/index.wxss (已删除)
unpackage/dist/dev/mp-weixin/pages/address/index.js (已删除)
unpackage/dist/dev/mp-weixin/pages/address/index.json (已删除)
unpackage/dist/dev/mp-weixin/pages/address/index.wxml (已删除)
unpackage/dist/dev/mp-weixin/pages/address/index.wxss (已删除)
unpackage/dist/dev/mp-weixin/pages/index/index.js (已删除)
unpackage/dist/dev/mp-weixin/pages/index/index.json (已删除)
unpackage/dist/dev/mp-weixin/pages/index/index.wxml (已删除)
unpackage/dist/dev/mp-weixin/pages/index/index.wxss (已删除)
unpackage/dist/dev/mp-weixin/pages/preSendWater/index.js (已删除)
unpackage/dist/dev/mp-weixin/pages/preSendWater/index.json (已删除)
unpackage/dist/dev/mp-weixin/pages/preSendWater/index.wxml (已删除)
unpackage/dist/dev/mp-weixin/pages/preSendWater/index.wxss (已删除)
unpackage/dist/dev/mp-weixin/pages/recharge/index.js (已删除)
unpackage/dist/dev/mp-weixin/pages/recharge/index.json (已删除)
unpackage/dist/dev/mp-weixin/pages/recharge/index.wxml (已删除)
unpackage/dist/dev/mp-weixin/pages/recharge/index.wxss (已删除)
unpackage/dist/dev/mp-weixin/pages/sendWater/index.js (已删除)
unpackage/dist/dev/mp-weixin/pages/sendWater/index.json (已删除)
unpackage/dist/dev/mp-weixin/pages/sendWater/index.wxml (已删除)
unpackage/dist/dev/mp-weixin/pages/sendWater/index.wxss (已删除)
unpackage/dist/dev/mp-weixin/project.config.json (已删除)
unpackage/dist/dev/mp-weixin/project.private.config.json (已删除)
unpackage/dist/dev/mp-weixin/static/images/addCard/back.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/back.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/bottom-line.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/code1.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/icon23.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/icon31.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/icon32.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/icon33.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/icon34.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/icon35.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/icon36.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/icon37.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/icon38.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/more-info5.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/vip-add.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/index/vip1.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/login/logo.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/login/wechat.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/recharge/balance.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/recharge/pay.png (已删除)
unpackage/dist/dev/mp-weixin/static/images/recharge/wx.png (已删除)
unpackage/dist/dev/mp-weixin/static/logo.png (已删除)
unpackage/dist/dev/mp-weixin/util/request.js (已删除) |