uview-pro 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/changelog.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## 0.0.5(2025-08-19)
2
+
3
+ ### ✨ Features | 新功能
4
+
5
+ - 新增 u-city-select 城市选择器组件 ([0eb4806](https://gitee.com/anyup/uView-Pro/commit/0eb4806db3be39e1a6c6f33c9ea511d8445da884))
6
+ - 完善 u-button 的 open-type 支持类型 ([37c0db5](https://gitee.com/anyup/uView-Pro/commit/37c0db527258bca57dbd55d7013b633230489853))
7
+
8
+ ### 🐛 Bug Fixes | Bug 修复
9
+
10
+ - u-upload 暴露 lists 属性 ([09f8424](https://gitee.com/anyup/uView-Pro/commit/09f8424774baaee3b6fc7a42458949f8d5903951))
11
+ - u-upload 深度监听文件列表变化并优化事件触发 ([a41a571](https://gitee.com/anyup/uView-Pro/commit/a41a5719ddf9d6793b78c55a13025bbdc88fdfe3))
12
+
13
+ ### 🚀 Demos | 示例页面优化
14
+
15
+ - 优化关于页面布局和内容 ([ad5f6a4](https://gitee.com/anyup/uView-Pro/commit/ad5f6a47847999268b43b8c5dbf1a34cb8f70802))
16
+ - 删除分类数据文件 ([5ed7a11](https://gitee.com/anyup/uView-Pro/commit/5ed7a1113db58ff493ad606296a210358348affe))
17
+ - 重构 index list 页面 ([13d780e](https://gitee.com/anyup/uView-Pro/commit/13d780ea5acc4c8eed72062482735df826d4b37a))
18
+ - 更新商场菜单组件引用 ([a5f1bf3](https://gitee.com/anyup/uView-Pro/commit/a5f1bf3f256705d6cad028d60701b4b0544332de))
19
+ - 修改图片地址 ([c459893](https://gitee.com/anyup/uView-Pro/commit/c459893848936aa9a44e7bda3277ab1428109869))
20
+ - 重构 upload 上传组件示例页面 ([686831d](https://gitee.com/anyup/uView-Pro/commit/686831de357aca67bbf7015e2f0696cf6bf48164))
21
+ - 优化多个组件的代码结构和样式 ([f2af44c](https://gitee.com/anyup/uView-Pro/commit/f2af44ca1710334495e4c4fad99d04027b3788f8))
22
+ - 添加提交规范相关配置文件 git-cz/husky/changelog ([d93b816](https://gitee.com/anyup/uView-Pro/commit/d93b816a5a3e468c4bc45e3161d7c006cba5fbf6))
23
+ - 优化 deepClone 和 deepMerge 页面的结果展示 ([b0daa70](https://gitee.com/anyup/uView-Pro/commit/b0daa700b6a385e037d38dc1f10b3612596e2403))
24
+ - 新增优惠券模板 ([1b77762](https://gitee.com/anyup/uView-Pro/commit/1b777621615f7ebe9d83606d53650987c8b2c4e0))
25
+ - 更新 easycom 配置说明,一定要放在 custom 里,否则不生效 ([fc14bf9](https://gitee.com/anyup/uView-Pro/commit/fc14bf90cb77088d258e20e79e3d25820f37e97e))
26
+ - 添加模板示例页面 ([3336af4](https://gitee.com/anyup/uView-Pro/commit/3336af406161648d18578c988d9b3ad79b86059a))
27
+ - 新增模版相关页面 ([8925a02](https://gitee.com/anyup/uView-Pro/commit/8925a02f9fa88f4742d984f2ff02909afc6ad0d7))
28
+ - 重构 request 类,优化泛型支持 ([d7b2e6a](https://gitee.com/anyup/uView-Pro/commit/d7b2e6a224d96f717e5bdbaf09edb19b712ced47))
29
+
1
30
  ## 0.0.4(2025-08-14)
2
31
 
3
32
  ### 新增
@@ -31,3 +60,105 @@
31
60
  - 兼容安卓,iOS,微信小程序,H5 等
32
61
  - 详尽的文档支持,现代化的演示效果
33
62
  - 按需引入,精简打包体积
63
+
64
+ ### 基础组件(8)
65
+
66
+ - Color 色彩
67
+ - Icon 图标
68
+ - Image 图片
69
+ - Button 按钮
70
+ - Layout 布局
71
+ - Cell 单元格
72
+ - Badge 徽标数
73
+ - Tag 标签
74
+
75
+ ---
76
+
77
+ ### 表单组件(15)
78
+
79
+ - Form 表单
80
+ - Calendar 日历
81
+ - Select 列选择器
82
+ - Keyboard 键盘
83
+ - Picker 选择器
84
+ - Rate 评分
85
+ - Search 搜索
86
+ - NumberBox 步进器
87
+ - Upload 上传
88
+ - VerificationCode 验证码倒计时
89
+ - Field 输入框
90
+ - Checkbox 复选框
91
+ - Radio 单选框
92
+ - Switch 开关选择器
93
+ - Slider 滑动选择器
94
+
95
+ ---
96
+
97
+ ### 数据组件(4)
98
+
99
+ - Progress 进度条
100
+ - Table 表格
101
+ - CountDown 倒计时
102
+ - CountTo 数字滚动
103
+
104
+ ---
105
+
106
+ ### 反馈组件(10)
107
+
108
+ - ActionSheet 操作菜单
109
+ - AlertTips 警告提示
110
+ - Toast 消息提示
111
+ - NoticeBar 滚动通知
112
+ - TopTips 顶部提示
113
+ - SwipeAction 滑动单元格
114
+ - Collapse 折叠面板
115
+ - Popup 弹出层
116
+ - Modal 模态框
117
+ - fullScreen 压窗屏
118
+
119
+ ---
120
+
121
+ ### 布局组件(11)
122
+
123
+ - Line 线条
124
+ - Card 卡片
125
+ - Mask 遮罩层
126
+ - NoNetwork 无网络提示
127
+ - Grid 宫格布局
128
+ - Swiper 轮播图
129
+ - TimeLine 时间轴
130
+ - Skeleton 骨架屏
131
+ - Sticky 吸顶
132
+ - Waterfall 瀑布流
133
+ - Divider 分割线
134
+
135
+ ---
136
+
137
+ ### 导航组件(11)
138
+
139
+ - Dropdown 下拉菜单
140
+ - Tabbar 底部导航栏
141
+ - BackTop 返回顶部
142
+ - Navbar 导航栏
143
+ - Tabs 标签
144
+ - TabsSwiper 全屏选项卡
145
+ - Subsection 分段器
146
+ - IndexList 索引列表
147
+ - Steps 步骤条
148
+ - Empty 内容为空
149
+ - Section 查看更多
150
+
151
+ ---
152
+
153
+ ### 其他组件(8)
154
+
155
+ - MessageInput 验证码输入
156
+ - Loadmore 加载更多
157
+ - ReadMore 展开阅读更多
158
+ - LazyLoad 懒加载
159
+ - Gap 间隔槽
160
+ - Avatar 头像
161
+ - Link 超链接
162
+ - Loading 加载动画
163
+
164
+ ---
@@ -15,7 +15,7 @@
15
15
  :hover-stay-time="Number(hoverStayTime)"
16
16
  :disabled="disabled"
17
17
  :form-type="formType"
18
- :open-type="openType"
18
+ :open-type="disabled || loading ? undefined : openType"
19
19
  :app-parameter="appParameter"
20
20
  :hover-stop-propagation="hoverStopPropagation"
21
21
  :send-message-title="sendMessageTitle"
@@ -25,11 +25,15 @@
25
25
  :session-from="sessionFrom"
26
26
  :send-message-img="sendMessageImg"
27
27
  :show-message-card="showMessageCard"
28
- @getphonenumber="getphonenumber"
28
+ @getAuthorize="getAuthorize"
29
29
  @getuserinfo="getuserinfo"
30
+ @contact="contact"
31
+ @getphonenumber="getphonenumber"
30
32
  @error="error"
31
- @opensetting="opensetting"
32
33
  @launchapp="launchapp"
34
+ @opensetting="opensetting"
35
+ @chooseavatar="chooseavatar"
36
+ @agreeprivacyauthorization="agreeprivacyauthorization"
33
37
  :style="[
34
38
  customStyle,
35
39
  {
@@ -57,7 +61,7 @@
57
61
  </template>
58
62
 
59
63
  <script setup lang="ts">
60
- import { ref, computed, nextTick } from 'vue';
64
+ import { ref, computed, nextTick, type PropType } from 'vue';
61
65
  import { $u } from '../../';
62
66
 
63
67
  defineOptions({
@@ -74,7 +78,7 @@ defineOptions({
74
78
  * @property {Boolean} plain 按钮是否镂空,背景色透明
75
79
  * @property {Boolean} disabled 是否禁用
76
80
  * @property {Boolean} hair-line 是否显示按钮的细边框(默认true)
77
- * @property {Boolean} shape 按钮外观形状,见文档说明
81
+ * @property {String} shape 按钮外观形状,见文档说明
78
82
  * @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈)
79
83
  * @property {String} form-type 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
80
84
  * @property {String} open-type 开放能力
@@ -83,23 +87,60 @@ defineOptions({
83
87
  * @property {Number} hover-start-time 按住后多久出现点击态,单位毫秒
84
88
  * @property {Number} hover-stay-time 手指松开后点击态保留时间,单位毫秒
85
89
  * @property {Object} custom-style 对按钮的自定义样式,对象形式,见文档说明
90
+ * @property {String} app-parameter 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
91
+ * @property {Boolean} hover-stop-propagation 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
92
+ * @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
93
+ * @property {String} session-from 会话来源,open-type="contact"时有效。只微信小程序有效
94
+ * @property {String} send-message-title 会话内消息卡片标题,open-type="contact"时有效
95
+ * @property {String} send-message-path 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
96
+ * @property {String} send-message-img 会话内消息卡片图片,open-type="contact"时有效
97
+ * @property {Boolean} show-message-card 是否显示会话内消息卡片,open-type="contact"时有效
98
+ * @property {Number|String} throttle-time 节流,一定时间内只能触发一次,单位毫秒
99
+ * @property {String} scope 支付宝小程序,当 open-type 为 getAuthorize 时有效。可选值:'phoneNumber' | 'userInfo'
86
100
  * @event {Function} click 按钮点击
87
101
  * @event {Function} getphonenumber open-type="getPhoneNumber"时有效
88
102
  * @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
89
103
  * @event {Function} error 当使用开放能力时,发生错误的回调
90
104
  * @event {Function} opensetting 在打开授权设置页并关闭后回调
91
105
  * @event {Function} launchapp 打开 APP 成功的回调
106
+ * @event {Function} contact 客服消息回调
107
+ * @event {Function} chooseavatar 头像选择回调
108
+ * @event {Function} agreeprivacyauthorization 用户点击允许授权回调
92
109
  * @example <u-button>月落</u-button>
93
110
  */
94
111
 
95
- const emit = defineEmits<{
96
- (e: 'click', event: any): void;
97
- (e: 'getphonenumber', res: any): void;
98
- (e: 'getuserinfo', res: any): void;
99
- (e: 'error', res: any): void;
100
- (e: 'opensetting', res: any): void;
101
- (e: 'launchapp', res: any): void;
102
- }>();
112
+ const emit = defineEmits(['click', 'getuserinfo', 'contact', 'getphonenumber', 'error', 'launchapp', 'opensetting', 'chooseavatar', 'agreeprivacyauthorization']);
113
+
114
+ type ButtonScope = 'phoneNumber' | 'userInfo';
115
+
116
+ type ButtonOpenType =
117
+ | 'feedback'
118
+ | 'share'
119
+ | 'getUserInfo'
120
+ | 'contact'
121
+ | 'getPhoneNumber'
122
+ | 'launchApp'
123
+ | 'openSetting'
124
+ | 'chooseAvatar'
125
+ | 'getAuthorize'
126
+ | 'lifestyle'
127
+ | 'contactShare'
128
+ | 'openGroupProfile'
129
+ | 'openGuildProfile'
130
+ | 'openPublicProfile'
131
+ | 'shareMessageToFriend'
132
+ | 'addFriend'
133
+ | 'addColorSign'
134
+ | 'addGroupApp'
135
+ | 'addToFavorites'
136
+ | 'chooseAddress'
137
+ | 'chooseInvoiceTitle'
138
+ | 'login'
139
+ | 'subscribe'
140
+ | 'favorite'
141
+ | 'watchLater'
142
+ | 'openProfile'
143
+ | 'agreePrivacyAuthorization';
103
144
 
104
145
  const props = defineProps({
105
146
  /** 是否细边框 */
@@ -116,8 +157,10 @@ const props = defineProps({
116
157
  disabled: { type: Boolean, default: false },
117
158
  /** 是否加载中 */
118
159
  loading: { type: Boolean, default: false },
160
+ /** 支付宝小程序,当 open-type 为 getAuthorize 时有效 */
161
+ scope: { type: String as PropType<ButtonScope>, default: '' },
119
162
  /** 开放能力,具体请看uniapp稳定关于button组件部分说明 */
120
- openType: { type: String, default: '' },
163
+ openType: { type: String as PropType<ButtonOpenType>, default: '' },
121
164
  /** 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件 */
122
165
  formType: { type: String, default: '' },
123
166
  /** 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 */
@@ -278,32 +321,66 @@ function getElQuery(): Promise<any[]> {
278
321
  /**
279
322
  * open-type="getPhoneNumber"时有效
280
323
  */
281
- function getphonenumber(res: any) {
282
- emit('getphonenumber', res);
324
+ function getphonenumber(event: any) {
325
+ emit('getphonenumber', event);
283
326
  }
284
327
  /**
285
328
  * 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
286
329
  */
287
- function getuserinfo(res: any) {
288
- emit('getuserinfo', res);
330
+ function getuserinfo(event: any) {
331
+ emit('getuserinfo', event);
289
332
  }
290
333
  /**
291
334
  * 当使用开放能力时,发生错误的回调
292
335
  */
293
- function error(res: any) {
294
- emit('error', res);
336
+ function error(event: any) {
337
+ emit('error', event);
295
338
  }
296
339
  /**
297
340
  * 在打开授权设置页并关闭后回调
298
341
  */
299
- function opensetting(res: any) {
300
- emit('opensetting', res);
342
+ function opensetting(event: any) {
343
+ emit('opensetting', event);
301
344
  }
302
345
  /**
303
346
  * 打开 APP 成功的回调
304
347
  */
305
- function launchapp(res: any) {
306
- emit('launchapp', res);
348
+ function launchapp(event: any) {
349
+ emit('launchapp', event);
350
+ }
351
+
352
+ /**
353
+ * 支付宝小程序授权
354
+ * @param event
355
+ */
356
+ function getAuthorize(event: any) {
357
+ if (props.scope === 'phoneNumber') {
358
+ getphonenumber(event);
359
+ } else if (props.scope === 'userInfo') {
360
+ getuserinfo(event);
361
+ }
362
+ }
363
+
364
+ /**
365
+ * 客服消息回调
366
+ * @param event
367
+ */
368
+ function contact(event: any) {
369
+ emit('contact', event);
370
+ }
371
+ /**
372
+ * 头像选择回调
373
+ * @param event
374
+ */
375
+ function chooseavatar(event: any) {
376
+ emit('chooseavatar', event);
377
+ }
378
+ /**
379
+ * 用户点击允许授权回调
380
+ * @param event
381
+ */
382
+ function agreeprivacyauthorization(event: any) {
383
+ emit('agreeprivacyauthorization', event);
307
384
  }
308
385
  </script>
309
386
 
@@ -0,0 +1,244 @@
1
+ <template>
2
+ <u-popup
3
+ v-model="popupValue"
4
+ mode="bottom"
5
+ :popup="false"
6
+ :mask="true"
7
+ :closeable="true"
8
+ :safe-area-inset-bottom="true"
9
+ close-icon-color="#ffffff"
10
+ :z-index="uZIndex"
11
+ :maskCloseAble="props.maskCloseAble"
12
+ @close="close"
13
+ >
14
+ <u-tabs v-if="popupValue" :list="genTabsList" :is-scroll="true" :current="tabsIndex" @change="tabsChange" ref="tabs"></u-tabs>
15
+ <view class="area-box">
16
+ <view class="u-flex" :class="{ change: isChange }">
17
+ <!-- 省 -->
18
+ <view class="area-item">
19
+ <view class="u-padding-10 u-bg-gray" style="height: 100%">
20
+ <scroll-view :scroll-y="true" style="height: 100%">
21
+ <u-cell-group>
22
+ <u-cell-item v-for="(item, index) in provincesList" :title="item.label" :arrow="false" :index="index" :key="index" @click="provinceChange(index)">
23
+ <template v-if="isChooseP && province === index" #right-icon>
24
+ <u-icon size="34" name="checkbox-mark"></u-icon>
25
+ </template>
26
+ </u-cell-item>
27
+ </u-cell-group>
28
+ </scroll-view>
29
+ </view>
30
+ </view>
31
+ <!-- 市 -->
32
+ <view class="area-item">
33
+ <view class="u-padding-10 u-bg-gray" style="height: 100%">
34
+ <scroll-view :scroll-y="true" style="height: 100%">
35
+ <u-cell-group v-if="isChooseP">
36
+ <u-cell-item v-for="(item, index) in citys" :title="item.label" :arrow="false" :index="index" :key="index" @click="cityChange(index)">
37
+ <template v-if="isChooseC && city === index" #right-icon>
38
+ <u-icon size="34" name="checkbox-mark"></u-icon>
39
+ </template>
40
+ </u-cell-item>
41
+ </u-cell-group>
42
+ </scroll-view>
43
+ </view>
44
+ </view>
45
+ <!-- 区 -->
46
+ <view class="area-item">
47
+ <view class="u-padding-10 u-bg-gray" style="height: 100%">
48
+ <scroll-view :scroll-y="true" style="height: 100%">
49
+ <u-cell-group v-if="isChooseC">
50
+ <u-cell-item v-for="(item, index) in areas" :title="item.label" :arrow="false" :index="index" :key="index" @click="areaChange(index)">
51
+ <template v-if="isChooseA && area === index" #right-icon>
52
+ <u-icon size="34" name="checkbox-mark"></u-icon>
53
+ </template>
54
+ </u-cell-item>
55
+ </u-cell-group>
56
+ </scroll-view>
57
+ </view>
58
+ </view>
59
+ </view>
60
+ </view>
61
+ </u-popup>
62
+ </template>
63
+
64
+ <script setup lang="ts">
65
+ import { ref, computed, onMounted } from 'vue';
66
+ import provinces from '../../libs/util/province';
67
+ import citysData from '../../libs/util/city';
68
+ import areasData from '../../libs/util/area';
69
+ import type { PropType } from 'vue';
70
+
71
+ defineOptions({
72
+ name: 'u-city-select'
73
+ });
74
+
75
+ /**
76
+ * u-city-select 城市选择器
77
+ * @description 用于选择省、市、区三级行政区域,支持回显和自定义初始值。
78
+ * @property {Boolean} modelValue 控制弹窗显示与隐藏(v-model)
79
+ * @property {Array} defaultRegion 默认选中的省市区名称数组,如 ['广东省', '广州市', '天河区']
80
+ * @property {Array} areaCode 默认选中的省市区编码数组,如 ['440000', '440100', '440106']
81
+ * @property {Boolean} maskCloseAble 是否允许点击遮罩关闭弹窗(默认 true)
82
+ * @property {String|Number} zIndex 弹窗层级(默认 0,自动适配)
83
+ * @event update:modelValue v-model 绑定值变化时触发
84
+ * @event city-change 选择省市区后触发,返回选中的省市区对象
85
+ * @example <u-city-select v-model="show" :defaultRegion="['广东省','广州市','天河区']" @city-change="onChange"></u-city-select>
86
+ */
87
+
88
+ // props 定义
89
+ const props = defineProps({
90
+ modelValue: { type: Boolean, default: false },
91
+ defaultRegion: { type: Array as PropType<string[]>, default: () => [] },
92
+ areaCode: { type: Array as PropType<string[]>, default: () => [] },
93
+ maskCloseAble: { type: Boolean, default: true },
94
+ zIndex: { type: [String, Number], default: 0 }
95
+ });
96
+
97
+ // emits 定义
98
+ const emit = defineEmits(['update:modelValue', 'city-change']);
99
+
100
+ // 省市区选择相关响应式数据
101
+ const cityValue = ref('');
102
+ const isChooseP = ref(false); // 是否已选省
103
+ const province = ref(0); // 省下标
104
+ const provincesList = ref(provinces);
105
+ const isChooseC = ref(false); // 是否已选市
106
+ const city = ref(0); // 市下标
107
+ const citys = ref(citysData[0]);
108
+ const isChooseA = ref(false); // 是否已选区
109
+ const area = ref(0); // 区下标
110
+ const areas = ref(areasData[0][0]);
111
+ const tabsIndex = ref(0);
112
+
113
+ // v-model 双向绑定
114
+ const popupValue = computed({
115
+ get: () => props.modelValue,
116
+ set: val => emit('update:modelValue', val)
117
+ });
118
+
119
+ // 是否切换到区级
120
+ const isChange = computed(() => tabsIndex.value > 1);
121
+
122
+ // 顶部 tab 列表
123
+ const genTabsList = computed(() => {
124
+ const tabsList = [{ name: '请选择' }];
125
+ if (isChooseP.value) {
126
+ tabsList[0].name = provincesList.value[province.value].label;
127
+ tabsList[1] = { name: '请选择' };
128
+ }
129
+ if (isChooseC.value) {
130
+ tabsList[1].name = citys.value[city.value].label;
131
+ tabsList[2] = { name: '请选择' };
132
+ }
133
+ if (isChooseA.value) {
134
+ tabsList[2].name = areas.value[area.value].label;
135
+ }
136
+ return tabsList;
137
+ });
138
+
139
+ // z-index 计算
140
+ const uZIndex = computed(() => (props.zIndex ? props.zIndex : (uni as any).$u?.zIndex?.popup || 1075));
141
+
142
+ // 初始化选中项
143
+ function init() {
144
+ if (props.areaCode.length === 3) {
145
+ setProvince('', props.areaCode[0]);
146
+ setCity('', props.areaCode[1]);
147
+ setArea('', props.areaCode[2]);
148
+ } else if (props.defaultRegion.length === 3) {
149
+ setProvince(props.defaultRegion[0], '');
150
+ setCity(props.defaultRegion[1], '');
151
+ setArea(props.defaultRegion[2], '');
152
+ }
153
+ }
154
+
155
+ // 选中省
156
+ function setProvince(label = '', value = '') {
157
+ provincesList.value.forEach((v, k) => {
158
+ if (value ? v.value == value : v.label == label) {
159
+ provinceChange(k);
160
+ }
161
+ });
162
+ }
163
+ // 选中市
164
+ function setCity(label = '', value = '') {
165
+ citys.value.forEach((v, k) => {
166
+ if (value ? v.value == value : v.label == label) {
167
+ cityChange(k);
168
+ }
169
+ });
170
+ }
171
+ // 选中区
172
+ function setArea(label = '', value = '') {
173
+ areas.value.forEach((v, k) => {
174
+ if (value ? v.value == value : v.label == label) {
175
+ isChooseA.value = true;
176
+ area.value = k;
177
+ }
178
+ });
179
+ }
180
+
181
+ // 关闭弹窗
182
+ function close() {
183
+ emit('update:modelValue', false);
184
+ }
185
+ // tab 切换
186
+ function tabsChange(index: number) {
187
+ tabsIndex.value = index;
188
+ }
189
+ // 省点击
190
+ function provinceChange(index: number) {
191
+ isChooseP.value = true;
192
+ isChooseC.value = false;
193
+ isChooseA.value = false;
194
+ province.value = index;
195
+ citys.value = citysData[index];
196
+ tabsIndex.value = 1;
197
+ }
198
+ // 市点击
199
+ function cityChange(index: number) {
200
+ isChooseC.value = true;
201
+ isChooseA.value = false;
202
+ city.value = index;
203
+ areas.value = areasData[province.value][index];
204
+ tabsIndex.value = 2;
205
+ }
206
+ // 区点击
207
+ function areaChange(index: number) {
208
+ isChooseA.value = true;
209
+ area.value = index;
210
+ const result = {
211
+ province: provincesList.value[province.value],
212
+ city: citys.value[city.value],
213
+ area: areas.value[area.value]
214
+ };
215
+ emit('city-change', result);
216
+ close();
217
+ }
218
+
219
+ // 挂载时初始化
220
+ onMounted(init);
221
+ </script>
222
+
223
+ <style lang="scss">
224
+ .area-box {
225
+ width: 100%;
226
+ overflow: hidden;
227
+ height: 800rpx;
228
+
229
+ > view {
230
+ width: 150%;
231
+ transition: transform 0.3s ease-in-out 0s;
232
+ transform: translateX(0);
233
+
234
+ &.change {
235
+ transform: translateX(-33.3333333%);
236
+ }
237
+ }
238
+
239
+ .area-item {
240
+ width: 33.3333333%;
241
+ height: 800rpx;
242
+ }
243
+ }
244
+ </style>
@@ -95,6 +95,8 @@ defineOptions({
95
95
  * @event {Function} on-progress 图片上传过程中的进度变化过程触发
96
96
  * @event {Function} on-uploaded 所有图片上传完毕触发
97
97
  * @event {Function} on-choose-complete 每次选择图片后触发,只是让外部可以得知每次选择后,内部的文件列表
98
+ * @event {Function} on-choose-fail 文件选择失败时触发
99
+ * @event {Function} on-list-change 文件列表发生变化时触发
98
100
  * @example <u-upload :action="action" :file-list="fileList" ></u-upload>
99
101
  */
100
102
 
@@ -193,13 +195,17 @@ watch(
193
195
  !tmp && lists.value.push({ url: value.url, error: false, progress: 100 });
194
196
  });
195
197
  },
196
- { immediate: true }
198
+ { immediate: true, deep: true }
197
199
  );
198
200
 
199
201
  // 监听 lists 变化,发出事件
200
- watch(lists, n => {
201
- emit('on-list-change', n, props.index);
202
- });
202
+ watch(
203
+ lists,
204
+ n => {
205
+ emit('on-list-change', n, props.index);
206
+ },
207
+ { deep: true }
208
+ );
203
209
 
204
210
  /**
205
211
  * 清除列表
@@ -502,7 +508,7 @@ function checkFileExt(file: any) {
502
508
  return noArrowExt;
503
509
  }
504
510
 
505
- defineExpose({ clear, reUpload, selectFile, upload, retry, remove, doPreviewImage });
511
+ defineExpose({ clear, reUpload, selectFile, upload, retry, remove, doPreviewImage, lists });
506
512
  </script>
507
513
 
508
514
  <style lang="scss" scoped>
@@ -0,0 +1,76 @@
1
+ import deepMerge from '../function/deepMerge';
2
+
3
+ export function isFunction(f: any): boolean {
4
+ return typeof f === 'function';
5
+ }
6
+
7
+ export function isPromise(p: any): boolean {
8
+ return !!(p && p.then && p.catch);
9
+ }
10
+
11
+ export function isArray(arr: any) {
12
+ return Object.prototype.toString.call(arr) === '[object Array]';
13
+ }
14
+
15
+ /**
16
+ * 构建基础类
17
+ */
18
+ class Builder<T> {
19
+ instance: any;
20
+
21
+ constructor(instance: any) {
22
+ this.instance = instance;
23
+ }
24
+ /**
25
+ *
26
+ * @param urlConfig url 配置表
27
+ * @param extra 其他请求方法对象
28
+ * @returns Object
29
+ */
30
+ dispatch(urlConfig: Record<string, any>, extra: Record<string, any> = {}): Record<string, any> {
31
+ const builder: Record<string, any> = {};
32
+ // 创建 API
33
+ Object.keys(urlConfig).forEach(name => {
34
+ builder[name] = this.use.bind(this, urlConfig[name]);
35
+ });
36
+ return { ...builder, ...extra };
37
+ }
38
+ /**
39
+ * 发送请求
40
+ * @param {*} urlConfig : url 配置表
41
+ * @demo urlConfig = { login: { url: '/user/login', method: 'GET', loading: true } }
42
+ * @param {*} config : 开放配置,用户主动配置的
43
+ * @demo api.login({ params: { username: "admin" } })
44
+ * @returns Promise
45
+ */
46
+ use(urlConfig: Record<string, any>, config: Record<string, any> = {}): Promise<T> {
47
+ // 请求地址
48
+ let url = config?.url ?? urlConfig.url;
49
+ // 兼容 restful url,如果是使用url为function,则为restful格式
50
+ if (config.url && isFunction(config.url)) {
51
+ url = `${urlConfig.url}${config.url()}`;
52
+ }
53
+ // 请求类型,get,post,put,delete
54
+ const method = config?.method ?? urlConfig?.method ?? 'GET';
55
+ // 如果有自定义的工厂函数基础类
56
+ const options = { ...deepMerge(urlConfig, config), url, method };
57
+ if (isFunction(this.instance) || isPromise(this.instance)) {
58
+ return this.instance(options);
59
+ }
60
+ // 如果是使用的 instance
61
+ // 默认的请求基础类
62
+ return this.instance.request(options);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Http 基础类
68
+ */
69
+ class AutoHttp {
70
+ static get Builder() {
71
+ return Builder;
72
+ }
73
+ constructor() {}
74
+ }
75
+
76
+ export { AutoHttp };
@@ -3,6 +3,7 @@
3
3
  * 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截
4
4
  * @author anyup
5
5
  */
6
+ import deepMerge from '../function/deepMerge';
6
7
 
7
8
  // 请求内容类型枚举
8
9
  export enum ContentType {
@@ -20,28 +21,28 @@ export type HttpHeaderType = Record<string, string>;
20
21
  // 请求配置类型
21
22
  export interface HttpRequestConfig {
22
23
  baseURL?: string;
23
- url?: string;
24
+ url?: string | Function;
24
25
  header?: HttpHeaderType;
25
- data?: any;
26
+ data?: unknown;
26
27
  method?: HttpMethod;
27
28
  dataType?: string;
28
29
  responseType?: string;
29
- success?: (res: any) => void;
30
- fail?: (err: any) => void;
31
- complete?: (res: any) => void;
32
- [key: string]: any;
30
+ success?: (res: unknown) => void;
31
+ fail?: (err: unknown) => void;
32
+ complete?: (res: unknown) => void;
33
+ [key: string]: unknown;
33
34
  }
34
35
 
35
36
  // 拦截器类型
36
- export interface Interceptor<T = any> {
37
- handler?: (arg: T, P: typeof Promise) => any;
38
- onerror?: (arg: T, P: typeof Promise) => any;
39
- complete?: (arg: T, P: typeof Promise) => any;
37
+ export interface Interceptor<T = unknown> {
38
+ handler?: (arg: T, P: typeof Promise) => unknown;
39
+ onerror?: (arg: T, P: typeof Promise) => unknown;
40
+ complete?: (arg: T, P: typeof Promise) => unknown;
40
41
  use: (...args: any[]) => void;
41
42
  lock?: () => void;
42
43
  unlock?: () => void;
43
44
  clear?: () => void;
44
- p?: Promise<any> | null;
45
+ p?: Promise<unknown> | null;
45
46
  }
46
47
 
47
48
  /**
@@ -81,15 +82,17 @@ export class Builder {
81
82
  /**
82
83
  * 发送请求
83
84
  */
84
- use(urlConfig: Record<string, any>, data?: any, config: HttpRequestConfig = {}) {
85
- let url: string = config.url || urlConfig.url;
86
- const append: string = config.append || '';
87
- url = url + append;
88
- const defaultFn = (res: any) => res;
89
- const successFn: (res: any) => any = config.success || defaultFn;
90
- // 只传递一个参数,避免类型不符
91
- const callbackFn = (res: any) => successFn(res);
92
- return this.http.request({ url, data, ...urlConfig, ...config }).then(callbackFn);
85
+ use<T = unknown>(urlConfig: Record<string, any>, data?: unknown, config: HttpRequestConfig = {}): Promise<T> {
86
+ // 请求地址
87
+ let url = config?.url ?? urlConfig.url;
88
+ // 兼容 restful url,如果是使用url为function,则为restful格式
89
+ if (config.url && typeof config.url === 'function') {
90
+ url = `${urlConfig.url}${config.url()}`;
91
+ }
92
+ // 请求类型,get,post,put,delete
93
+ const method = config?.method ?? urlConfig?.method ?? 'GET';
94
+ const options = { ...deepMerge(urlConfig, config), url, method };
95
+ return this.http.request<T>(options);
93
96
  }
94
97
  }
95
98
 
@@ -154,11 +157,13 @@ export class Http {
154
157
  /**
155
158
  * 设置 data
156
159
  */
157
- setData(data: any) {
160
+ setData(data: unknown) {
158
161
  if (isArray(data)) {
159
162
  this.config.data = data;
163
+ } else if (isObject(data) && isObject(this.config.data)) {
164
+ this.config.data = { ...(this.config.data as object), ...(data as object) };
160
165
  } else {
161
- this.config.data = { ...this.config.data, ...data };
166
+ this.config.data = data;
162
167
  }
163
168
  return this;
164
169
  }
@@ -176,7 +181,7 @@ export class Http {
176
181
  * @returns Promise
177
182
  * 说明:配置优先级 实时传递的 options > 公共配置的 config
178
183
  */
179
- request<T = any>(options: HttpRequestConfig = {}): Promise<T> {
184
+ request<T = unknown>(options: HttpRequestConfig = {}): Promise<T> {
180
185
  if (!options) options = {};
181
186
  // 请求体
182
187
  const data = options.data || {};
@@ -191,8 +196,10 @@ export class Http {
191
196
  // 请求体:优先级为:实时传递的 > 公共配置的
192
197
  if (isArray(data)) {
193
198
  options.data = data;
199
+ } else if (isObject(data) && isObject(this.config.data)) {
200
+ options.data = { ...(this.config.data as object), ...(data as object) };
194
201
  } else {
195
- options.data = { ...this.config.data, ...data };
202
+ options.data = data;
196
203
  }
197
204
 
198
205
  // 拦截器处理
@@ -201,11 +208,11 @@ export class Http {
201
208
  const responseInterceptor = interceptors.response;
202
209
  const requestInterceptorHandler = requestInterceptor.handler;
203
210
 
204
- return new Promise((resolve: (value: T) => void, reject: (reason?: any) => void) => {
205
- function isPromise(p: any): p is Promise<any> {
206
- return p && typeof p.then === 'function' && typeof p.catch === 'function';
211
+ return new Promise((resolve: (value: T) => void, reject: (reason?: unknown) => void) => {
212
+ function isPromise(p: unknown): p is Promise<unknown> {
213
+ return !!p && typeof (p as any).then === 'function' && typeof (p as any).catch === 'function';
207
214
  }
208
- function enqueueIfLocked(promise: Promise<any> | null | undefined, callback: () => void) {
215
+ function enqueueIfLocked(promise: Promise<unknown> | null | undefined, callback: () => void) {
209
216
  if (promise) {
210
217
  promise.then(() => {
211
218
  callback();
@@ -214,31 +221,27 @@ export class Http {
214
221
  callback();
215
222
  }
216
223
  }
217
- function onresult(
218
- handler: ((arg: any, P: typeof Promise) => any) | undefined,
219
- response: any,
220
- type: number
221
- ) {
224
+ function onresult(handler: ((arg: unknown, P: typeof Promise) => unknown) | undefined, response: unknown, type: number) {
222
225
  enqueueIfLocked(responseInterceptor.p, function () {
223
226
  if (handler) {
224
- response.request = options;
227
+ (response as any).request = options;
225
228
  const ret = handler.call(responseInterceptor, response, Promise);
226
229
  response = ret === undefined ? response : ret;
227
230
  }
228
231
  if (!isPromise(response)) {
229
232
  response = Promise[type === 0 ? 'resolve' : 'reject'](response);
230
233
  }
231
- response
232
- .then((d: any) => {
233
- resolve(d.data);
234
+ (response as Promise<unknown>)
235
+ .then((d: unknown) => {
236
+ resolve((d as any).data as T);
234
237
  })
235
- .catch((e: any) => {
238
+ .catch((e: unknown) => {
236
239
  reject(e);
237
240
  });
238
241
  });
239
242
  }
240
- options.complete = (response: any) => {
241
- const statusCode: number = response.statusCode;
243
+ (options as any).complete = (response: unknown) => {
244
+ const statusCode: number = (response as any).statusCode;
242
245
  let type = 0;
243
246
  if ((statusCode >= 200 && statusCode < 300) || statusCode === 304) {
244
247
  type = 0;
@@ -251,26 +254,32 @@ export class Http {
251
254
  };
252
255
  enqueueIfLocked(requestInterceptor.p, () => {
253
256
  options = Object.assign({}, this.config, options);
254
- options.requestId = new Date().getTime();
255
- let ret: any = options;
257
+ (options as any).requestId = new Date().getTime();
258
+ let ret: unknown = options;
256
259
  if (requestInterceptorHandler) {
257
260
  ret = requestInterceptorHandler.call(requestInterceptor, options, Promise) || options;
258
261
  }
259
262
  if (!isPromise(ret)) {
260
263
  ret = Promise.resolve(ret);
261
264
  }
262
- ret.then(
263
- (d: any) => {
265
+ (ret as Promise<unknown>).then(
266
+ (d: unknown) => {
264
267
  if (d === options) {
265
- d.url = d.url && d.url.indexOf('http') !== 0 ? d.baseURL + d.url : d.url;
266
- d.url = d.restURL ? d.url + d.restURL : d.url;
267
- d.method = d.method.toUpperCase();
268
- d.method === 'UPLOAD' ? uni.uploadFile(d) : uni.request(d);
268
+ // 这里断言为 any,兼容 baseURL、restURL、url、method 等属性
269
+ const req = d as any;
270
+ req.url = req.url && req.url.indexOf('http') !== 0 ? req.baseURL + req.url : req.url;
271
+ req.url = req.restURL ? req.url + req.restURL : req.url;
272
+ req.method = req.method.toUpperCase();
273
+ if (req.method === 'UPLOAD') {
274
+ uni.uploadFile(req);
275
+ } else {
276
+ uni.request(req);
277
+ }
269
278
  } else {
270
- resolve(d);
279
+ resolve(d as T);
271
280
  }
272
281
  },
273
- (err: any) => {
282
+ (err: unknown) => {
274
283
  reject(err);
275
284
  }
276
285
  );
@@ -281,29 +290,81 @@ export class Http {
281
290
  /**
282
291
  * 并发请求
283
292
  */
284
- all<T = any>(promises: Promise<T>[]): Promise<T[]> {
293
+ all<T = unknown>(promises: Promise<T>[]): Promise<T[]> {
285
294
  return Promise.all(promises);
286
295
  }
287
296
 
288
- // 动态方法扩展:get/post/put/patch/head/delete/upload
289
- [key: string]: any;
290
- }
291
-
292
- ['get', 'post', 'put', 'patch', 'head', 'delete', 'upload'].forEach(e => {
293
- Http.prototype[e] = function (url: string, data?: any, option?: HttpRequestConfig) {
294
- return this.request(merge({ url, data, method: e.toUpperCase() }, option));
295
- };
296
- });
297
+ /**
298
+ * GET 请求
299
+ */
300
+ get<T = unknown>(url: string, data?: unknown, option?: HttpRequestConfig): Promise<T> {
301
+ return this.request<T>(merge({ url, data, method: 'GET' }, option));
302
+ }
303
+ /**
304
+ * POST 请求
305
+ */
306
+ post<T = unknown>(url: string, data?: unknown, option?: HttpRequestConfig): Promise<T> {
307
+ return this.request<T>(merge({ url, data, method: 'POST' }, option));
308
+ }
309
+ /**
310
+ * PUT 请求
311
+ */
312
+ put<T = unknown>(url: string, data?: unknown, option?: HttpRequestConfig): Promise<T> {
313
+ return this.request<T>(merge({ url, data, method: 'PUT' }, option));
314
+ }
315
+ /**
316
+ * PATCH 请求
317
+ */
318
+ patch<T = unknown>(url: string, data?: unknown, option?: HttpRequestConfig): Promise<T> {
319
+ return this.request<T>(merge({ url, data, method: 'PATCH' }, option));
320
+ }
321
+ /**
322
+ * HEAD 请求
323
+ */
324
+ head<T = unknown>(url: string, data?: unknown, option?: HttpRequestConfig): Promise<T> {
325
+ return this.request<T>(merge({ url, data, method: 'HEAD' }, option));
326
+ }
327
+ /**
328
+ * DELETE 请求
329
+ */
330
+ delete<T = unknown>(url: string, data?: unknown, option?: HttpRequestConfig): Promise<T> {
331
+ return this.request<T>(merge({ url, data, method: 'DELETE' }, option));
332
+ }
333
+ /**
334
+ * UPLOAD 文件上传
335
+ */
336
+ upload<T = unknown>(url: string, data?: unknown, option?: HttpRequestConfig): Promise<T> {
337
+ return this.request<T>(merge({ url, data, method: 'UPLOAD' }, option));
338
+ }
297
339
 
298
- ['lock', 'unlock', 'clear'].forEach(e => {
299
- Http.prototype[e] = function () {
300
- const fn = this.interceptors.request[e as keyof Interceptor];
340
+ /**
341
+ * 拦截器锁定
342
+ */
343
+ lock(): void {
344
+ const fn = this.interceptors.request.lock;
301
345
  if (typeof fn === 'function') {
302
- // 只传递 this 作为上下文,不传参数,适配所有类型
303
- (fn as () => void).call(this.interceptors.request);
346
+ fn.call(this.interceptors.request);
304
347
  }
305
- };
306
- });
348
+ }
349
+ /**
350
+ * 拦截器解锁
351
+ */
352
+ unlock(): void {
353
+ const fn = this.interceptors.request.unlock;
354
+ if (typeof fn === 'function') {
355
+ fn.call(this.interceptors.request);
356
+ }
357
+ }
358
+ /**
359
+ * 拦截器清空
360
+ */
361
+ clear(): void {
362
+ const fn = this.interceptors.request.clear;
363
+ if (typeof fn === 'function') {
364
+ fn.call(this.interceptors.request);
365
+ }
366
+ }
367
+ }
307
368
 
308
369
  // 数据合并
309
370
  function merge(a: any, b: any): any {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "id": "uview-pro",
3
3
  "name": "uview-pro",
4
- "displayName": "uView Pro是uni-app全面兼容Vue3+TS的uni-app生态框架,70+精选组件",
5
- "version": "0.0.4",
6
- "description": "uView Pro,是uni-app全面兼容Vue3的uni-app生态框架,70+精选组件已使用TypeScript重构,已全面支持uni-app Vue3.0",
4
+ "displayName": "uView Pro是uni-app全面支持Vue3+TS的uni-app生态框架,70+精选组件",
5
+ "version": "0.0.5",
6
+ "description": "uView Pro,是uni-app全面支持Vue3的uni-app生态框架,70+精选组件已使用TypeScript重构,已全面支持uni-app Vue3.0",
7
7
  "main": "index.ts",
8
8
  "module": "index.ts",
9
9
  "browser": "index.ts",
@@ -58,6 +58,8 @@
58
58
  "app-vue": "y",
59
59
  "app-nvue": "n",
60
60
  "app-uvue": "n",
61
+ "app-android": "y",
62
+ "app-ios": "y",
61
63
  "app-harmony": "u"
62
64
  },
63
65
  "H5-mobile": {
package/readme.md CHANGED
@@ -69,7 +69,7 @@ uView Pro QQ 交流群: [点击进入](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027
69
69
 
70
70
  <table class="table">
71
71
  <tr>
72
- <td><img src="https://ik.imagekit.io/anyup/images/social/weixin-chat.png" width="250" height="345" ></td>
72
+ <td><img src="https://ik.imagekit.io/anyup/images/social/weixin-chat.png?updatedAt=1755597368053" width="250" height="345" ></td>
73
73
  <td><img src="https://ik.imagekit.io/anyup/images/social/qq-chat.png" width="250" height="345" ></td>
74
74
  </tr>
75
75
  <tr>
@@ -112,6 +112,7 @@ pnpm add uview-pro
112
112
  import { createSSRApp } from 'vue';
113
113
  // npm安装方式
114
114
  import uViewPro from 'uview-pro';
115
+
115
116
  // uni_modules安装方式
116
117
  // import uViewPro from '@/uni_modules/uview-pro';
117
118
 
@@ -154,11 +155,14 @@ export function createApp() {
154
155
  // pages.json
155
156
  {
156
157
  "easycom": {
157
- // uni_modules安装的方式需要前面的"@/uni_modules/",npm安装的方式无需"@/",以下方式任选其一
158
- // npm安装方式
159
- "^u-(.*)": "uview-pro/components/u-$1/u-$1.vue"
160
- // uni_modules安装方式
161
- // "^u-(.*)": "@/uni_modules/uview-pro/components/u-$1/u-$1.vue"
158
+ // 注意一定要放在custom里,否则无效,https://ask.dcloud.net.cn/question/131175
159
+ "custom": {
160
+ // uni_modules安装的方式需要前面的"@/uni_modules/",npm安装的方式无需"@/",以下方式任选其一
161
+ // npm安装方式
162
+ "^u-(.*)": "uview-pro/components/u-$1/u-$1.vue"
163
+ // uni_modules安装方式
164
+ // "^u-(.*)": "@/uni_modules/uview-pro/components/u-$1/u-$1.vue"
165
+ }
162
166
  },
163
167
  // 此为本身已有的内容
164
168
  "pages": [