From dd67c9edb6148f9cc06e97b88122fe79f4d1558d Mon Sep 17 00:00:00 2001 From: kaeery <3491123437@qq.com> Date: Tue, 25 Feb 2025 23:25:52 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E7=94=B5=E9=94=80=E8=80=81=E5=B8=88?= =?UTF-8?q?=E5=92=8C=E6=8B=9B=E7=94=9F=E8=80=81=E5=B8=88=E3=80=91=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E#=20=E6=80=BB=E7=BB=93=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 19 + src/components/date-strip/date-strip-item.vue | 51 + src/components/date-strip/index.scss | 21 + src/components/date-strip/index.vue | 146 + src/components/date-strip/props.ts | 108 + src/components/date-strip/stripItemProps.ts | 58 + src/components/date-strip/utils.ts | 74 + .../widgets/date-strip/date-strip.vue | 26 + .../widgets/summary-form/summary-form.vue | 70 + src/pages.json | 12 + src/pages/recruitsale/summary/index.vue | 41 + src/pages/telesale/summary/index.vue | 26 + src/styles/public.scss | 1 + src/uni_modules/lime-date-strip/changelog.md | 4 + .../l-date-strip-item/l-date-strip-item.uvue | 90 + .../l-date-strip-item/l-date-strip-item.vue | 99 + .../components/l-date-strip-item/props.ts | 54 + .../components/l-date-strip-item/type.ts | 17 + .../components/l-date-strip/index.scss | 153 + .../components/l-date-strip/l-date-strip.uvue | 195 + .../components/l-date-strip/l-date-strip.vue | 215 + .../components/l-date-strip/props.ts | 108 + .../components/l-date-strip/type.ts | 77 + .../components/l-date-strip/utils.uts | 72 + .../lime-date-strip/lime-date-strip.uvue | 149 + .../lime-date-strip/lime-date-strip.vue | 153 + src/uni_modules/lime-date-strip/index.ts | 2 + src/uni_modules/lime-date-strip/package.json | 90 + src/uni_modules/lime-date-strip/readme.md | 176 + src/uni_modules/lime-shared/addUnit/index.ts | 42 + .../lime-shared/animation/bezier.ts | 82 + src/uni_modules/lime-shared/animation/ease.ts | 3 + .../lime-shared/animation/index.ts | 10 + .../lime-shared/animation/useTransition.ts | 103 + .../lime-shared/animation/uvue.uts | 119 + src/uni_modules/lime-shared/animation/vue.ts | 123 + .../lime-shared/areaData/city-china.json | 3888 +++++++++++++++++ src/uni_modules/lime-shared/areaData/index.ts | 71 + .../lime-shared/arrayBufferToFile/index.ts | 8 + .../lime-shared/arrayBufferToFile/uvue.uts | 10 + .../lime-shared/arrayBufferToFile/vue.ts | 63 + .../lime-shared/base64ToArrayBuffer/index.ts | 13 + .../lime-shared/base64ToPath/index.ts | 9 + .../lime-shared/base64ToPath/uvue.uts | 22 + .../lime-shared/base64ToPath/vue.ts | 75 + .../lime-shared/camelCase/index.ts | 21 + .../lime-shared/canIUseCanvas2d/index.ts | 67 + .../lime-shared/capitalizedAmount/index.ts | 111 + src/uni_modules/lime-shared/changelog.md | 63 + .../lime-shared/characterLimit/index.ts | 63 + src/uni_modules/lime-shared/clamp/index.ts | 16 + .../lime-shared/cloneDeep/index.ts | 10 + src/uni_modules/lime-shared/cloneDeep/uvue.ts | 17 + src/uni_modules/lime-shared/cloneDeep/vue.ts | 103 + src/uni_modules/lime-shared/closest/index.ts | 22 + .../components/lime-shared/lime-shared.vue | 156 + .../lime-shared/createAnimation/index.ts | 9 + .../lime-shared/createAnimation/type.ts | 25 + .../lime-shared/createAnimation/uvue.ts | 5 + .../lime-shared/createAnimation/vue.ts | 148 + .../lime-shared/createCanvas/index.ts | 73 + .../lime-shared/createImage/index.ts | 71 + src/uni_modules/lime-shared/debounce/index.ts | 10 + src/uni_modules/lime-shared/debounce/uvue.ts | 36 + src/uni_modules/lime-shared/debounce/vue.ts | 40 + src/uni_modules/lime-shared/exif/index.ts | 9 + src/uni_modules/lime-shared/exif/uvue.ts | 7 + src/uni_modules/lime-shared/exif/vue.ts | 1057 +++++ src/uni_modules/lime-shared/fillZero/index.ts | 11 + src/uni_modules/lime-shared/floatAdd/index.ts | 36 + src/uni_modules/lime-shared/floatDiv/index.ts | 45 + src/uni_modules/lime-shared/floatMul/index.ts | 44 + src/uni_modules/lime-shared/floatSub/index.ts | 32 + .../lime-shared/getClassStr/index.ts | 53 + .../lime-shared/getCurrentPage/index.ts | 9 + .../lime-shared/getCurrentPage/uvue.uts | 5 + .../lime-shared/getCurrentPage/vue.ts | 6 + .../lime-shared/getLocalFilePath/index.ts | 62 + src/uni_modules/lime-shared/getRect/index.ts | 9 + src/uni_modules/lime-shared/getRect/uvue.uts | 16 + src/uni_modules/lime-shared/getRect/vue.ts | 117 + .../lime-shared/getStyleStr/index.ts | 54 + .../lime-shared/getStyleStr/index_.uts | 39 + src/uni_modules/lime-shared/hasOwn/index.ts | 9 + src/uni_modules/lime-shared/hasOwn/uvue.ts | 39 + src/uni_modules/lime-shared/hasOwn/vue.ts | 30 + src/uni_modules/lime-shared/index.ts | 43 + src/uni_modules/lime-shared/isBase64/index.ts | 23 + .../lime-shared/isBrowser/index.ts | 8 + src/uni_modules/lime-shared/isDef/index.ts | 23 + src/uni_modules/lime-shared/isEmpty/index.ts | 83 + .../lime-shared/isFunction/index.ts | 16 + src/uni_modules/lime-shared/isNumber/index.ts | 26 + .../lime-shared/isNumeric/index.ts | 33 + src/uni_modules/lime-shared/isObject/index.ts | 19 + .../lime-shared/isPromise/index.ts | 22 + src/uni_modules/lime-shared/isString/index.ts | 19 + .../lime-shared/kebabCase/index.ts | 24 + src/uni_modules/lime-shared/obj2url/index.ts | 11 + src/uni_modules/lime-shared/package.json | 87 + .../lime-shared/pathToBase64/index.ts | 9 + .../lime-shared/pathToBase64/uvue.uts | 17 + .../lime-shared/pathToBase64/vue.ts | 121 + src/uni_modules/lime-shared/platform/index.ts | 34 + src/uni_modules/lime-shared/raf/index.ts | 10 + src/uni_modules/lime-shared/raf/uvue.ts | 48 + src/uni_modules/lime-shared/raf/vue.ts | 32 + src/uni_modules/lime-shared/random/index.ts | 24 + src/uni_modules/lime-shared/range/index.ts | 36 + src/uni_modules/lime-shared/readme.md | 464 ++ .../lime-shared/selectAllComponent/index.ts | 8 + .../lime-shared/selectAllComponent/uvue.uts | 39 + .../lime-shared/selectAllComponent/vue.ts | 151 + .../lime-shared/selectComponent/index.ts | 7 + .../lime-shared/selectComponent/uvue.uts | 75 + .../lime-shared/selectComponent/vue.ts | 149 + .../lime-shared/selectElement/index.uts | 275 ++ src/uni_modules/lime-shared/shuffle/index.ts | 16 + src/uni_modules/lime-shared/sleep/index.ts | 44 + src/uni_modules/lime-shared/throttle/index.ts | 77 + src/uni_modules/lime-shared/toArray/index.ts | 21 + .../lime-shared/toBoolean/index.ts | 40 + src/uni_modules/lime-shared/toNumber/index.ts | 28 + .../lime-shared/unitConvert/index.ts | 80 + src/uni_modules/lime-shared/vue/index.ts | 16 + 125 files changed, 11961 insertions(+) create mode 100644 src/components/date-strip/date-strip-item.vue create mode 100644 src/components/date-strip/index.scss create mode 100644 src/components/date-strip/index.vue create mode 100644 src/components/date-strip/props.ts create mode 100644 src/components/date-strip/stripItemProps.ts create mode 100644 src/components/date-strip/utils.ts create mode 100644 src/components/widgets/date-strip/date-strip.vue create mode 100644 src/components/widgets/summary-form/summary-form.vue create mode 100644 src/pages/recruitsale/summary/index.vue create mode 100644 src/pages/telesale/summary/index.vue create mode 100644 src/uni_modules/lime-date-strip/changelog.md create mode 100644 src/uni_modules/lime-date-strip/components/l-date-strip-item/l-date-strip-item.uvue create mode 100644 src/uni_modules/lime-date-strip/components/l-date-strip-item/l-date-strip-item.vue create mode 100644 src/uni_modules/lime-date-strip/components/l-date-strip-item/props.ts create mode 100644 src/uni_modules/lime-date-strip/components/l-date-strip-item/type.ts create mode 100644 src/uni_modules/lime-date-strip/components/l-date-strip/index.scss create mode 100644 src/uni_modules/lime-date-strip/components/l-date-strip/l-date-strip.uvue create mode 100644 src/uni_modules/lime-date-strip/components/l-date-strip/l-date-strip.vue create mode 100644 src/uni_modules/lime-date-strip/components/l-date-strip/props.ts create mode 100644 src/uni_modules/lime-date-strip/components/l-date-strip/type.ts create mode 100644 src/uni_modules/lime-date-strip/components/l-date-strip/utils.uts create mode 100644 src/uni_modules/lime-date-strip/components/lime-date-strip/lime-date-strip.uvue create mode 100644 src/uni_modules/lime-date-strip/components/lime-date-strip/lime-date-strip.vue create mode 100644 src/uni_modules/lime-date-strip/index.ts create mode 100644 src/uni_modules/lime-date-strip/package.json create mode 100644 src/uni_modules/lime-date-strip/readme.md create mode 100644 src/uni_modules/lime-shared/addUnit/index.ts create mode 100644 src/uni_modules/lime-shared/animation/bezier.ts create mode 100644 src/uni_modules/lime-shared/animation/ease.ts create mode 100644 src/uni_modules/lime-shared/animation/index.ts create mode 100644 src/uni_modules/lime-shared/animation/useTransition.ts create mode 100644 src/uni_modules/lime-shared/animation/uvue.uts create mode 100644 src/uni_modules/lime-shared/animation/vue.ts create mode 100644 src/uni_modules/lime-shared/areaData/city-china.json create mode 100644 src/uni_modules/lime-shared/areaData/index.ts create mode 100644 src/uni_modules/lime-shared/arrayBufferToFile/index.ts create mode 100644 src/uni_modules/lime-shared/arrayBufferToFile/uvue.uts create mode 100644 src/uni_modules/lime-shared/arrayBufferToFile/vue.ts create mode 100644 src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts create mode 100644 src/uni_modules/lime-shared/base64ToPath/index.ts create mode 100644 src/uni_modules/lime-shared/base64ToPath/uvue.uts create mode 100644 src/uni_modules/lime-shared/base64ToPath/vue.ts create mode 100644 src/uni_modules/lime-shared/camelCase/index.ts create mode 100644 src/uni_modules/lime-shared/canIUseCanvas2d/index.ts create mode 100644 src/uni_modules/lime-shared/capitalizedAmount/index.ts create mode 100644 src/uni_modules/lime-shared/changelog.md create mode 100644 src/uni_modules/lime-shared/characterLimit/index.ts create mode 100644 src/uni_modules/lime-shared/clamp/index.ts create mode 100644 src/uni_modules/lime-shared/cloneDeep/index.ts create mode 100644 src/uni_modules/lime-shared/cloneDeep/uvue.ts create mode 100644 src/uni_modules/lime-shared/cloneDeep/vue.ts create mode 100644 src/uni_modules/lime-shared/closest/index.ts create mode 100644 src/uni_modules/lime-shared/components/lime-shared/lime-shared.vue create mode 100644 src/uni_modules/lime-shared/createAnimation/index.ts create mode 100644 src/uni_modules/lime-shared/createAnimation/type.ts create mode 100644 src/uni_modules/lime-shared/createAnimation/uvue.ts create mode 100644 src/uni_modules/lime-shared/createAnimation/vue.ts create mode 100644 src/uni_modules/lime-shared/createCanvas/index.ts create mode 100644 src/uni_modules/lime-shared/createImage/index.ts create mode 100644 src/uni_modules/lime-shared/debounce/index.ts create mode 100644 src/uni_modules/lime-shared/debounce/uvue.ts create mode 100644 src/uni_modules/lime-shared/debounce/vue.ts create mode 100644 src/uni_modules/lime-shared/exif/index.ts create mode 100644 src/uni_modules/lime-shared/exif/uvue.ts create mode 100644 src/uni_modules/lime-shared/exif/vue.ts create mode 100644 src/uni_modules/lime-shared/fillZero/index.ts create mode 100644 src/uni_modules/lime-shared/floatAdd/index.ts create mode 100644 src/uni_modules/lime-shared/floatDiv/index.ts create mode 100644 src/uni_modules/lime-shared/floatMul/index.ts create mode 100644 src/uni_modules/lime-shared/floatSub/index.ts create mode 100644 src/uni_modules/lime-shared/getClassStr/index.ts create mode 100644 src/uni_modules/lime-shared/getCurrentPage/index.ts create mode 100644 src/uni_modules/lime-shared/getCurrentPage/uvue.uts create mode 100644 src/uni_modules/lime-shared/getCurrentPage/vue.ts create mode 100644 src/uni_modules/lime-shared/getLocalFilePath/index.ts create mode 100644 src/uni_modules/lime-shared/getRect/index.ts create mode 100644 src/uni_modules/lime-shared/getRect/uvue.uts create mode 100644 src/uni_modules/lime-shared/getRect/vue.ts create mode 100644 src/uni_modules/lime-shared/getStyleStr/index.ts create mode 100644 src/uni_modules/lime-shared/getStyleStr/index_.uts create mode 100644 src/uni_modules/lime-shared/hasOwn/index.ts create mode 100644 src/uni_modules/lime-shared/hasOwn/uvue.ts create mode 100644 src/uni_modules/lime-shared/hasOwn/vue.ts create mode 100644 src/uni_modules/lime-shared/index.ts create mode 100644 src/uni_modules/lime-shared/isBase64/index.ts create mode 100644 src/uni_modules/lime-shared/isBrowser/index.ts create mode 100644 src/uni_modules/lime-shared/isDef/index.ts create mode 100644 src/uni_modules/lime-shared/isEmpty/index.ts create mode 100644 src/uni_modules/lime-shared/isFunction/index.ts create mode 100644 src/uni_modules/lime-shared/isNumber/index.ts create mode 100644 src/uni_modules/lime-shared/isNumeric/index.ts create mode 100644 src/uni_modules/lime-shared/isObject/index.ts create mode 100644 src/uni_modules/lime-shared/isPromise/index.ts create mode 100644 src/uni_modules/lime-shared/isString/index.ts create mode 100644 src/uni_modules/lime-shared/kebabCase/index.ts create mode 100644 src/uni_modules/lime-shared/obj2url/index.ts create mode 100644 src/uni_modules/lime-shared/package.json create mode 100644 src/uni_modules/lime-shared/pathToBase64/index.ts create mode 100644 src/uni_modules/lime-shared/pathToBase64/uvue.uts create mode 100644 src/uni_modules/lime-shared/pathToBase64/vue.ts create mode 100644 src/uni_modules/lime-shared/platform/index.ts create mode 100644 src/uni_modules/lime-shared/raf/index.ts create mode 100644 src/uni_modules/lime-shared/raf/uvue.ts create mode 100644 src/uni_modules/lime-shared/raf/vue.ts create mode 100644 src/uni_modules/lime-shared/random/index.ts create mode 100644 src/uni_modules/lime-shared/range/index.ts create mode 100644 src/uni_modules/lime-shared/readme.md create mode 100644 src/uni_modules/lime-shared/selectAllComponent/index.ts create mode 100644 src/uni_modules/lime-shared/selectAllComponent/uvue.uts create mode 100644 src/uni_modules/lime-shared/selectAllComponent/vue.ts create mode 100644 src/uni_modules/lime-shared/selectComponent/index.ts create mode 100644 src/uni_modules/lime-shared/selectComponent/uvue.uts create mode 100644 src/uni_modules/lime-shared/selectComponent/vue.ts create mode 100644 src/uni_modules/lime-shared/selectElement/index.uts create mode 100644 src/uni_modules/lime-shared/shuffle/index.ts create mode 100644 src/uni_modules/lime-shared/sleep/index.ts create mode 100644 src/uni_modules/lime-shared/throttle/index.ts create mode 100644 src/uni_modules/lime-shared/toArray/index.ts create mode 100644 src/uni_modules/lime-shared/toBoolean/index.ts create mode 100644 src/uni_modules/lime-shared/toNumber/index.ts create mode 100644 src/uni_modules/lime-shared/unitConvert/index.ts create mode 100644 src/uni_modules/lime-shared/vue/index.ts diff --git a/src/App.vue b/src/App.vue index e16cffa..2965e0f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -28,5 +28,24 @@ onShow(async () => { appStore.updateLocation() } else appStore.closeTimer() }) + +// function genDates() { +// const weekdays = ['日', '一', '二', '三', '四', '五', '六'] +// const currentDate = new Date() +// const currentDayOfWeek = currentDate.getDay() +// let preDateOfWeek = currentDate.getDate() - 1 +// const daysInRange = [] +// for (let i = currentDayOfWeek; i <= weekdays.length - 1; i++) { +// preDateOfWeek = preDateOfWeek + 1 +// daysInRange.push(preDateOfWeek) +// } +// let currentDateOfWeek = currentDate.getDate() +// for (let i = 0; i <= weekdays.length - daysInRange.length; i++) { +// currentDateOfWeek = currentDateOfWeek - 1 +// daysInRange.unshift(currentDateOfWeek) +// } +// console.log(daysInRange) +// } +// genDates() diff --git a/src/components/date-strip/date-strip-item.vue b/src/components/date-strip/date-strip-item.vue new file mode 100644 index 0000000..5e78f42 --- /dev/null +++ b/src/components/date-strip/date-strip-item.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/src/components/date-strip/index.scss b/src/components/date-strip/index.scss new file mode 100644 index 0000000..4e31c3d --- /dev/null +++ b/src/components/date-strip/index.scss @@ -0,0 +1,21 @@ +.date-strip__item { + display: flex; + .date-strip__grid { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 12rpx; + &-info { + display: flex; + align-items: center; + justify-content: center; + width: 72rpx; + height: 72rpx; + } + &--active-text { + color: #fff; + } + } +} diff --git a/src/components/date-strip/index.vue b/src/components/date-strip/index.vue new file mode 100644 index 0000000..d0184ff --- /dev/null +++ b/src/components/date-strip/index.vue @@ -0,0 +1,146 @@ + + + + diff --git a/src/components/date-strip/props.ts b/src/components/date-strip/props.ts new file mode 100644 index 0000000..a4f8c92 --- /dev/null +++ b/src/components/date-strip/props.ts @@ -0,0 +1,108 @@ +export default { + // 第一天从星期几开始,默认 1 = 周一 + firstDayOfWeek: { + type: Number, + default: 0 + }, + // 日期格式,可选 + format: { + type: Function, + default: null + }, + + // 最大可选日期,不传则默认半年后 + maxDate: { + type: Number, + default: null + }, + + // 最小可选日期,不传则默认今天 + minDate: { + type: Number, + default: null + }, + + // 当前选择的日期 + value: { + type: Number, + default: null + }, + + // 默认值(如果使用非 v-model 绑定) + defaultValue: { + type: Number, + default: null + }, + + // 模型值(如果使用 v-model 绑定) + modelValue: { + type: Number, + default: null + }, + + // 日期行高 + height: { + type: String, + default: null + }, + + // 网格宽度 + gridWidth: { + type: String, + default: null + }, + + // 主题色,对底部按钮和选中日期生效 + color: { + type: String, + default: null + }, + + // 选中日期的背景色 + activeBgColor: { + type: String, + default: null + }, + + // 选中日期的文字颜色 + activeColor: { + type: String, + default: null + }, + + // 背景色 + bgColor: { + type: String, + default: null + }, + + // 圆角半径 + radius: { + type: String, + default: null + }, + + // 切换模式,'week' 或 'none' + switchMode: { + type: String, + default: 'week' + }, + + // 形状,'square'、'circle' 或 'none' + shape: { + type: String, + default: 'square' + }, + + // 控制是否显示切换上一屏和下一屏的按钮,仅在按周切换时有效 + showNavigation: { + type: Boolean, + default: false + }, + + // 星期几的名称数组 + weekdays: { + type: Array, + default: () => ['日', '一', '二', '三', '四', '五', '六'] + } +} diff --git a/src/components/date-strip/stripItemProps.ts b/src/components/date-strip/stripItemProps.ts new file mode 100644 index 0000000..9492595 --- /dev/null +++ b/src/components/date-strip/stripItemProps.ts @@ -0,0 +1,58 @@ +// @ts-nocheck +export default { + dates: { + type: Array, + default: () => [] + }, + // 网格宽度 + gridWidth: { + type: String, + default: null + }, + + // 主题色,对底部按钮和选中日期生效 + color: { + type: String, + default: null + }, + + // 选中日期的背景色 + activeBgColor: { + type: String, + default: null + }, + + // 选中日期的文字颜色 + activeColor: { + type: String, + default: null + }, + + // 背景色 + bgColor: { + type: String, + default: null + }, + + // 圆角半径 + radius: { + type: String, + default: null + }, + + // 切换模式,'week' 或 'none' + switchMode: { + type: String, + default: 'week' + }, + + // 形状,'square'、'circle' 或 'none' + shape: { + type: String, + default: 'square' + }, + selectedDate: { + type: [Date, String], + default: null + } +} diff --git a/src/components/date-strip/utils.ts b/src/components/date-strip/utils.ts new file mode 100644 index 0000000..3d939ed --- /dev/null +++ b/src/components/date-strip/utils.ts @@ -0,0 +1,74 @@ +// @ts-nocheck +import { WeekRange, DateType } from './type' + +/** + * 获取指定日期所在周的日期范围。 + * @param {Date} date - 指定日期。 + * @param {number} firstDayOfWeek - 一周的第一天,0 表示周日,1 表示周一,以此类推。 + * @returns {WeekRange} 返回一个包含周起始和结束日期的对象。 + */ + +export function getWeekRange(date: Date, firstDayOfWeek: number): WeekRange { + const start = new Date(date.getTime()) + const dayOffset = (date.getDay() - firstDayOfWeek + 7) % 7 + start.setDate(start.getDate() - dayOffset) + + const end = new Date(start.getTime()) + end.setDate(end.getDate() + 6) + return { start, end } as WeekRange +} + +/** + * 向指定日期添加天数。 + * @param {Date} date - 基础日期。 + * @param {number} days - 要添加的天数,可以是正数或负数。 + * @returns {Date} 返回一个新的日期对象,该对象是基础日期加上指定天数后的结果。 + */ +export function addDays(date: Date, days: number): Date { + const result = new Date(date.getTime()) + result.setDate(result.getDate() + days) + return result +} + +export function addWeeks(date: Date, weeks: number): Date { + const result = new Date(date.getTime()) + result.setDate(result.getDate() + weeks * 7) + return result +} + +/** + * 判断两个日期是否表示同一天(忽略时间部分)。 + * + * @param date1 - 第一个日期。 + * @param date2 - 第二个日期。 + * @returns 如果两个日期是同一天,返回 true;否则返回 false。 + */ +function isSameDate(date1: Date, date2: Date): boolean { + return ( + date1.getFullYear() == date2.getFullYear() && + date1.getMonth() == date2.getMonth() && + date1.getDate() == date2.getDate() + ) +} +export function calcType( + date: Date, + minDate: Date, + maxDate: Date, + selectedDate: Date | null +): DateType { + // 检查日期是否早于 minDate 或晚于 maxDate + if (date.getTime() < minDate.getTime() || date.getTime() > maxDate.getTime()) { + return 'disabled' + } + // 如果 selectedDate 不为 null,检查日期是否等于 selectedDate + if (selectedDate != null && isSameDate(date, selectedDate)) { + return 'selected' + } + return '' +} + +export function daysBetween(date1: Date, date2: Date): number { + // 将两个日期转换为毫秒 + const diffInMilliseconds = Math.abs(date2.getTime() - date1.getTime()) + return Math.floor(diffInMilliseconds / (1000 * 3600 * 24)) +} diff --git a/src/components/widgets/date-strip/date-strip.vue b/src/components/widgets/date-strip/date-strip.vue new file mode 100644 index 0000000..a768232 --- /dev/null +++ b/src/components/widgets/date-strip/date-strip.vue @@ -0,0 +1,26 @@ + + + + diff --git a/src/components/widgets/summary-form/summary-form.vue b/src/components/widgets/summary-form/summary-form.vue new file mode 100644 index 0000000..c2c71fe --- /dev/null +++ b/src/components/widgets/summary-form/summary-form.vue @@ -0,0 +1,70 @@ + + + + diff --git a/src/pages.json b/src/pages.json index 464c4d4..76c036e 100644 --- a/src/pages.json +++ b/src/pages.json @@ -1,5 +1,17 @@ { "pages": [ + { + "path": "pages/recruitsale/summary/index", + "style": { + "navigationBarTitleText": "" + } + }, + { + "path": "pages/telesale/summary/index", + "style": { + "navigationBarTitleText": "" + } + }, { "path": "pages/recruitsale/home/index", "style": { diff --git a/src/pages/recruitsale/summary/index.vue b/src/pages/recruitsale/summary/index.vue new file mode 100644 index 0000000..96f41cb --- /dev/null +++ b/src/pages/recruitsale/summary/index.vue @@ -0,0 +1,41 @@ + + + + diff --git a/src/pages/telesale/summary/index.vue b/src/pages/telesale/summary/index.vue new file mode 100644 index 0000000..bae2787 --- /dev/null +++ b/src/pages/telesale/summary/index.vue @@ -0,0 +1,26 @@ + + + + diff --git a/src/styles/public.scss b/src/styles/public.scss index 10bc384..62a9adb 100644 --- a/src/styles/public.scss +++ b/src/styles/public.scss @@ -126,6 +126,7 @@ page { padding-right: 0 !important; // background-color: $gray-1 !important; } + // 显示字段的表单,不需要输入 .c-form-item-wrapper { @apply px-[32rpx]; diff --git a/src/uni_modules/lime-date-strip/changelog.md b/src/uni_modules/lime-date-strip/changelog.md new file mode 100644 index 0000000..7247b34 --- /dev/null +++ b/src/uni_modules/lime-date-strip/changelog.md @@ -0,0 +1,4 @@ +## 0.0.2(2025-02-10) +- fix: 修复因导入`unitConvert`为小写名称导致微信小程序无法使用 +## 0.0.1(2025-01-28) +- init diff --git a/src/uni_modules/lime-date-strip/components/l-date-strip-item/l-date-strip-item.uvue b/src/uni_modules/lime-date-strip/components/l-date-strip-item/l-date-strip-item.uvue new file mode 100644 index 0000000..6d9e88e --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/l-date-strip-item/l-date-strip-item.uvue @@ -0,0 +1,90 @@ + + + + + \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/l-date-strip-item/l-date-strip-item.vue b/src/uni_modules/lime-date-strip/components/l-date-strip-item/l-date-strip-item.vue new file mode 100644 index 0000000..6b68d56 --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/l-date-strip-item/l-date-strip-item.vue @@ -0,0 +1,99 @@ + + + + + \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/l-date-strip-item/props.ts b/src/uni_modules/lime-date-strip/components/l-date-strip-item/props.ts new file mode 100644 index 0000000..c182fed --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/l-date-strip-item/props.ts @@ -0,0 +1,54 @@ +// @ts-nocheck +export default { + dates: { + type: Array, + default: () => [] + }, + // 网格宽度 + gridWidth: { + type: String, + default: null + }, + + // 主题色,对底部按钮和选中日期生效 + color: { + type: String, + default: null + }, + + // 选中日期的背景色 + activeBgColor: { + type: String, + default: null + }, + + // 选中日期的文字颜色 + activeColor: { + type: String, + default: null + }, + + // 背景色 + bgColor: { + type: String, + default: null + }, + + // 圆角半径 + radius: { + type: String, + default: null + }, + + // 切换模式,'week' 或 'none' + switchMode: { + type: String, + default: 'week', + }, + + // 形状,'square'、'circle' 或 'none' + shape: { + type: String, + default: 'square' + }, +} \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/l-date-strip-item/type.ts b/src/uni_modules/lime-date-strip/components/l-date-strip-item/type.ts new file mode 100644 index 0000000..5d7a943 --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/l-date-strip-item/type.ts @@ -0,0 +1,17 @@ +// @ts-nocheck +import { Day } from '../l-date-strip/type'; + +export interface DateStripItemProps { + dates: Day[]; + /** + * 主题色,对底部按钮和选中日期生效 + */ + color ?: string; + activeBgColor ?: string; + activeColor ?: string; + bgColor ?: string; + radius ?: string; + gridWidth ?: string; + switchMode: 'week' | 'none'; + shape: 'square' | 'circle' | 'text'; +} \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/l-date-strip/index.scss b/src/uni_modules/lime-date-strip/components/l-date-strip/index.scss new file mode 100644 index 0000000..49c6aad --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/l-date-strip/index.scss @@ -0,0 +1,153 @@ +@import '~@/uni_modules/lime-style/index.scss'; +$prefix: l !default; +$date-strip: #{$prefix}-date-strip; + +$date-strip-bg-color: create-var(date-strip-bg-color, $bg-color-container); +$date-strip-height: create-var(date-strip-height, 86px); +$date-strip-padding: create-var(date-strip-padding, 8px 0); +$date-strip-font-size: create-var(date-strip-font-size, 16px); +$date-strip-color: create-var(date-strip-color, $text-color-1); +$date-strip-prefix-color: create-var(date-strip-prefix-color, $text-color-3); +$date-strip-prefix-font-size: create-var(date-strip-prefix-font-size, 14px); +$date-strip-suffix-color: create-var(date-strip-color, $text-color-2); +$date-strip-suffix-font-size: create-var(date-strip-font-size, 12px); +$date-strip-active-color: create-var(date-strip-active-color, $primary-color); +$date-strip-square-radius: create-var(date-strip-square-radius, 5px); + +$date-strip-grid-width: create-var(date-strip-grid-width, 50px); +// $date-strip-grid-prefix-padding: create-var(date-strip-grid-prefix-padding, 4px); +$date-strip-grid-square-padding: create-var(date-strip-grid-square-padding, 6px 0); +$date-strip-grid-circle-radius: create-var(date-strip-grid-circle-radius, 99px); +// $date-strip-grid-suffix-translate: create-var(date-strip-grid-suffix-translate, 60%); + +.#{$date-strip} { + height: $date-strip-height; + background-color: $date-strip-bg-color; + &__scroll { + flex-direction: row; + } + &__item { + display: flex; + flex-direction: row; + padding: $date-strip-padding; + box-sizing: border-box; + /* #ifndef APP-ANDROID || APP-IOS */ + height: 100%; + /* #endif */ + &--week { + flex: 1; + .#{$date-strip}__grid { + flex: 1; + } + } + &--none { + .#{$date-strip}__grid { + width: $date-strip-grid-width; + } + } + } + + &__grid { + /* #ifndef APP-ANDROID || APP-IOS */ + flex-shrink: 0; + /* #endif */ + display: flex; + flex-direction: column; + margin: 0 4rpx; + transition-duration: 300ms; + transition-property: background-color, color; + transition-timing-function: linear; + + &-prefix,&-day,&-suffix { + text-align: center; + transition-duration: 200ms; + transition-property: color; + transition-timing-function: linear; + } + &-prefix { + font-size: $date-strip-prefix-font-size; + // font-weight: bold; + color: $date-strip-prefix-color; + } + &--none { + .l-date-strip__grid-prefix { + padding-bottom: 4px; + padding-top: 4px; + } + } + &--circle { + .l-date-strip__grid-prefix { + padding-bottom: 4px; + } + .l-date-strip__grid-info { + border-radius: $date-strip-grid-circle-radius; + } + } + &--square { + border-radius: $date-strip-square-radius; + padding: $date-strip-grid-square-padding; + } + &-suffix { + position: absolute; + top: 50%; + transform: translateY(60%); + font-size: $date-strip-suffix-font-size; + color: $date-strip-suffix-color; + } + &-day { + font-size: $date-strip-font-size; + color: $date-strip-color; + font-weight: bold; + } + &-info { + display: flex; + flex: 1; + justify-content: center; + align-items: center; + position: relative; + } + &--active-bg { + background-color: $date-strip-active-color; + } + &--active-text { + color: white; + &-none { + color: $date-strip-active-color; + } + } + // &--selected { + // &.l-date-strip__grid--circle { + // .l-date-strip__grid { + // &-info { + // // border-radius: 99px; + // background-color: $date-strip-active-color; + // } + // &-day,&-suffix { + // color: white; + // } + // } + // } + // &.l-date-strip__grid--square { + // background-color: $date-strip-active-color; + // border-radius: $date-strip-square-radius; + + // .l-date-strip__grid { + // &-prefix,&-day,&-suffix { + // color: white; + // } + // } + // } + // &.l-date-strip__grid--none { + // .l-date-strip__grid { + // &-prefix,&-day,&-suffix { + // color: $date-strip-active-color; + // // font-weight: bold; + // } + // } + // } + // } + &--disabled { + opacity: 0.4; + } + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/l-date-strip/l-date-strip.uvue b/src/uni_modules/lime-date-strip/components/l-date-strip/l-date-strip.uvue new file mode 100644 index 0000000..b33e28b --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/l-date-strip/l-date-strip.uvue @@ -0,0 +1,195 @@ + + + \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/l-date-strip/l-date-strip.vue b/src/uni_modules/lime-date-strip/components/l-date-strip/l-date-strip.vue new file mode 100644 index 0000000..49fdc10 --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/l-date-strip/l-date-strip.vue @@ -0,0 +1,215 @@ + + + \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/l-date-strip/props.ts b/src/uni_modules/lime-date-strip/components/l-date-strip/props.ts new file mode 100644 index 0000000..2c69dd5 --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/l-date-strip/props.ts @@ -0,0 +1,108 @@ +export default { + // 第一天从星期几开始,默认 1 = 周一 + firstDayOfWeek: { + type: Number, + default: 0, + }, + // 日期格式,可选 + format: { + type: Function, + default: null + }, + + // 最大可选日期,不传则默认半年后 + maxDate: { + type: Number, + default: null + }, + + // 最小可选日期,不传则默认今天 + minDate: { + type: Number, + default: null + }, + + // 当前选择的日期 + value: { + type: Number, + default: null + }, + + // 默认值(如果使用非 v-model 绑定) + defaultValue: { + type: Number, + default: null + }, + + // 模型值(如果使用 v-model 绑定) + modelValue: { + type: Number, + default: null + }, + + // 日期行高 + height: { + type: String, + default: null + }, + + // 网格宽度 + gridWidth: { + type: String, + default: null + }, + + // 主题色,对底部按钮和选中日期生效 + color: { + type: String, + default: null + }, + + // 选中日期的背景色 + activeBgColor: { + type: String, + default: null + }, + + // 选中日期的文字颜色 + activeColor: { + type: String, + default: null + }, + + // 背景色 + bgColor: { + type: String, + default: null + }, + + // 圆角半径 + radius: { + type: String, + default: null + }, + + // 切换模式,'week' 或 'none' + switchMode: { + type: String, + default: 'week', + }, + + // 形状,'square'、'circle' 或 'none' + shape: { + type: String, + default: 'square' + }, + + // 控制是否显示切换上一屏和下一屏的按钮,仅在按周切换时有效 + showNavigation: { + type: Boolean, + default: false + }, + + // 星期几的名称数组 + weekdays: { + type: Array, + default: () => ['日', '一', '二', '三', '四', '五', '六'] + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/l-date-strip/type.ts b/src/uni_modules/lime-date-strip/components/l-date-strip/type.ts new file mode 100644 index 0000000..555f636 --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/l-date-strip/type.ts @@ -0,0 +1,77 @@ +// @ts-nocheck +export type DateType = 'selected' | 'disabled' | ''; + +/** + * 定义一个表示周范围的类 + */ +export type WeekRange = { + start: Date + end: Date +} + +export type Day = { + date : Date; + key: string, + day : number; + year : number; + month : number; + text: string; + type : DateType; + prefix : string; + prefixStyle ?: UTSJSONObject; + textStyle?: UTSJSONObject; + suffix ?: string; + suffixStyle ?: UTSJSONObject; +} + + +export type WeekDateCollection = { + start: number, + end: number, + dates : Day[]; +} + +export type DateFormatType = (day : Day) => Day; +export interface DateStripProps { + /** + * 第一天从星期几开始,默认 0 = 周日 + * @default 1 + */ + firstDayOfWeek : number; + format ?: DateFormatType; + /** + * 最大可选的日期,不传则默认半年后 + */ + maxDate ?: number; + /** + * 最小可选的日期,不传则默认今天 + */ + minDate ?: number; + /** + * 当前选择的日期 + */ + value ?: number; + defaultValue ?: number; + modelValue ?: number; + /** + * 日期行高 + */ + height ?: string; + gridWidth ?: string; + /** + * 主题色,对底部按钮和选中日期生效 + */ + color ?: string; + activeBgColor ?: string; + activeColor ?: string; + bgColor ?: string; + radius ?: string; + switchMode: 'week' | 'none'; + shape: 'square' | 'circle' | 'none'; + /** + * 控制是否显示切换上一屏和下一屏的按钮。 + * 该属性仅在按周切换时有效。 + */ + showNavigation ?: boolean; + weekdays : string[]; +} \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/l-date-strip/utils.uts b/src/uni_modules/lime-date-strip/components/l-date-strip/utils.uts new file mode 100644 index 0000000..2ac520b --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/l-date-strip/utils.uts @@ -0,0 +1,72 @@ +// @ts-nocheck +import { WeekRange, DateType } from './type'; + + +/** + * 获取指定日期所在周的日期范围。 + * @param {Date} date - 指定日期。 + * @param {number} firstDayOfWeek - 一周的第一天,0 表示周日,1 表示周一,以此类推。 + * @returns {WeekRange} 返回一个包含周起始和结束日期的对象。 + */ + +export function getWeekRange(date : Date, firstDayOfWeek : number) : WeekRange { + const start = new Date(date.getTime()); + const dayOffset = (date.getDay() - firstDayOfWeek + 7) % 7; + start.setDate(start.getDate() - dayOffset); + + const end = new Date(start.getTime()); + end.setDate(end.getDate() + 6); + return { start, end } as WeekRange +}; + +/** + * 向指定日期添加天数。 + * @param {Date} date - 基础日期。 + * @param {number} days - 要添加的天数,可以是正数或负数。 + * @returns {Date} 返回一个新的日期对象,该对象是基础日期加上指定天数后的结果。 + */ +export function addDays(date : Date, days : number) : Date { + const result = new Date(date.getTime()); + result.setDate(result.getDate() + days); + return result; +}; + + +export function addWeeks(date : Date, weeks : number) : Date { + const result = new Date(date.getTime()); + result.setDate(result.getDate() + weeks * 7); + return result; +}; + +/** + * 判断两个日期是否表示同一天(忽略时间部分)。 + * + * @param date1 - 第一个日期。 + * @param date2 - 第二个日期。 + * @returns 如果两个日期是同一天,返回 true;否则返回 false。 + */ +function isSameDate(date1 : Date, date2 : Date) : boolean { + return ( + date1.getFullYear() == date2.getFullYear() && + date1.getMonth() == date2.getMonth() && + date1.getDate() == date2.getDate() + ); +} +export function calcType(date : Date, minDate : Date, maxDate : Date, selectedDate : Date | null) : DateType { + // 检查日期是否早于 minDate 或晚于 maxDate + if (date.getTime() < minDate.getTime() || date.getTime() > maxDate.getTime()) { + return 'disabled'; + } + // 如果 selectedDate 不为 null,检查日期是否等于 selectedDate + if (selectedDate != null && isSameDate(date, selectedDate)) { + return 'selected'; + } + return '' +} + + +export function daysBetween(date1: Date, date2: Date): number { + // 将两个日期转换为毫秒 + const diffInMilliseconds = Math.abs(date2.getTime() - date1.getTime()); + return Math.floor(diffInMilliseconds / (1000 * 3600 * 24)); +} \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/lime-date-strip/lime-date-strip.uvue b/src/uni_modules/lime-date-strip/components/lime-date-strip/lime-date-strip.uvue new file mode 100644 index 0000000..d03cd56 --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/lime-date-strip/lime-date-strip.uvue @@ -0,0 +1,149 @@ + + + + + \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/components/lime-date-strip/lime-date-strip.vue b/src/uni_modules/lime-date-strip/components/lime-date-strip/lime-date-strip.vue new file mode 100644 index 0000000..91f9dd9 --- /dev/null +++ b/src/uni_modules/lime-date-strip/components/lime-date-strip/lime-date-strip.vue @@ -0,0 +1,153 @@ + + + + + \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/index.ts b/src/uni_modules/lime-date-strip/index.ts new file mode 100644 index 0000000..b6731f6 --- /dev/null +++ b/src/uni_modules/lime-date-strip/index.ts @@ -0,0 +1,2 @@ +// @ts-nocheck +export * from './components/l-date-strip/type'; \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/package.json b/src/uni_modules/lime-date-strip/package.json new file mode 100644 index 0000000..bb53822 --- /dev/null +++ b/src/uni_modules/lime-date-strip/package.json @@ -0,0 +1,90 @@ +{ + "id": "lime-date-strip", + "displayName": "lime-date-strip 日期横条", + "version": "0.0.2", + "description": "lime-date-strip 日期横条用于展示周日历或一组日历信息,兼容uniapp/uniappx", + "keywords": [ + "lime-date-strip", + "周日历", + "时间周", + "日期横条" +], + "repository": "", + "engines": { + "HBuilderX": "^4.45" + }, + "dcloudext": { + "type": "component-vue", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [ + "lime-style", + "lime-shared" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "y" + }, + "client": { + "Vue": { + "vue2": "u", + "vue3": "y" + }, + "App": { + "app-vue": "y", + "app-nvue": "u", + "app-uvue": "y", + "app-harmony": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-date-strip/readme.md b/src/uni_modules/lime-date-strip/readme.md new file mode 100644 index 0000000..d9d4539 --- /dev/null +++ b/src/uni_modules/lime-date-strip/readme.md @@ -0,0 +1,176 @@ +# lime-date-strip 日期横条 +- 用于展示周日历或一组日历信息 +- 插件依赖`lime-style`,`lime-shared`不喜勿下 + +## 安装 +插件市场导入即可,首次可能需要重新编译 + +## 代码演示 + + +### 基础用法 +通过`v-model`可以绑定选择的日历 +```html + +``` +```js +const value = new Date().getTime() + +const onChange = (time: number) => { + +} +``` + + +### 切换方式 +默认是以周为周期`(swiper)`的切换方式,通过`switchMode`设置为`none`可以换成`scroll-view`,这时是从`minDate`到`maxDate`的所有日期。 + +```html + +``` +```js +const minDate: new Date(2022, 0, 10).getTime() +const maxDate: new Date(2027, 10, 27).getTime() +``` + +### 选中样式 + +通过 `shape` 可以设置为选中框的形状,可选值有:`circle`,`square`,`none`。 + +```html + +``` + + +### 自定义样式 + +通过 `bgColor` 可以设置背景色,`activeBgColor`可设置选中的背景色。 + +```html + +``` + +### 自定义日期文案 +通过传入 `format` 函数来对日历上每一格的内容进行格式化。 + +```html + +``` +```js +// uniapp 不需要设置类型 +import { Day } from '@/uni_modules/lime-date-strip'; +const customFormat = (day : Day) : Day => { + const { date } = day; + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const curDate = date.getDate(); + + day.suffix = '¥60'; + + if (year == 2025) { + if (month == 2) { + const map = new Map([ + [1, '初一'], + [2, '初二'], + [3, '初三'], + [14, '情人节'], + [15, '元宵节'], + ]) + if (map.has(curDate)) { + day.prefix = map.get(curDate)!; + day.suffix = '¥100'; + } + } + } + + return day; +}; +``` + + + +### 查看示例 +- 导入后直接使用这个标签查看演示效果 + +```html + // 代码位于 uni_modules/lime-date-strip/compoents/lime-date-strip + +``` + +### 插件标签 +- 默认 l-date-strip 为 component +- 默认 lime-date-strip 为 demo + +### 关于vue2的使用方式 +- 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置 +- 关键代码是: 在main.js中 在vue2部分加上这一段即可. + +```js +// vue2 +import Vue from 'vue' +import VueCompositionAPI from '@vue/composition-api' +Vue.use(VueCompositionAPI) +``` + +## API + +### Props + +| 参数 | 说明 | 类型 | 默认值 | +| ------ | ---------------------- | --------- | ------- | +| v-modle | 选中的日期 | _number_ | `` | +| defaultValue | 选中的日期 | _number_ | `` | +| value | 选中的日期 | _number_ | `` | +| switchMode |切换模式:
`none` 平铺展示所有日期,不展示切换按钮,
`week` 按周方式切换 | _string_ | `week` | +| shape |选中框形状:
`square` 方块,包含星期和日期,
`circle` 圆形,只包含日期,
`none` 文本高亮 | _string_ | `square` | +| minDate | 可选择的最小日期 | _number_ | | +| maxDate | 可选择的最大日期 | _number_ | 当前日期的31天 | +| height | 插件高度 | _string_ | | +| gridWidth | 每格日期宽度,仅switchMode为`none`生效 | _string_ | | +| activeBgColor | 选中框背景色 | _string_ | | +| activeColor | 选中框文本色 | _string_ | | +| bgColor | 横条背景色 | _string_ | | +| radius | 选中框圆角 | _string_ | | +| firstDayOfWeek | 第一天从星期几开始,默认 0 = 周日 | _0-6_ | `0` | + + + +### Events + +| 事件名 | 说明 | 回调参数 | +| ------ | ---------------- | ------------------- | +| change | 点击时触发 | _event: number_ | +| select | 点击时触发 | _event: number_ | + + + + +## 主题定制 + +### 样式变量 + +组件提供了下列 CSS 变量,可用于自定义样式)。uvue app无效。 + +| 名称 | 默认值 | 描述 | +| --- | --- | --- | +| --l-date-strip-bg-color: | _$bg-color-container_ | - | +| --l-date-strip-height: | _86px_ | - | +| --l-date-strip-padding: | _8px 0_ | - | +| --l-date-strip-font-size: | _16px_ | - | +| --l-date-strip-color: | _$text-color-1_ | - | +| --l-date-strip-prefix-color: | _$text-color-3_ | - | +| --l-date-strip-prefix-font-size: | _14px_ | - | +| --l-date-strip-suffix-color: | _$text-color-2_ | - | +| --l-date-strip-suffix-font-size: | _12px_ | - | +| --l-date-strip-active-color: | _$primary-color_ | - | +| --l-date-strip-square-radius: | _5px_ | - | +| --l-date-strip-grid-width: | _50px_ | - | +| --l-date-strip-grid-square-padding: | _6px 0_ | - | +| --l-date-strip-grid-circle-radius: | _99px_ | - | + + +## 打赏 + +如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。 +![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png) +![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png) \ No newline at end of file diff --git a/src/uni_modules/lime-shared/addUnit/index.ts b/src/uni_modules/lime-shared/addUnit/index.ts new file mode 100644 index 0000000..78476c9 --- /dev/null +++ b/src/uni_modules/lime-shared/addUnit/index.ts @@ -0,0 +1,42 @@ +// @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) \ No newline at end of file diff --git a/src/uni_modules/lime-shared/animation/bezier.ts b/src/uni_modules/lime-shared/animation/bezier.ts new file mode 100644 index 0000000..b4239e1 --- /dev/null +++ b/src/uni_modules/lime-shared/animation/bezier.ts @@ -0,0 +1,82 @@ +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; +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/animation/ease.ts b/src/uni_modules/lime-shared/animation/ease.ts new file mode 100644 index 0000000..0af156e --- /dev/null +++ b/src/uni_modules/lime-shared/animation/ease.ts @@ -0,0 +1,3 @@ +import {cubicBezier} from './bezier'; +export let ease = cubicBezier(0.25, 0.1, 0.25, 1); +export let linear = cubicBezier(0,0,1,1); \ No newline at end of file diff --git a/src/uni_modules/lime-shared/animation/index.ts b/src/uni_modules/lime-shared/animation/index.ts new file mode 100644 index 0000000..5979064 --- /dev/null +++ b/src/uni_modules/lime-shared/animation/index.ts @@ -0,0 +1,10 @@ +// @ts-nocheck +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif + + + +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif diff --git a/src/uni_modules/lime-shared/animation/useTransition.ts b/src/uni_modules/lime-shared/animation/useTransition.ts new file mode 100644 index 0000000..0939704 --- /dev/null +++ b/src/uni_modules/lime-shared/animation/useTransition.ts @@ -0,0 +1,103 @@ +// @ts-nocheck +import type { 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, type Ref } from '@/uni_modules/lime-shared/vue' + +export function useTransition(percent : Ref|(() => number), options : UseTransitionOptions) : Ref { + 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 +export function useTransition(source : any, options : UseTransitionOptions) : UseTransitionReturnType { + const outputRef : Ref = 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 + 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 if(typeof source == 'function'){ + watch(source, watchFunc, { immediate }) + } else if(isRef(source) && typeof source.value == 'number') { + watch(source as Ref, watchFunc, { immediate }) + } + // else if(source instanceof Ref){ + // watch(source as Ref, watchFunc, { immediate }) + // } + + const stop = ()=>{ + + } + return outputRef //as UseTransitionReturnType +} + +// #endif \ No newline at end of file diff --git a/src/uni_modules/lime-shared/animation/uvue.uts b/src/uni_modules/lime-shared/animation/uvue.uts new file mode 100644 index 0000000..37e2bc9 --- /dev/null +++ b/src/uni_modules/lime-shared/animation/uvue.uts @@ -0,0 +1,119 @@ +import { raf, cancelRaf} from '../raf' +// @ts-nocheck +export class Timeline { + state : string + animations : Set = new Set() + delAnimations : Animation[] = [] + startTimes : Map = new Map() + 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); + } + // cancelAnimationFrame(this.tickHandler); + if (this.state != 'Started') return + + this.tickHandler = raf(()=>{ + this.tick!() + }) + + this.tickHandlers.push(this.tickHandler) + } + if(this.tick != null) { + this.tick!() + } + + } + pause() { + if (!(this.state === 'Started')) return; + this.state = 'Paused'; + this.pauseStart = Date.now(); + cancelRaf(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) + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/animation/vue.ts b/src/uni_modules/lime-shared/animation/vue.ts new file mode 100644 index 0000000..30f89e5 --- /dev/null +++ b/src/uni_modules/lime-shared/animation/vue.ts @@ -0,0 +1,123 @@ +// @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) + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/areaData/city-china.json b/src/uni_modules/lime-shared/areaData/city-china.json new file mode 100644 index 0000000..d9872a0 --- /dev/null +++ b/src/uni_modules/lime-shared/areaData/city-china.json @@ -0,0 +1,3888 @@ +{ + "province_list": { + "110000": "北京市", + "120000": "天津市", + "130000": "河北省", + "140000": "山西省", + "150000": "内蒙古自治区", + "210000": "辽宁省", + "220000": "吉林省", + "230000": "黑龙江省", + "310000": "上海市", + "320000": "江苏省", + "330000": "浙江省", + "340000": "安徽省", + "350000": "福建省", + "360000": "江西省", + "370000": "山东省", + "410000": "河南省", + "420000": "湖北省", + "430000": "湖南省", + "440000": "广东省", + "450000": "广西壮族自治区", + "460000": "海南省", + "500000": "重庆市", + "510000": "四川省", + "520000": "贵州省", + "530000": "云南省", + "540000": "西藏自治区", + "610000": "陕西省", + "620000": "甘肃省", + "630000": "青海省", + "640000": "宁夏回族自治区", + "650000": "新疆维吾尔自治区", + "710000": "台湾省", + "810000": "香港特别行政区", + "820000": "澳门特别行政区" + }, + "city_list": { + "110100": "北京市", + "120100": "天津市", + "130100": "石家庄市", + "130200": "唐山市", + "130300": "秦皇岛市", + "130400": "邯郸市", + "130500": "邢台市", + "130600": "保定市", + "130700": "张家口市", + "130800": "承德市", + "130900": "沧州市", + "131000": "廊坊市", + "131100": "衡水市", + "140100": "太原市", + "140200": "大同市", + "140300": "阳泉市", + "140400": "长治市", + "140500": "晋城市", + "140600": "朔州市", + "140700": "晋中市", + "140800": "运城市", + "140900": "忻州市", + "141000": "临汾市", + "141100": "吕梁市", + "150100": "呼和浩特市", + "150200": "包头市", + "150300": "乌海市", + "150400": "赤峰市", + "150500": "通辽市", + "150600": "鄂尔多斯市", + "150700": "呼伦贝尔市", + "150800": "巴彦淖尔市", + "150900": "乌兰察布市", + "152200": "兴安盟", + "152500": "锡林郭勒盟", + "152900": "阿拉善盟", + "210100": "沈阳市", + "210200": "大连市", + "210300": "鞍山市", + "210400": "抚顺市", + "210500": "本溪市", + "210600": "丹东市", + "210700": "锦州市", + "210800": "营口市", + "210900": "阜新市", + "211000": "辽阳市", + "211100": "盘锦市", + "211200": "铁岭市", + "211300": "朝阳市", + "211400": "葫芦岛市", + "220100": "长春市", + "220200": "吉林市", + "220300": "四平市", + "220400": "辽源市", + "220500": "通化市", + "220600": "白山市", + "220700": "松原市", + "220800": "白城市", + "222400": "延边朝鲜族自治州", + "230100": "哈尔滨市", + "230200": "齐齐哈尔市", + "230300": "鸡西市", + "230400": "鹤岗市", + "230500": "双鸭山市", + "230600": "大庆市", + "230700": "伊春市", + "230800": "佳木斯市", + "230900": "七台河市", + "231000": "牡丹江市", + "231100": "黑河市", + "231200": "绥化市", + "232700": "大兴安岭地区", + "310100": "上海市", + "320100": "南京市", + "320200": "无锡市", + "320300": "徐州市", + "320400": "常州市", + "320500": "苏州市", + "320600": "南通市", + "320700": "连云港市", + "320800": "淮安市", + "320900": "盐城市", + "321000": "扬州市", + "321100": "镇江市", + "321200": "泰州市", + "321300": "宿迁市", + "330100": "杭州市", + "330200": "宁波市", + "330300": "温州市", + "330400": "嘉兴市", + "330500": "湖州市", + "330600": "绍兴市", + "330700": "金华市", + "330800": "衢州市", + "330900": "舟山市", + "331000": "台州市", + "331100": "丽水市", + "340100": "合肥市", + "340200": "芜湖市", + "340300": "蚌埠市", + "340400": "淮南市", + "340500": "马鞍山市", + "340600": "淮北市", + "340700": "铜陵市", + "340800": "安庆市", + "341000": "黄山市", + "341100": "滁州市", + "341200": "阜阳市", + "341300": "宿州市", + "341500": "六安市", + "341600": "亳州市", + "341700": "池州市", + "341800": "宣城市", + "350100": "福州市", + "350200": "厦门市", + "350300": "莆田市", + "350400": "三明市", + "350500": "泉州市", + "350600": "漳州市", + "350700": "南平市", + "350800": "龙岩市", + "350900": "宁德市", + "360100": "南昌市", + "360200": "景德镇市", + "360300": "萍乡市", + "360400": "九江市", + "360500": "新余市", + "360600": "鹰潭市", + "360700": "赣州市", + "360800": "吉安市", + "360900": "宜春市", + "361000": "抚州市", + "361100": "上饶市", + "370100": "济南市", + "370200": "青岛市", + "370300": "淄博市", + "370400": "枣庄市", + "370500": "东营市", + "370600": "烟台市", + "370700": "潍坊市", + "370800": "济宁市", + "370900": "泰安市", + "371000": "威海市", + "371100": "日照市", + "371300": "临沂市", + "371400": "德州市", + "371500": "聊城市", + "371600": "滨州市", + "371700": "菏泽市", + "410100": "郑州市", + "410200": "开封市", + "410300": "洛阳市", + "410400": "平顶山市", + "410500": "安阳市", + "410600": "鹤壁市", + "410700": "新乡市", + "410800": "焦作市", + "410900": "濮阳市", + "411000": "许昌市", + "411100": "漯河市", + "411200": "三门峡市", + "411300": "南阳市", + "411400": "商丘市", + "411500": "信阳市", + "411600": "周口市", + "411700": "驻马店市", + "419000": "省直辖县", + "420100": "武汉市", + "420200": "黄石市", + "420300": "十堰市", + "420500": "宜昌市", + "420600": "襄阳市", + "420700": "鄂州市", + "420800": "荆门市", + "420900": "孝感市", + "421000": "荆州市", + "421100": "黄冈市", + "421200": "咸宁市", + "421300": "随州市", + "422800": "恩施土家族苗族自治州", + "429000": "省直辖县", + "430100": "长沙市", + "430200": "株洲市", + "430300": "湘潭市", + "430400": "衡阳市", + "430500": "邵阳市", + "430600": "岳阳市", + "430700": "常德市", + "430800": "张家界市", + "430900": "益阳市", + "431000": "郴州市", + "431100": "永州市", + "431200": "怀化市", + "431300": "娄底市", + "433100": "湘西土家族苗族自治州", + "440100": "广州市", + "440200": "韶关市", + "440300": "深圳市", + "440400": "珠海市", + "440500": "汕头市", + "440600": "佛山市", + "440700": "江门市", + "440800": "湛江市", + "440900": "茂名市", + "441200": "肇庆市", + "441300": "惠州市", + "441400": "梅州市", + "441500": "汕尾市", + "441600": "河源市", + "441700": "阳江市", + "441800": "清远市", + "441900": "东莞市", + "442000": "中山市", + "445100": "潮州市", + "445200": "揭阳市", + "445300": "云浮市", + "450100": "南宁市", + "450200": "柳州市", + "450300": "桂林市", + "450400": "梧州市", + "450500": "北海市", + "450600": "防城港市", + "450700": "钦州市", + "450800": "贵港市", + "450900": "玉林市", + "451000": "百色市", + "451100": "贺州市", + "451200": "河池市", + "451300": "来宾市", + "451400": "崇左市", + "460100": "海口市", + "460200": "三亚市", + "460300": "三沙市", + "460400": "儋州市", + "469000": "省直辖县", + "500100": "重庆市", + "500200": "县", + "510100": "成都市", + "510300": "自贡市", + "510400": "攀枝花市", + "510500": "泸州市", + "510600": "德阳市", + "510700": "绵阳市", + "510800": "广元市", + "510900": "遂宁市", + "511000": "内江市", + "511100": "乐山市", + "511300": "南充市", + "511400": "眉山市", + "511500": "宜宾市", + "511600": "广安市", + "511700": "达州市", + "511800": "雅安市", + "511900": "巴中市", + "512000": "资阳市", + "513200": "阿坝藏族羌族自治州", + "513300": "甘孜藏族自治州", + "513400": "凉山彝族自治州", + "520100": "贵阳市", + "520200": "六盘水市", + "520300": "遵义市", + "520400": "安顺市", + "520500": "毕节市", + "520600": "铜仁市", + "522300": "黔西南布依族苗族自治州", + "522600": "黔东南苗族侗族自治州", + "522700": "黔南布依族苗族自治州", + "530100": "昆明市", + "530300": "曲靖市", + "530400": "玉溪市", + "530500": "保山市", + "530600": "昭通市", + "530700": "丽江市", + "530800": "普洱市", + "530900": "临沧市", + "532300": "楚雄彝族自治州", + "532500": "红河哈尼族彝族自治州", + "532600": "文山壮族苗族自治州", + "532800": "西双版纳傣族自治州", + "532900": "大理白族自治州", + "533100": "德宏傣族景颇族自治州", + "533300": "怒江傈僳族自治州", + "533400": "迪庆藏族自治州", + "540100": "拉萨市", + "540200": "日喀则市", + "540300": "昌都市", + "540400": "林芝市", + "540500": "山南市", + "540600": "那曲市", + "542500": "阿里地区", + "610100": "西安市", + "610200": "铜川市", + "610300": "宝鸡市", + "610400": "咸阳市", + "610500": "渭南市", + "610600": "延安市", + "610700": "汉中市", + "610800": "榆林市", + "610900": "安康市", + "611000": "商洛市", + "620100": "兰州市", + "620200": "嘉峪关市", + "620300": "金昌市", + "620400": "白银市", + "620500": "天水市", + "620600": "武威市", + "620700": "张掖市", + "620800": "平凉市", + "620900": "酒泉市", + "621000": "庆阳市", + "621100": "定西市", + "621200": "陇南市", + "622900": "临夏回族自治州", + "623000": "甘南藏族自治州", + "630100": "西宁市", + "630200": "海东市", + "632200": "海北藏族自治州", + "632300": "黄南藏族自治州", + "632500": "海南藏族自治州", + "632600": "果洛藏族自治州", + "632700": "玉树藏族自治州", + "632800": "海西蒙古族藏族自治州", + "640100": "银川市", + "640200": "石嘴山市", + "640300": "吴忠市", + "640400": "固原市", + "640500": "中卫市", + "650100": "乌鲁木齐市", + "650200": "克拉玛依市", + "650400": "吐鲁番市", + "650500": "哈密市", + "652300": "昌吉回族自治州", + "652700": "博尔塔拉蒙古自治州", + "652800": "巴音郭楞蒙古自治州", + "652900": "阿克苏地区", + "653000": "克孜勒苏柯尔克孜自治州", + "653100": "喀什地区", + "653200": "和田地区", + "654000": "伊犁哈萨克自治州", + "654200": "塔城地区", + "654300": "阿勒泰地区", + "659000": "自治区直辖县级行政区划", + "710100": "台北市", + "710200": "高雄市", + "710300": "台南市", + "710400": "台中市", + "710500": "金门县", + "710600": "南投县", + "710700": "基隆市", + "710800": "新竹市", + "710900": "嘉义市", + "711100": "新北市", + "711200": "宜兰县", + "711300": "新竹县", + "711400": "桃园市", + "711500": "苗栗县", + "711700": "彰化县", + "711900": "嘉义县", + "712100": "云林县", + "712400": "屏东县", + "712500": "台东县", + "712600": "花莲县", + "712700": "澎湖县", + "712800": "连江县", + "810100": "香港岛", + "810200": "九龙", + "810300": "新界", + "820100": "澳门半岛", + "820200": "离岛" + }, + "county_list": { + "110101": "东城区", + "110102": "西城区", + "110105": "朝阳区", + "110106": "丰台区", + "110107": "石景山区", + "110108": "海淀区", + "110109": "门头沟区", + "110111": "房山区", + "110112": "通州区", + "110113": "顺义区", + "110114": "昌平区", + "110115": "大兴区", + "110116": "怀柔区", + "110117": "平谷区", + "110118": "密云区", + "110119": "延庆区", + "120101": "和平区", + "120102": "河东区", + "120103": "河西区", + "120104": "南开区", + "120105": "河北区", + "120106": "红桥区", + "120110": "东丽区", + "120111": "西青区", + "120112": "津南区", + "120113": "北辰区", + "120114": "武清区", + "120115": "宝坻区", + "120116": "滨海新区", + "120117": "宁河区", + "120118": "静海区", + "120119": "蓟州区", + "130102": "长安区", + "130104": "桥西区", + "130105": "新华区", + "130107": "井陉矿区", + "130108": "裕华区", + "130109": "藁城区", + "130110": "鹿泉区", + "130111": "栾城区", + "130121": "井陉县", + "130123": "正定县", + "130125": "行唐县", + "130126": "灵寿县", + "130127": "高邑县", + "130128": "深泽县", + "130129": "赞皇县", + "130130": "无极县", + "130131": "平山县", + "130132": "元氏县", + "130133": "赵县", + "130171": "石家庄高新技术产业开发区", + "130172": "石家庄循环化工园区", + "130181": "辛集市", + "130183": "晋州市", + "130184": "新乐市", + "130202": "路南区", + "130203": "路北区", + "130204": "古冶区", + "130205": "开平区", + "130207": "丰南区", + "130208": "丰润区", + "130209": "曹妃甸区", + "130224": "滦南县", + "130225": "乐亭县", + "130227": "迁西县", + "130229": "玉田县", + "130273": "唐山高新技术产业开发区", + "130274": "河北唐山海港经济开发区", + "130281": "遵化市", + "130283": "迁安市", + "130284": "滦州市", + "130302": "海港区", + "130303": "山海关区", + "130304": "北戴河区", + "130306": "抚宁区", + "130321": "青龙满族自治县", + "130322": "昌黎县", + "130324": "卢龙县", + "130371": "秦皇岛市经济技术开发区", + "130372": "北戴河新区", + "130390": "经济技术开发区", + "130402": "邯山区", + "130403": "丛台区", + "130404": "复兴区", + "130406": "峰峰矿区", + "130407": "肥乡区", + "130408": "永年区", + "130423": "临漳县", + "130424": "成安县", + "130425": "大名县", + "130426": "涉县", + "130427": "磁县", + "130430": "邱县", + "130431": "鸡泽县", + "130432": "广平县", + "130433": "馆陶县", + "130434": "魏县", + "130435": "曲周县", + "130471": "邯郸经济技术开发区", + "130473": "邯郸冀南新区", + "130481": "武安市", + "130502": "襄都区", + "130503": "信都区", + "130505": "任泽区", + "130506": "南和区", + "130522": "临城县", + "130523": "内丘县", + "130524": "柏乡县", + "130525": "隆尧县", + "130528": "宁晋县", + "130529": "巨鹿县", + "130530": "新河县", + "130531": "广宗县", + "130532": "平乡县", + "130533": "威县", + "130534": "清河县", + "130535": "临西县", + "130571": "河北邢台经济开发区", + "130581": "南宫市", + "130582": "沙河市", + "130602": "竞秀区", + "130606": "莲池区", + "130607": "满城区", + "130608": "清苑区", + "130609": "徐水区", + "130623": "涞水县", + "130624": "阜平县", + "130626": "定兴县", + "130627": "唐县", + "130628": "高阳县", + "130629": "容城县", + "130630": "涞源县", + "130631": "望都县", + "130632": "安新县", + "130633": "易县", + "130634": "曲阳县", + "130635": "蠡县", + "130636": "顺平县", + "130637": "博野县", + "130638": "雄县", + "130671": "保定高新技术产业开发区", + "130672": "保定白沟新城", + "130681": "涿州市", + "130682": "定州市", + "130683": "安国市", + "130684": "高碑店市", + "130702": "桥东区", + "130703": "桥西区", + "130705": "宣化区", + "130706": "下花园区", + "130708": "万全区", + "130709": "崇礼区", + "130722": "张北县", + "130723": "康保县", + "130724": "沽源县", + "130725": "尚义县", + "130726": "蔚县", + "130727": "阳原县", + "130728": "怀安县", + "130730": "怀来县", + "130731": "涿鹿县", + "130732": "赤城县", + "130772": "张家口市察北管理区", + "130802": "双桥区", + "130803": "双滦区", + "130804": "鹰手营子矿区", + "130821": "承德县", + "130822": "兴隆县", + "130824": "滦平县", + "130825": "隆化县", + "130826": "丰宁满族自治县", + "130827": "宽城满族自治县", + "130828": "围场满族蒙古族自治县", + "130871": "承德高新技术产业开发区", + "130881": "平泉市", + "130902": "新华区", + "130903": "运河区", + "130921": "沧县", + "130922": "青县", + "130923": "东光县", + "130924": "海兴县", + "130925": "盐山县", + "130926": "肃宁县", + "130927": "南皮县", + "130928": "吴桥县", + "130929": "献县", + "130930": "孟村回族自治县", + "130971": "河北沧州经济开发区", + "130972": "沧州高新技术产业开发区", + "130973": "沧州渤海新区", + "130981": "泊头市", + "130982": "任丘市", + "130983": "黄骅市", + "130984": "河间市", + "131002": "安次区", + "131003": "广阳区", + "131022": "固安县", + "131023": "永清县", + "131024": "香河县", + "131025": "大城县", + "131026": "文安县", + "131028": "大厂回族自治县", + "131071": "廊坊经济技术开发区", + "131081": "霸州市", + "131082": "三河市", + "131090": "开发区", + "131102": "桃城区", + "131103": "冀州区", + "131121": "枣强县", + "131122": "武邑县", + "131123": "武强县", + "131124": "饶阳县", + "131125": "安平县", + "131126": "故城县", + "131127": "景县", + "131128": "阜城县", + "131171": "河北衡水经济开发区", + "131172": "衡水滨湖新区", + "131182": "深州市", + "140105": "小店区", + "140106": "迎泽区", + "140107": "杏花岭区", + "140108": "尖草坪区", + "140109": "万柏林区", + "140110": "晋源区", + "140121": "清徐县", + "140122": "阳曲县", + "140123": "娄烦县", + "140181": "古交市", + "140212": "新荣区", + "140213": "平城区", + "140214": "云冈区", + "140215": "云州区", + "140221": "阳高县", + "140222": "天镇县", + "140223": "广灵县", + "140224": "灵丘县", + "140225": "浑源县", + "140226": "左云县", + "140271": "山西大同经济开发区", + "140302": "城区", + "140303": "矿区", + "140311": "郊区", + "140321": "平定县", + "140322": "盂县", + "140403": "潞州区", + "140404": "上党区", + "140405": "屯留区", + "140406": "潞城区", + "140423": "襄垣县", + "140425": "平顺县", + "140426": "黎城县", + "140427": "壶关县", + "140428": "长子县", + "140429": "武乡县", + "140430": "沁县", + "140431": "沁源县", + "140471": "山西长治高新技术产业园区", + "140502": "城区", + "140521": "沁水县", + "140522": "阳城县", + "140524": "陵川县", + "140525": "泽州县", + "140581": "高平市", + "140602": "朔城区", + "140603": "平鲁区", + "140621": "山阴县", + "140622": "应县", + "140623": "右玉县", + "140671": "山西朔州经济开发区", + "140681": "怀仁市", + "140702": "榆次区", + "140703": "太谷区", + "140721": "榆社县", + "140722": "左权县", + "140723": "和顺县", + "140724": "昔阳县", + "140725": "寿阳县", + "140727": "祁县", + "140728": "平遥县", + "140729": "灵石县", + "140781": "介休市", + "140802": "盐湖区", + "140821": "临猗县", + "140822": "万荣县", + "140823": "闻喜县", + "140824": "稷山县", + "140825": "新绛县", + "140826": "绛县", + "140827": "垣曲县", + "140828": "夏县", + "140829": "平陆县", + "140830": "芮城县", + "140881": "永济市", + "140882": "河津市", + "140902": "忻府区", + "140921": "定襄县", + "140922": "五台县", + "140923": "代县", + "140924": "繁峙县", + "140925": "宁武县", + "140926": "静乐县", + "140927": "神池县", + "140928": "五寨县", + "140929": "岢岚县", + "140930": "河曲县", + "140931": "保德县", + "140932": "偏关县", + "140971": "五台山风景名胜区", + "140981": "原平市", + "141002": "尧都区", + "141021": "曲沃县", + "141022": "翼城县", + "141023": "襄汾县", + "141024": "洪洞县", + "141025": "古县", + "141026": "安泽县", + "141027": "浮山县", + "141028": "吉县", + "141029": "乡宁县", + "141030": "大宁县", + "141031": "隰县", + "141032": "永和县", + "141033": "蒲县", + "141034": "汾西县", + "141081": "侯马市", + "141082": "霍州市", + "141102": "离石区", + "141121": "文水县", + "141122": "交城县", + "141123": "兴县", + "141124": "临县", + "141125": "柳林县", + "141126": "石楼县", + "141127": "岚县", + "141128": "方山县", + "141129": "中阳县", + "141130": "交口县", + "141181": "孝义市", + "141182": "汾阳市", + "150102": "新城区", + "150103": "回民区", + "150104": "玉泉区", + "150105": "赛罕区", + "150121": "土默特左旗", + "150122": "托克托县", + "150123": "和林格尔县", + "150124": "清水河县", + "150125": "武川县", + "150172": "呼和浩特经济技术开发区", + "150202": "东河区", + "150203": "昆都仑区", + "150204": "青山区", + "150205": "石拐区", + "150206": "白云鄂博矿区", + "150207": "九原区", + "150221": "土默特右旗", + "150222": "固阳县", + "150223": "达尔罕茂明安联合旗", + "150271": "包头稀土高新技术产业开发区", + "150302": "海勃湾区", + "150303": "海南区", + "150304": "乌达区", + "150402": "红山区", + "150403": "元宝山区", + "150404": "松山区", + "150421": "阿鲁科尔沁旗", + "150422": "巴林左旗", + "150423": "巴林右旗", + "150424": "林西县", + "150425": "克什克腾旗", + "150426": "翁牛特旗", + "150428": "喀喇沁旗", + "150429": "宁城县", + "150430": "敖汉旗", + "150502": "科尔沁区", + "150521": "科尔沁左翼中旗", + "150522": "科尔沁左翼后旗", + "150523": "开鲁县", + "150524": "库伦旗", + "150525": "奈曼旗", + "150526": "扎鲁特旗", + "150571": "通辽经济技术开发区", + "150581": "霍林郭勒市", + "150602": "东胜区", + "150603": "康巴什区", + "150621": "达拉特旗", + "150622": "准格尔旗", + "150623": "鄂托克前旗", + "150624": "鄂托克旗", + "150625": "杭锦旗", + "150626": "乌审旗", + "150627": "伊金霍洛旗", + "150702": "海拉尔区", + "150703": "扎赉诺尔区", + "150721": "阿荣旗", + "150722": "莫力达瓦达斡尔族自治旗", + "150723": "鄂伦春自治旗", + "150724": "鄂温克族自治旗", + "150725": "陈巴尔虎旗", + "150726": "新巴尔虎左旗", + "150727": "新巴尔虎右旗", + "150781": "满洲里市", + "150782": "牙克石市", + "150783": "扎兰屯市", + "150784": "额尔古纳市", + "150785": "根河市", + "150802": "临河区", + "150821": "五原县", + "150822": "磴口县", + "150823": "乌拉特前旗", + "150824": "乌拉特中旗", + "150825": "乌拉特后旗", + "150826": "杭锦后旗", + "150902": "集宁区", + "150921": "卓资县", + "150922": "化德县", + "150923": "商都县", + "150924": "兴和县", + "150925": "凉城县", + "150926": "察哈尔右翼前旗", + "150927": "察哈尔右翼中旗", + "150928": "察哈尔右翼后旗", + "150929": "四子王旗", + "150981": "丰镇市", + "152201": "乌兰浩特市", + "152202": "阿尔山市", + "152221": "科尔沁右翼前旗", + "152222": "科尔沁右翼中旗", + "152223": "扎赉特旗", + "152224": "突泉县", + "152501": "二连浩特市", + "152502": "锡林浩特市", + "152522": "阿巴嘎旗", + "152523": "苏尼特左旗", + "152524": "苏尼特右旗", + "152525": "东乌珠穆沁旗", + "152526": "西乌珠穆沁旗", + "152527": "太仆寺旗", + "152528": "镶黄旗", + "152529": "正镶白旗", + "152530": "正蓝旗", + "152531": "多伦县", + "152571": "乌拉盖管委会", + "152921": "阿拉善左旗", + "152922": "阿拉善右旗", + "152923": "额济纳旗", + "152971": "内蒙古阿拉善经济开发区", + "210102": "和平区", + "210103": "沈河区", + "210104": "大东区", + "210105": "皇姑区", + "210106": "铁西区", + "210111": "苏家屯区", + "210112": "浑南区", + "210113": "沈北新区", + "210114": "于洪区", + "210115": "辽中区", + "210123": "康平县", + "210124": "法库县", + "210181": "新民市", + "210190": "经济技术开发区", + "210202": "中山区", + "210203": "西岗区", + "210204": "沙河口区", + "210211": "甘井子区", + "210212": "旅顺口区", + "210213": "金州区", + "210214": "普兰店区", + "210224": "长海县", + "210281": "瓦房店市", + "210283": "庄河市", + "210302": "铁东区", + "210303": "铁西区", + "210304": "立山区", + "210311": "千山区", + "210321": "台安县", + "210323": "岫岩满族自治县", + "210381": "海城市", + "210390": "高新区", + "210402": "新抚区", + "210403": "东洲区", + "210404": "望花区", + "210411": "顺城区", + "210421": "抚顺县", + "210422": "新宾满族自治县", + "210423": "清原满族自治县", + "210502": "平山区", + "210503": "溪湖区", + "210504": "明山区", + "210505": "南芬区", + "210521": "本溪满族自治县", + "210522": "桓仁满族自治县", + "210602": "元宝区", + "210603": "振兴区", + "210604": "振安区", + "210624": "宽甸满族自治县", + "210681": "东港市", + "210682": "凤城市", + "210702": "古塔区", + "210703": "凌河区", + "210711": "太和区", + "210726": "黑山县", + "210727": "义县", + "210781": "凌海市", + "210782": "北镇市", + "210793": "经济技术开发区", + "210802": "站前区", + "210803": "西市区", + "210804": "鲅鱼圈区", + "210811": "老边区", + "210881": "盖州市", + "210882": "大石桥市", + "210902": "海州区", + "210903": "新邱区", + "210904": "太平区", + "210905": "清河门区", + "210911": "细河区", + "210921": "阜新蒙古族自治县", + "210922": "彰武县", + "211002": "白塔区", + "211003": "文圣区", + "211004": "宏伟区", + "211005": "弓长岭区", + "211011": "太子河区", + "211021": "辽阳县", + "211081": "灯塔市", + "211102": "双台子区", + "211103": "兴隆台区", + "211104": "大洼区", + "211122": "盘山县", + "211202": "银州区", + "211204": "清河区", + "211221": "铁岭县", + "211223": "西丰县", + "211224": "昌图县", + "211281": "调兵山市", + "211282": "开原市", + "211302": "双塔区", + "211303": "龙城区", + "211321": "朝阳县", + "211322": "建平县", + "211324": "喀喇沁左翼蒙古族自治县", + "211381": "北票市", + "211382": "凌源市", + "211402": "连山区", + "211403": "龙港区", + "211404": "南票区", + "211421": "绥中县", + "211422": "建昌县", + "211481": "兴城市", + "220102": "南关区", + "220103": "宽城区", + "220104": "朝阳区", + "220105": "二道区", + "220106": "绿园区", + "220112": "双阳区", + "220113": "九台区", + "220122": "农安县", + "220171": "长春经济技术开发区", + "220172": "长春净月高新技术产业开发区", + "220173": "长春高新技术产业开发区", + "220174": "长春汽车经济技术开发区", + "220182": "榆树市", + "220183": "德惠市", + "220184": "公主岭市", + "220192": "经济技术开发区", + "220202": "昌邑区", + "220203": "龙潭区", + "220204": "船营区", + "220211": "丰满区", + "220221": "永吉县", + "220271": "吉林经济开发区", + "220272": "吉林高新技术产业开发区", + "220281": "蛟河市", + "220282": "桦甸市", + "220283": "舒兰市", + "220284": "磐石市", + "220302": "铁西区", + "220303": "铁东区", + "220322": "梨树县", + "220323": "伊通满族自治县", + "220382": "双辽市", + "220402": "龙山区", + "220403": "西安区", + "220421": "东丰县", + "220422": "东辽县", + "220502": "东昌区", + "220503": "二道江区", + "220521": "通化县", + "220523": "辉南县", + "220524": "柳河县", + "220581": "梅河口市", + "220582": "集安市", + "220602": "浑江区", + "220605": "江源区", + "220621": "抚松县", + "220622": "靖宇县", + "220623": "长白朝鲜族自治县", + "220681": "临江市", + "220702": "宁江区", + "220721": "前郭尔罗斯蒙古族自治县", + "220722": "长岭县", + "220723": "乾安县", + "220771": "吉林松原经济开发区", + "220781": "扶余市", + "220802": "洮北区", + "220821": "镇赉县", + "220822": "通榆县", + "220871": "吉林白城经济开发区", + "220881": "洮南市", + "220882": "大安市", + "222401": "延吉市", + "222402": "图们市", + "222403": "敦化市", + "222404": "珲春市", + "222405": "龙井市", + "222406": "和龙市", + "222424": "汪清县", + "222426": "安图县", + "230102": "道里区", + "230103": "南岗区", + "230104": "道外区", + "230108": "平房区", + "230109": "松北区", + "230110": "香坊区", + "230111": "呼兰区", + "230112": "阿城区", + "230113": "双城区", + "230123": "依兰县", + "230124": "方正县", + "230125": "宾县", + "230126": "巴彦县", + "230127": "木兰县", + "230128": "通河县", + "230129": "延寿县", + "230183": "尚志市", + "230184": "五常市", + "230202": "龙沙区", + "230203": "建华区", + "230204": "铁锋区", + "230205": "昂昂溪区", + "230206": "富拉尔基区", + "230207": "碾子山区", + "230208": "梅里斯达斡尔族区", + "230221": "龙江县", + "230223": "依安县", + "230224": "泰来县", + "230225": "甘南县", + "230227": "富裕县", + "230229": "克山县", + "230230": "克东县", + "230231": "拜泉县", + "230281": "讷河市", + "230302": "鸡冠区", + "230303": "恒山区", + "230304": "滴道区", + "230305": "梨树区", + "230306": "城子河区", + "230307": "麻山区", + "230321": "鸡东县", + "230381": "虎林市", + "230382": "密山市", + "230402": "向阳区", + "230403": "工农区", + "230404": "南山区", + "230405": "兴安区", + "230406": "东山区", + "230407": "兴山区", + "230421": "萝北县", + "230422": "绥滨县", + "230502": "尖山区", + "230503": "岭东区", + "230505": "四方台区", + "230506": "宝山区", + "230521": "集贤县", + "230522": "友谊县", + "230523": "宝清县", + "230524": "饶河县", + "230602": "萨尔图区", + "230603": "龙凤区", + "230604": "让胡路区", + "230605": "红岗区", + "230606": "大同区", + "230621": "肇州县", + "230622": "肇源县", + "230623": "林甸县", + "230624": "杜尔伯特蒙古族自治县", + "230671": "大庆高新技术产业开发区", + "230717": "伊美区", + "230718": "乌翠区", + "230719": "友好区", + "230722": "嘉荫县", + "230723": "汤旺县", + "230724": "丰林县", + "230725": "大箐山县", + "230726": "南岔县", + "230751": "金林区", + "230781": "铁力市", + "230803": "向阳区", + "230804": "前进区", + "230805": "东风区", + "230811": "郊区", + "230822": "桦南县", + "230826": "桦川县", + "230828": "汤原县", + "230881": "同江市", + "230882": "富锦市", + "230883": "抚远市", + "230902": "新兴区", + "230903": "桃山区", + "230904": "茄子河区", + "230921": "勃利县", + "231002": "东安区", + "231003": "阳明区", + "231004": "爱民区", + "231005": "西安区", + "231025": "林口县", + "231081": "绥芬河市", + "231083": "海林市", + "231084": "宁安市", + "231085": "穆棱市", + "231086": "东宁市", + "231102": "爱辉区", + "231123": "逊克县", + "231124": "孙吴县", + "231181": "北安市", + "231182": "五大连池市", + "231183": "嫩江市", + "231202": "北林区", + "231221": "望奎县", + "231222": "兰西县", + "231223": "青冈县", + "231224": "庆安县", + "231225": "明水县", + "231226": "绥棱县", + "231281": "安达市", + "231282": "肇东市", + "231283": "海伦市", + "232701": "漠河市", + "232721": "呼玛县", + "232722": "塔河县", + "232761": "加格达奇区", + "232762": "松岭区", + "232763": "新林区", + "232764": "呼中区", + "310101": "黄浦区", + "310104": "徐汇区", + "310105": "长宁区", + "310106": "静安区", + "310107": "普陀区", + "310109": "虹口区", + "310110": "杨浦区", + "310112": "闵行区", + "310113": "宝山区", + "310114": "嘉定区", + "310115": "浦东新区", + "310116": "金山区", + "310117": "松江区", + "310118": "青浦区", + "310120": "奉贤区", + "310151": "崇明区", + "320102": "玄武区", + "320104": "秦淮区", + "320105": "建邺区", + "320106": "鼓楼区", + "320111": "浦口区", + "320112": "江北新区", + "320113": "栖霞区", + "320114": "雨花台区", + "320115": "江宁区", + "320116": "六合区", + "320117": "溧水区", + "320118": "高淳区", + "320205": "锡山区", + "320206": "惠山区", + "320211": "滨湖区", + "320213": "梁溪区", + "320214": "新吴区", + "320281": "江阴市", + "320282": "宜兴市", + "320302": "鼓楼区", + "320303": "云龙区", + "320305": "贾汪区", + "320311": "泉山区", + "320312": "铜山区", + "320321": "丰县", + "320322": "沛县", + "320324": "睢宁县", + "320371": "徐州经济技术开发区", + "320381": "新沂市", + "320382": "邳州市", + "320391": "工业园区", + "320402": "天宁区", + "320404": "钟楼区", + "320411": "新北区", + "320412": "武进区", + "320413": "金坛区", + "320481": "溧阳市", + "320505": "虎丘区", + "320506": "吴中区", + "320507": "相城区", + "320508": "姑苏区", + "320509": "吴江区", + "320571": "苏州工业园区", + "320581": "常熟市", + "320582": "张家港市", + "320583": "昆山市", + "320585": "太仓市", + "320590": "工业园区", + "320591": "高新区", + "320611": "港闸区", + "320612": "通州区", + "320613": "崇川区", + "320614": "海门区", + "320623": "如东县", + "320681": "启东市", + "320682": "如皋市", + "320685": "海安市", + "320691": "高新区", + "320703": "连云区", + "320706": "海州区", + "320707": "赣榆区", + "320722": "东海县", + "320723": "灌云县", + "320724": "灌南县", + "320771": "连云港经济技术开发区", + "320803": "淮安区", + "320804": "淮阴区", + "320812": "清江浦区", + "320813": "洪泽区", + "320826": "涟水县", + "320830": "盱眙县", + "320831": "金湖县", + "320871": "淮安经济技术开发区", + "320890": "经济开发区", + "320902": "亭湖区", + "320903": "盐都区", + "320904": "大丰区", + "320921": "响水县", + "320922": "滨海县", + "320923": "阜宁县", + "320924": "射阳县", + "320925": "建湖县", + "320971": "盐城经济技术开发区", + "320981": "东台市", + "321002": "广陵区", + "321003": "邗江区", + "321012": "江都区", + "321023": "宝应县", + "321071": "扬州经济技术开发区", + "321081": "仪征市", + "321084": "高邮市", + "321090": "经济开发区", + "321102": "京口区", + "321111": "润州区", + "321112": "丹徒区", + "321150": "镇江新区", + "321181": "丹阳市", + "321182": "扬中市", + "321183": "句容市", + "321202": "海陵区", + "321203": "高港区", + "321204": "姜堰区", + "321271": "泰州医药高新技术产业开发区", + "321281": "兴化市", + "321282": "靖江市", + "321283": "泰兴市", + "321302": "宿城区", + "321311": "宿豫区", + "321322": "沭阳县", + "321323": "泗阳县", + "321324": "泗洪县", + "321371": "宿迁经济技术开发区", + "330102": "上城区", + "330105": "拱墅区", + "330106": "西湖区", + "330108": "滨江区", + "330109": "萧山区", + "330110": "余杭区", + "330111": "富阳区", + "330112": "临安区", + "330113": "临平区", + "330114": "钱塘区", + "330122": "桐庐县", + "330127": "淳安县", + "330182": "建德市", + "330203": "海曙区", + "330205": "江北区", + "330206": "北仑区", + "330211": "镇海区", + "330212": "鄞州区", + "330213": "奉化区", + "330225": "象山县", + "330226": "宁海县", + "330281": "余姚市", + "330282": "慈溪市", + "330302": "鹿城区", + "330303": "龙湾区", + "330304": "瓯海区", + "330305": "洞头区", + "330324": "永嘉县", + "330326": "平阳县", + "330327": "苍南县", + "330328": "文成县", + "330329": "泰顺县", + "330381": "瑞安市", + "330382": "乐清市", + "330383": "龙港市", + "330402": "南湖区", + "330411": "秀洲区", + "330421": "嘉善县", + "330424": "海盐县", + "330481": "海宁市", + "330482": "平湖市", + "330483": "桐乡市", + "330502": "吴兴区", + "330503": "南浔区", + "330521": "德清县", + "330522": "长兴县", + "330523": "安吉县", + "330602": "越城区", + "330603": "柯桥区", + "330604": "上虞区", + "330624": "新昌县", + "330681": "诸暨市", + "330683": "嵊州市", + "330702": "婺城区", + "330703": "金东区", + "330723": "武义县", + "330726": "浦江县", + "330727": "磐安县", + "330781": "兰溪市", + "330782": "义乌市", + "330783": "东阳市", + "330784": "永康市", + "330802": "柯城区", + "330803": "衢江区", + "330822": "常山县", + "330824": "开化县", + "330825": "龙游县", + "330881": "江山市", + "330902": "定海区", + "330903": "普陀区", + "330921": "岱山县", + "330922": "嵊泗县", + "331002": "椒江区", + "331003": "黄岩区", + "331004": "路桥区", + "331022": "三门县", + "331023": "天台县", + "331024": "仙居县", + "331081": "温岭市", + "331082": "临海市", + "331083": "玉环市", + "331102": "莲都区", + "331121": "青田县", + "331122": "缙云县", + "331123": "遂昌县", + "331124": "松阳县", + "331125": "云和县", + "331126": "庆元县", + "331127": "景宁畲族自治县", + "331181": "龙泉市", + "340102": "瑶海区", + "340103": "庐阳区", + "340104": "蜀山区", + "340111": "包河区", + "340121": "长丰县", + "340122": "肥东县", + "340123": "肥西县", + "340124": "庐江县", + "340171": "合肥高新技术产业开发区", + "340172": "合肥经济技术开发区", + "340173": "合肥新站高新技术产业开发区", + "340181": "巢湖市", + "340190": "高新技术开发区", + "340191": "经济技术开发区", + "340202": "镜湖区", + "340207": "鸠江区", + "340209": "弋江区", + "340210": "湾沚区", + "340212": "繁昌区", + "340223": "南陵县", + "340281": "无为市", + "340302": "龙子湖区", + "340303": "蚌山区", + "340304": "禹会区", + "340311": "淮上区", + "340321": "怀远县", + "340322": "五河县", + "340323": "固镇县", + "340371": "蚌埠市高新技术开发区", + "340372": "蚌埠市经济开发区", + "340402": "大通区", + "340403": "田家庵区", + "340404": "谢家集区", + "340405": "八公山区", + "340406": "潘集区", + "340421": "凤台县", + "340422": "寿县", + "340503": "花山区", + "340504": "雨山区", + "340506": "博望区", + "340521": "当涂县", + "340522": "含山县", + "340523": "和县", + "340602": "杜集区", + "340603": "相山区", + "340604": "烈山区", + "340621": "濉溪县", + "340705": "铜官区", + "340706": "义安区", + "340711": "郊区", + "340722": "枞阳县", + "340802": "迎江区", + "340803": "大观区", + "340811": "宜秀区", + "340822": "怀宁县", + "340825": "太湖县", + "340826": "宿松县", + "340827": "望江县", + "340828": "岳西县", + "340881": "桐城市", + "340882": "潜山市", + "341002": "屯溪区", + "341003": "黄山区", + "341004": "徽州区", + "341021": "歙县", + "341022": "休宁县", + "341023": "黟县", + "341024": "祁门县", + "341102": "琅琊区", + "341103": "南谯区", + "341122": "来安县", + "341124": "全椒县", + "341125": "定远县", + "341126": "凤阳县", + "341181": "天长市", + "341182": "明光市", + "341202": "颍州区", + "341203": "颍东区", + "341204": "颍泉区", + "341221": "临泉县", + "341222": "太和县", + "341225": "阜南县", + "341226": "颍上县", + "341271": "阜阳合肥现代产业园区", + "341282": "界首市", + "341302": "埇桥区", + "341321": "砀山县", + "341322": "萧县", + "341323": "灵璧县", + "341324": "泗县", + "341371": "宿州马鞍山现代产业园区", + "341372": "宿州经济技术开发区", + "341390": "经济开发区", + "341502": "金安区", + "341503": "裕安区", + "341504": "叶集区", + "341522": "霍邱县", + "341523": "舒城县", + "341524": "金寨县", + "341525": "霍山县", + "341602": "谯城区", + "341621": "涡阳县", + "341622": "蒙城县", + "341623": "利辛县", + "341702": "贵池区", + "341721": "东至县", + "341722": "石台县", + "341723": "青阳县", + "341802": "宣州区", + "341821": "郎溪县", + "341823": "泾县", + "341824": "绩溪县", + "341825": "旌德县", + "341871": "宣城市经济开发区", + "341881": "宁国市", + "341882": "广德市", + "350102": "鼓楼区", + "350103": "台江区", + "350104": "仓山区", + "350105": "马尾区", + "350111": "晋安区", + "350112": "长乐区", + "350121": "闽侯县", + "350122": "连江县", + "350123": "罗源县", + "350124": "闽清县", + "350125": "永泰县", + "350128": "平潭县", + "350181": "福清市", + "350203": "思明区", + "350205": "海沧区", + "350206": "湖里区", + "350211": "集美区", + "350212": "同安区", + "350213": "翔安区", + "350302": "城厢区", + "350303": "涵江区", + "350304": "荔城区", + "350305": "秀屿区", + "350322": "仙游县", + "350402": "梅列区", + "350404": "三元区", + "350405": "沙县区", + "350421": "明溪县", + "350423": "清流县", + "350424": "宁化县", + "350425": "大田县", + "350426": "尤溪县", + "350428": "将乐县", + "350429": "泰宁县", + "350430": "建宁县", + "350481": "永安市", + "350502": "鲤城区", + "350503": "丰泽区", + "350504": "洛江区", + "350505": "泉港区", + "350521": "惠安县", + "350524": "安溪县", + "350525": "永春县", + "350526": "德化县", + "350527": "金门县", + "350581": "石狮市", + "350582": "晋江市", + "350583": "南安市", + "350602": "芗城区", + "350603": "龙文区", + "350604": "龙海区", + "350605": "长泰区", + "350622": "云霄县", + "350623": "漳浦县", + "350624": "诏安县", + "350626": "东山县", + "350627": "南靖县", + "350628": "平和县", + "350629": "华安县", + "350702": "延平区", + "350703": "建阳区", + "350721": "顺昌县", + "350722": "浦城县", + "350723": "光泽县", + "350724": "松溪县", + "350725": "政和县", + "350781": "邵武市", + "350782": "武夷山市", + "350783": "建瓯市", + "350802": "新罗区", + "350803": "永定区", + "350821": "长汀县", + "350823": "上杭县", + "350824": "武平县", + "350825": "连城县", + "350881": "漳平市", + "350902": "蕉城区", + "350921": "霞浦县", + "350922": "古田县", + "350923": "屏南县", + "350924": "寿宁县", + "350925": "周宁县", + "350926": "柘荣县", + "350981": "福安市", + "350982": "福鼎市", + "360102": "东湖区", + "360103": "西湖区", + "360104": "青云谱区", + "360111": "青山湖区", + "360112": "新建区", + "360113": "红谷滩区", + "360121": "南昌县", + "360123": "安义县", + "360124": "进贤县", + "360190": "经济技术开发区", + "360192": "高新区", + "360202": "昌江区", + "360203": "珠山区", + "360222": "浮梁县", + "360281": "乐平市", + "360302": "安源区", + "360313": "湘东区", + "360321": "莲花县", + "360322": "上栗县", + "360323": "芦溪县", + "360402": "濂溪区", + "360403": "浔阳区", + "360404": "柴桑区", + "360423": "武宁县", + "360424": "修水县", + "360425": "永修县", + "360426": "德安县", + "360428": "都昌县", + "360429": "湖口县", + "360430": "彭泽县", + "360481": "瑞昌市", + "360482": "共青城市", + "360483": "庐山市", + "360490": "经济技术开发区", + "360502": "渝水区", + "360521": "分宜县", + "360602": "月湖区", + "360603": "余江区", + "360681": "贵溪市", + "360702": "章贡区", + "360703": "南康区", + "360704": "赣县区", + "360722": "信丰县", + "360723": "大余县", + "360724": "上犹县", + "360725": "崇义县", + "360726": "安远县", + "360728": "定南县", + "360729": "全南县", + "360730": "宁都县", + "360731": "于都县", + "360732": "兴国县", + "360733": "会昌县", + "360734": "寻乌县", + "360735": "石城县", + "360781": "瑞金市", + "360783": "龙南市", + "360802": "吉州区", + "360803": "青原区", + "360821": "吉安县", + "360822": "吉水县", + "360823": "峡江县", + "360824": "新干县", + "360825": "永丰县", + "360826": "泰和县", + "360827": "遂川县", + "360828": "万安县", + "360829": "安福县", + "360830": "永新县", + "360881": "井冈山市", + "360902": "袁州区", + "360921": "奉新县", + "360922": "万载县", + "360923": "上高县", + "360924": "宜丰县", + "360925": "靖安县", + "360926": "铜鼓县", + "360981": "丰城市", + "360982": "樟树市", + "360983": "高安市", + "361002": "临川区", + "361003": "东乡区", + "361021": "南城县", + "361022": "黎川县", + "361023": "南丰县", + "361024": "崇仁县", + "361025": "乐安县", + "361026": "宜黄县", + "361027": "金溪县", + "361028": "资溪县", + "361030": "广昌县", + "361102": "信州区", + "361103": "广丰区", + "361104": "广信区", + "361123": "玉山县", + "361124": "铅山县", + "361125": "横峰县", + "361126": "弋阳县", + "361127": "余干县", + "361128": "鄱阳县", + "361129": "万年县", + "361130": "婺源县", + "361181": "德兴市", + "370102": "历下区", + "370103": "市中区", + "370104": "槐荫区", + "370105": "天桥区", + "370112": "历城区", + "370113": "长清区", + "370114": "章丘区", + "370115": "济阳区", + "370116": "莱芜区", + "370117": "钢城区", + "370124": "平阴县", + "370126": "商河县", + "370171": "济南高新技术产业开发区", + "370190": "高新区", + "370202": "市南区", + "370203": "市北区", + "370211": "黄岛区", + "370212": "崂山区", + "370213": "李沧区", + "370214": "城阳区", + "370215": "即墨区", + "370271": "青岛高新技术产业开发区", + "370281": "胶州市", + "370283": "平度市", + "370285": "莱西市", + "370290": "开发区", + "370302": "淄川区", + "370303": "张店区", + "370304": "博山区", + "370305": "临淄区", + "370306": "周村区", + "370321": "桓台县", + "370322": "高青县", + "370323": "沂源县", + "370402": "市中区", + "370403": "薛城区", + "370404": "峄城区", + "370405": "台儿庄区", + "370406": "山亭区", + "370481": "滕州市", + "370502": "东营区", + "370503": "河口区", + "370505": "垦利区", + "370522": "利津县", + "370523": "广饶县", + "370571": "东营经济技术开发区", + "370572": "东营港经济开发区", + "370602": "芝罘区", + "370611": "福山区", + "370612": "牟平区", + "370613": "莱山区", + "370614": "蓬莱区", + "370634": "长岛县", + "370671": "烟台高新技术产业开发区", + "370672": "烟台经济技术开发区", + "370681": "龙口市", + "370682": "莱阳市", + "370683": "莱州市", + "370685": "招远市", + "370686": "栖霞市", + "370687": "海阳市", + "370690": "开发区", + "370702": "潍城区", + "370703": "寒亭区", + "370704": "坊子区", + "370705": "奎文区", + "370724": "临朐县", + "370725": "昌乐县", + "370772": "潍坊滨海经济技术开发区", + "370781": "青州市", + "370782": "诸城市", + "370783": "寿光市", + "370784": "安丘市", + "370785": "高密市", + "370786": "昌邑市", + "370790": "开发区", + "370791": "高新区", + "370811": "任城区", + "370812": "兖州区", + "370826": "微山县", + "370827": "鱼台县", + "370828": "金乡县", + "370829": "嘉祥县", + "370830": "汶上县", + "370831": "泗水县", + "370832": "梁山县", + "370871": "济宁高新技术产业开发区", + "370881": "曲阜市", + "370883": "邹城市", + "370890": "高新区", + "370902": "泰山区", + "370911": "岱岳区", + "370921": "宁阳县", + "370923": "东平县", + "370982": "新泰市", + "370983": "肥城市", + "371002": "环翠区", + "371003": "文登区", + "371071": "威海火炬高技术产业开发区", + "371072": "威海经济技术开发区", + "371082": "荣成市", + "371083": "乳山市", + "371091": "经济技术开发区", + "371102": "东港区", + "371103": "岚山区", + "371121": "五莲县", + "371122": "莒县", + "371171": "日照经济技术开发区", + "371302": "兰山区", + "371311": "罗庄区", + "371312": "河东区", + "371321": "沂南县", + "371322": "郯城县", + "371323": "沂水县", + "371324": "兰陵县", + "371325": "费县", + "371326": "平邑县", + "371327": "莒南县", + "371328": "蒙阴县", + "371329": "临沭县", + "371371": "临沂高新技术产业开发区", + "371402": "德城区", + "371403": "陵城区", + "371422": "宁津县", + "371423": "庆云县", + "371424": "临邑县", + "371425": "齐河县", + "371426": "平原县", + "371427": "夏津县", + "371428": "武城县", + "371472": "德州运河经济开发区", + "371481": "乐陵市", + "371482": "禹城市", + "371502": "东昌府区", + "371503": "茌平区", + "371521": "阳谷县", + "371522": "莘县", + "371524": "东阿县", + "371525": "冠县", + "371526": "高唐县", + "371581": "临清市", + "371602": "滨城区", + "371603": "沾化区", + "371621": "惠民县", + "371622": "阳信县", + "371623": "无棣县", + "371625": "博兴县", + "371681": "邹平市", + "371702": "牡丹区", + "371703": "定陶区", + "371721": "曹县", + "371722": "单县", + "371723": "成武县", + "371724": "巨野县", + "371725": "郓城县", + "371726": "鄄城县", + "371728": "东明县", + "371771": "菏泽经济技术开发区", + "371772": "菏泽高新技术开发区", + "410102": "中原区", + "410103": "二七区", + "410104": "管城回族区", + "410105": "金水区", + "410106": "上街区", + "410108": "惠济区", + "410122": "中牟县", + "410171": "郑州经济技术开发区", + "410172": "郑州高新技术产业开发区", + "410173": "郑州航空港经济综合实验区", + "410181": "巩义市", + "410182": "荥阳市", + "410183": "新密市", + "410184": "新郑市", + "410185": "登封市", + "410190": "高新技术开发区", + "410191": "经济技术开发区", + "410202": "龙亭区", + "410203": "顺河回族区", + "410204": "鼓楼区", + "410205": "禹王台区", + "410212": "祥符区", + "410221": "杞县", + "410222": "通许县", + "410223": "尉氏县", + "410225": "兰考县", + "410302": "老城区", + "410303": "西工区", + "410304": "瀍河回族区", + "410305": "涧西区", + "410307": "偃师区", + "410308": "孟津区", + "410311": "洛龙区", + "410323": "新安县", + "410324": "栾川县", + "410325": "嵩县", + "410326": "汝阳县", + "410327": "宜阳县", + "410328": "洛宁县", + "410329": "伊川县", + "410402": "新华区", + "410403": "卫东区", + "410404": "石龙区", + "410411": "湛河区", + "410421": "宝丰县", + "410422": "叶县", + "410423": "鲁山县", + "410425": "郏县", + "410471": "平顶山高新技术产业开发区", + "410481": "舞钢市", + "410482": "汝州市", + "410502": "文峰区", + "410503": "北关区", + "410505": "殷都区", + "410506": "龙安区", + "410522": "安阳县", + "410523": "汤阴县", + "410526": "滑县", + "410527": "内黄县", + "410581": "林州市", + "410590": "开发区", + "410602": "鹤山区", + "410603": "山城区", + "410611": "淇滨区", + "410621": "浚县", + "410622": "淇县", + "410702": "红旗区", + "410703": "卫滨区", + "410704": "凤泉区", + "410711": "牧野区", + "410721": "新乡县", + "410724": "获嘉县", + "410725": "原阳县", + "410726": "延津县", + "410727": "封丘县", + "410771": "新乡高新技术产业开发区", + "410772": "新乡经济技术开发区", + "410781": "卫辉市", + "410782": "辉县市", + "410783": "长垣市", + "410802": "解放区", + "410803": "中站区", + "410804": "马村区", + "410811": "山阳区", + "410821": "修武县", + "410822": "博爱县", + "410823": "武陟县", + "410825": "温县", + "410871": "焦作城乡一体化示范区", + "410882": "沁阳市", + "410883": "孟州市", + "410902": "华龙区", + "410922": "清丰县", + "410923": "南乐县", + "410926": "范县", + "410927": "台前县", + "410928": "濮阳县", + "410971": "河南濮阳工业园区", + "411002": "魏都区", + "411003": "建安区", + "411024": "鄢陵县", + "411025": "襄城县", + "411071": "许昌经济技术开发区", + "411081": "禹州市", + "411082": "长葛市", + "411102": "源汇区", + "411103": "郾城区", + "411104": "召陵区", + "411121": "舞阳县", + "411122": "临颍县", + "411171": "漯河经济技术开发区", + "411202": "湖滨区", + "411203": "陕州区", + "411221": "渑池县", + "411224": "卢氏县", + "411271": "河南三门峡经济开发区", + "411281": "义马市", + "411282": "灵宝市", + "411302": "宛城区", + "411303": "卧龙区", + "411321": "南召县", + "411322": "方城县", + "411323": "西峡县", + "411324": "镇平县", + "411325": "内乡县", + "411326": "淅川县", + "411327": "社旗县", + "411328": "唐河县", + "411329": "新野县", + "411330": "桐柏县", + "411372": "南阳市城乡一体化示范区", + "411381": "邓州市", + "411402": "梁园区", + "411403": "睢阳区", + "411421": "民权县", + "411422": "睢县", + "411423": "宁陵县", + "411424": "柘城县", + "411425": "虞城县", + "411426": "夏邑县", + "411481": "永城市", + "411502": "浉河区", + "411503": "平桥区", + "411521": "罗山县", + "411522": "光山县", + "411523": "新县", + "411524": "商城县", + "411525": "固始县", + "411526": "潢川县", + "411527": "淮滨县", + "411528": "息县", + "411602": "川汇区", + "411603": "淮阳区", + "411621": "扶沟县", + "411622": "西华县", + "411623": "商水县", + "411624": "沈丘县", + "411625": "郸城县", + "411627": "太康县", + "411628": "鹿邑县", + "411671": "河南周口经济开发区", + "411681": "项城市", + "411690": "经济开发区", + "411702": "驿城区", + "411721": "西平县", + "411722": "上蔡县", + "411723": "平舆县", + "411724": "正阳县", + "411725": "确山县", + "411726": "泌阳县", + "411727": "汝南县", + "411728": "遂平县", + "411729": "新蔡县", + "419001": "济源市", + "420102": "江岸区", + "420103": "江汉区", + "420104": "硚口区", + "420105": "汉阳区", + "420106": "武昌区", + "420107": "青山区", + "420111": "洪山区", + "420112": "东西湖区", + "420113": "汉南区", + "420114": "蔡甸区", + "420115": "江夏区", + "420116": "黄陂区", + "420117": "新洲区", + "420202": "黄石港区", + "420203": "西塞山区", + "420204": "下陆区", + "420205": "铁山区", + "420222": "阳新县", + "420281": "大冶市", + "420302": "茅箭区", + "420303": "张湾区", + "420304": "郧阳区", + "420322": "郧西县", + "420323": "竹山县", + "420324": "竹溪县", + "420325": "房县", + "420381": "丹江口市", + "420502": "西陵区", + "420503": "伍家岗区", + "420504": "点军区", + "420505": "猇亭区", + "420506": "夷陵区", + "420525": "远安县", + "420526": "兴山县", + "420527": "秭归县", + "420528": "长阳土家族自治县", + "420529": "五峰土家族自治县", + "420581": "宜都市", + "420582": "当阳市", + "420583": "枝江市", + "420590": "经济开发区", + "420602": "襄城区", + "420606": "樊城区", + "420607": "襄州区", + "420624": "南漳县", + "420625": "谷城县", + "420626": "保康县", + "420682": "老河口市", + "420683": "枣阳市", + "420684": "宜城市", + "420702": "梁子湖区", + "420703": "华容区", + "420704": "鄂城区", + "420802": "东宝区", + "420804": "掇刀区", + "420822": "沙洋县", + "420881": "钟祥市", + "420882": "京山市", + "420902": "孝南区", + "420921": "孝昌县", + "420922": "大悟县", + "420923": "云梦县", + "420981": "应城市", + "420982": "安陆市", + "420984": "汉川市", + "421002": "沙市区", + "421003": "荆州区", + "421022": "公安县", + "421024": "江陵县", + "421081": "石首市", + "421083": "洪湖市", + "421087": "松滋市", + "421088": "监利市", + "421102": "黄州区", + "421121": "团风县", + "421122": "红安县", + "421123": "罗田县", + "421124": "英山县", + "421125": "浠水县", + "421126": "蕲春县", + "421127": "黄梅县", + "421171": "龙感湖管理区", + "421181": "麻城市", + "421182": "武穴市", + "421202": "咸安区", + "421221": "嘉鱼县", + "421222": "通城县", + "421223": "崇阳县", + "421224": "通山县", + "421281": "赤壁市", + "421303": "曾都区", + "421321": "随县", + "421381": "广水市", + "422801": "恩施市", + "422802": "利川市", + "422822": "建始县", + "422823": "巴东县", + "422825": "宣恩县", + "422826": "咸丰县", + "422827": "来凤县", + "422828": "鹤峰县", + "429004": "仙桃市", + "429005": "潜江市", + "429006": "天门市", + "429021": "神农架林区", + "430102": "芙蓉区", + "430103": "天心区", + "430104": "岳麓区", + "430105": "开福区", + "430111": "雨花区", + "430112": "望城区", + "430121": "长沙县", + "430181": "浏阳市", + "430182": "宁乡市", + "430202": "荷塘区", + "430203": "芦淞区", + "430204": "石峰区", + "430211": "天元区", + "430212": "渌口区", + "430223": "攸县", + "430224": "茶陵县", + "430225": "炎陵县", + "430271": "云龙示范区", + "430281": "醴陵市", + "430302": "雨湖区", + "430304": "岳塘区", + "430321": "湘潭县", + "430373": "湘潭九华示范区", + "430381": "湘乡市", + "430382": "韶山市", + "430405": "珠晖区", + "430406": "雁峰区", + "430407": "石鼓区", + "430408": "蒸湘区", + "430412": "南岳区", + "430421": "衡阳县", + "430422": "衡南县", + "430423": "衡山县", + "430424": "衡东县", + "430426": "祁东县", + "430481": "耒阳市", + "430482": "常宁市", + "430502": "双清区", + "430503": "大祥区", + "430511": "北塔区", + "430522": "新邵县", + "430523": "邵阳县", + "430524": "隆回县", + "430525": "洞口县", + "430527": "绥宁县", + "430528": "新宁县", + "430529": "城步苗族自治县", + "430581": "武冈市", + "430582": "邵东市", + "430602": "岳阳楼区", + "430603": "云溪区", + "430611": "君山区", + "430621": "岳阳县", + "430623": "华容县", + "430624": "湘阴县", + "430626": "平江县", + "430681": "汨罗市", + "430682": "临湘市", + "430702": "武陵区", + "430703": "鼎城区", + "430721": "安乡县", + "430722": "汉寿县", + "430723": "澧县", + "430724": "临澧县", + "430725": "桃源县", + "430726": "石门县", + "430781": "津市市", + "430802": "永定区", + "430811": "武陵源区", + "430821": "慈利县", + "430822": "桑植县", + "430902": "资阳区", + "430903": "赫山区", + "430921": "南县", + "430922": "桃江县", + "430923": "安化县", + "430971": "益阳市大通湖管理区", + "430981": "沅江市", + "431002": "北湖区", + "431003": "苏仙区", + "431021": "桂阳县", + "431022": "宜章县", + "431023": "永兴县", + "431024": "嘉禾县", + "431025": "临武县", + "431026": "汝城县", + "431027": "桂东县", + "431028": "安仁县", + "431081": "资兴市", + "431102": "零陵区", + "431103": "冷水滩区", + "431122": "东安县", + "431123": "双牌县", + "431124": "道县", + "431125": "江永县", + "431126": "宁远县", + "431127": "蓝山县", + "431128": "新田县", + "431129": "江华瑶族自治县", + "431181": "祁阳市", + "431202": "鹤城区", + "431221": "中方县", + "431222": "沅陵县", + "431223": "辰溪县", + "431224": "溆浦县", + "431225": "会同县", + "431226": "麻阳苗族自治县", + "431227": "新晃侗族自治县", + "431228": "芷江侗族自治县", + "431229": "靖州苗族侗族自治县", + "431230": "通道侗族自治县", + "431271": "怀化市洪江管理区", + "431281": "洪江市", + "431302": "娄星区", + "431321": "双峰县", + "431322": "新化县", + "431381": "冷水江市", + "431382": "涟源市", + "433101": "吉首市", + "433122": "泸溪县", + "433123": "凤凰县", + "433124": "花垣县", + "433125": "保靖县", + "433126": "古丈县", + "433127": "永顺县", + "433130": "龙山县", + "440103": "荔湾区", + "440104": "越秀区", + "440105": "海珠区", + "440106": "天河区", + "440111": "白云区", + "440112": "黄埔区", + "440113": "番禺区", + "440114": "花都区", + "440115": "南沙区", + "440117": "从化区", + "440118": "增城区", + "440203": "武江区", + "440204": "浈江区", + "440205": "曲江区", + "440222": "始兴县", + "440224": "仁化县", + "440229": "翁源县", + "440232": "乳源瑶族自治县", + "440233": "新丰县", + "440281": "乐昌市", + "440282": "南雄市", + "440303": "罗湖区", + "440304": "福田区", + "440305": "南山区", + "440306": "宝安区", + "440307": "龙岗区", + "440308": "盐田区", + "440309": "龙华区", + "440310": "坪山区", + "440311": "光明区", + "440402": "香洲区", + "440403": "斗门区", + "440404": "金湾区", + "440507": "龙湖区", + "440511": "金平区", + "440512": "濠江区", + "440513": "潮阳区", + "440514": "潮南区", + "440515": "澄海区", + "440523": "南澳县", + "440604": "禅城区", + "440605": "南海区", + "440606": "顺德区", + "440607": "三水区", + "440608": "高明区", + "440703": "蓬江区", + "440704": "江海区", + "440705": "新会区", + "440781": "台山市", + "440783": "开平市", + "440784": "鹤山市", + "440785": "恩平市", + "440802": "赤坎区", + "440803": "霞山区", + "440804": "坡头区", + "440811": "麻章区", + "440823": "遂溪县", + "440825": "徐闻县", + "440881": "廉江市", + "440882": "雷州市", + "440883": "吴川市", + "440890": "经济技术开发区", + "440902": "茂南区", + "440904": "电白区", + "440981": "高州市", + "440982": "化州市", + "440983": "信宜市", + "441202": "端州区", + "441203": "鼎湖区", + "441204": "高要区", + "441223": "广宁县", + "441224": "怀集县", + "441225": "封开县", + "441226": "德庆县", + "441284": "四会市", + "441302": "惠城区", + "441303": "惠阳区", + "441322": "博罗县", + "441323": "惠东县", + "441324": "龙门县", + "441402": "梅江区", + "441403": "梅县区", + "441422": "大埔县", + "441423": "丰顺县", + "441424": "五华县", + "441426": "平远县", + "441427": "蕉岭县", + "441481": "兴宁市", + "441502": "城区", + "441521": "海丰县", + "441523": "陆河县", + "441581": "陆丰市", + "441602": "源城区", + "441621": "紫金县", + "441622": "龙川县", + "441623": "连平县", + "441624": "和平县", + "441625": "东源县", + "441702": "江城区", + "441704": "阳东区", + "441721": "阳西县", + "441781": "阳春市", + "441802": "清城区", + "441803": "清新区", + "441821": "佛冈县", + "441823": "阳山县", + "441825": "连山壮族瑶族自治县", + "441826": "连南瑶族自治县", + "441881": "英德市", + "441882": "连州市", + "441901": "中堂镇", + "441903": "南城街道", + "441904": "长安镇", + "441905": "东坑镇", + "441906": "樟木头镇", + "441907": "莞城街道", + "441908": "石龙镇", + "441909": "桥头镇", + "441910": "万江街道", + "441911": "麻涌镇", + "441912": "虎门镇", + "441913": "谢岗镇", + "441914": "石碣镇", + "441915": "茶山镇", + "441916": "东城街道", + "441917": "洪梅镇", + "441918": "道滘镇", + "441919": "高埗镇", + "441920": "企石镇", + "441921": "凤岗镇", + "441922": "大岭山镇", + "441923": "松山湖", + "441924": "清溪镇", + "441925": "望牛墩镇", + "441926": "厚街镇", + "441927": "常平镇", + "441928": "寮步镇", + "441929": "石排镇", + "441930": "横沥镇", + "441931": "塘厦镇", + "441932": "黄江镇", + "441933": "大朗镇", + "441934": "东莞港", + "441935": "东莞生态园", + "441990": "沙田镇", + "442001": "南头镇", + "442002": "神湾镇", + "442003": "东凤镇", + "442004": "五桂山街道", + "442005": "黄圃镇", + "442006": "小榄镇", + "442007": "石岐街道", + "442008": "横栏镇", + "442009": "三角镇", + "442010": "三乡镇", + "442011": "港口镇", + "442012": "沙溪镇", + "442013": "板芙镇", + "442015": "东升镇", + "442016": "阜沙镇", + "442017": "民众镇", + "442018": "东区街道", + "442019": "火炬开发区街道办事处", + "442020": "西区街道", + "442021": "南区街道", + "442022": "古镇镇", + "442023": "坦洲镇", + "442024": "大涌镇", + "442025": "南朗镇", + "445102": "湘桥区", + "445103": "潮安区", + "445122": "饶平县", + "445202": "榕城区", + "445203": "揭东区", + "445222": "揭西县", + "445224": "惠来县", + "445281": "普宁市", + "445302": "云城区", + "445303": "云安区", + "445321": "新兴县", + "445322": "郁南县", + "445381": "罗定市", + "450102": "兴宁区", + "450103": "青秀区", + "450105": "江南区", + "450107": "西乡塘区", + "450108": "良庆区", + "450109": "邕宁区", + "450110": "武鸣区", + "450123": "隆安县", + "450124": "马山县", + "450125": "上林县", + "450126": "宾阳县", + "450181": "横州市", + "450202": "城中区", + "450203": "鱼峰区", + "450204": "柳南区", + "450205": "柳北区", + "450206": "柳江区", + "450222": "柳城县", + "450223": "鹿寨县", + "450224": "融安县", + "450225": "融水苗族自治县", + "450226": "三江侗族自治县", + "450302": "秀峰区", + "450303": "叠彩区", + "450304": "象山区", + "450305": "七星区", + "450311": "雁山区", + "450312": "临桂区", + "450321": "阳朔县", + "450323": "灵川县", + "450324": "全州县", + "450325": "兴安县", + "450326": "永福县", + "450327": "灌阳县", + "450328": "龙胜各族自治县", + "450329": "资源县", + "450330": "平乐县", + "450332": "恭城瑶族自治县", + "450381": "荔浦市", + "450403": "万秀区", + "450405": "长洲区", + "450406": "龙圩区", + "450421": "苍梧县", + "450422": "藤县", + "450423": "蒙山县", + "450481": "岑溪市", + "450502": "海城区", + "450503": "银海区", + "450512": "铁山港区", + "450521": "合浦县", + "450602": "港口区", + "450603": "防城区", + "450621": "上思县", + "450681": "东兴市", + "450702": "钦南区", + "450703": "钦北区", + "450721": "灵山县", + "450722": "浦北县", + "450802": "港北区", + "450803": "港南区", + "450804": "覃塘区", + "450821": "平南县", + "450881": "桂平市", + "450902": "玉州区", + "450903": "福绵区", + "450921": "容县", + "450922": "陆川县", + "450923": "博白县", + "450924": "兴业县", + "450981": "北流市", + "451002": "右江区", + "451003": "田阳区", + "451022": "田东县", + "451024": "德保县", + "451026": "那坡县", + "451027": "凌云县", + "451028": "乐业县", + "451029": "田林县", + "451030": "西林县", + "451031": "隆林各族自治县", + "451081": "靖西市", + "451082": "平果市", + "451102": "八步区", + "451103": "平桂区", + "451121": "昭平县", + "451122": "钟山县", + "451123": "富川瑶族自治县", + "451202": "金城江区", + "451203": "宜州区", + "451221": "南丹县", + "451222": "天峨县", + "451223": "凤山县", + "451224": "东兰县", + "451225": "罗城仫佬族自治县", + "451226": "环江毛南族自治县", + "451227": "巴马瑶族自治县", + "451228": "都安瑶族自治县", + "451229": "大化瑶族自治县", + "451302": "兴宾区", + "451321": "忻城县", + "451322": "象州县", + "451323": "武宣县", + "451324": "金秀瑶族自治县", + "451381": "合山市", + "451402": "江州区", + "451421": "扶绥县", + "451422": "宁明县", + "451423": "龙州县", + "451424": "大新县", + "451425": "天等县", + "451481": "凭祥市", + "460105": "秀英区", + "460106": "龙华区", + "460107": "琼山区", + "460108": "美兰区", + "460202": "海棠区", + "460203": "吉阳区", + "460204": "天涯区", + "460205": "崖州区", + "460321": "西沙区", + "460322": "南沙区", + "460401": "那大镇", + "460402": "和庆镇", + "460403": "南丰镇", + "460404": "大成镇", + "460405": "雅星镇", + "460406": "兰洋镇", + "460407": "光村镇", + "460408": "木棠镇", + "460409": "海头镇", + "460410": "峨蔓镇", + "460411": "王五镇", + "460412": "白马井镇", + "460413": "中和镇", + "460414": "排浦镇", + "460415": "东成镇", + "460416": "新州镇", + "460417": "洋浦经济开发区", + "460418": "华南热作学院", + "469001": "五指山市", + "469002": "琼海市", + "469005": "文昌市", + "469006": "万宁市", + "469007": "东方市", + "469021": "定安县", + "469022": "屯昌县", + "469023": "澄迈县", + "469024": "临高县", + "469025": "白沙黎族自治县", + "469026": "昌江黎族自治县", + "469027": "乐东黎族自治县", + "469028": "陵水黎族自治县", + "469029": "保亭黎族苗族自治县", + "469030": "琼中黎族苗族自治县", + "500101": "万州区", + "500102": "涪陵区", + "500103": "渝中区", + "500104": "大渡口区", + "500105": "江北区", + "500106": "沙坪坝区", + "500107": "九龙坡区", + "500108": "南岸区", + "500109": "北碚区", + "500110": "綦江区", + "500111": "大足区", + "500112": "渝北区", + "500113": "巴南区", + "500114": "黔江区", + "500115": "长寿区", + "500116": "江津区", + "500117": "合川区", + "500118": "永川区", + "500119": "南川区", + "500120": "璧山区", + "500151": "铜梁区", + "500152": "潼南区", + "500153": "荣昌区", + "500154": "开州区", + "500155": "梁平区", + "500156": "武隆区", + "500229": "城口县", + "500230": "丰都县", + "500231": "垫江县", + "500233": "忠县", + "500235": "云阳县", + "500236": "奉节县", + "500237": "巫山县", + "500238": "巫溪县", + "500240": "石柱土家族自治县", + "500241": "秀山土家族苗族自治县", + "500242": "酉阳土家族苗族自治县", + "500243": "彭水苗族土家族自治县", + "510104": "锦江区", + "510105": "青羊区", + "510106": "金牛区", + "510107": "武侯区", + "510108": "成华区", + "510112": "龙泉驿区", + "510113": "青白江区", + "510114": "新都区", + "510115": "温江区", + "510116": "双流区", + "510117": "郫都区", + "510118": "新津区", + "510121": "金堂县", + "510129": "大邑县", + "510131": "蒲江县", + "510181": "都江堰市", + "510182": "彭州市", + "510183": "邛崃市", + "510184": "崇州市", + "510185": "简阳市", + "510191": "高新区", + "510302": "自流井区", + "510303": "贡井区", + "510304": "大安区", + "510311": "沿滩区", + "510321": "荣县", + "510322": "富顺县", + "510402": "东区", + "510403": "西区", + "510411": "仁和区", + "510421": "米易县", + "510422": "盐边县", + "510502": "江阳区", + "510503": "纳溪区", + "510504": "龙马潭区", + "510521": "泸县", + "510522": "合江县", + "510524": "叙永县", + "510525": "古蔺县", + "510603": "旌阳区", + "510604": "罗江区", + "510623": "中江县", + "510681": "广汉市", + "510682": "什邡市", + "510683": "绵竹市", + "510703": "涪城区", + "510704": "游仙区", + "510705": "安州区", + "510722": "三台县", + "510723": "盐亭县", + "510725": "梓潼县", + "510726": "北川羌族自治县", + "510727": "平武县", + "510781": "江油市", + "510791": "高新区", + "510802": "利州区", + "510811": "昭化区", + "510812": "朝天区", + "510821": "旺苍县", + "510822": "青川县", + "510823": "剑阁县", + "510824": "苍溪县", + "510903": "船山区", + "510904": "安居区", + "510921": "蓬溪县", + "510923": "大英县", + "510981": "射洪市", + "511002": "市中区", + "511011": "东兴区", + "511024": "威远县", + "511025": "资中县", + "511083": "隆昌市", + "511102": "市中区", + "511111": "沙湾区", + "511112": "五通桥区", + "511113": "金口河区", + "511123": "犍为县", + "511124": "井研县", + "511126": "夹江县", + "511129": "沐川县", + "511132": "峨边彝族自治县", + "511133": "马边彝族自治县", + "511181": "峨眉山市", + "511302": "顺庆区", + "511303": "高坪区", + "511304": "嘉陵区", + "511321": "南部县", + "511322": "营山县", + "511323": "蓬安县", + "511324": "仪陇县", + "511325": "西充县", + "511381": "阆中市", + "511402": "东坡区", + "511403": "彭山区", + "511421": "仁寿县", + "511423": "洪雅县", + "511424": "丹棱县", + "511425": "青神县", + "511502": "翠屏区", + "511503": "南溪区", + "511504": "叙州区", + "511523": "江安县", + "511524": "长宁县", + "511525": "高县", + "511526": "珙县", + "511527": "筠连县", + "511528": "兴文县", + "511529": "屏山县", + "511602": "广安区", + "511603": "前锋区", + "511621": "岳池县", + "511622": "武胜县", + "511623": "邻水县", + "511681": "华蓥市", + "511702": "通川区", + "511703": "达川区", + "511722": "宣汉县", + "511723": "开江县", + "511724": "大竹县", + "511725": "渠县", + "511781": "万源市", + "511802": "雨城区", + "511803": "名山区", + "511822": "荥经县", + "511823": "汉源县", + "511824": "石棉县", + "511825": "天全县", + "511826": "芦山县", + "511827": "宝兴县", + "511902": "巴州区", + "511903": "恩阳区", + "511921": "通江县", + "511922": "南江县", + "511923": "平昌县", + "511971": "巴中经济开发区", + "512002": "雁江区", + "512021": "安岳县", + "512022": "乐至县", + "513201": "马尔康市", + "513221": "汶川县", + "513222": "理县", + "513223": "茂县", + "513224": "松潘县", + "513225": "九寨沟县", + "513226": "金川县", + "513227": "小金县", + "513228": "黑水县", + "513230": "壤塘县", + "513231": "阿坝县", + "513232": "若尔盖县", + "513233": "红原县", + "513301": "康定市", + "513322": "泸定县", + "513323": "丹巴县", + "513324": "九龙县", + "513325": "雅江县", + "513326": "道孚县", + "513327": "炉霍县", + "513328": "甘孜县", + "513329": "新龙县", + "513330": "德格县", + "513331": "白玉县", + "513332": "石渠县", + "513333": "色达县", + "513334": "理塘县", + "513335": "巴塘县", + "513336": "乡城县", + "513337": "稻城县", + "513338": "得荣县", + "513401": "西昌市", + "513402": "会理市", + "513422": "木里藏族自治县", + "513423": "盐源县", + "513424": "德昌县", + "513426": "会东县", + "513427": "宁南县", + "513428": "普格县", + "513429": "布拖县", + "513430": "金阳县", + "513431": "昭觉县", + "513432": "喜德县", + "513433": "冕宁县", + "513434": "越西县", + "513435": "甘洛县", + "513436": "美姑县", + "513437": "雷波县", + "520102": "南明区", + "520103": "云岩区", + "520111": "花溪区", + "520112": "乌当区", + "520113": "白云区", + "520115": "观山湖区", + "520121": "开阳县", + "520122": "息烽县", + "520123": "修文县", + "520181": "清镇市", + "520201": "钟山区", + "520203": "六枝特区", + "520204": "水城区", + "520281": "盘州市", + "520302": "红花岗区", + "520303": "汇川区", + "520304": "播州区", + "520322": "桐梓县", + "520323": "绥阳县", + "520324": "正安县", + "520325": "道真仡佬族苗族自治县", + "520326": "务川仡佬族苗族自治县", + "520327": "凤冈县", + "520328": "湄潭县", + "520329": "余庆县", + "520330": "习水县", + "520381": "赤水市", + "520382": "仁怀市", + "520402": "西秀区", + "520403": "平坝区", + "520422": "普定县", + "520423": "镇宁布依族苗族自治县", + "520424": "关岭布依族苗族自治县", + "520425": "紫云苗族布依族自治县", + "520502": "七星关区", + "520521": "大方县", + "520523": "金沙县", + "520524": "织金县", + "520525": "纳雍县", + "520526": "威宁彝族回族苗族自治县", + "520527": "赫章县", + "520581": "黔西市", + "520602": "碧江区", + "520603": "万山区", + "520621": "江口县", + "520622": "玉屏侗族自治县", + "520623": "石阡县", + "520624": "思南县", + "520625": "印江土家族苗族自治县", + "520626": "德江县", + "520627": "沿河土家族自治县", + "520628": "松桃苗族自治县", + "522301": "兴义市", + "522302": "兴仁市", + "522323": "普安县", + "522324": "晴隆县", + "522325": "贞丰县", + "522326": "望谟县", + "522327": "册亨县", + "522328": "安龙县", + "522601": "凯里市", + "522622": "黄平县", + "522623": "施秉县", + "522624": "三穗县", + "522625": "镇远县", + "522626": "岑巩县", + "522627": "天柱县", + "522628": "锦屏县", + "522629": "剑河县", + "522630": "台江县", + "522631": "黎平县", + "522632": "榕江县", + "522633": "从江县", + "522634": "雷山县", + "522635": "麻江县", + "522636": "丹寨县", + "522701": "都匀市", + "522702": "福泉市", + "522722": "荔波县", + "522723": "贵定县", + "522725": "瓮安县", + "522726": "独山县", + "522727": "平塘县", + "522728": "罗甸县", + "522729": "长顺县", + "522730": "龙里县", + "522731": "惠水县", + "522732": "三都水族自治县", + "530102": "五华区", + "530103": "盘龙区", + "530111": "官渡区", + "530112": "西山区", + "530113": "东川区", + "530114": "呈贡区", + "530115": "晋宁区", + "530124": "富民县", + "530125": "宜良县", + "530126": "石林彝族自治县", + "530127": "嵩明县", + "530128": "禄劝彝族苗族自治县", + "530129": "寻甸回族彝族自治县", + "530181": "安宁市", + "530302": "麒麟区", + "530303": "沾益区", + "530304": "马龙区", + "530322": "陆良县", + "530323": "师宗县", + "530324": "罗平县", + "530325": "富源县", + "530326": "会泽县", + "530381": "宣威市", + "530402": "红塔区", + "530403": "江川区", + "530423": "通海县", + "530424": "华宁县", + "530425": "易门县", + "530426": "峨山彝族自治县", + "530427": "新平彝族傣族自治县", + "530428": "元江哈尼族彝族傣族自治县", + "530481": "澄江市", + "530502": "隆阳区", + "530521": "施甸县", + "530523": "龙陵县", + "530524": "昌宁县", + "530581": "腾冲市", + "530602": "昭阳区", + "530621": "鲁甸县", + "530622": "巧家县", + "530623": "盐津县", + "530624": "大关县", + "530625": "永善县", + "530626": "绥江县", + "530627": "镇雄县", + "530628": "彝良县", + "530629": "威信县", + "530681": "水富市", + "530702": "古城区", + "530721": "玉龙纳西族自治县", + "530722": "永胜县", + "530723": "华坪县", + "530724": "宁蒗彝族自治县", + "530802": "思茅区", + "530821": "宁洱哈尼族彝族自治县", + "530822": "墨江哈尼族自治县", + "530823": "景东彝族自治县", + "530824": "景谷傣族彝族自治县", + "530825": "镇沅彝族哈尼族拉祜族自治县", + "530826": "江城哈尼族彝族自治县", + "530827": "孟连傣族拉祜族佤族自治县", + "530828": "澜沧拉祜族自治县", + "530829": "西盟佤族自治县", + "530902": "临翔区", + "530921": "凤庆县", + "530922": "云县", + "530923": "永德县", + "530924": "镇康县", + "530925": "双江拉祜族佤族布朗族傣族自治县", + "530926": "耿马傣族佤族自治县", + "530927": "沧源佤族自治县", + "532301": "楚雄市", + "532302": "禄丰市", + "532322": "双柏县", + "532323": "牟定县", + "532324": "南华县", + "532325": "姚安县", + "532326": "大姚县", + "532327": "永仁县", + "532328": "元谋县", + "532329": "武定县", + "532501": "个旧市", + "532502": "开远市", + "532503": "蒙自市", + "532504": "弥勒市", + "532523": "屏边苗族自治县", + "532524": "建水县", + "532525": "石屏县", + "532527": "泸西县", + "532528": "元阳县", + "532529": "红河县", + "532530": "金平苗族瑶族傣族自治县", + "532531": "绿春县", + "532532": "河口瑶族自治县", + "532601": "文山市", + "532622": "砚山县", + "532623": "西畴县", + "532624": "麻栗坡县", + "532625": "马关县", + "532626": "丘北县", + "532627": "广南县", + "532628": "富宁县", + "532801": "景洪市", + "532822": "勐海县", + "532823": "勐腊县", + "532901": "大理市", + "532922": "漾濞彝族自治县", + "532923": "祥云县", + "532924": "宾川县", + "532925": "弥渡县", + "532926": "南涧彝族自治县", + "532927": "巍山彝族回族自治县", + "532928": "永平县", + "532929": "云龙县", + "532930": "洱源县", + "532931": "剑川县", + "532932": "鹤庆县", + "533102": "瑞丽市", + "533103": "芒市", + "533122": "梁河县", + "533123": "盈江县", + "533124": "陇川县", + "533301": "泸水市", + "533323": "福贡县", + "533324": "贡山独龙族怒族自治县", + "533325": "兰坪白族普米族自治县", + "533401": "香格里拉市", + "533422": "德钦县", + "533423": "维西傈僳族自治县", + "540102": "城关区", + "540103": "堆龙德庆区", + "540104": "达孜区", + "540121": "林周县", + "540122": "当雄县", + "540123": "尼木县", + "540124": "曲水县", + "540127": "墨竹工卡县", + "540202": "桑珠孜区", + "540221": "南木林县", + "540222": "江孜县", + "540223": "定日县", + "540224": "萨迦县", + "540225": "拉孜县", + "540226": "昂仁县", + "540227": "谢通门县", + "540228": "白朗县", + "540229": "仁布县", + "540230": "康马县", + "540231": "定结县", + "540232": "仲巴县", + "540233": "亚东县", + "540234": "吉隆县", + "540235": "聂拉木县", + "540236": "萨嘎县", + "540237": "岗巴县", + "540302": "卡若区", + "540321": "江达县", + "540322": "贡觉县", + "540323": "类乌齐县", + "540324": "丁青县", + "540325": "察雅县", + "540326": "八宿县", + "540327": "左贡县", + "540328": "芒康县", + "540329": "洛隆县", + "540330": "边坝县", + "540402": "巴宜区", + "540421": "工布江达县", + "540423": "墨脱县", + "540424": "波密县", + "540425": "察隅县", + "540426": "朗县", + "540481": "米林市", + "540502": "乃东区", + "540521": "扎囊县", + "540522": "贡嘎县", + "540523": "桑日县", + "540524": "琼结县", + "540525": "曲松县", + "540526": "措美县", + "540527": "洛扎县", + "540528": "加查县", + "540529": "隆子县", + "540531": "浪卡子县", + "540581": "错那市", + "540602": "色尼区", + "540621": "嘉黎县", + "540622": "比如县", + "540623": "聂荣县", + "540624": "安多县", + "540625": "申扎县", + "540626": "索县", + "540627": "班戈县", + "540628": "巴青县", + "540629": "尼玛县", + "540630": "双湖县", + "542521": "普兰县", + "542522": "札达县", + "542523": "噶尔县", + "542524": "日土县", + "542525": "革吉县", + "542526": "改则县", + "542527": "措勤县", + "610102": "新城区", + "610103": "碑林区", + "610104": "莲湖区", + "610111": "灞桥区", + "610112": "未央区", + "610113": "雁塔区", + "610114": "阎良区", + "610115": "临潼区", + "610116": "长安区", + "610117": "高陵区", + "610118": "鄠邑区", + "610122": "蓝田县", + "610124": "周至县", + "610202": "王益区", + "610203": "印台区", + "610204": "耀州区", + "610222": "宜君县", + "610302": "渭滨区", + "610303": "金台区", + "610304": "陈仓区", + "610305": "凤翔区", + "610323": "岐山县", + "610324": "扶风县", + "610326": "眉县", + "610327": "陇县", + "610328": "千阳县", + "610329": "麟游县", + "610330": "凤县", + "610331": "太白县", + "610402": "秦都区", + "610403": "杨陵区", + "610404": "渭城区", + "610422": "三原县", + "610423": "泾阳县", + "610424": "乾县", + "610425": "礼泉县", + "610426": "永寿县", + "610428": "长武县", + "610429": "旬邑县", + "610430": "淳化县", + "610431": "武功县", + "610481": "兴平市", + "610482": "彬州市", + "610502": "临渭区", + "610503": "华州区", + "610522": "潼关县", + "610523": "大荔县", + "610524": "合阳县", + "610525": "澄城县", + "610526": "蒲城县", + "610527": "白水县", + "610528": "富平县", + "610581": "韩城市", + "610582": "华阴市", + "610602": "宝塔区", + "610603": "安塞区", + "610621": "延长县", + "610622": "延川县", + "610625": "志丹县", + "610626": "吴起县", + "610627": "甘泉县", + "610628": "富县", + "610629": "洛川县", + "610630": "宜川县", + "610631": "黄龙县", + "610632": "黄陵县", + "610681": "子长市", + "610702": "汉台区", + "610703": "南郑区", + "610722": "城固县", + "610723": "洋县", + "610724": "西乡县", + "610725": "勉县", + "610726": "宁强县", + "610727": "略阳县", + "610728": "镇巴县", + "610729": "留坝县", + "610730": "佛坪县", + "610802": "榆阳区", + "610803": "横山区", + "610822": "府谷县", + "610824": "靖边县", + "610825": "定边县", + "610826": "绥德县", + "610827": "米脂县", + "610828": "佳县", + "610829": "吴堡县", + "610830": "清涧县", + "610831": "子洲县", + "610881": "神木市", + "610902": "汉滨区", + "610921": "汉阴县", + "610922": "石泉县", + "610923": "宁陕县", + "610924": "紫阳县", + "610925": "岚皋县", + "610926": "平利县", + "610927": "镇坪县", + "610929": "白河县", + "610981": "旬阳市", + "611002": "商州区", + "611021": "洛南县", + "611022": "丹凤县", + "611023": "商南县", + "611024": "山阳县", + "611025": "镇安县", + "611026": "柞水县", + "620102": "城关区", + "620103": "七里河区", + "620104": "西固区", + "620105": "安宁区", + "620111": "红古区", + "620121": "永登县", + "620122": "皋兰县", + "620123": "榆中县", + "620171": "兰州新区", + "620201": "市辖区", + "620290": "雄关区", + "620291": "长城区", + "620292": "镜铁区", + "620293": "新城镇", + "620294": "峪泉镇", + "620295": "文殊镇", + "620302": "金川区", + "620321": "永昌县", + "620402": "白银区", + "620403": "平川区", + "620421": "靖远县", + "620422": "会宁县", + "620423": "景泰县", + "620502": "秦州区", + "620503": "麦积区", + "620521": "清水县", + "620522": "秦安县", + "620523": "甘谷县", + "620524": "武山县", + "620525": "张家川回族自治县", + "620602": "凉州区", + "620621": "民勤县", + "620622": "古浪县", + "620623": "天祝藏族自治县", + "620702": "甘州区", + "620721": "肃南裕固族自治县", + "620722": "民乐县", + "620723": "临泽县", + "620724": "高台县", + "620725": "山丹县", + "620802": "崆峒区", + "620821": "泾川县", + "620822": "灵台县", + "620823": "崇信县", + "620825": "庄浪县", + "620826": "静宁县", + "620881": "华亭市", + "620902": "肃州区", + "620921": "金塔县", + "620922": "瓜州县", + "620923": "肃北蒙古族自治县", + "620924": "阿克塞哈萨克族自治县", + "620981": "玉门市", + "620982": "敦煌市", + "621002": "西峰区", + "621021": "庆城县", + "621022": "环县", + "621023": "华池县", + "621024": "合水县", + "621025": "正宁县", + "621026": "宁县", + "621027": "镇原县", + "621102": "安定区", + "621121": "通渭县", + "621122": "陇西县", + "621123": "渭源县", + "621124": "临洮县", + "621125": "漳县", + "621126": "岷县", + "621202": "武都区", + "621221": "成县", + "621222": "文县", + "621223": "宕昌县", + "621224": "康县", + "621225": "西和县", + "621226": "礼县", + "621227": "徽县", + "621228": "两当县", + "622901": "临夏市", + "622921": "临夏县", + "622922": "康乐县", + "622923": "永靖县", + "622924": "广河县", + "622925": "和政县", + "622926": "东乡族自治县", + "622927": "积石山保安族东乡族撒拉族自治县", + "623001": "合作市", + "623021": "临潭县", + "623022": "卓尼县", + "623023": "舟曲县", + "623024": "迭部县", + "623025": "玛曲县", + "623026": "碌曲县", + "623027": "夏河县", + "630102": "城东区", + "630103": "城中区", + "630104": "城西区", + "630105": "城北区", + "630106": "湟中区", + "630121": "大通回族土族自治县", + "630123": "湟源县", + "630202": "乐都区", + "630203": "平安区", + "630222": "民和回族土族自治县", + "630223": "互助土族自治县", + "630224": "化隆回族自治县", + "630225": "循化撒拉族自治县", + "632221": "门源回族自治县", + "632222": "祁连县", + "632223": "海晏县", + "632224": "刚察县", + "632301": "同仁市", + "632322": "尖扎县", + "632323": "泽库县", + "632324": "河南蒙古族自治县", + "632521": "共和县", + "632522": "同德县", + "632523": "贵德县", + "632524": "兴海县", + "632525": "贵南县", + "632621": "玛沁县", + "632622": "班玛县", + "632623": "甘德县", + "632624": "达日县", + "632625": "久治县", + "632626": "玛多县", + "632701": "玉树市", + "632722": "杂多县", + "632723": "称多县", + "632724": "治多县", + "632725": "囊谦县", + "632726": "曲麻莱县", + "632801": "格尔木市", + "632802": "德令哈市", + "632803": "茫崖市", + "632821": "乌兰县", + "632822": "都兰县", + "632823": "天峻县", + "632857": "大柴旦行政委员会", + "640104": "兴庆区", + "640105": "西夏区", + "640106": "金凤区", + "640121": "永宁县", + "640122": "贺兰县", + "640181": "灵武市", + "640202": "大武口区", + "640205": "惠农区", + "640221": "平罗县", + "640302": "利通区", + "640303": "红寺堡区", + "640323": "盐池县", + "640324": "同心县", + "640381": "青铜峡市", + "640402": "原州区", + "640422": "西吉县", + "640423": "隆德县", + "640424": "泾源县", + "640425": "彭阳县", + "640502": "沙坡头区", + "640521": "中宁县", + "640522": "海原县", + "650102": "天山区", + "650103": "沙依巴克区", + "650104": "新市区", + "650105": "水磨沟区", + "650106": "头屯河区", + "650107": "达坂城区", + "650109": "米东区", + "650121": "乌鲁木齐县", + "650202": "独山子区", + "650203": "克拉玛依区", + "650204": "白碱滩区", + "650205": "乌尔禾区", + "650402": "高昌区", + "650421": "鄯善县", + "650422": "托克逊县", + "650502": "伊州区", + "650521": "巴里坤哈萨克自治县", + "650522": "伊吾县", + "652301": "昌吉市", + "652302": "阜康市", + "652323": "呼图壁县", + "652324": "玛纳斯县", + "652325": "奇台县", + "652327": "吉木萨尔县", + "652328": "木垒哈萨克自治县", + "652701": "博乐市", + "652702": "阿拉山口市", + "652722": "精河县", + "652723": "温泉县", + "652801": "库尔勒市", + "652822": "轮台县", + "652823": "尉犁县", + "652824": "若羌县", + "652825": "且末县", + "652826": "焉耆回族自治县", + "652827": "和静县", + "652828": "和硕县", + "652829": "博湖县", + "652901": "阿克苏市", + "652902": "库车市", + "652922": "温宿县", + "652924": "沙雅县", + "652925": "新和县", + "652926": "拜城县", + "652927": "乌什县", + "652928": "阿瓦提县", + "652929": "柯坪县", + "653001": "阿图什市", + "653022": "阿克陶县", + "653023": "阿合奇县", + "653024": "乌恰县", + "653101": "喀什市", + "653121": "疏附县", + "653122": "疏勒县", + "653123": "英吉沙县", + "653124": "泽普县", + "653125": "莎车县", + "653126": "叶城县", + "653127": "麦盖提县", + "653128": "岳普湖县", + "653129": "伽师县", + "653130": "巴楚县", + "653131": "塔什库尔干塔吉克自治县", + "653201": "和田市", + "653221": "和田县", + "653222": "墨玉县", + "653223": "皮山县", + "653224": "洛浦县", + "653225": "策勒县", + "653226": "于田县", + "653227": "民丰县", + "654002": "伊宁市", + "654003": "奎屯市", + "654004": "霍尔果斯市", + "654021": "伊宁县", + "654022": "察布查尔锡伯自治县", + "654023": "霍城县", + "654024": "巩留县", + "654025": "新源县", + "654026": "昭苏县", + "654027": "特克斯县", + "654028": "尼勒克县", + "654201": "塔城市", + "654202": "乌苏市", + "654203": "沙湾市", + "654221": "额敏县", + "654224": "托里县", + "654225": "裕民县", + "654226": "和布克赛尔蒙古自治县", + "654301": "阿勒泰市", + "654321": "布尔津县", + "654322": "富蕴县", + "654323": "福海县", + "654324": "哈巴河县", + "654325": "青河县", + "654326": "吉木乃县", + "659001": "石河子市", + "659002": "阿拉尔市", + "659003": "图木舒克市", + "659004": "五家渠市", + "659005": "北屯市", + "659006": "铁门关市", + "659007": "双河市", + "659008": "可克达拉市", + "659009": "昆玉市", + "659010": "胡杨河市", + "659011": "新星市", + "659012": "白杨市", + "710101": "中正区", + "710102": "大同区", + "710103": "中山区", + "710104": "松山区", + "710105": "大安区", + "710106": "万华区", + "710107": "信义区", + "710108": "士林区", + "710109": "北投区", + "710110": "内湖区", + "710111": "南港区", + "710112": "文山区", + "710199": "其它区", + "710201": "新兴区", + "710202": "前金区", + "710203": "芩雅区", + "710204": "盐埕区", + "710205": "鼓山区", + "710206": "旗津区", + "710207": "前镇区", + "710208": "三民区", + "710209": "左营区", + "710210": "楠梓区", + "710211": "小港区", + "710241": "苓雅区", + "710242": "仁武区", + "710243": "大社区", + "710244": "冈山区", + "710245": "路竹区", + "710246": "阿莲区", + "710247": "田寮区", + "710248": "燕巢区", + "710249": "桥头区", + "710250": "梓官区", + "710251": "弥陀区", + "710252": "永安区", + "710253": "湖内区", + "710254": "凤山区", + "710255": "大寮区", + "710256": "林园区", + "710257": "鸟松区", + "710258": "大树区", + "710259": "旗山区", + "710260": "美浓区", + "710261": "六龟区", + "710262": "内门区", + "710263": "杉林区", + "710264": "甲仙区", + "710265": "桃源区", + "710266": "那玛夏区", + "710267": "茂林区", + "710268": "茄萣区", + "710299": "其它区", + "710301": "中西区", + "710302": "东区", + "710303": "南区", + "710304": "北区", + "710305": "安平区", + "710306": "安南区", + "710339": "永康区", + "710340": "归仁区", + "710341": "新化区", + "710342": "左镇区", + "710343": "玉井区", + "710344": "楠西区", + "710345": "南化区", + "710346": "仁德区", + "710347": "关庙区", + "710348": "龙崎区", + "710349": "官田区", + "710350": "麻豆区", + "710351": "佳里区", + "710352": "西港区", + "710353": "七股区", + "710354": "将军区", + "710355": "学甲区", + "710356": "北门区", + "710357": "新营区", + "710358": "后壁区", + "710359": "白河区", + "710360": "东山区", + "710361": "六甲区", + "710362": "下营区", + "710363": "柳营区", + "710364": "盐水区", + "710365": "善化区", + "710366": "大内区", + "710367": "山上区", + "710368": "新市区", + "710369": "安定区", + "710399": "其它区", + "710401": "中区", + "710402": "东区", + "710403": "南区", + "710404": "西区", + "710405": "北区", + "710406": "北屯区", + "710407": "西屯区", + "710408": "南屯区", + "710431": "太平区", + "710432": "大里区", + "710433": "雾峰区", + "710434": "乌日区", + "710435": "丰原区", + "710436": "后里区", + "710437": "石冈区", + "710438": "东势区", + "710439": "和平区", + "710440": "新社区", + "710441": "潭子区", + "710442": "大雅区", + "710443": "神冈区", + "710444": "大肚区", + "710445": "沙鹿区", + "710446": "龙井区", + "710447": "梧栖区", + "710448": "清水区", + "710449": "大甲区", + "710450": "外埔区", + "710451": "大安区", + "710499": "其它区", + "710507": "金沙镇", + "710508": "金湖镇", + "710509": "金宁乡", + "710510": "金城镇", + "710511": "烈屿乡", + "710512": "乌坵乡", + "710614": "南投市", + "710615": "中寮乡", + "710616": "草屯镇", + "710617": "国姓乡", + "710618": "埔里镇", + "710619": "仁爱乡", + "710620": "名间乡", + "710621": "集集镇", + "710622": "水里乡", + "710623": "鱼池乡", + "710624": "信义乡", + "710625": "竹山镇", + "710626": "鹿谷乡", + "710701": "仁爱区", + "710702": "信义区", + "710703": "中正区", + "710704": "中山区", + "710705": "安乐区", + "710706": "暖暖区", + "710707": "七堵区", + "710799": "其它区", + "710801": "东区", + "710802": "北区", + "710803": "香山区", + "710899": "其它区", + "710901": "东区", + "710902": "西区", + "710999": "其它区", + "711130": "万里区", + "711132": "板桥区", + "711133": "汐止区", + "711134": "深坑区", + "711135": "石碇区", + "711136": "瑞芳区", + "711137": "平溪区", + "711138": "双溪区", + "711139": "贡寮区", + "711140": "新店区", + "711141": "坪林区", + "711142": "乌来区", + "711143": "永和区", + "711144": "中和区", + "711145": "土城区", + "711146": "三峡区", + "711147": "树林区", + "711148": "莺歌区", + "711149": "三重区", + "711150": "新庄区", + "711151": "泰山区", + "711152": "林口区", + "711153": "芦洲区", + "711154": "五股区", + "711155": "八里区", + "711156": "淡水区", + "711157": "三芝区", + "711158": "石门区", + "711287": "宜兰市", + "711288": "头城镇", + "711289": "礁溪乡", + "711290": "壮围乡", + "711291": "员山乡", + "711292": "罗东镇", + "711293": "三星乡", + "711294": "大同乡", + "711295": "五结乡", + "711296": "冬山乡", + "711297": "苏澳镇", + "711298": "南澳乡", + "711299": "钓鱼台", + "711387": "竹北市", + "711388": "湖口乡", + "711389": "新丰乡", + "711390": "新埔镇", + "711391": "关西镇", + "711392": "芎林乡", + "711393": "宝山乡", + "711394": "竹东镇", + "711395": "五峰乡", + "711396": "横山乡", + "711397": "尖石乡", + "711398": "北埔乡", + "711399": "峨眉乡", + "711414": "中坜区", + "711415": "平镇区", + "711417": "杨梅区", + "711418": "新屋区", + "711419": "观音区", + "711420": "桃园区", + "711421": "龟山区", + "711422": "八德区", + "711423": "大溪区", + "711425": "大园区", + "711426": "芦竹区", + "711487": "中坜市", + "711488": "平镇市", + "711489": "龙潭乡", + "711490": "杨梅市", + "711491": "新屋乡", + "711492": "观音乡", + "711493": "桃园市", + "711494": "龟山乡", + "711495": "八德市", + "711496": "大溪镇", + "711497": "复兴乡", + "711498": "大园乡", + "711499": "芦竹乡", + "711520": "头份市", + "711582": "竹南镇", + "711583": "头份镇", + "711584": "三湾乡", + "711585": "南庄乡", + "711586": "狮潭乡", + "711587": "后龙镇", + "711588": "通霄镇", + "711589": "苑里镇", + "711590": "苗栗市", + "711591": "造桥乡", + "711592": "头屋乡", + "711593": "公馆乡", + "711594": "大湖乡", + "711595": "泰安乡", + "711596": "铜锣乡", + "711597": "三义乡", + "711598": "西湖乡", + "711599": "卓兰镇", + "711736": "员林市", + "711774": "彰化市", + "711775": "芬园乡", + "711776": "花坛乡", + "711777": "秀水乡", + "711778": "鹿港镇", + "711779": "福兴乡", + "711780": "线西乡", + "711781": "和美镇", + "711782": "伸港乡", + "711783": "员林镇", + "711784": "社头乡", + "711785": "永靖乡", + "711786": "埔心乡", + "711787": "溪湖镇", + "711788": "大村乡", + "711789": "埔盐乡", + "711790": "田中镇", + "711791": "北斗镇", + "711792": "田尾乡", + "711793": "埤头乡", + "711794": "溪州乡", + "711795": "竹塘乡", + "711796": "二林镇", + "711797": "大城乡", + "711798": "芳苑乡", + "711799": "二水乡", + "711982": "番路乡", + "711983": "梅山乡", + "711984": "竹崎乡", + "711985": "阿里山乡", + "711986": "中埔乡", + "711987": "大埔乡", + "711988": "水上乡", + "711989": "鹿草乡", + "711990": "太保市", + "711991": "朴子市", + "711992": "东石乡", + "711993": "六脚乡", + "711994": "新港乡", + "711995": "民雄乡", + "711996": "大林镇", + "711997": "溪口乡", + "711998": "义竹乡", + "711999": "布袋镇", + "712180": "斗南镇", + "712181": "大埤乡", + "712182": "虎尾镇", + "712183": "土库镇", + "712184": "褒忠乡", + "712185": "东势乡", + "712186": "台西乡", + "712187": "仑背乡", + "712188": "麦寮乡", + "712189": "斗六市", + "712190": "林内乡", + "712191": "古坑乡", + "712192": "莿桐乡", + "712193": "西螺镇", + "712194": "二仑乡", + "712195": "北港镇", + "712196": "水林乡", + "712197": "口湖乡", + "712198": "四湖乡", + "712199": "元长乡", + "712451": "崁顶乡", + "712467": "屏东市", + "712468": "三地门乡", + "712469": "雾台乡", + "712470": "玛家乡", + "712471": "九如乡", + "712472": "里港乡", + "712473": "高树乡", + "712474": "盐埔乡", + "712475": "长治乡", + "712476": "麟洛乡", + "712477": "竹田乡", + "712478": "内埔乡", + "712479": "万丹乡", + "712480": "潮州镇", + "712481": "泰武乡", + "712482": "来义乡", + "712483": "万峦乡", + "712484": "莰顶乡", + "712485": "新埤乡", + "712486": "南州乡", + "712487": "林边乡", + "712488": "东港镇", + "712489": "琉球乡", + "712490": "佳冬乡", + "712491": "新园乡", + "712492": "枋寮乡", + "712493": "枋山乡", + "712494": "春日乡", + "712495": "狮子乡", + "712496": "车城乡", + "712497": "牡丹乡", + "712498": "恒春镇", + "712499": "满州乡", + "712584": "台东市", + "712585": "绿岛乡", + "712586": "兰屿乡", + "712587": "延平乡", + "712588": "卑南乡", + "712589": "鹿野乡", + "712590": "关山镇", + "712591": "海端乡", + "712592": "池上乡", + "712593": "东河乡", + "712594": "成功镇", + "712595": "长滨乡", + "712596": "金峰乡", + "712597": "大武乡", + "712598": "达仁乡", + "712599": "太麻里乡", + "712686": "花莲市", + "712687": "新城乡", + "712688": "太鲁阁", + "712689": "秀林乡", + "712690": "吉安乡", + "712691": "寿丰乡", + "712692": "凤林镇", + "712693": "光复乡", + "712694": "丰滨乡", + "712695": "瑞穗乡", + "712696": "万荣乡", + "712697": "玉里镇", + "712698": "卓溪乡", + "712699": "富里乡", + "712794": "马公市", + "712795": "西屿乡", + "712796": "望安乡", + "712797": "七美乡", + "712798": "白沙乡", + "712799": "湖西乡", + "712896": "南竿乡", + "712897": "北竿乡", + "712898": "东引乡", + "712899": "莒光乡", + "810101": "中西区", + "810102": "湾仔区", + "810103": "东区", + "810104": "南区", + "810201": "九龙城区", + "810202": "油尖旺区", + "810203": "深水埗区", + "810204": "黄大仙区", + "810205": "观塘区", + "810301": "北区", + "810302": "大埔区", + "810303": "沙田区", + "810304": "西贡区", + "810305": "元朗区", + "810306": "屯门区", + "810307": "荃湾区", + "810308": "葵青区", + "810309": "离岛区", + "820102": "花地玛堂区", + "820103": "花王堂区", + "820104": "望德堂区", + "820105": "大堂区", + "820106": "风顺堂区", + "820202": "嘉模堂区", + "820203": "路氹填海区", + "820204": "圣方济各堂区" + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/areaData/index.ts b/src/uni_modules/lime-shared/areaData/index.ts new file mode 100644 index 0000000..401f77f --- /dev/null +++ b/src/uni_modules/lime-shared/areaData/index.ts @@ -0,0 +1,71 @@ +// @ts-nocheck +import _areaList from './city-china.json'; +export const areaList = _areaList +// #ifndef UNI-APP-X +type UTSJSONObject = Record +// #endif +// #ifdef UNI-APP-X +type Object = UTSJSONObject +// #endif +type AreaList = { + province_list : Map; + city_list : Map; + county_list : Map; +} +// type CascaderOption = { +// text : string; +// value : string; +// children ?: CascaderOption[]; +// }; + +const makeOption = ( + label : string, + value : string, + children ?: UTSJSONObject[], +) : UTSJSONObject => ({ + label, + value, + children, +}); + + + +export function useCascaderAreaData() : UTSJSONObject[] { + const city = areaList['city_list'] as UTSJSONObject + const county = areaList['county_list'] as UTSJSONObject + const province = areaList['province_list'] as UTSJSONObject + const provinceMap = new Map(); + Object.keys(province).forEach((code) => { + provinceMap.set(code.slice(0, 2), makeOption(`${province[code]}`, code, [])); + }); + + const cityMap = new Map(); + + Object.keys(city).forEach((code) => { + const option = makeOption(`${city[code]}`, code, []); + cityMap.set(code.slice(0, 4), option); + + const _province = provinceMap.get(code.slice(0, 2)); + if (_province != null) { + (_province['children'] as UTSJSONObject[]).push(option) + } + }); + + Object.keys(county).forEach((code) => { + const _city = cityMap.get(code.slice(0, 4)); + if (_city != null) { + (_city['children'] as UTSJSONObject[]).push(makeOption(`${county[code]}`, code, null)); + } + }); + + // #ifndef APP-ANDROID || APP-IOS + return Array.from(provinceMap.values()); + // #endif + // #ifdef APP-ANDROID || APP-IOS + const obj : UTSJSONObject[] = [] + provinceMap.forEach((value, code) => { + obj.push(value) + }) + return obj + // #endif +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/arrayBufferToFile/index.ts b/src/uni_modules/lime-shared/arrayBufferToFile/index.ts new file mode 100644 index 0000000..49ee530 --- /dev/null +++ b/src/uni_modules/lime-shared/arrayBufferToFile/index.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif + +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif \ No newline at end of file diff --git a/src/uni_modules/lime-shared/arrayBufferToFile/uvue.uts b/src/uni_modules/lime-shared/arrayBufferToFile/uvue.uts new file mode 100644 index 0000000..65c7b14 --- /dev/null +++ b/src/uni_modules/lime-shared/arrayBufferToFile/uvue.uts @@ -0,0 +1,10 @@ +// @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] 当前环境不支持') +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/arrayBufferToFile/vue.ts b/src/uni_modules/lime-shared/arrayBufferToFile/vue.ts new file mode 100644 index 0000000..9760b20 --- /dev/null +++ b/src/uni_modules/lime-shared/arrayBufferToFile/vue.ts @@ -0,0 +1,63 @@ +// @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 + }) +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts b/src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts new file mode 100644 index 0000000..f83b640 --- /dev/null +++ b/src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts @@ -0,0 +1,13 @@ +// @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 { + + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/base64ToPath/index.ts b/src/uni_modules/lime-shared/base64ToPath/index.ts new file mode 100644 index 0000000..af9a9d2 --- /dev/null +++ b/src/uni_modules/lime-shared/base64ToPath/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif + + +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif diff --git a/src/uni_modules/lime-shared/base64ToPath/uvue.uts b/src/uni_modules/lime-shared/base64ToPath/uvue.uts new file mode 100644 index 0000000..364b494 --- /dev/null +++ b/src/uni_modules/lime-shared/base64ToPath/uvue.uts @@ -0,0 +1,22 @@ +// @ts-nocheck +import { processFile, type ProcessFileOptions } from '@/uni_modules/lime-file-utils' + +/** + * base64转路径 + * @param {Object} base64 + */ +export function base64ToPath(base64: string, filename: string | null = null):Promise { + return new Promise((resolve,reject) => { + processFile({ + type: 'toDataURL', + path: base64, + filename, + success(res: string){ + resolve(res) + }, + fail(err){ + reject(err) + } + } as ProcessFileOptions) + }) +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/base64ToPath/vue.ts b/src/uni_modules/lime-shared/base64ToPath/vue.ts new file mode 100644 index 0000000..735000f --- /dev/null +++ b/src/uni_modules/lime-shared/base64ToPath/vue.ts @@ -0,0 +1,75 @@ +// @ts-nocheck +import {platform} from '../platform' +/** + * base64转路径 + * @param {Object} base64 + */ +export function base64ToPath(base64: string, filename?: string):Promise { + 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 + }) +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/camelCase/index.ts b/src/uni_modules/lime-shared/camelCase/index.ts new file mode 100644 index 0000000..dd470ab --- /dev/null +++ b/src/uni_modules/lime-shared/camelCase/index.ts @@ -0,0 +1,21 @@ +/** + * 将字符串转换为 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(''); +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/canIUseCanvas2d/index.ts b/src/uni_modules/lime-shared/canIUseCanvas2d/index.ts new file mode 100644 index 0000000..be5623a --- /dev/null +++ b/src/uni_modules/lime-shared/canIUseCanvas2d/index.ts @@ -0,0 +1,67 @@ +// @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 + // #ifdef UNI-APP-X && WEB || UNI-APP-X && APP + return true; + // #endif + // #ifndef MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO + return false + // #endif + +} diff --git a/src/uni_modules/lime-shared/capitalizedAmount/index.ts b/src/uni_modules/lime-shared/capitalizedAmount/index.ts new file mode 100644 index 0000000..9a0c1f4 --- /dev/null +++ b/src/uni_modules/lime-shared/capitalizedAmount/index.ts @@ -0,0 +1,111 @@ +// @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; + } +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/changelog.md b/src/uni_modules/lime-shared/changelog.md new file mode 100644 index 0000000..0c6691b --- /dev/null +++ b/src/uni_modules/lime-shared/changelog.md @@ -0,0 +1,63 @@ + +## 0.2.9(2025-02-19) +- chore: 更新文档 +## 0.2.8(2025-02-11) +- chore: 更新文档 +## 0.2.7(2025-01-17) +- fix: 针对canvas 平台判断优化 +## 0.2.6(2025-01-09) +- feat: 增加`areaData`中国省市区数据 +## 0.2.5(2025-01-07) +- fix: animation在app上类型问题 +## 0.2.4(2025-01-04) +- feat: getRect类型问题 +## 0.2.3(2025-01-01) +- chore: unitConvert使用uni.rpx2px +## 0.2.2(2024-12-11) +- chore: 动画使用`requestAnimationFrame` +## 0.2.1(2024-11-20) +- feat: 增加`characterLimit` +## 0.2.0(2024-11-14) +- fix: vue2的类型问题 +## 0.1.9(2024-11-14) +- feat: 增加`shuffle` +## 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) +- 无 diff --git a/src/uni_modules/lime-shared/characterLimit/index.ts b/src/uni_modules/lime-shared/characterLimit/index.ts new file mode 100644 index 0000000..215de23 --- /dev/null +++ b/src/uni_modules/lime-shared/characterLimit/index.ts @@ -0,0 +1,63 @@ +// @ts-nocheck +/** + * 计算字符串字符的长度并可以截取字符串。 + * @param char 传入字符串(maxcharacter条件下,一个汉字表示两个字符) + * @param max 规定最大字符串长度 + * @returns 当没有传入maxCharacter/maxLength 时返回字符串字符长度,当传入maxCharacter/maxLength时返回截取之后的字符串和长度。 + */ +export type CharacterLengthResult = { + length : number; + characters : string; +} +// #ifdef APP-ANDROID +type ChartType = any +// #endif +// #ifndef APP-ANDROID +type ChartType = string | number +// #endif + +export function characterLimit(type : string, char : ChartType, max : number) : CharacterLengthResult { + const str = `${char}`; + + if (str.length == 0) { + return { + length: 0, + characters: '', + } as CharacterLengthResult + } + + if (type == 'maxcharacter') { + let len = 0; + for (let i = 0; i < str.length; i += 1) { + let currentStringLength : number// = 0; + const code = str.charCodeAt(i)! + if (code > 127 || code == 94) { + currentStringLength = 2; + } else { + currentStringLength = 1; + } + if (len + currentStringLength > max) { + return { + length: len, + characters: str.slice(0, i), + } as CharacterLengthResult + } + len += currentStringLength; + } + return { + length: len, + characters: str, + } as CharacterLengthResult + } else if (type == 'maxlength') { + const length = str.length > max ? max : str.length; + return { + length: length, + characters: str.slice(0, length), + } as CharacterLengthResult + } + + return { + length: str.length, + characters: str, + } as CharacterLengthResult +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/clamp/index.ts b/src/uni_modules/lime-shared/clamp/index.ts new file mode 100644 index 0000000..0e16358 --- /dev/null +++ b/src/uni_modules/lime-shared/clamp/index.ts @@ -0,0 +1,16 @@ +// @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(大于最大值,被限制为最大值) \ No newline at end of file diff --git a/src/uni_modules/lime-shared/cloneDeep/index.ts b/src/uni_modules/lime-shared/cloneDeep/index.ts new file mode 100644 index 0000000..3428c49 --- /dev/null +++ b/src/uni_modules/lime-shared/cloneDeep/index.ts @@ -0,0 +1,10 @@ +// @ts-nocheck + +// #ifdef UNI-APP-X && APP +export * from './uvue.ts' +// #endif + + +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif \ No newline at end of file diff --git a/src/uni_modules/lime-shared/cloneDeep/uvue.ts b/src/uni_modules/lime-shared/cloneDeep/uvue.ts new file mode 100644 index 0000000..7929bc2 --- /dev/null +++ b/src/uni_modules/lime-shared/cloneDeep/uvue.ts @@ -0,0 +1,17 @@ +// @ts-nocheck +/** + * 深度克隆一个对象或数组 + * @param obj 要克隆的对象或数组 + * @returns 克隆后的对象或数组 + */ +export function cloneDeep(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 +} + diff --git a/src/uni_modules/lime-shared/cloneDeep/vue.ts b/src/uni_modules/lime-shared/cloneDeep/vue.ts new file mode 100644 index 0000000..ded334d --- /dev/null +++ b/src/uni_modules/lime-shared/cloneDeep/vue.ts @@ -0,0 +1,103 @@ +// @ts-nocheck +/** + * 深度克隆一个对象或数组 + * @param obj 要克隆的对象或数组 + * @returns 克隆后的对象或数组 + */ +export function cloneDeep(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 (嵌套对象的副本也是独立的) \ No newline at end of file diff --git a/src/uni_modules/lime-shared/closest/index.ts b/src/uni_modules/lime-shared/closest/index.ts new file mode 100644 index 0000000..e6e79c2 --- /dev/null +++ b/src/uni_modules/lime-shared/closest/index.ts @@ -0,0 +1,22 @@ +// @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 \ No newline at end of file diff --git a/src/uni_modules/lime-shared/components/lime-shared/lime-shared.vue b/src/uni_modules/lime-shared/components/lime-shared/lime-shared.vue new file mode 100644 index 0000000..e2d7fe2 --- /dev/null +++ b/src/uni_modules/lime-shared/components/lime-shared/lime-shared.vue @@ -0,0 +1,156 @@ + + + + + \ No newline at end of file diff --git a/src/uni_modules/lime-shared/createAnimation/index.ts b/src/uni_modules/lime-shared/createAnimation/index.ts new file mode 100644 index 0000000..999ec1c --- /dev/null +++ b/src/uni_modules/lime-shared/createAnimation/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifndef UNI-APP-X +export * from './type.ts' +export * from './vue.ts' +// #endif + +// #ifdef UNI-APP-X +export * from './uvue.ts' +// #endif \ No newline at end of file diff --git a/src/uni_modules/lime-shared/createAnimation/type.ts b/src/uni_modules/lime-shared/createAnimation/type.ts new file mode 100644 index 0000000..0f8ad34 --- /dev/null +++ b/src/uni_modules/lime-shared/createAnimation/type.ts @@ -0,0 +1,25 @@ +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; +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/createAnimation/uvue.ts b/src/uni_modules/lime-shared/createAnimation/uvue.ts new file mode 100644 index 0000000..96d8263 --- /dev/null +++ b/src/uni_modules/lime-shared/createAnimation/uvue.ts @@ -0,0 +1,5 @@ +// @ts-nocheck +// export * from '@/uni_modules/lime-animateIt' +export function createAnimation() { + console.error('当前环境不支持,请使用:lime-animateIt') +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/createAnimation/vue.ts b/src/uni_modules/lime-shared/createAnimation/vue.ts new file mode 100644 index 0000000..6934f27 --- /dev/null +++ b/src/uni_modules/lime-shared/createAnimation/vue.ts @@ -0,0 +1,148 @@ +// @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 { type 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 +} diff --git a/src/uni_modules/lime-shared/createCanvas/index.ts b/src/uni_modules/lime-shared/createCanvas/index.ts new file mode 100644 index 0000000..987be92 --- /dev/null +++ b/src/uni_modules/lime-shared/createCanvas/index.ts @@ -0,0 +1,73 @@ + +// @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 +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/createImage/index.ts b/src/uni_modules/lime-shared/createImage/index.ts new file mode 100644 index 0000000..c9e72fe --- /dev/null +++ b/src/uni_modules/lime-shared/createImage/index.ts @@ -0,0 +1,71 @@ +// @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 diff --git a/src/uni_modules/lime-shared/debounce/index.ts b/src/uni_modules/lime-shared/debounce/index.ts new file mode 100644 index 0000000..a168ca3 --- /dev/null +++ b/src/uni_modules/lime-shared/debounce/index.ts @@ -0,0 +1,10 @@ +// @ts-nocheck + +// #ifdef UNI-APP-X && APP +export * from './uvue.ts' + +// #endif + +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif \ No newline at end of file diff --git a/src/uni_modules/lime-shared/debounce/uvue.ts b/src/uni_modules/lime-shared/debounce/uvue.ts new file mode 100644 index 0000000..f1fc29d --- /dev/null +++ b/src/uni_modules/lime-shared/debounce/uvue.ts @@ -0,0 +1,36 @@ +// @ts-nocheck +/** + * 防抖函数,通过延迟一定时间来限制函数的执行频率。 + * @param fn 要防抖的函数。 + * @param wait 触发防抖的等待时间,单位为毫秒。 + * @returns 防抖函数。 + */ +export function debounce(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" \ No newline at end of file diff --git a/src/uni_modules/lime-shared/debounce/vue.ts b/src/uni_modules/lime-shared/debounce/vue.ts new file mode 100644 index 0000000..694b44d --- /dev/null +++ b/src/uni_modules/lime-shared/debounce/vue.ts @@ -0,0 +1,40 @@ +// @ts-nocheck +type Timeout = ReturnType | null; +/** + * 防抖函数,通过延迟一定时间来限制函数的执行频率。 + * @param fn 要防抖的函数。 + * @param wait 触发防抖的等待时间,单位为毫秒。 + * @returns 防抖函数。 + */ +export function debounce( + 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" \ No newline at end of file diff --git a/src/uni_modules/lime-shared/exif/index.ts b/src/uni_modules/lime-shared/exif/index.ts new file mode 100644 index 0000000..c1e7e3f --- /dev/null +++ b/src/uni_modules/lime-shared/exif/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif + + +// #ifdef UNI-APP-X && APP +export * from './uvue.ts' +// #endif diff --git a/src/uni_modules/lime-shared/exif/uvue.ts b/src/uni_modules/lime-shared/exif/uvue.ts new file mode 100644 index 0000000..01d21a2 --- /dev/null +++ b/src/uni_modules/lime-shared/exif/uvue.ts @@ -0,0 +1,7 @@ +class EXIF { + constructor(){ + console.error('当前环境不支持') + } +} + +export const exif = new EXIF() \ No newline at end of file diff --git a/src/uni_modules/lime-shared/exif/vue.ts b/src/uni_modules/lime-shared/exif/vue.ts new file mode 100644 index 0000000..86f2454 --- /dev/null +++ b/src/uni_modules/lime-shared/exif/vue.ts @@ -0,0 +1,1057 @@ +// @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(' 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); + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/fillZero/index.ts b/src/uni_modules/lime-shared/fillZero/index.ts new file mode 100644 index 0000000..9952c45 --- /dev/null +++ b/src/uni_modules/lime-shared/fillZero/index.ts @@ -0,0 +1,11 @@ +// @ts-nocheck +/** + * 在数字前填充零,返回字符串形式的结果 + * @param number 要填充零的数字 + * @param length 填充零后的字符串长度,默认为2 + * @returns 填充零后的字符串 + */ +export function fillZero(number: number, length: number = 2): string { + // 将数字转换为字符串,然后使用 padStart 方法填充零到指定长度 + return `${number}`.padStart(length, '0'); +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/floatAdd/index.ts b/src/uni_modules/lime-shared/floatAdd/index.ts new file mode 100644 index 0000000..aad3929 --- /dev/null +++ b/src/uni_modules/lime-shared/floatAdd/index.ts @@ -0,0 +1,36 @@ +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; +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/floatDiv/index.ts b/src/uni_modules/lime-shared/floatDiv/index.ts new file mode 100644 index 0000000..195d4ab --- /dev/null +++ b/src/uni_modules/lime-shared/floatDiv/index.ts @@ -0,0 +1,45 @@ +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 +} diff --git a/src/uni_modules/lime-shared/floatMul/index.ts b/src/uni_modules/lime-shared/floatMul/index.ts new file mode 100644 index 0000000..51a867e --- /dev/null +++ b/src/uni_modules/lime-shared/floatMul/index.ts @@ -0,0 +1,44 @@ +// @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 +} diff --git a/src/uni_modules/lime-shared/floatSub/index.ts b/src/uni_modules/lime-shared/floatSub/index.ts new file mode 100644 index 0000000..9bc25cb --- /dev/null +++ b/src/uni_modules/lime-shared/floatSub/index.ts @@ -0,0 +1,32 @@ +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 +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/getClassStr/index.ts b/src/uni_modules/lime-shared/getClassStr/index.ts new file mode 100644 index 0000000..ef8c7d6 --- /dev/null +++ b/src/uni_modules/lime-shared/getClassStr/index.ts @@ -0,0 +1,53 @@ +// @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(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" \ No newline at end of file diff --git a/src/uni_modules/lime-shared/getCurrentPage/index.ts b/src/uni_modules/lime-shared/getCurrentPage/index.ts new file mode 100644 index 0000000..af9a9d2 --- /dev/null +++ b/src/uni_modules/lime-shared/getCurrentPage/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif + + +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif diff --git a/src/uni_modules/lime-shared/getCurrentPage/uvue.uts b/src/uni_modules/lime-shared/getCurrentPage/uvue.uts new file mode 100644 index 0000000..9e96d2b --- /dev/null +++ b/src/uni_modules/lime-shared/getCurrentPage/uvue.uts @@ -0,0 +1,5 @@ +// @ts-nocheck +export const getCurrentPage = ():Page => { + const pages = getCurrentPages(); + return pages[pages.length - 1] +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/getCurrentPage/vue.ts b/src/uni_modules/lime-shared/getCurrentPage/vue.ts new file mode 100644 index 0000000..79ecac8 --- /dev/null +++ b/src/uni_modules/lime-shared/getCurrentPage/vue.ts @@ -0,0 +1,6 @@ +// @ts-nocheck +/** 获取当前页 */ +export const getCurrentPage = () => { + const pages = getCurrentPages(); + return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance; +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/getLocalFilePath/index.ts b/src/uni_modules/lime-shared/getLocalFilePath/index.ts new file mode 100644 index 0000000..c496a07 --- /dev/null +++ b/src/uni_modules/lime-shared/getLocalFilePath/index.ts @@ -0,0 +1,62 @@ +// @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(" { +// try { +// let uri = path +// if (uri.startsWith("http") || uri.startsWith(" { + return new Promise((resolve)=>{ + uni.createSelectorQuery().in(context).select(selector).boundingClientRect(res =>{ + resolve(res as NodeInfo) + }).exec(); + }) +} + +export function getAllRect(selector : string, context: ComponentPublicInstance):Promise { + return new Promise((resolve)=>{ + uni.createSelectorQuery().in(context).selectAll(selector).boundingClientRect(res =>{ + resolve(res as NodeInfo[]) + }).exec(); + }) +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/getRect/vue.ts b/src/uni_modules/lime-shared/getRect/vue.ts new file mode 100644 index 0000000..a4aabed --- /dev/null +++ b/src/uni_modules/lime-shared/getRect/vue.ts @@ -0,0 +1,117 @@ +// @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|ComponentPublicInstance, 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((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|ComponentPublicInstance, 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((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 + }); +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/getStyleStr/index.ts b/src/uni_modules/lime-shared/getStyleStr/index.ts new file mode 100644 index 0000000..665941c --- /dev/null +++ b/src/uni_modules/lime-shared/getStyleStr/index.ts @@ -0,0 +1,54 @@ +// @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;" \ No newline at end of file diff --git a/src/uni_modules/lime-shared/getStyleStr/index_.uts b/src/uni_modules/lime-shared/getStyleStr/index_.uts new file mode 100644 index 0000000..a4c4494 --- /dev/null +++ b/src/uni_modules/lime-shared/getStyleStr/index_.uts @@ -0,0 +1,39 @@ +// @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;" \ No newline at end of file diff --git a/src/uni_modules/lime-shared/hasOwn/index.ts b/src/uni_modules/lime-shared/hasOwn/index.ts new file mode 100644 index 0000000..5e2b6da --- /dev/null +++ b/src/uni_modules/lime-shared/hasOwn/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifdef UNI-APP-X && APP +export * from './uvue.ts' +// #endif + + +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif \ No newline at end of file diff --git a/src/uni_modules/lime-shared/hasOwn/uvue.ts b/src/uni_modules/lime-shared/hasOwn/uvue.ts new file mode 100644 index 0000000..50345bf --- /dev/null +++ b/src/uni_modules/lime-shared/hasOwn/uvue.ts @@ -0,0 +1,39 @@ +// @ts-nocheck +/** + * 检查对象或数组是否具有指定的属性或键 + * @param obj 要检查的对象或数组 + * @param key 指定的属性或键 + * @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false + */ +function hasOwn(obj: UTSJSONObject, key: string): boolean +function hasOwn(obj: Map, key: string): boolean +function hasOwn(obj: any, key: string): boolean { + if(obj instanceof UTSJSONObject){ + return obj[key] != null + } + if(obj instanceof Map){ + return (obj as Map).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' 属性 \ No newline at end of file diff --git a/src/uni_modules/lime-shared/hasOwn/vue.ts b/src/uni_modules/lime-shared/hasOwn/vue.ts new file mode 100644 index 0000000..7317879 --- /dev/null +++ b/src/uni_modules/lime-shared/hasOwn/vue.ts @@ -0,0 +1,30 @@ +// @ts-nocheck +const hasOwnProperty = Object.prototype.hasOwnProperty +/** + * 检查对象或数组是否具有指定的属性或键 + * @param obj 要检查的对象或数组 + * @param key 指定的属性或键 + * @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false + */ +export function hasOwn(obj: Object | Array, 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' 属性 \ No newline at end of file diff --git a/src/uni_modules/lime-shared/index.ts b/src/uni_modules/lime-shared/index.ts new file mode 100644 index 0000000..2eb1d94 --- /dev/null +++ b/src/uni_modules/lime-shared/index.ts @@ -0,0 +1,43 @@ +// @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' + diff --git a/src/uni_modules/lime-shared/isBase64/index.ts b/src/uni_modules/lime-shared/isBase64/index.ts new file mode 100644 index 0000000..dcb8f3e --- /dev/null +++ b/src/uni_modules/lime-shared/isBase64/index.ts @@ -0,0 +1,23 @@ +// @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); +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isBrowser/index.ts b/src/uni_modules/lime-shared/isBrowser/index.ts new file mode 100644 index 0000000..6baee2b --- /dev/null +++ b/src/uni_modules/lime-shared/isBrowser/index.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +// #ifdef WEB +export const isBrowser = typeof window !== 'undefined'; +// #endif + +// #ifndef WEB +export const isBrowser = false; +// #endif \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isDef/index.ts b/src/uni_modules/lime-shared/isDef/index.ts new file mode 100644 index 0000000..f8ca9bc --- /dev/null +++ b/src/uni_modules/lime-shared/isDef/index.ts @@ -0,0 +1,23 @@ +// @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 \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isEmpty/index.ts b/src/uni_modules/lime-shared/isEmpty/index.ts new file mode 100644 index 0000000..74dbc35 --- /dev/null +++ b/src/uni_modules/lime-shared/isEmpty/index.ts @@ -0,0 +1,83 @@ +// @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).length == 0 + } + // Map + if(value instanceof Map) { + return value.size == 0 + } + // Set + if(value instanceof Set) { + 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 \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isFunction/index.ts b/src/uni_modules/lime-shared/isFunction/index.ts new file mode 100644 index 0000000..12605c3 --- /dev/null +++ b/src/uni_modules/lime-shared/isFunction/index.ts @@ -0,0 +1,16 @@ +// @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 diff --git a/src/uni_modules/lime-shared/isNumber/index.ts b/src/uni_modules/lime-shared/isNumber/index.ts new file mode 100644 index 0000000..cf8dd9b --- /dev/null +++ b/src/uni_modules/lime-shared/isNumber/index.ts @@ -0,0 +1,26 @@ +// @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 \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isNumeric/index.ts b/src/uni_modules/lime-shared/isNumeric/index.ts new file mode 100644 index 0000000..af5020e --- /dev/null +++ b/src/uni_modules/lime-shared/isNumeric/index.ts @@ -0,0 +1,33 @@ +// @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 diff --git a/src/uni_modules/lime-shared/isObject/index.ts b/src/uni_modules/lime-shared/isObject/index.ts new file mode 100644 index 0000000..d06c15b --- /dev/null +++ b/src/uni_modules/lime-shared/isObject/index.ts @@ -0,0 +1,19 @@ +// @ts-nocheck +/** + * 检查一个值是否为对象类型 + * @param val 要检查的值 + * @returns 如果值的类型是对象类型,则返回 true;否则返回 false + */ + +// #ifndef UNI-APP-X && APP +export const isObject = (val : unknown) : val is Record => + 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 diff --git a/src/uni_modules/lime-shared/isPromise/index.ts b/src/uni_modules/lime-shared/isPromise/index.ts new file mode 100644 index 0000000..b13c10b --- /dev/null +++ b/src/uni_modules/lime-shared/isPromise/index.ts @@ -0,0 +1,22 @@ +// @ts-nocheck +import {isFunction} from '../isFunction' +import {isObject} from '../isObject' +/** + * 检查一个值是否为 Promise 类型 + * @param val 要检查的值 + * @returns 如果值的类型是 Promise 类型,则返回 true;否则返回 false + */ +// #ifndef APP-ANDROID +export const isPromise = (val: unknown): val is Promise => { + // 使用 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 +}; +// #endif \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isString/index.ts b/src/uni_modules/lime-shared/isString/index.ts new file mode 100644 index 0000000..143f876 --- /dev/null +++ b/src/uni_modules/lime-shared/isString/index.ts @@ -0,0 +1,19 @@ +// @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 diff --git a/src/uni_modules/lime-shared/kebabCase/index.ts b/src/uni_modules/lime-shared/kebabCase/index.ts new file mode 100644 index 0000000..114c52c --- /dev/null +++ b/src/uni_modules/lime-shared/kebabCase/index.ts @@ -0,0 +1,24 @@ +// @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(); // 将结果转换为全小写 +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/obj2url/index.ts b/src/uni_modules/lime-shared/obj2url/index.ts new file mode 100644 index 0000000..518af60 --- /dev/null +++ b/src/uni_modules/lime-shared/obj2url/index.ts @@ -0,0 +1,11 @@ +// @ts-nocheck +// #ifndef UNI-APP-X +type UTSJSONObject = Record +// #endif +export function obj2url(data: UTSJSONObject, isPrefix: boolean = false): string { + const prefix = isPrefix ? '?' : ''; + const _result = []; + + + +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/package.json b/src/uni_modules/lime-shared/package.json new file mode 100644 index 0000000..7dd9a97 --- /dev/null +++ b/src/uni_modules/lime-shared/package.json @@ -0,0 +1,87 @@ +{ + "id": "lime-shared", + "displayName": "lime-shared", + "version": "0.2.9", + "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", + "app-harmony": "u" + }, + "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" + } + } + } + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/pathToBase64/index.ts b/src/uni_modules/lime-shared/pathToBase64/index.ts new file mode 100644 index 0000000..af9a9d2 --- /dev/null +++ b/src/uni_modules/lime-shared/pathToBase64/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +// #ifndef UNI-APP-X && APP +export * from './vue.ts' +// #endif + + +// #ifdef UNI-APP-X && APP +export * from './uvue.uts' +// #endif diff --git a/src/uni_modules/lime-shared/pathToBase64/uvue.uts b/src/uni_modules/lime-shared/pathToBase64/uvue.uts new file mode 100644 index 0000000..d5bbdb1 --- /dev/null +++ b/src/uni_modules/lime-shared/pathToBase64/uvue.uts @@ -0,0 +1,17 @@ +// @ts-nocheck +// import { processFile, ProcessFileOptions } from '@/uni_modules/lime-file-utils' +export function pathToBase64(path : string) : Promise { + 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) + // }) +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/pathToBase64/vue.ts b/src/uni_modules/lime-shared/pathToBase64/vue.ts new file mode 100644 index 0000000..8167f88 --- /dev/null +++ b/src/uni_modules/lime-shared/pathToBase64/vue.ts @@ -0,0 +1,121 @@ +// @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 { + 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 + }) +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/platform/index.ts b/src/uni_modules/lime-shared/platform/index.ts new file mode 100644 index 0000000..55fd412 --- /dev/null +++ b/src/uni_modules/lime-shared/platform/index.ts @@ -0,0 +1,34 @@ +// @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 +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/raf/index.ts b/src/uni_modules/lime-shared/raf/index.ts new file mode 100644 index 0000000..d9b2cdf --- /dev/null +++ b/src/uni_modules/lime-shared/raf/index.ts @@ -0,0 +1,10 @@ +// @ts-nocheck + +// #ifdef UNI-APP-X && APP +export * from './uvue' +// #endif + + +// #ifndef UNI-APP-X && APP +export * from './vue' +// #endif diff --git a/src/uni_modules/lime-shared/raf/uvue.ts b/src/uni_modules/lime-shared/raf/uvue.ts new file mode 100644 index 0000000..1b0d9f2 --- /dev/null +++ b/src/uni_modules/lime-shared/raf/uvue.ts @@ -0,0 +1,48 @@ +// @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 + diff --git a/src/uni_modules/lime-shared/raf/vue.ts b/src/uni_modules/lime-shared/raf/vue.ts new file mode 100644 index 0000000..98a364e --- /dev/null +++ b/src/uni_modules/lime-shared/raf/vue.ts @@ -0,0 +1,32 @@ +// @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) + }); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果 +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/random/index.ts b/src/uni_modules/lime-shared/random/index.ts new file mode 100644 index 0000000..49a21ed --- /dev/null +++ b/src/uni_modules/lime-shared/random/index.ts @@ -0,0 +1,24 @@ +// @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 之间的一个保留三位小数的随机数 \ No newline at end of file diff --git a/src/uni_modules/lime-shared/range/index.ts b/src/uni_modules/lime-shared/range/index.ts new file mode 100644 index 0000000..483b7d1 --- /dev/null +++ b/src/uni_modules/lime-shared/range/index.ts @@ -0,0 +1,36 @@ +// @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] \ No newline at end of file diff --git a/src/uni_modules/lime-shared/readme.md b/src/uni_modules/lime-shared/readme.md new file mode 100644 index 0000000..de213cf --- /dev/null +++ b/src/uni_modules/lime-shared/readme.md @@ -0,0 +1,464 @@ +# lime-shared 工具库 +- 本人插件的几个公共函数 +- 按需引入 + +## 安装 +在插件市场导入即可 + +## 文档 +[shared](https://limex.qcoon.cn/shared/overview.html) + +## 使用 +按需引入只会引入相关的方法,不要看着 插件函数列表多 而占空间,只要不引用不会被打包 +```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): 在给定数组中找到最接近目标数字的元素 ++ [shuffle](#api_shuffle): 将给定的数组打乱 ++ [isBase64](#api_isBase64): 判断字符串是否为base64 ++ [isNumber](#api_isNumber): 检查一个值是否为数字类型 ++ [isNumeric](#api_isNumeric): 检查一个值是否为数字类型或表示数字的字符串 ++ [isString](#api_isString): 检查一个值是否为字符串类型 ++ [composition-api](#api_composition-api): 为兼容vue2 + +## Utils + + +### getRect +- 返回节点尺寸信息 + +```js +// 组件内需要传入上下文 +// 如果是nvue 则需要在节点上加与id或class同名的ref +getRect('#id',this).then(res => {}) +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + + +### addUnit +- 将未带单位的数值添加px,如果有单位则返回原值 + +```js +addUnit(10) +// 10px +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + + +### unitConvert +- 将带有rpx|px的字符转成number,若本身是number则直接返回 + +```js +unitConvert('10rpx') +// 5 设备不同 返回的值也不同 +unitConvert('10px') +// 10 +unitConvert(10) +// 10 +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### canIUseCanvas2d +- 环境是否支持使用 canvas 2d + +```js +canIUseCanvas2d() +// 若支持返回 true 否则 false +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### getCurrentPage +- 获取当前页 + +```js +const page = getCurrentPage() +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### base64ToPath +- 把base64的图片转成临时路径 + +```js +base64ToPath(`xxxxx`).then(res => {}) +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### pathToBase64 +- 把图片的临时路径转成base64 + +```js +pathToBase64(`xxxxx/xxx.png`).then(res => {}) +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### sleep +- 睡眠,让 async 内部程序等待一定时间后再执行 + +```js +async next () => { + await sleep(300) + console.log('limeui'); +} +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### throttle +- 节流 + +```js +throttle((nama) => {console.log(nama)}, 200)('limeui'); +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### debounce +- 防抖 + +```js +debounce((nama) => {console.log(nama)}, 200)('limeui'); +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### random +- 返回指定范围的随机数 + +```js +random(1, 5); +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### range +- 生成区间数组 + +```js +range(0, 5) +// [0,1,2,3,4,5] +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### clamp +- 夹在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 +- 返回两个浮点数相加的结果 + +```js +floatAdd(0.1, 0.2) // 0.3 +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### fillZero +- 补零,如果传入的是`个位数`则在前面补0 + +```js +fillZero(9); +// 09 +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### exif +- 获取图片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 +- 获取页面或当前实例的指定组件,会在页面或实例向所有的节点查找(包括子组件或子子组件) +- 仅vue3,vue2没有测试过 + +```js +// 当前页面 +const page = getCurrentPage() +selectComponent('.custom', {context: page}).then(res => { +}) +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | x | + + + +### createAnimation +- 创建动画,与uni.createAnimation使用方法一致,只为了抹平nvue + +```html + +``` +```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 +- 将字符串转换为 camelCase 或 PascalCase 风格的命名约定 + +```js +camelCase("hello world") // helloWorld +camelCase("hello world", true) // HelloWorld +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### kebabCase +- 将字符串转换为指定连接符的命名约定 + +```js +kebabCase("helloWorld") // hello-world +kebabCase("hello world_example") // hello-world-example +kebabCase("helloWorld", "_") // hello_world +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + + +### closest +- 在给定数组中找到最接近目标数字的元素 + +```js +closest([1, 3, 5, 7, 9], 6) // 5 +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### shuffle +- 在给定数组中找到最接近目标数字的元素 + +```js +shuffle([1, 3, 5, 7, 9]) +``` + +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + +### isBase64 +- 判断字符串是否为base64 + +```js +isBase64('xxxxx') +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### isNumber +- 检查一个值是否为数字类型 + +```js +isNumber('0') // false +isNumber(0) // true +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + +### isNumeric +- 检查一个值是否为数字类型或表示数字的字符串 + +```js +isNumeric('0') // true +isNumeric(0) // true +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + +### isString +- 检查一个值是否为数字类型或表示数字的字符串 + +```js +isString('0') // true +isString(0) // false +``` +##### 兼容性 +| uni-app | uni-app x | +|------------|----------------------------------| +| √ | √ | + + + + +## composition-api +- 因本人插件需要兼容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 | diff --git a/src/uni_modules/lime-shared/selectAllComponent/index.ts b/src/uni_modules/lime-shared/selectAllComponent/index.ts new file mode 100644 index 0000000..17c3a3c --- /dev/null +++ b/src/uni_modules/lime-shared/selectAllComponent/index.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +// #ifdef UNI-APP-X +export * from './uvue.uts' +// #endif + +// #ifndef UNI-APP-X +export * from './vue.ts' +// #endif \ No newline at end of file diff --git a/src/uni_modules/lime-shared/selectAllComponent/uvue.uts b/src/uni_modules/lime-shared/selectAllComponent/uvue.uts new file mode 100644 index 0000000..07c9fcd --- /dev/null +++ b/src/uni_modules/lime-shared/selectAllComponent/uvue.uts @@ -0,0 +1,39 @@ +// @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 +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/selectAllComponent/vue.ts b/src/uni_modules/lime-shared/selectAllComponent/vue.ts new file mode 100644 index 0000000..380bd7a --- /dev/null +++ b/src/uni_modules/lime-shared/selectAllComponent/vue.ts @@ -0,0 +1,151 @@ +// @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 +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/selectComponent/index.ts b/src/uni_modules/lime-shared/selectComponent/index.ts new file mode 100644 index 0000000..52454fb --- /dev/null +++ b/src/uni_modules/lime-shared/selectComponent/index.ts @@ -0,0 +1,7 @@ +// @ts-nocheck +// #ifndef UNI-APP-X +export * from './vue.ts' +// #endif +// #ifdef UNI-APP-X +export * from './uvue.uts' +// #endif \ No newline at end of file diff --git a/src/uni_modules/lime-shared/selectComponent/uvue.uts b/src/uni_modules/lime-shared/selectComponent/uvue.uts new file mode 100644 index 0000000..c2aa2bc --- /dev/null +++ b/src/uni_modules/lime-shared/selectComponent/uvue.uts @@ -0,0 +1,75 @@ +// @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() diff --git a/src/uni_modules/lime-shared/selectComponent/vue.ts b/src/uni_modules/lime-shared/selectComponent/vue.ts new file mode 100644 index 0000000..9fca0cd --- /dev/null +++ b/src/uni_modules/lime-shared/selectComponent/vue.ts @@ -0,0 +1,149 @@ +// @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) +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/selectElement/index.uts b/src/uni_modules/lime-shared/selectElement/index.uts new file mode 100644 index 0000000..355b043 --- /dev/null +++ b/src/uni_modules/lime-shared/selectElement/index.uts @@ -0,0 +1,275 @@ +// @ts-nocheck +import {isDef} from '../isDef' +import {type 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 +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() \ No newline at end of file diff --git a/src/uni_modules/lime-shared/shuffle/index.ts b/src/uni_modules/lime-shared/shuffle/index.ts new file mode 100644 index 0000000..5b3ab51 --- /dev/null +++ b/src/uni_modules/lime-shared/shuffle/index.ts @@ -0,0 +1,16 @@ +// @ts-nocheck +/** + * 随机化数组中元素的顺序,使用 Fisher-Yates 算法 + * @description 函数接受一个数组作为参数,返回一个新的数组,其中包含原数组随机化顺序后的元素。 + * @param arr 要随机化的数组 + * @returns 一个新的数组,其中包含原数组随机化顺序后的元素。 + */ +export function shuffle(arr : T[]) : T[] { + for (let i = arr.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)) + const temp = arr[i] + arr[i] = arr[j] + arr[j] = temp + } + return arr +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/sleep/index.ts b/src/uni_modules/lime-shared/sleep/index.ts new file mode 100644 index 0000000..a01cdfd --- /dev/null +++ b/src/uni_modules/lime-shared/sleep/index.ts @@ -0,0 +1,44 @@ +// @ts-nocheck +/** + * 延迟指定时间后解析的 Promise + * @param delay 延迟的时间(以毫秒为单位),默认为 300 毫秒 + * @returns 一个 Promise,在延迟结束后解析 + */ + + +// #ifdef UNI-APP-X && APP +function sleep(delay: number = 300):Promise { + 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(); \ No newline at end of file diff --git a/src/uni_modules/lime-shared/throttle/index.ts b/src/uni_modules/lime-shared/throttle/index.ts new file mode 100644 index 0000000..fff1d82 --- /dev/null +++ b/src/uni_modules/lime-shared/throttle/index.ts @@ -0,0 +1,77 @@ +// @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( + 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); \ No newline at end of file diff --git a/src/uni_modules/lime-shared/toArray/index.ts b/src/uni_modules/lime-shared/toArray/index.ts new file mode 100644 index 0000000..e0840d2 --- /dev/null +++ b/src/uni_modules/lime-shared/toArray/index.ts @@ -0,0 +1,21 @@ +// @ts-nocheck +/** + * 将一个或多个元素转换为数组 + * @param item 要转换为数组的元素 + * @returns 转换后的数组 + */ +// #ifndef UNI-APP-X && APP +export const toArray = (item: T | T[]): T[] => Array.isArray(item) ? item : [item]; +// #endif + + +// #ifdef UNI-APP-X && APP +export function toArray(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"] \ No newline at end of file diff --git a/src/uni_modules/lime-shared/toBoolean/index.ts b/src/uni_modules/lime-shared/toBoolean/index.ts new file mode 100644 index 0000000..f61d49d --- /dev/null +++ b/src/uni_modules/lime-shared/toBoolean/index.ts @@ -0,0 +1,40 @@ +// @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 { + return !!value//value !== null && value !== undefined; +} +// #endif + +export { + toBoolean +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/toNumber/index.ts b/src/uni_modules/lime-shared/toNumber/index.ts new file mode 100644 index 0000000..a8e1b1d --- /dev/null +++ b/src/uni_modules/lime-shared/toNumber/index.ts @@ -0,0 +1,28 @@ +// @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" \ No newline at end of file diff --git a/src/uni_modules/lime-shared/unitConvert/index.ts b/src/uni_modules/lime-shared/unitConvert/index.ts new file mode 100644 index 0000000..455d40d --- /dev/null +++ b/src/uni_modules/lime-shared/unitConvert/index.ts @@ -0,0 +1,80 @@ +// @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 | null | undefined, 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; + return uni.rpx2px(_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 (数字不需要转换) \ No newline at end of file diff --git a/src/uni_modules/lime-shared/vue/index.ts b/src/uni_modules/lime-shared/vue/index.ts new file mode 100644 index 0000000..07f7135 --- /dev/null +++ b/src/uni_modules/lime-shared/vue/index.ts @@ -0,0 +1,16 @@ +// @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