jmash-core-mp 0.1.76 → 0.1.78

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.
Files changed (201) hide show
  1. package/.editorconfig +9 -9
  2. package/.gitignore +8 -0
  3. package/.npmignore +44 -0
  4. package/README.md +60 -60
  5. package/babel.config.json +34 -34
  6. package/eslint.config.mjs +7 -7
  7. package/lib/api/auth/index.d.ts +22 -22
  8. package/lib/api/auth/index.js +211 -211
  9. package/lib/api/auth/types.d.ts +90 -90
  10. package/lib/api/auth/types.js +9 -9
  11. package/lib/api/cms/index.d.ts +20 -20
  12. package/lib/api/cms/index.js +161 -161
  13. package/lib/api/cms/types.d.ts +201 -201
  14. package/lib/api/cms/types.js +1 -1
  15. package/lib/api/constant.d.ts +6 -6
  16. package/lib/api/constant.js +8 -8
  17. package/lib/api/dict/index.d.ts +24 -24
  18. package/lib/api/dict/index.js +51 -51
  19. package/lib/api/dict/types.d.ts +40 -40
  20. package/lib/api/dict/types.js +1 -1
  21. package/lib/api/dicts.d.ts +32 -32
  22. package/lib/api/dicts.js +118 -118
  23. package/lib/api/files/index.d.ts +25 -25
  24. package/lib/api/files/index.js +134 -134
  25. package/lib/api/files/types.d.ts +38 -38
  26. package/lib/api/files/types.js +1 -1
  27. package/lib/api/index.d.ts +5 -5
  28. package/lib/api/index.js +8 -8
  29. package/lib/api/myorgan/index.d.ts +8 -8
  30. package/lib/api/myorgan/index.js +24 -24
  31. package/lib/api/myorgan/types.d.ts +22 -22
  32. package/lib/api/myorgan/types.js +1 -1
  33. package/lib/api/region/index.d.ts +7 -7
  34. package/lib/api/region/index.js +20 -20
  35. package/lib/api/region/types.d.ts +19 -19
  36. package/lib/api/region/types.js +1 -1
  37. package/lib/api/shop/index.d.ts +11 -11
  38. package/lib/api/shop/index.js +53 -53
  39. package/lib/api/shop/types.d.ts +123 -123
  40. package/lib/api/shop/types.js +1 -1
  41. package/lib/api/storage/index.d.ts +11 -11
  42. package/lib/api/storage/index.js +53 -53
  43. package/lib/api/storage/types.d.ts +39 -39
  44. package/lib/api/storage/types.js +1 -1
  45. package/lib/api/types.d.ts +43 -43
  46. package/lib/api/types.js +1 -1
  47. package/lib/components/organ/browser-organ-list.mpx.d.ts +1 -1
  48. package/lib/components/organ/my-organ-list.mpx.d.ts +1 -1
  49. package/lib/components/{auth-user/jmash-update-user.mpx.d.ts → organ/organ-search.mpx.d.ts} +1 -1
  50. package/lib/components/{auth-user/jmash-user.mpx.d.ts → shop/branch-search.mpx.d.ts} +1 -1
  51. package/lib/components/{core/jmash-login.mpx.d.ts → shop/shop-search.mpx.d.ts} +1 -1
  52. package/lib/index.d.ts +28 -28
  53. package/lib/index.js +23 -23
  54. package/lib/{packages/pages/auth/setting-page.mpx.d.ts → pages/branch-search.mpx.d.ts} +1 -1
  55. package/lib/pages/organ-search.mpx.d.ts +1 -0
  56. package/lib/pages/organ.mpx.d.ts +1 -1
  57. package/lib/pages/shop-search.mpx.d.ts +1 -0
  58. package/lib/store/organ.d.ts +19 -19
  59. package/lib/store/organ.js +83 -83
  60. package/lib/store/shop.d.ts +18 -18
  61. package/lib/store/shop.js +104 -104
  62. package/lib/store/user.d.ts +35 -35
  63. package/lib/store/user.js +243 -243
  64. package/lib/utils/common.d.ts +37 -35
  65. package/lib/utils/common.js +178 -165
  66. package/lib/utils/config.d.ts +5 -5
  67. package/lib/utils/config.js +36 -36
  68. package/lib/utils/db.d.ts +52 -52
  69. package/lib/utils/db.js +202 -202
  70. package/lib/utils/grpc.d.ts +5 -5
  71. package/lib/utils/grpc.js +20 -20
  72. package/lib/utils/net.d.ts +7 -7
  73. package/lib/utils/net.js +15 -15
  74. package/lib/utils/request.d.ts +2 -2
  75. package/lib/utils/request.js +114 -114
  76. package/lib/utils/share.d.ts +16 -16
  77. package/lib/utils/share.js +196 -196
  78. package/mpx.config.js +38 -38
  79. package/package.json +1 -1
  80. package/pnpm-workspace.yaml +5 -0
  81. package/postcss.config.js +11 -11
  82. package/project.config.json +24 -24
  83. package/project.private.config.json +13 -13
  84. package/public/index.html +15 -15
  85. package/src/api/auth/index.ts +241 -241
  86. package/src/api/auth/types.ts +173 -173
  87. package/src/api/cms/index.ts +218 -218
  88. package/src/api/cms/types.ts +385 -385
  89. package/src/api/constant.ts +8 -8
  90. package/src/api/dict/index.ts +68 -68
  91. package/src/api/dict/types.ts +73 -73
  92. package/src/api/dicts.ts +132 -132
  93. package/src/api/files/index.ts +162 -162
  94. package/src/api/files/types.ts +67 -67
  95. package/src/api/index.ts +15 -15
  96. package/src/api/myorgan/index.ts +29 -29
  97. package/src/api/myorgan/types.ts +42 -42
  98. package/src/api/region/index.ts +24 -24
  99. package/src/api/region/types.ts +36 -36
  100. package/src/api/shop/index.ts +74 -74
  101. package/src/api/shop/types.ts +244 -244
  102. package/src/api/storage/index.ts +74 -74
  103. package/src/api/storage/types.ts +74 -74
  104. package/src/api/types.ts +74 -74
  105. package/src/app.mpx +78 -78
  106. package/src/components/auth-user/jmash-logout.mpx +93 -93
  107. package/src/components/auth-user/jmash-update-user.mpx +387 -387
  108. package/src/components/auth-user/jmash-update-user.web.mpx +367 -367
  109. package/src/components/auth-user/jmash-user.mpx +310 -310
  110. package/src/components/cms/jmash-cms-activity-item.mpx +207 -207
  111. package/src/components/cms/jmash-cms-activity-list.mpx +357 -357
  112. package/src/components/cms/jmash-cms-article-item.mpx +221 -221
  113. package/src/components/cms/jmash-cms-article-list.mpx +248 -248
  114. package/src/components/cms/jmash-cms-article-view.mpx +131 -131
  115. package/src/components/cms/jmash-cms-article.mpx +231 -231
  116. package/src/components/cms/jmash-cms-like.mpx +151 -151
  117. package/src/components/cms/jmash-cms-location-list.mpx +286 -286
  118. package/src/components/cms/jmash-cms-location.mpx +256 -256
  119. package/src/components/cms/jmash-cms-product-item.mpx +185 -185
  120. package/src/components/cms/jmash-cms-product-list.mpx +230 -230
  121. package/src/components/common/jmash-avatar-edit.mpx +154 -154
  122. package/src/components/common/jmash-cascader-region.mpx +118 -118
  123. package/src/components/common/jmash-cell-group.mpx +206 -206
  124. package/src/components/common/jmash-code-interest-popup.mpx +223 -223
  125. package/src/components/common/jmash-form-cell.mpx +491 -525
  126. package/src/components/common/jmash-menu.mpx +129 -129
  127. package/src/components/common/jmash-none-data.mpx +95 -95
  128. package/src/components/common/jmash-pic-swiper.mpx +72 -72
  129. package/src/components/common/jmash-popup.mpx +138 -138
  130. package/src/components/common/jmash-search.mpx +130 -130
  131. package/src/components/common/jmash-single-btn.mpx +53 -53
  132. package/src/components/common/jmash-stepper.mpx +236 -236
  133. package/src/components/common/jmash-swipe-cell.mpx +153 -153
  134. package/src/components/common/jmash-tab-bar.mpx +58 -58
  135. package/src/components/common/jmash-textarea.mpx +88 -88
  136. package/src/components/common/jmash-top-navigation.mpx +203 -203
  137. package/src/components/common/jmash-upload-picture.mpx +199 -199
  138. package/src/components/core/jmash-cms-protocol.mpx +72 -72
  139. package/src/components/core/jmash-login.ali.mpx +284 -284
  140. package/src/components/core/jmash-login.mpx +378 -378
  141. package/src/components/core/jmash-login.web.mpx +420 -420
  142. package/src/components/core/jmash-popup-login.ali.mpx +312 -312
  143. package/src/components/core/jmash-popup-login.mpx +414 -416
  144. package/src/components/core/jmash-popup-login.web.mpx +389 -389
  145. package/src/components/organ/browser-organ-list.mpx +66 -66
  146. package/src/components/organ/my-organ-list.mpx +67 -67
  147. package/src/components/organ/organ-item.mpx +124 -124
  148. package/src/components/organ/organ-search.mpx +148 -148
  149. package/src/components/organ/organ-selected-item.mpx +88 -88
  150. package/src/components/shop/branch-item.mpx +111 -111
  151. package/src/components/shop/branch-search.mpx +142 -142
  152. package/src/components/shop/shop-item.mpx +112 -112
  153. package/src/components/shop/shop-search.mpx +142 -142
  154. package/src/components/shop/shop-selected-item.mpx +89 -89
  155. package/src/custom-tab-bar/index.mpx +10 -10
  156. package/src/global.d.ts +11 -11
  157. package/src/index.ts +94 -94
  158. package/src/packages/index.mpx +16 -16
  159. package/src/packages/pages/auth/cms-protocol.mpx +35 -35
  160. package/src/packages/pages/auth/login.mpx +62 -62
  161. package/src/packages/pages/auth/setting-page.mpx +134 -134
  162. package/src/packages/pages/auth/update-user.mpx +17 -17
  163. package/src/packages/pages/cms/cms-activity-list.mpx +86 -86
  164. package/src/packages/pages/cms/cms-article-list.mpx +88 -88
  165. package/src/packages/pages/cms/cms-article.mpx +32 -32
  166. package/src/packages/pages/cms/cms-location-list.mpx +83 -83
  167. package/src/packages/pages/cms/cms-location.mpx +32 -32
  168. package/src/packages/pages/cms/cms-product-list.mpx +88 -88
  169. package/src/packages/pages/share/scan.mpx +103 -103
  170. package/src/pages/basic-component.mpx +30 -30
  171. package/src/pages/branch-search.mpx +60 -60
  172. package/src/pages/half-home.mpx +58 -58
  173. package/src/pages/home.mpx +54 -54
  174. package/src/pages/home2.mpx +45 -45
  175. package/src/pages/index.mpx +43 -43
  176. package/src/pages/organ-search.mpx +60 -60
  177. package/src/pages/organ.mpx +105 -105
  178. package/src/pages/shop-search.mpx +60 -60
  179. package/src/pages/tabbar/home.mpx +114 -114
  180. package/src/pages/tabbar/mall.mpx +87 -87
  181. package/src/pages/tabbar/my.mpx +79 -79
  182. package/src/pages/tabbar/organ.mpx +86 -86
  183. package/src/store/organ.ts +100 -100
  184. package/src/store/shop.ts +130 -130
  185. package/src/store/user.ts +288 -288
  186. package/src/styles/index.scss +20 -20
  187. package/src/styles/vars.scss +27 -27
  188. package/src/utils/common.ts +196 -178
  189. package/src/utils/config.ts +43 -43
  190. package/src/utils/db.ts +229 -229
  191. package/src/utils/grpc.ts +21 -21
  192. package/src/utils/net.ts +18 -18
  193. package/src/utils/request.ts +126 -126
  194. package/src/utils/share.ts +194 -194
  195. package/static/ali/mini.project.json +4 -4
  196. package/static/dd/project.config.json +15 -15
  197. package/static/swan/project.swan.json +14 -14
  198. package/static/tt/project.config.json +11 -11
  199. package/static/wx/project.config.json +40 -40
  200. package/uno.config.js +9 -9
  201. package/lib/packages/pages/share/scan.mpx.d.ts +0 -1
@@ -1,525 +1,491 @@
1
- <template>
2
- <!-- 表单基础单元:左侧标题 + 右侧内容/输入/开关 -->
3
- <view
4
- class="form-cell {{ required ? 'form-cell-required' : '' }} {{ border ? 'form-cell-border' : '' }} {{ type === 'textarea' ? 'form-cell-textarea-main' : '' }}">
5
- <!-- 标题容器 -->
6
- <view class="form-cell-label-container" wx:if="{{ label }}">
7
- <view class="form-cell-label">
8
- <text class="form-cell-label-required" wx:if="{{ required }}">*</text>
9
- {{ label }}
10
- </view>
11
- <view wx:if="{{ subLabel }}" class="form-cell-sub-label">{{ subLabel }}</view>
12
- </view>
13
-
14
- <!-- 内容容器 -->
15
- <view class="form-cell-content">
16
- <!-- 输入框类型 -->
17
- <block wx:if="{{ type === 'input' }}">
18
- <input class="form-cell-input" style="{{ label ? 'text-align: right;' : 'text-align: left;' }}"
19
- disabled="{{ readonly }}" type="{{ inputType }}" value="{{ value }}" placeholder="{{ placeholder }}"
20
- placeholder-class="form-cell-placeholder" maxlength="{{ maxLength }}" data-field="{{ field }}"
21
- bind:input="handleInput" />
22
- </block>
23
-
24
- <!-- 数字输入框类型 -->
25
- <block wx:if="{{ type === 'number' }}">
26
- <input class="form-cell-input" style="{{ label ? 'text-align: right;' : 'text-align: left;' }}"
27
- disabled="{{ readonly }}" type="{{ inputType }}" value="{{ value }}" placeholder="{{ placeholder }}"
28
- placeholder-class="form-cell-placeholder" maxlength="{{ maxLength }}" data-field="{{ field }}"
29
- bind:input="handleInput" />
30
- </block>
31
-
32
- <!-- 文本域类型 -->
33
- <block wx:elif="{{ type === 'textarea' }}">
34
- <textarea class="form-cell-textarea" disabled="{{ readonly }}" value="{{ value }}"
35
- placeholder="{{ placeholder }}" placeholder-class="form-cell-placeholder"
36
- maxlength="{{ maxLength }}" data-field="{{ field }}" bind:input="handleInput" />
37
- </block>
38
-
39
- <!-- 开关类型 -->
40
- <block wx:elif="{{ type === 'switch' }}">
41
- <switch class="form-cell-switch" disabled="{{ readonly }}" checked="{{ value }}" color="#FC8E24"
42
- data-field="{{ field }}" bind:change="handleSwitch" />
43
- </block>
44
-
45
- <!-- 选择器类型 -->
46
- <block wx:elif="{{ type === 'picker' }}">
47
- <view class="form-cell-picker"
48
- style="{{ label ? 'justify-content: flex-end;' : 'justify-content: flex-start;' }}"
49
- data-field="{{ field }}" bind:tap="{{ readonly ? '' : 'handlePicker' }}">
50
- <view class="form-cell-picker-text {{ value ? '' : 'form-cell-picker-empty' }}"
51
- style="{{ label ? '' : 'margin-right: auto;' }}">
52
- {{ value || placeholder }}
53
- </view>
54
- <text class="form-cell-arrow">›</text>
55
- </view>
56
- </block>
57
-
58
- <!-- 单选类型 -->
59
- <block wx:elif="{{ type === 'radio' }}">
60
- <cube-radio-group class="form-cell-radio-group" inline>
61
- <cube-radio class="form-cell-radio" wx:for="{{ radioOptionList }}" wx:key="index"
62
- wx:for-item="option" option="{{ option }}" value="{{ radioInnerValue }}"
63
- data-field="{{ field }}" bind:input="handleRadioChange" />
64
- </cube-radio-group>
65
- </block>
66
-
67
- <!-- 选择器选项类型 -->
68
- <block wx:elif="{{ type === 'picker-option' }}">
69
- <!-- 展示区 -->
70
- <view class="form-cell-picker"
71
- style="{{ label ? 'justify-content: flex-end;' : 'justify-content: flex-start;' }}"
72
- data-field="{{ field }}" bind:tap="{{ readonly ? '' : 'handlePickerOptTap' }}">
73
- <view class="form-cell-picker-text {{ pickerOptDisplayText ? '' : 'form-cell-picker-empty' }}"
74
- style="{{ label ? '' : 'margin-right: auto;' }}">
75
- {{ pickerOptDisplayText || placeholder }}
76
- </view>
77
- <text class="form-cell-arrow">›</text>
78
- </view>
79
- <!-- 弹出选择列表 -->
80
- <cube-picker-popup wx:ref="pickerOption" show="{{ pickerOptShow }}" list="{{ pickerOptList }}"
81
- selectedIndex="{{ pickerOptSelectedIndex }}" fullyStop="true" bind:change="handlePickerOptChange"
82
- bind:confirm="handlePickerOptConfirm" bind:cancel="handlePickerOptClose" />
83
- </block>
84
-
85
- <!-- 上传图片类型 -->
86
- <block wx:elif="{{ type === 'upload' }}">
87
- <jmash-upload-picture limit="1" value="{{ value }}" bind:getImageArray="handleImageChange" />
88
- </block>
89
-
90
- <!-- 插槽类型 -->
91
- <block wx:elif="{{ type === 'slot' }}">
92
- <slot />
93
- </block>
94
- </view>
95
- </view>
96
- </template>
97
-
98
- <script lang="ts">
99
- import { createComponent, ref, computed, watch } from "@mpxjs/core";
100
- import { EventBase } from "../../api/types";
101
-
102
- createComponent({
103
- options: {
104
- styleIsolation: "apply-shared",
105
- },
106
- properties: {
107
- // 表单字段名
108
- field: {
109
- type: String,
110
- value: "",
111
- },
112
- // 左侧标题
113
- label: {
114
- type: String,
115
- value: "",
116
- },
117
- // 是否必填
118
- required: {
119
- type: Boolean,
120
- value: false,
121
- },
122
- // 类型:number / input / textarea / radio / switch / picker / picker-option / upload / slot
123
- type: {
124
- type: String,
125
- value: "input",
126
- },
127
- // 单选选项(type 为 radio 时有效)
128
- radioData: {
129
- type: Array as any,
130
- value: [],
131
- },
132
- // 单选选项映射:{ valueLable: "显示字段", valueKey: "值字段" }
133
- radioOptions: {
134
- type: Object,
135
- value: {
136
- valueLable: "value",
137
- valueKey: "key",
138
- },
139
- },
140
- // 输入类型
141
- inputType: {
142
- type: String,
143
- value: "text",
144
- },
145
- // 当前值
146
- value: {
147
- type: null,
148
- value: "",
149
- },
150
- // 占位文本
151
- placeholder: {
152
- type: String,
153
- value: "",
154
- },
155
- // 最大长度(textarea 有效)
156
- maxLength: {
157
- type: Number,
158
- value: 500,
159
- },
160
- // 是否显示底边框
161
- border: {
162
- type: Boolean,
163
- value: true,
164
- },
165
- // 子标题
166
- subLabel: {
167
- type: String,
168
- value: "",
169
- },
170
- // 是否只读
171
- readonly: {
172
- type: Boolean,
173
- value: false,
174
- },
175
- // 自动选择器数据列表
176
- pickerData: {
177
- type: Array as any,
178
- value: [],
179
- },
180
- // 自动选择器选项映射:{ valueLable: "显示字段", valueKey: "值字段" }
181
- pickerOptions: {
182
- type: Object,
183
- value: {
184
- valueLable: "value",
185
- valueKey: "key",
186
- },
187
- },
188
- },
189
- setup(props) {
190
- // 自动选择器弹框显隐
191
- let pickerOptShow = ref(false);
192
- // 自动选择器当前选中索引
193
- let pickerOptSelectedIndex = ref([0]);
194
- // 自动选择器展示文本
195
- let pickerOptDisplayText = ref("");
196
- // 自动选择器数据转 cube-picker-popup 格式(二维数组)
197
- let pickerOptList = computed(() => {
198
- const { pickerData, pickerOptions } = props;
199
- if (!pickerData || !pickerData.length) return [[]];
200
- return [
201
- pickerData.map((item: any) => ({
202
- text: item[pickerOptions.valueLable],
203
- value: item[pickerOptions.valueKey],
204
- ...item,
205
- })),
206
- ];
207
- });
208
-
209
- // 单选选项 value 是否为 boolean 类型
210
- let isBooleanRadio = computed(() => {
211
- const { radioData, radioOptions } = props;
212
- if (!radioData || !radioData.length) return false;
213
- return radioData.some((item) => typeof item[radioOptions.valueKey] === "boolean");
214
- });
215
- // 单选选项转 cube-radio 格式
216
- let radioOptionList = computed(() => {
217
- const { radioData, radioOptions } = props;
218
- if (!radioData || !radioData.length) return [];
219
- return radioData.map((item: any) => ({
220
- text: item[radioOptions.valueLable],
221
- value: isBooleanRadio.value ? String(item[radioOptions.valueKey]) : item[radioOptions.valueKey],
222
- disabled: props.readonly,
223
- }));
224
- });
225
- // 传给 cube-radio 的 value(boolean 时转字符串)
226
- let radioInnerValue = ref<string | number | boolean>("");
227
-
228
- watch(() => props.value, (val) => {
229
- if (isBooleanRadio.value) {
230
- radioInnerValue.value = val ? "true" : "false";
231
- } else {
232
- radioInnerValue.value = val;
233
- }
234
- }, { immediate: true });
235
-
236
- return {
237
- pickerOptShow,
238
- pickerOptSelectedIndex,
239
- pickerOptDisplayText,
240
- pickerOptList,
241
- isBooleanRadio,
242
- radioOptionList,
243
- radioInnerValue,
244
- };
245
- },
246
- observers: {
247
- // 当 value、data 或 options 变化时,刷新展示文本和选中索引
248
- "value, pickerData, pickerOptions"(value: any, pickerData: any[], pickerOptions: any) {
249
- if (!pickerData || !pickerData.length || !pickerOptions || !pickerOptions.valueKey) {
250
- this.pickerOptSelectedIndex = [0];
251
- this.pickerOptDisplayText = "";
252
- return;
253
- }
254
- const index = pickerData.findIndex((item) => item[pickerOptions.valueKey] === value);
255
- const selectedIndex = index >= 0 ? index : 0;
256
- const displayText = index >= 0 ? pickerData[index][pickerOptions.valueLable] : "";
257
- this.pickerOptSelectedIndex = [selectedIndex];
258
- this.pickerOptDisplayText = displayText;
259
- },
260
- },
261
- methods: {
262
- // 输入框内容变化
263
- handleInput(e: EventBase) {
264
- const field = e.currentTarget.dataset.field;
265
- this.triggerEvent("change", { field, value: e.detail.value });
266
- },
267
-
268
- // switch 状态变化
269
- handleSwitch(e: EventBase) {
270
- const field = e.currentTarget.dataset.field;
271
- this.triggerEvent("change", { field, value: e.detail.value });
272
- },
273
-
274
- // 单选变化
275
- handleRadioChange(e: EventBase) {
276
- const field = e.currentTarget.dataset.field;
277
- let value = e.detail.value;
278
- // boolean 类型时转回 boolean
279
- if (this.isBooleanRadio) {
280
- value = value === "true";
281
- }
282
- this.triggerEvent("change", { field, value });
283
- },
284
-
285
- // 选择器点击
286
- handlePicker(e: EventBase) {
287
- const field = e.currentTarget.dataset.field;
288
- this.triggerEvent("picker", { field });
289
- },
290
-
291
- // 上传图片变化
292
- handleImageChange(e: EventBase) {
293
- const imageArr = e.detail as string[];
294
- this.triggerEvent("change", { field: this.field, value: imageArr[0] || "" });
295
- },
296
-
297
- // === 自动选择器方法 ===
298
-
299
- // 点击展示区,打开选择弹框
300
- handlePickerOptTap() {
301
- // 同步当前 value 对应的索引
302
- this._feedbackPickerOpt();
303
- this.pickerOptShow = true;
304
- if (this.$refs.pickerOption) this.$refs.pickerOption.show();
305
- },
306
- // 关闭弹框(未确认,索引回滚到当前值对应项)
307
- handlePickerOptClose() {
308
- this.pickerOptShow = false;
309
- if (this.$refs.pickerOption) this.$refs.pickerOption.hide();
310
- this._feedbackPickerOpt();
311
- },
312
- // 滚动选择(仅记录索引,显示文本保持已确认值,避免未确认关闭时显示与数据不一致)
313
- handlePickerOptChange(e: EventBase) {
314
- const index = e.detail.selectedIndex as number[];
315
- this.pickerOptSelectedIndex = index;
316
- },
317
- // 确认选择
318
- handlePickerOptConfirm(e: EventBase) {
319
- const index = e.detail.selectedIndex as number[];
320
- const item = this.pickerData[index[0]];
321
- this.pickerOptSelectedIndex = index;
322
- this.pickerOptDisplayText = item[this.pickerOptions.valueLable];
323
- // 关闭弹框
324
- this.handlePickerOptClose();
325
- this.triggerEvent("change", {
326
- field: this.field,
327
- value: item[this.pickerOptions.valueKey],
328
- name: item[this.pickerOptions.valueLable],
329
- info: item,
330
- });
331
- },
332
- // 同步 value 到选中索引
333
- _feedbackPickerOpt() {
334
- if (!this.pickerData || !this.pickerData.length || !this.pickerOptions || !this.pickerOptions.valueKey) {
335
- this.pickerOptSelectedIndex = [0];
336
- return;
337
- }
338
- const index = this.pickerData.findIndex(
339
- (item: any) => item[this.pickerOptions.valueKey] === this.value
340
- );
341
- this.pickerOptSelectedIndex = [index >= 0 ? index : 0];
342
- },
343
- },
344
- });
345
- </script>
346
-
347
- <style lang="scss">
348
- @use "../../styles/index.scss" as *;
349
-
350
- .form-cell {
351
- display: flex;
352
- align-items: center;
353
- justify-content: space-between;
354
- min-height: 100rpx;
355
- padding: 20rpx 30rpx;
356
- box-sizing: border-box;
357
- background: $background-white-color;
358
- }
359
-
360
- // 标题容器
361
- .form-cell-label-container {
362
- display: flex;
363
- flex-direction: column;
364
- gap: 10rpx;
365
-
366
- // 标题
367
- .form-cell-label {
368
- font-weight: 400;
369
- font-size: 30rpx;
370
- color: $font-color;
371
- line-height: 42rpx;
372
- margin-right: 30rpx;
373
-
374
- .form-cell-label-required {
375
- color: $red-color;
376
- margin-right: 6rpx;
377
- }
378
- }
379
-
380
- // 副标题
381
- .form-cell-sub-label {
382
- font-weight: 400;
383
- font-size: $font-size-mini;
384
- color: $font-color-grey-opacity;
385
- }
386
- }
387
-
388
- // 内容容器
389
- .form-cell-content {
390
- flex: 1;
391
- display: flex;
392
- align-items: center;
393
- justify-content: flex-end;
394
-
395
- // 输入框
396
- .form-cell-input {
397
- flex: 1;
398
- font-weight: 400;
399
- font-size: 30rpx;
400
- color: $font-color;
401
- line-height: 42rpx;
402
- }
403
-
404
- // 占位符
405
- .form-cell-placeholder {
406
- color: $font-color-grey-opacity;
407
- }
408
-
409
- // 开关
410
- .form-cell-switch {
411
- transform: scale(0.8);
412
- }
413
-
414
- // 单选框组
415
- .form-cell-radio-group {
416
- flex: 1;
417
- display: flex;
418
- align-items: center;
419
- justify-content: flex-end;
420
-
421
- .form-cell-radio {
422
- margin-left: 40rpx;
423
- }
424
- }
425
-
426
- // 自动选择器
427
- .form-cell-picker {
428
- flex: 1;
429
- display: flex;
430
- align-items: center;
431
- gap: 10rpx;
432
-
433
- .form-cell-picker-text {
434
- font-weight: 400;
435
- font-size: 30rpx;
436
- color: $font-color;
437
- line-height: 42rpx;
438
- }
439
-
440
- .form-cell-picker-empty {
441
- color: $font-color-grey-opacity;
442
- }
443
-
444
- .form-cell-arrow {
445
- color: $font-color-grey-opacity;
446
- font-size: 32rpx;
447
- }
448
- }
449
- }
450
-
451
- // 必填项
452
- .form-cell-required {
453
- .form-cell-label {
454
- text {
455
- color: $red-color;
456
- }
457
- }
458
- }
459
-
460
- // 边框
461
- .form-cell-border {
462
- border-bottom: 1rpx solid $border-line;
463
-
464
- :last-child {
465
- border-bottom: none;
466
- }
467
- }
468
-
469
- // 文本域主容器 垂直分布
470
- .form-cell-textarea-main {
471
- display: flex;
472
- flex-direction: column;
473
- gap: 10rpx;
474
- align-items: flex-start;
475
-
476
- .form-cell-content {
477
- display: block;
478
- width: 100%;
479
-
480
- // 文本域
481
- .form-cell-textarea {
482
- width: 100%;
483
- height: 160rpx;
484
- text-align: left;
485
- font-weight: 400;
486
- font-size: 28rpx;
487
- color: $font-color;
488
- line-height: 40rpx;
489
- padding: 20rpx;
490
- box-sizing: border-box;
491
- border-radius: $border-mini-radius;
492
- background-color: $background-color;
493
- }
494
- }
495
- }
496
-
497
- // 上传图片组件穿透样式
498
- .upload-picture-box {
499
- .upload-box {
500
- width: 90rpx !important;
501
- height: 90rpx !important;
502
- flex: auto !important;
503
- }
504
-
505
- .image_box {
506
- width: 90rpx !important;
507
- height: 90rpx !important;
508
- flex: auto !important;
509
- margin: 0 !important;
510
- }
511
- }
512
- </style>
513
-
514
- <script type="application/json">
515
- {
516
- "component": true,
517
- "usingComponents": {
518
- "jmash-upload-picture": "./jmash-upload-picture",
519
- "cube-popup": "@mpxjs/mpx-cube-ui/lib/components/popup/index",
520
- "cube-picker-popup": "@mpxjs/mpx-cube-ui/lib/components/picker-popup/index",
521
- "cube-radio-group": "@mpxjs/mpx-cube-ui/lib/components/radio-group/index",
522
- "cube-radio": "@mpxjs/mpx-cube-ui/lib/components/radio/index",
523
- }
524
- }
525
- </script>
1
+ <template>
2
+ <!-- 表单基础单元:左侧标题 + 右侧内容/输入/开关 -->
3
+ <view
4
+ class="form-cell {{ required ? 'form-cell-required' : '' }} {{ border ? 'form-cell-border' : '' }} {{ type === 'textarea' ? 'form-cell-textarea-main' : '' }}">
5
+ <!-- 标题容器 -->
6
+ <view class="form-cell-label-container" wx:if="{{ label }}">
7
+ <view class="form-cell-label">
8
+ <text class="form-cell-label-required" wx:if="{{ required }}">*</text>
9
+ {{ label }}
10
+ </view>
11
+ <view wx:if="{{ subLabel }}" class="form-cell-sub-label">{{ subLabel }}</view>
12
+ </view>
13
+
14
+ <!-- 内容容器 -->
15
+ <view class="form-cell-content">
16
+ <!-- 输入框类型 -->
17
+ <block wx:if="{{ type === 'input' }}">
18
+ <input class="form-cell-input" style="{{ label ? 'text-align: right;' : 'text-align: left;' }}"
19
+ disabled="{{ readonly }}" type="{{ inputType }}" value="{{ value }}" placeholder="{{ placeholder }}"
20
+ placeholder-class="form-cell-placeholder" maxlength="{{ maxLength }}" data-field="{{ field }}"
21
+ bind:input="handleInput" />
22
+ </block>
23
+
24
+ <!-- 数字输入框类型 -->
25
+ <block wx:if="{{ type === 'number' }}">
26
+ <input class="form-cell-input" style="{{ label ? 'text-align: right;' : 'text-align: left;' }}"
27
+ disabled="{{ readonly }}" type="{{ inputType }}" value="{{ value }}" placeholder="{{ placeholder }}"
28
+ placeholder-class="form-cell-placeholder" maxlength="{{ maxLength }}" data-field="{{ field }}"
29
+ bind:input="handleInput" />
30
+ </block>
31
+
32
+ <!-- 文本域类型 -->
33
+ <block wx:elif="{{ type === 'textarea' }}">
34
+ <textarea class="form-cell-textarea" disabled="{{ readonly }}" value="{{ value }}"
35
+ placeholder="{{ placeholder }}" placeholder-class="form-cell-placeholder"
36
+ maxlength="{{ maxLength }}" data-field="{{ field }}" bind:input="handleInput" />
37
+ </block>
38
+
39
+ <!-- 开关类型 -->
40
+ <block wx:elif="{{ type === 'switch' }}">
41
+ <switch class="form-cell-switch" disabled="{{ readonly }}" checked="{{ value }}" color="#FC8E24"
42
+ data-field="{{ field }}" bind:change="handleSwitch" />
43
+ </block>
44
+
45
+ <!-- 选择器类型 -->
46
+ <block wx:elif="{{ type === 'picker' }}">
47
+ <view class="form-cell-picker"
48
+ style="{{ label ? 'justify-content: flex-end;' : 'justify-content: flex-start;' }}"
49
+ data-field="{{ field }}" bind:tap="{{ readonly ? '' : 'handlePicker' }}">
50
+ <view class="form-cell-picker-text {{ value ? '' : 'form-cell-picker-empty' }}"
51
+ style="{{ label ? '' : 'margin-right: auto;' }}">
52
+ {{ value || placeholder }}
53
+ </view>
54
+ <text class="form-cell-arrow">›</text>
55
+ </view>
56
+ </block>
57
+
58
+ <!-- 单选类型 -->
59
+ <block wx:elif="{{ type === 'radio' }}">
60
+ <cube-radio-group class="form-cell-radio-group" inline>
61
+ <cube-radio class="form-cell-radio" wx:for="{{ radioOptionList }}" wx:key="index"
62
+ wx:for-item="option" option="{{ option }}" value="{{ radioInnerValue }}"
63
+ data-field="{{ field }}" bind:input="handleRadioChange" />
64
+ </cube-radio-group>
65
+ </block>
66
+
67
+ <!-- 选择器选项类型 -->
68
+ <block wx:elif="{{ type === 'picker-option' }}">
69
+ <!-- 原生选择器:点击展示区唤起系统选择器,取消不触发 change,无需手动管理显隐 -->
70
+ <picker class="form-cell-picker-wrap" mode="selector" range="{{ pickerOptList }}" range-key="text"
71
+ value="{{ pickerOptSelectedIndex }}" disabled="{{ readonly }}" bind:change="handlePickerOptChange"
72
+ style="{{ label ? 'justify-content: flex-end;' : 'justify-content: flex-start;' }}">
73
+ <view class="form-cell-picker">
74
+ <view class="form-cell-picker-text {{ pickerOptDisplayText ? '' : 'form-cell-picker-empty' }}"
75
+ style="{{ label ? '' : 'margin-right: auto;' }}">
76
+ {{ pickerOptDisplayText || placeholder }}
77
+ </view>
78
+ <text class="form-cell-arrow">›</text>
79
+ </view>
80
+ </picker>
81
+ </block>
82
+
83
+ <!-- 上传图片类型 -->
84
+ <block wx:elif="{{ type === 'upload' }}">
85
+ <jmash-upload-picture limit="1" value="{{ value }}" bind:getImageArray="handleImageChange" />
86
+ </block>
87
+
88
+ <!-- 插槽类型 -->
89
+ <block wx:elif="{{ type === 'slot' }}">
90
+ <slot />
91
+ </block>
92
+ </view>
93
+ </view>
94
+ </template>
95
+
96
+ <script lang="ts">
97
+ import { createComponent, ref, computed, watch } from "@mpxjs/core";
98
+ import { EventBase } from "../../api/types";
99
+
100
+ createComponent({
101
+ options: {
102
+ styleIsolation: "apply-shared",
103
+ },
104
+ properties: {
105
+ // 表单字段名
106
+ field: {
107
+ type: String,
108
+ value: "",
109
+ },
110
+ // 左侧标题
111
+ label: {
112
+ type: String,
113
+ value: "",
114
+ },
115
+ // 是否必填
116
+ required: {
117
+ type: Boolean,
118
+ value: false,
119
+ },
120
+ // 类型:number / input / textarea / radio / switch / picker / picker-option / upload / slot
121
+ type: {
122
+ type: String,
123
+ value: "input",
124
+ },
125
+ // 单选选项(type 为 radio 时有效)
126
+ radioData: {
127
+ type: Array as any,
128
+ value: [],
129
+ },
130
+ // 单选选项映射:{ valueLable: "显示字段", valueKey: "值字段" }
131
+ radioOptions: {
132
+ type: Object,
133
+ value: {
134
+ valueLable: "value",
135
+ valueKey: "key",
136
+ },
137
+ },
138
+ // 输入类型
139
+ inputType: {
140
+ type: String,
141
+ value: "text",
142
+ },
143
+ // 当前值
144
+ value: {
145
+ type: null,
146
+ value: "",
147
+ },
148
+ // 占位文本
149
+ placeholder: {
150
+ type: String,
151
+ value: "",
152
+ },
153
+ // 最大长度(textarea 有效)
154
+ maxLength: {
155
+ type: Number,
156
+ value: 500,
157
+ },
158
+ // 是否显示底边框
159
+ border: {
160
+ type: Boolean,
161
+ value: true,
162
+ },
163
+ // 子标题
164
+ subLabel: {
165
+ type: String,
166
+ value: "",
167
+ },
168
+ // 是否只读
169
+ readonly: {
170
+ type: Boolean,
171
+ value: false,
172
+ },
173
+ // 自动选择器数据列表
174
+ pickerData: {
175
+ type: Array as any,
176
+ value: [],
177
+ },
178
+ // 自动选择器选项映射:{ valueLable: "显示字段", valueKey: "值字段" }
179
+ pickerOptions: {
180
+ type: Object,
181
+ value: {
182
+ valueLable: "value",
183
+ valueKey: "key",
184
+ },
185
+ },
186
+ },
187
+ setup(props) {
188
+ // 自动选择器当前选中索引
189
+ let pickerOptSelectedIndex = ref(0);
190
+ // 自动选择器展示文本
191
+ let pickerOptDisplayText = ref("");
192
+ // 自动选择器数据转原生 picker range 格式
193
+ let pickerOptList = computed(() => {
194
+ const { pickerData, pickerOptions } = props;
195
+ if (!pickerData || !pickerData.length) return [];
196
+ return pickerData.map((item: any) => ({
197
+ text: item[pickerOptions.valueLable],
198
+ value: item[pickerOptions.valueKey],
199
+ ...item,
200
+ }));
201
+ });
202
+
203
+ // 单选选项 value 是否为 boolean 类型
204
+ let isBooleanRadio = computed(() => {
205
+ const { radioData, radioOptions } = props;
206
+ if (!radioData || !radioData.length) return false;
207
+ return radioData.some((item) => typeof item[radioOptions.valueKey] === "boolean");
208
+ });
209
+ // 单选选项转 cube-radio 格式
210
+ let radioOptionList = computed(() => {
211
+ const { radioData, radioOptions } = props;
212
+ if (!radioData || !radioData.length) return [];
213
+ return radioData.map((item: any) => ({
214
+ text: item[radioOptions.valueLable],
215
+ value: isBooleanRadio.value ? String(item[radioOptions.valueKey]) : item[radioOptions.valueKey],
216
+ disabled: props.readonly,
217
+ }));
218
+ });
219
+ // 传给 cube-radio 的 value(boolean 时转字符串)
220
+ let radioInnerValue = ref<string | number | boolean>("");
221
+
222
+ watch(() => props.value, (val) => {
223
+ if (isBooleanRadio.value) {
224
+ radioInnerValue.value = val ? "true" : "false";
225
+ } else {
226
+ radioInnerValue.value = val;
227
+ }
228
+ }, { immediate: true });
229
+
230
+ return {
231
+ pickerOptSelectedIndex,
232
+ pickerOptDisplayText,
233
+ pickerOptList,
234
+ isBooleanRadio,
235
+ radioOptionList,
236
+ radioInnerValue,
237
+ };
238
+ },
239
+ observers: {
240
+ // 当 value、data 或 options 变化时,刷新展示文本和选中索引
241
+ "value, pickerData, pickerOptions"(value: any, pickerData: any[], pickerOptions: any) {
242
+ if (!pickerData || !pickerData.length || !pickerOptions || !pickerOptions.valueKey) {
243
+ this.pickerOptSelectedIndex = 0;
244
+ this.pickerOptDisplayText = "";
245
+ return;
246
+ }
247
+ const index = pickerData.findIndex((item) => item[pickerOptions.valueKey] === value);
248
+ this.pickerOptSelectedIndex = index >= 0 ? index : 0;
249
+ this.pickerOptDisplayText = index >= 0 ? pickerData[index][pickerOptions.valueLable] : "";
250
+ },
251
+ },
252
+ methods: {
253
+ // 输入框内容变化
254
+ handleInput(e: EventBase) {
255
+ const field = e.currentTarget.dataset.field;
256
+ this.triggerEvent("change", { field, value: e.detail.value });
257
+ },
258
+
259
+ // switch 状态变化
260
+ handleSwitch(e: EventBase) {
261
+ const field = e.currentTarget.dataset.field;
262
+ this.triggerEvent("change", { field, value: e.detail.value });
263
+ },
264
+
265
+ // 单选变化
266
+ handleRadioChange(e: EventBase) {
267
+ const field = e.currentTarget.dataset.field;
268
+ let value = e.detail.value;
269
+ // boolean 类型时转回 boolean
270
+ if (this.isBooleanRadio) {
271
+ value = value === "true";
272
+ }
273
+ this.triggerEvent("change", { field, value });
274
+ },
275
+
276
+ // 选择器点击
277
+ handlePicker(e: EventBase) {
278
+ const field = e.currentTarget.dataset.field;
279
+ this.triggerEvent("picker", { field });
280
+ },
281
+
282
+ // 上传图片变化
283
+ handleImageChange(e: EventBase) {
284
+ const imageArr = e.detail as string[];
285
+ this.triggerEvent("change", { field: this.field, value: imageArr[0] || "" });
286
+ },
287
+
288
+ // === 自动选择器方法 ===
289
+
290
+ // 确认选择(原生 picker 的 change 在点击确定后触发,取消不触发任何回调,无需回滚索引)
291
+ handlePickerOptChange(e: EventBase) {
292
+ const index = e.detail.value as number;
293
+ const item = this.pickerData[index];
294
+ if (!item) return;
295
+ this.pickerOptSelectedIndex = index;
296
+ this.pickerOptDisplayText = item[this.pickerOptions.valueLable];
297
+ this.triggerEvent("change", {
298
+ field: this.field,
299
+ value: item[this.pickerOptions.valueKey],
300
+ name: item[this.pickerOptions.valueLable],
301
+ info: item,
302
+ });
303
+ },
304
+ },
305
+ });
306
+ </script>
307
+
308
+ <style lang="scss">
309
+ @use "../../styles/index.scss" as *;
310
+
311
+ .form-cell {
312
+ display: flex;
313
+ align-items: center;
314
+ justify-content: space-between;
315
+ min-height: 100rpx;
316
+ padding: 20rpx 30rpx;
317
+ box-sizing: border-box;
318
+ background: $background-white-color;
319
+ }
320
+
321
+ // 标题容器
322
+ .form-cell-label-container {
323
+ display: flex;
324
+ flex-direction: column;
325
+ gap: 10rpx;
326
+
327
+ // 标题
328
+ .form-cell-label {
329
+ font-weight: 400;
330
+ font-size: 30rpx;
331
+ color: $font-color;
332
+ line-height: 42rpx;
333
+ margin-right: 30rpx;
334
+
335
+ .form-cell-label-required {
336
+ color: $red-color;
337
+ margin-right: 6rpx;
338
+ }
339
+ }
340
+
341
+ // 副标题
342
+ .form-cell-sub-label {
343
+ font-weight: 400;
344
+ font-size: $font-size-mini;
345
+ color: $font-color-grey-opacity;
346
+ }
347
+ }
348
+
349
+ // 内容容器
350
+ .form-cell-content {
351
+ flex: 1;
352
+ display: flex;
353
+ align-items: center;
354
+ justify-content: flex-end;
355
+
356
+ // 输入框
357
+ .form-cell-input {
358
+ flex: 1;
359
+ font-weight: 400;
360
+ font-size: 30rpx;
361
+ color: $font-color;
362
+ line-height: 42rpx;
363
+ }
364
+
365
+ // 占位符
366
+ .form-cell-placeholder {
367
+ color: $font-color-grey-opacity;
368
+ }
369
+
370
+ // 开关
371
+ .form-cell-switch {
372
+ transform: scale(0.8);
373
+ }
374
+
375
+ // 单选框组
376
+ .form-cell-radio-group {
377
+ flex: 1;
378
+ display: flex;
379
+ align-items: center;
380
+ justify-content: flex-end;
381
+
382
+ .form-cell-radio {
383
+ margin-left: 40rpx;
384
+ }
385
+ }
386
+
387
+ // 原生选择器包裹层
388
+ .form-cell-picker-wrap {
389
+ flex: 1;
390
+ display: flex;
391
+ }
392
+
393
+ // 自动选择器
394
+ .form-cell-picker {
395
+ flex: 1;
396
+ display: flex;
397
+ align-items: center;
398
+ gap: 10rpx;
399
+
400
+ .form-cell-picker-text {
401
+ font-weight: 400;
402
+ font-size: 30rpx;
403
+ color: $font-color;
404
+ line-height: 42rpx;
405
+ }
406
+
407
+ .form-cell-picker-empty {
408
+ color: $font-color-grey-opacity;
409
+ }
410
+
411
+ .form-cell-arrow {
412
+ color: $font-color-grey-opacity;
413
+ font-size: 32rpx;
414
+ }
415
+ }
416
+ }
417
+
418
+ // 必填项
419
+ .form-cell-required {
420
+ .form-cell-label {
421
+ text {
422
+ color: $red-color;
423
+ }
424
+ }
425
+ }
426
+
427
+ // 边框
428
+ .form-cell-border {
429
+ border-bottom: 1rpx solid $border-line;
430
+
431
+ :last-child {
432
+ border-bottom: none;
433
+ }
434
+ }
435
+
436
+ // 文本域主容器 垂直分布
437
+ .form-cell-textarea-main {
438
+ display: flex;
439
+ flex-direction: column;
440
+ gap: 10rpx;
441
+ align-items: flex-start;
442
+
443
+ .form-cell-content {
444
+ display: block;
445
+ width: 100%;
446
+
447
+ // 文本域
448
+ .form-cell-textarea {
449
+ width: 100%;
450
+ height: 160rpx;
451
+ text-align: left;
452
+ font-weight: 400;
453
+ font-size: 28rpx;
454
+ color: $font-color;
455
+ line-height: 40rpx;
456
+ padding: 20rpx;
457
+ box-sizing: border-box;
458
+ border-radius: $border-mini-radius;
459
+ background-color: $background-color;
460
+ }
461
+ }
462
+ }
463
+
464
+ // 上传图片组件穿透样式
465
+ .upload-picture-box {
466
+ .upload-box {
467
+ width: 90rpx !important;
468
+ height: 90rpx !important;
469
+ flex: auto !important;
470
+ }
471
+
472
+ .image_box {
473
+ width: 90rpx !important;
474
+ height: 90rpx !important;
475
+ flex: auto !important;
476
+ margin: 0 !important;
477
+ }
478
+ }
479
+ </style>
480
+
481
+ <script type="application/json">
482
+ {
483
+ "component": true,
484
+ "usingComponents": {
485
+ "jmash-upload-picture": "./jmash-upload-picture",
486
+ "cube-popup": "@mpxjs/mpx-cube-ui/lib/components/popup/index",
487
+ "cube-radio-group": "@mpxjs/mpx-cube-ui/lib/components/radio-group/index",
488
+ "cube-radio": "@mpxjs/mpx-cube-ui/lib/components/radio/index",
489
+ }
490
+ }
491
+ </script>