h5-tdsign-for-vue 0.1.0

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/README.md ADDED
@@ -0,0 +1,330 @@
1
+ # h5-tdsign-for-vue
2
+
3
+ 基于 [TDesign Mobile Vue](https://tdesign.tencent.com/mobile-vue/) 的智能表单组件库,提供开箱即用的动态表单解决方案。
4
+
5
+ [![npm version](https://img.shields.io/npm/v/h5-tdsign-for-vue.svg)](https://www.npmjs.com/package/h5-tdsign-for-vue)
6
+ [![license](https://img.shields.io/npm/l/h5-tdsign-for-vue.svg)](https://github.com/your-username/h5-tdsign-for-vue/blob/main/LICENSE)
7
+
8
+ ## ✨ 特性
9
+
10
+ - 🚀 **动态表单** - 通过 JSON Schema 配置生成表单,无需手写模板
11
+ - 📱 **移动端优化** - 基于 TDesign Mobile,完美适配移动端
12
+ - 🎯 **TypeScript** - 完整的类型定义,提供良好的开发体验
13
+ - 🔌 **自动引入** - 支持 `unplugin-vue-components` 自动按需引入
14
+ - 🎨 **丰富组件** - 内置多种表单控件,满足常见业务场景
15
+
16
+ ## 📦 安装
17
+
18
+ ```bash
19
+ # npm
20
+ npm install h5-tdsign-for-vue
21
+
22
+ # pnpm
23
+ pnpm add h5-tdsign-for-vue
24
+
25
+ # yarn
26
+ yarn add h5-tdsign-for-vue
27
+ ```
28
+
29
+ ### 前置依赖
30
+
31
+ ```bash
32
+ pnpm add vue tdesign-mobile-vue
33
+ ```
34
+
35
+ ## 🚀 快速开始
36
+
37
+ ### 全局注册
38
+
39
+ ```typescript
40
+ // main.ts
41
+ import { createApp } from 'vue'
42
+ import TDesign from 'tdesign-mobile-vue'
43
+ import 'tdesign-mobile-vue/es/style/index.css'
44
+ import { SmartForm, SmartUpload } from 'h5-tdsign-for-vue'
45
+ import 'h5-tdsign-for-vue/style.css'
46
+
47
+ const app = createApp(App)
48
+ app.use(TDesign)
49
+ app.component('SmartForm', SmartForm)
50
+ app.component('SmartUpload', SmartUpload)
51
+ app.mount('#app')
52
+ ```
53
+
54
+ ### 自动引入(推荐)
55
+
56
+ ```typescript
57
+ // vite.config.ts
58
+ import Components from 'unplugin-vue-components/vite'
59
+ import { SmartFormResolver } from 'h5-tdsign-for-vue/resolver'
60
+
61
+ export default defineConfig({
62
+ plugins: [
63
+ Components({
64
+ resolvers: [SmartFormResolver()],
65
+ }),
66
+ ],
67
+ })
68
+ ```
69
+
70
+ ## 📖 组件文档
71
+
72
+ ### SmartForm 智能表单
73
+
74
+ 通过 JSON Schema 配置生成动态表单。
75
+
76
+ ```vue
77
+ <template>
78
+ <SmartForm
79
+ v-model="formData"
80
+ :schemas="schemas"
81
+ @submit="handleSubmit"
82
+ />
83
+ </template>
84
+
85
+ <script setup lang="ts">
86
+ import { ref } from 'vue'
87
+ import type { FormSchema } from 'h5-tdsign-for-vue'
88
+
89
+ interface FormData {
90
+ name: string
91
+ phone: string
92
+ gender: string
93
+ }
94
+
95
+ const formData = ref<FormData>({
96
+ name: '',
97
+ phone: '',
98
+ gender: '',
99
+ })
100
+
101
+ const schemas: FormSchema<FormData>[] = [
102
+ {
103
+ name: 'name',
104
+ label: '姓名',
105
+ type: 'input',
106
+ required: true,
107
+ props: { placeholder: '请输入姓名' },
108
+ },
109
+ {
110
+ name: 'phone',
111
+ label: '手机号',
112
+ type: 'input',
113
+ props: { type: 'tel', placeholder: '请输入手机号' },
114
+ },
115
+ {
116
+ name: 'gender',
117
+ label: '性别',
118
+ type: 'select',
119
+ props: {
120
+ columns: [
121
+ { label: '男', value: 'male' },
122
+ { label: '女', value: 'female' },
123
+ ],
124
+ },
125
+ },
126
+ ]
127
+
128
+ const handleSubmit = (data: FormData) => {
129
+ console.log('提交数据:', data)
130
+ }
131
+ </script>
132
+ ```
133
+
134
+ #### Props
135
+
136
+ | 属性 | 说明 | 类型 | 默认值 |
137
+ |------|------|------|--------|
138
+ | `modelValue` | 表单数据 | `T` | - |
139
+ | `schemas` | 表单配置 | `FormSchema<T>[]` | `[]` |
140
+ | `labelWidth` | 标签宽度 | `string \| number` | - |
141
+ | `labelAlign` | 标签对齐 | `'left' \| 'right' \| 'top'` | `'left'` |
142
+ | `showFooter` | 显示底部按钮 | `boolean` | `true` |
143
+ | `submitBtnText` | 提交按钮文字 | `string` | `'提交'` |
144
+ | `resetBtnText` | 重置按钮文字 | `string` | `'重置'` |
145
+ | `loading` | 加载状态 | `boolean` | `false` |
146
+ | `disabled` | 禁用状态 | `boolean` | `false` |
147
+
148
+ #### 支持的组件类型
149
+
150
+ | type | 说明 | 对应组件 |
151
+ |------|------|----------|
152
+ | `input` | 输入框 | Input |
153
+ | `textarea` | 多行文本 | Textarea |
154
+ | `password` | 密码输入 | Input |
155
+ | `number` | 数字输入 | Input |
156
+ | `select` | 选择器 | SmartSelect |
157
+ | `cascader` | 级联选择 | SmartCascader |
158
+ | `tree-select` | 树形选择 | SmartTreeSelect |
159
+ | `date` | 日期选择 | SmartTimePicker |
160
+ | `time` | 时间选择 | SmartTimePicker |
161
+ | `switch` | 开关 | Switch |
162
+ | `radio` | 单选 | RadioGroup |
163
+ | `checkbox` | 多选 | CheckboxGroup |
164
+ | `stepper` | 步进器 | Stepper |
165
+ | `upload` | 上传 | SmartUpload |
166
+ | `slot` | 自定义插槽 | - |
167
+
168
+ ---
169
+
170
+ ### SmartUpload 智能上传
171
+
172
+ 支持多种数据格式的图片/文件上传组件。
173
+
174
+ ```vue
175
+ <template>
176
+ <SmartUpload
177
+ v-model="fileList"
178
+ :request-method="uploadFile"
179
+ :format-response="formatResponse"
180
+ value-type="objectArray"
181
+ url-key="fileUrl"
182
+ name-key="filename"
183
+ />
184
+ </template>
185
+
186
+ <script setup lang="ts">
187
+ import { ref } from 'vue'
188
+
189
+ interface FileData {
190
+ id: number
191
+ filename: string
192
+ fileUrl: string
193
+ }
194
+
195
+ const fileList = ref<FileData[]>([])
196
+
197
+ const uploadFile = async (file: File) => {
198
+ const formData = new FormData()
199
+ formData.append('file', file)
200
+ const res = await fetch('/api/upload', { method: 'POST', body: formData })
201
+ return { status: 'success', response: await res.json() }
202
+ }
203
+
204
+ const formatResponse = (res: any) => ({
205
+ id: res.data.id,
206
+ filename: res.data.name,
207
+ fileUrl: res.data.url,
208
+ })
209
+ </script>
210
+ ```
211
+
212
+ #### Props
213
+
214
+ | 属性 | 说明 | 类型 | 默认值 |
215
+ |------|------|------|--------|
216
+ | `modelValue` | 文件列表 | `string \| string[] \| object[]` | `''` |
217
+ | `valueType` | 值类型 | `'string' \| 'stringArray' \| 'objectArray'` | 自动推断 |
218
+ | `urlKey` | URL 字段名 | `string` | `'fileUrl'` |
219
+ | `nameKey` | 名称字段名 | `string` | `'filename'` |
220
+ | `separator` | 分隔符 | `string` | `','` |
221
+ | `urlPrefix` | URL 前缀 | `string` | `''` |
222
+ | `mode` | 上传模式 | `'image' \| 'file'` | `'image'` |
223
+ | `accept` | 接受类型 | `string \| string[]` | `'image/*'` |
224
+ | `max` | 最大数量 | `number` | `9` |
225
+ | `maxSize` | 最大大小(KB) | `number` | `10240` |
226
+ | `multiple` | 多选 | `boolean` | `true` |
227
+ | `disabled` | 禁用 | `boolean` | `false` |
228
+ | `requestMethod` | 上传方法 | `Function` | - |
229
+ | `formatResponse` | 格式化响应 | `Function` | - |
230
+
231
+ ---
232
+
233
+ ### SmartSelect 选择器
234
+
235
+ 带输入框触发的选择器组件。
236
+
237
+ ```vue
238
+ <SmartSelect
239
+ v-model="value"
240
+ label="城市"
241
+ placeholder="请选择城市"
242
+ :columns="[
243
+ { label: '北京', value: 'beijing' },
244
+ { label: '上海', value: 'shanghai' },
245
+ ]"
246
+ />
247
+ ```
248
+
249
+ ---
250
+
251
+ ### SmartCascader 级联选择
252
+
253
+ 多级联动选择器。
254
+
255
+ ```vue
256
+ <SmartCascader
257
+ v-model="value"
258
+ label="地区"
259
+ placeholder="请选择地区"
260
+ :options="areaOptions"
261
+ />
262
+ ```
263
+
264
+ ---
265
+
266
+ ### SmartTreeSelect 树形选择
267
+
268
+ 树形结构选择器。
269
+
270
+ ```vue
271
+ <SmartTreeSelect
272
+ v-model="value"
273
+ label="部门"
274
+ placeholder="请选择部门"
275
+ :options="deptOptions"
276
+ />
277
+ ```
278
+
279
+ ---
280
+
281
+ ### SmartTimePicker 时间选择器
282
+
283
+ 日期/时间选择器。
284
+
285
+ ```vue
286
+ <SmartTimePicker
287
+ v-model="value"
288
+ label="日期"
289
+ placeholder="请选择日期"
290
+ mode="date"
291
+ format="YYYY-MM-DD"
292
+ />
293
+ ```
294
+
295
+ ## 📝 类型导出
296
+
297
+ ```typescript
298
+ import type {
299
+ FormSchema,
300
+ SmartFormProps,
301
+ SmartUploadProps,
302
+ SmartUploadFile,
303
+ FileListValue,
304
+ FileListValueType,
305
+ ComponentType,
306
+ } from 'h5-tdsign-for-vue'
307
+ ```
308
+
309
+ ## 🔧 开发
310
+
311
+ ```bash
312
+ # 安装依赖
313
+ pnpm install
314
+
315
+ # 启动开发服务器
316
+ pnpm dev
317
+
318
+ # 构建组件库
319
+ pnpm build:lib
320
+
321
+ # 类型检查
322
+ pnpm type-check
323
+
324
+ # 代码检查
325
+ pnpm lint
326
+ ```
327
+
328
+ ## 📄 License
329
+
330
+ [MIT](./LICENSE)
Binary file
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("vue"),m=require("tdesign-mobile-vue");function T(u){const b=e.ref(!1),r=e.computed(()=>{const y=u.modelValue.value;if(y==null||y==="")return"";const h=u.findLabel(u.options.value,y);return h!==null?h:u.displayLabel?.value?u.displayLabel.value:String(y)});return{visible:b,displayValue:r,openPopup:()=>{b.value=!0},closePopup:()=>{b.value=!1}}}const U={class:"smart-select"},$={name:"SmartSelect"},N=e.defineComponent({...$,props:{modelValue:{type:[String,Number,Boolean],default:""},columns:{type:[Array,Function],default:()=>[]},rules:{default:()=>[]},required:{type:Boolean,default:!1},title:{default:""},label:{},placeholder:{default:"请选择"},displayLabel:{},align:{default:"left"},allowInputOverMax:{type:Boolean},autocomplete:{},autofocus:{type:Boolean},borderless:{type:Boolean,default:!0},clearTrigger:{},clearable:{type:Boolean,default:!1},cursorColor:{},disabled:{type:Boolean,default:!1},enterkeyhint:{},extra:{},format:{},layout:{},maxcharacter:{},maxlength:{},name:{},prefixIcon:{},readonly:{type:Boolean,default:!1},spellCheck:{type:Boolean},status:{},suffix:{},suffixIcon:{},tips:{},type:{},onBlur:{},onClear:{},onFocus:{},onValidate:{},cancelBtn:{type:[Boolean,String],default:!0},confirmBtn:{type:[Boolean,String],default:!0},footer:{},header:{},keys:{},option:{},renderLabel:{},swipeDuration:{},onPick:{},attach:{},closeBtn:{type:Boolean},closeOnOverlayClick:{type:Boolean},destroyOnClose:{type:Boolean},duration:{},overlayProps:{},placement:{default:"bottom"},preventScrollThrough:{type:Boolean},showOverlay:{type:Boolean,default:!0},transitionName:{},defaultVisible:{type:Boolean},zIndex:{},onClose:{},onClosed:{},onOpen:{},onOpened:{}},emits:["update:modelValue","change","confirm","cancel"],setup(u,{emit:b}){const r=u,p=b,f=(s,l)=>{let a=[];Array.isArray(s)&&(s.length>0&&Array.isArray(s[0])?a=s.flat():a=s);const t=a.find(o=>o.value===l);return t?t.label:null},{visible:y,displayValue:h,openPopup:g,closePopup:P}=T({modelValue:e.toRef(()=>r.modelValue),options:e.toRef(()=>r.columns),findLabel:(s,l)=>f(s,l),displayLabel:e.toRef(()=>r.displayLabel)}),S=["attach","closeBtn","closeOnOverlayClick","destroyOnClose","duration","overlayProps","placement","preventScrollThrough","showOverlay","transitionName","zIndex"],x=e.computed(()=>{const s={},l=r;return S.forEach(a=>{l[a]!==void 0&&(s[a]=l[a])}),s}),C=["align","autofocus","borderless","clearable","clearTrigger","disabled","label","layout","maxcharacter","maxlength","name","placeholder","prefixIcon","readonly","size","status","suffix","suffixIcon","tips","type"],V=e.computed(()=>{const s={},l=r;return C.forEach(a=>{l[a]!==void 0&&(s[a]=l[a])}),s}),i=(s,l)=>{const a=s[0];a!==void 0&&typeof a!="object"&&p("update:modelValue",a),p("confirm",s,l),P()},v=s=>{p("cancel",s),P()},c=(s,l)=>{p("change",s,l)};return(s,l)=>(e.openBlock(),e.createElementBlock("div",U,[e.createVNode(e.unref(m.Input),e.mergeProps({value:e.unref(h),onClick:e.unref(g)},{...s.$attrs,...V.value},{readonly:"","suffix-icon":()=>e.h(e.unref(m.Icon),{name:"chevron-right"})}),e.createSlots({_:2},[e.renderList(s.$slots,(a,t)=>({name:t,fn:e.withCtx(o=>[e.renderSlot(s.$slots,t,e.normalizeProps(e.guardReactiveProps(o||{})),void 0,!0)])}))]),1040,["value","onClick","suffix-icon"]),e.createVNode(e.unref(m.Popup),e.mergeProps({modelValue:e.unref(y),"onUpdate:modelValue":l[0]||(l[0]=a=>e.isRef(y)?y.value=a:null),placement:"bottom"},x.value),{default:e.withCtx(()=>[e.createVNode(e.unref(m.Picker),{value:[u.modelValue],columns:u.columns,title:u.title,"cancel-btn":u.cancelBtn,"confirm-btn":u.confirmBtn,onConfirm:i,onCancel:v,onChange:c},null,8,["value","columns","title","cancel-btn","confirm-btn"])]),_:1},16,["modelValue"])]))}}),I=(u,b)=>{const r=u.__vccOpts||u;for(const[p,f]of b)r[p]=f;return r},w=I(N,[["__scopeId","data-v-af49dba1"]]),F={class:"smart-time-picker"},M={name:"SmartTimePicker"},z=e.defineComponent({...M,props:{modelValue:{default:""},rules:{default:()=>[]},required:{type:Boolean,default:!1},label:{},placeholder:{default:"请选择时间"},mode:{default:"date"},inputFormat:{},align:{default:"left"},allowInputOverMax:{type:Boolean},autocomplete:{},autofocus:{type:Boolean,default:!1},borderless:{type:Boolean,default:!0},clearTrigger:{default:"always"},clearable:{type:Boolean,default:!1},cursorColor:{default:"#0052d9"},disabled:{type:Boolean,default:!1},enterkeyhint:{},extra:{},layout:{default:"horizontal"},maxcharacter:{},maxlength:{},name:{default:""},prefixIcon:{},readonly:{type:Boolean,default:!1},spellCheck:{type:Boolean},status:{default:"default"},suffix:{},suffixIcon:{},tips:{},type:{default:"text"},onBlur:{},onClear:{},onFocus:{},onValidate:{},cancelBtn:{default:""},confirmBtn:{default:""},end:{},footer:{},format:{default:"YYYY-MM-DD HH:mm:ss"},header:{},renderLabel:{},showWeek:{type:Boolean,default:!1},start:{},steps:{default:()=>({})},title:{default:""},onPick:{},attach:{type:[String,Function],default:"body"},closeBtn:{type:Boolean},closeOnOverlayClick:{type:Boolean,default:!0},destroyOnClose:{type:Boolean},duration:{default:240},overlayProps:{default:()=>({})},placement:{default:"bottom"},preventScrollThrough:{type:Boolean,default:!0},showOverlay:{type:Boolean,default:!0},transitionName:{default:""},defaultVisible:{type:Boolean},zIndex:{},onClose:{},onClosed:{},onOpen:{},onOpened:{}},emits:["update:modelValue","change","confirm","cancel","pick"],setup(u,{emit:b}){const r=u,p=b,f=e.ref(!1),y=["attach","closeBtn","closeOnOverlayClick","destroyOnClose","duration","overlayProps","placement","preventScrollThrough","showOverlay","transitionName","zIndex","onClose","onClosed","onOpen","onOpened"],h=r,g=e.computed(()=>{const s={};return y.forEach(l=>{h[l]!==void 0&&(s[l]=h[l])}),s}),P=e.computed(()=>{const{modelValue:s,title:l,inputFormat:a,...t}=r,o={...t};return y.forEach(d=>{delete o[d]}),["cancelBtn","confirmBtn","end","footer","header","mode","renderLabel","showWeek","start","steps","format"].forEach(d=>delete o[d]),a&&(o.format=a),o}),S=e.computed(()=>{const{label:s,placeholder:l,rules:a,required:t,modelValue:o,...n}=r,d={...n};return y.forEach(B=>{delete d[B]}),d}),x=e.computed(()=>String(r.modelValue||"")),C=()=>{f.value=!0},V=s=>{p("update:modelValue",s),p("confirm",s),f.value=!1},i=s=>{p("cancel",s),f.value=!1},v=s=>{p("change",s)},c=s=>{p("pick",s)};return(s,l)=>(e.openBlock(),e.createElementBlock("div",F,[e.createVNode(e.unref(m.Input),e.mergeProps({value:x.value,onClick:C},{...s.$attrs,...P.value},{readonly:"","suffix-icon":()=>e.h(e.unref(m.Icon),{name:"chevron-right"})}),e.createSlots({_:2},[e.renderList(s.$slots,(a,t)=>({name:t,fn:e.withCtx(o=>[e.renderSlot(s.$slots,t,e.normalizeProps(e.guardReactiveProps(o||{})),void 0,!0)])}))]),1040,["value","suffix-icon"]),e.createVNode(e.unref(m.Popup),e.mergeProps({modelValue:f.value,"onUpdate:modelValue":l[0]||(l[0]=a=>f.value=a),placement:"bottom"},g.value),{default:e.withCtx(()=>[e.createVNode(e.unref(m.DateTimePicker),e.mergeProps({value:u.modelValue},S.value,{onConfirm:V,onCancel:i,onChange:v,onPick:c}),null,16,["value"])]),_:1},16,["modelValue"])]))}}),O=I(z,[["__scopeId","data-v-cf6cd38e"]]),j={class:"smart-cascader"},K={name:"SmartCascader"},q=e.defineComponent({...K,props:{modelValue:{default:""},placeholder:{default:"请选择"},rules:{default:()=>[]},required:{type:Boolean,default:!1},label:{},displayLabel:{},align:{default:"left"},allowInputOverMax:{type:Boolean},autocomplete:{},autofocus:{type:Boolean},borderless:{type:Boolean,default:!0},clearTrigger:{},clearable:{type:Boolean,default:!1},cursorColor:{},disabled:{type:Boolean,default:!1},enterkeyhint:{},extra:{},format:{},layout:{},maxcharacter:{},maxlength:{},name:{},prefixIcon:{},readonly:{type:Boolean,default:!1},spellCheck:{type:Boolean},status:{},suffix:{},suffixIcon:{},tips:{},type:{},onBlur:{},onClear:{},onFocus:{},onValidate:{},checkStrictly:{type:Boolean,default:!1},closeBtn:{type:Boolean},header:{},keys:{},load:{},middleContent:{},options:{default:()=>[]},overlayProps:{},subTitles:{},theme:{default:"step"},title:{default:""},defaultValue:{}},emits:["update:modelValue","change","pick","close"],setup(u,{emit:b}){const r=u,p=b,f=(l,a,t=[])=>{for(const o of l){const n=o.value??o.id,d=o.label??o.name;if(n===a)return[...t,String(d)];if(o.children&&o.children.length>0){const B=f(o.children,a,[...t,String(d)]);if(B)return B}}return null},y=(l,a)=>{const t=f(l,a);return t?t.join(" / "):null},{visible:h,displayValue:g,openPopup:P,closePopup:S}=T({modelValue:e.toRef(()=>r.modelValue),options:e.toRef(()=>r.options||[]),findLabel:y,displayLabel:e.toRef(()=>r.displayLabel)}),x=["align","autofocus","borderless","clearable","clearTrigger","disabled","label","layout","maxcharacter","maxlength","name","placeholder","prefixIcon","readonly","size","status","suffix","suffixIcon","tips","type"],C=e.computed(()=>{const l={},a=r;return x.forEach(t=>{a[t]!==void 0&&(l[t]=a[t])}),l}),V=["checkStrictly","closeBtn","header","keys","load","middleContent","options","overlayProps","subTitles","theme","title"],i=e.computed(()=>{const l={},a=r;return V.forEach(t=>{a[t]!==void 0&&(l[t]=a[t])}),l}),v=(l,a)=>{p("update:modelValue",l),p("change",l,a),S()},c=l=>{p("pick",l.value,l)},s=l=>{p("close",l),S()};return(l,a)=>(e.openBlock(),e.createElementBlock("div",j,[e.createVNode(e.unref(m.Input),e.mergeProps({value:e.unref(g),onClick:e.unref(P)},{...l.$attrs,...C.value},{readonly:"","suffix-icon":()=>e.h(e.unref(m.Icon),{name:"chevron-right"})}),e.createSlots({_:2},[e.renderList(l.$slots,(t,o)=>({name:o,fn:e.withCtx(n=>[e.renderSlot(l.$slots,o,e.normalizeProps(e.guardReactiveProps(n||{})),void 0,!0)])}))]),1040,["value","onClick","suffix-icon"]),e.createVNode(e.unref(m.Cascader),e.mergeProps({visible:e.unref(h),"onUpdate:visible":a[0]||(a[0]=t=>e.isRef(h)?h.value=t:null),value:u.modelValue},i.value,{onChange:v,onPick:c,onClose:s}),null,16,["visible","value"])]))}}),_=I(q,[["__scopeId","data-v-f420c3ed"]]),Y={class:"smart-tree-select"},D={class:"tree-select-wrapper"},W={name:"SmartTreeSelect"},G=e.defineComponent({...W,props:{modelValue:{},options:{default:()=>[]},rules:{default:()=>[]},required:{type:Boolean,default:!1},title:{default:""},label:{},placeholder:{default:"请选择"},align:{default:"left"},allowInputOverMax:{type:Boolean},autocomplete:{},autofocus:{type:Boolean,default:!1},borderless:{type:Boolean,default:!0},clearTrigger:{default:"always"},clearable:{type:Boolean,default:!1},cursorColor:{default:"#0052d9"},disabled:{type:Boolean,default:!1},enterkeyhint:{},extra:{},format:{},layout:{default:"horizontal"},maxcharacter:{},maxlength:{},name:{default:""},prefixIcon:{},readonly:{type:Boolean,default:!1},spellCheck:{type:Boolean},status:{default:"default"},suffix:{},suffixIcon:{},tips:{},type:{default:"text"},onBlur:{},onClear:{},onFocus:{},onValidate:{},customStyle:{default:""},filterable:{type:Boolean,default:!1},height:{default:336},keys:{},multiple:{type:Boolean,default:!1},attach:{type:[String,Function],default:"body"},closeBtn:{type:Boolean},closeOnOverlayClick:{type:Boolean,default:!0},destroyOnClose:{type:Boolean},duration:{default:240},overlayProps:{default:()=>({})},placement:{default:"bottom"},preventScrollThrough:{type:Boolean,default:!0},showOverlay:{type:Boolean,default:!0},transitionName:{default:""},zIndex:{},onClose:{},onClosed:{},onOpen:{},onOpened:{}},emits:["update:modelValue","change","close"],setup(u,{emit:b}){const r=u,p=b,f=e.ref(!1),y=["attach","closeBtn","closeOnOverlayClick","destroyOnClose","duration","overlayProps","placement","preventScrollThrough","showOverlay","transitionName","zIndex","onClose","onClosed","onOpen","onOpened"],h=["customStyle","filterable","height","keys","multiple","options"],g=r,P=e.computed(()=>{const t={};return y.forEach(o=>{g[o]!==void 0&&(t[o]=g[o])}),t}),S=e.computed(()=>{const{modelValue:t,...o}=r,n={...o};return y.forEach(d=>{delete n[d]}),h.forEach(d=>{delete n[d]}),delete n.rules,delete n.required,delete n.title,n}),x=e.computed(()=>{const t={};return h.forEach(o=>{o!=="options"&&g[o]!==void 0&&(t[o]=g[o])}),g.keys!==void 0&&(t.keys=g.keys),t}),C=(t,o)=>{for(const n of t){if(n.value===o)return!n.children||n.children.length===0;if(n.children&&n.children.length>0){const d=C(n.children,o);if(d!==!1)return d}}return!1},V=(t,o,n=[])=>{for(const d of t){if(d.value===o)return[...n,d.value];if(d.children&&d.children.length>0){const B=V(d.children,o,[...n,d.value]);if(B)return B}}return null},i=e.ref([]),v=t=>Array.isArray(t)?t.flatMap(o=>Array.isArray(o)?o:[o]).filter(o=>typeof o=="string"||typeof o=="number"):t==null||t===""?[]:[t];e.watch(()=>r.modelValue,t=>{if(Array.isArray(t))i.value=v(t);else if(t!==void 0&&t!==""&&t!==null){const o=r.options||[],n=V(o,t);i.value=n||[]}else i.value=[]},{immediate:!0}),e.watch(()=>r.options,()=>{const t=r.modelValue;if(!Array.isArray(t)&&t!==void 0&&t!==""&&t!==null){const o=r.options||[],n=V(o,t);i.value=n||[]}},{deep:!0});const c=(t,o)=>{for(const n of t){if(n.value===o)return n.label||String(n.value);if(n.children&&n.children.length>0){const d=c(n.children,o);if(d)return d}}return null},s=e.computed(()=>{const t=r.modelValue;if(!t&&t!==0)return"";const o=r.options||[];if(Array.isArray(t)){const n=t[t.length-1];return n!==void 0?c(o,n)||String(n):""}return c(o,t)||String(t)}),l=()=>{r.disabled||(f.value=!0)},a=(t,o)=>{if(Array.isArray(t)){const n=v(t),d=n[n.length-1];i.value=n;const B=r.options||[];C(B,d)&&(p("update:modelValue",d),p("change",d,o),r.multiple||(f.value=!1))}else i.value=v(t),p("update:modelValue",t),p("change",t,o)};return(t,o)=>(e.openBlock(),e.createElementBlock("div",Y,[e.createVNode(e.unref(m.Input),e.mergeProps({value:s.value,onClick:l},{...t.$attrs,...S.value},{readonly:"","suffix-icon":()=>e.h(e.unref(m.Icon),{name:"chevron-right"})}),e.createSlots({_:2},[e.renderList(t.$slots,(n,d)=>({name:d,fn:e.withCtx(B=>[e.renderSlot(t.$slots,d,e.normalizeProps(e.guardReactiveProps(B||{})),void 0,!0)])}))]),1040,["value","suffix-icon"]),e.createVNode(e.unref(m.Popup),e.mergeProps({modelValue:f.value,"onUpdate:modelValue":o[0]||(o[0]=n=>f.value=n),placement:"bottom"},P.value),{default:e.withCtx(()=>[e.createElementVNode("div",D,[f.value?(e.openBlock(),e.createBlock(e.unref(m.TreeSelect),e.mergeProps({key:0,value:i.value,options:u.options},x.value,{onChange:a}),null,16,["value","options"])):e.createCommentVNode("",!0)])]),_:1},16,["modelValue"])]))}}),R=I(G,[["__scopeId","data-v-4d44f50a"]]),H={class:"smart-upload"},J={name:"SmartUpload"},Z=e.defineComponent({...J,props:{modelValue:{default:""},valueType:{},separator:{default:","},urlPrefix:{default:""},urlKey:{default:"fileUrl"},nameKey:{default:"filename"},mode:{default:"image"},accept:{default:"image/*"},action:{},max:{default:9},maxSize:{default:10*1024},multiple:{type:Boolean,default:!0},disabled:{type:Boolean,default:!1},requestMethod:{},formatResponse:{}},emits:["update:modelValue","success","fail","remove"],setup(u,{emit:b}){const r=u,p=b,f=e.ref([]),y=e.computed(()=>Array.isArray(r.accept)?r.accept.join(","):r.accept),h=e.computed(()=>{if(r.valueType)return r.valueType;const l=r.modelValue;return typeof l=="string"?"string":Array.isArray(l)?l.length===0?"stringArray":typeof l[0]=="object"&&l[0]!==null?"objectArray":"stringArray":"string"}),g=l=>/^(https?:|blob:|data:)/i.test(l),P=l=>{if(typeof l=="string")return l;const a=l[r.urlKey];return typeof a=="string"?a:""},S=(l,a)=>{if(typeof l=="object"&&l[r.nameKey]){const t=l[r.nameKey];return typeof t=="string"?t:String(t)}return a.split("/").pop()||""},x=l=>!r.urlPrefix||!l?l:l.startsWith(r.urlPrefix)?l.slice(r.urlPrefix.length):l,C=l=>{if(!l)return[];let a=[];return typeof l=="string"?a=l.split(r.separator).filter(Boolean):a=l.filter(Boolean),a.map((t,o)=>{const n=P(t);if(!n)return null;const B={url:g(n)?n:r.urlPrefix+n,name:S(t,n),status:"success",originalPath:n};return typeof t=="object"&&(B.__rawResult__=t),B}).filter(Boolean)};e.watch(()=>r.modelValue,l=>{const a=C(l),t=f.value.filter(n=>n.status==="success").map(n=>n.originalPath||n.url),o=a.map(n=>n.originalPath||n.url);if(JSON.stringify(t)!==JSON.stringify(o)){const n=f.value.filter(d=>d.status!=="success");f.value=[...a,...n]}},{immediate:!0});const V=()=>{const l=f.value.filter(t=>t.status==="success");let a;switch(h.value){case"string":a=l.map(t=>x(t.originalPath||t.url||"")).filter(Boolean).join(r.separator);break;case"objectArray":a=l.map(t=>{if(t.__rawResult__&&typeof t.__rawResult__=="object"){const n=t.__rawResult__[r.urlKey];return{...t.__rawResult__,[r.urlKey]:x(typeof n=="string"?n:t.originalPath||t.url||"")}}const o={[r.urlKey]:x(t.originalPath||t.url||"")};return r.nameKey&&(o[r.nameKey]=t.name),o});break;default:a=l.map(t=>x(t.originalPath||t.url||"")).filter(Boolean);break}p("update:modelValue",a)},i=l=>{const{file:a,response:t}=l;if(console.log("[SmartUpload] onSuccess",{file:a,response:t}),a){let o="";if(r.formatResponse)o=r.formatResponse(t);else if(t&&typeof t=="object"){const n=Array.isArray(t)?t[0]:t;n&&"url"in n&&(o=n.url)}if(console.log("[SmartUpload] uploadResult:",o),o){const n=typeof o=="object";let d="",B;if(n){const k=o[r.urlKey];d=typeof k=="string"?k:"",B=o}else d=o;const E=g(d)?d:r.urlPrefix+d;e.nextTick(()=>{const k=f.value.findIndex(L=>L.name===a.name);console.log("[SmartUpload] fileIndex:",k),k>=0&&(f.value[k]={...f.value[k],status:"success",url:E,originalPath:d,__rawResult__:B}),V()})}}p("success",l)},v=l=>{const{file:a}=l;if(a){const t=f.value.findIndex(o=>o.name===a.name);t>=0&&f.value.splice(t,1)}m.Message.error("上传失败,请重试"),p("fail",l)},c=l=>{const{index:a=0,file:t}=l;p("remove",t,a),e.nextTick(()=>{V()})},s=l=>{const{type:a,files:t}=l;switch(console.log("[SmartUpload] onValidate",{type:a,files:t}),a){case"FILTER_FILE_SAME_NAME":m.Message.info("请勿上传重复的文件");break;case"FILE_OVER_SIZE_LIMIT":m.Message.error(`文件大小超过限制(最大 ${Math.round(r.maxSize/1024)}MB)`);break;case"FILES_OVER_LENGTH_LIMIT":m.Message.info(`最多只能上传 ${r.max} 个文件`);break}};return(l,a)=>(e.openBlock(),e.createElementBlock("div",H,[u.mode==="image"?(e.openBlock(),e.createBlock(e.unref(m.Upload),{key:0,modelValue:f.value,"onUpdate:modelValue":a[0]||(a[0]=t=>f.value=t),action:u.action,accept:y.value,max:u.max,"size-limit":{size:u.maxSize,unit:"KB"},multiple:u.multiple,disabled:u.disabled,"request-method":u.requestMethod,onSuccess:i,onFail:v,onRemove:c,onValidate:s},null,8,["modelValue","action","accept","max","size-limit","multiple","disabled","request-method"])):(e.openBlock(),e.createBlock(e.unref(m.Upload),{key:1,modelValue:f.value,"onUpdate:modelValue":a[1]||(a[1]=t=>f.value=t),action:u.action,accept:y.value,max:u.max,"size-limit":{size:u.maxSize,unit:"KB"},multiple:u.multiple,disabled:u.disabled,"request-method":u.requestMethod,onSuccess:i,onFail:v,onRemove:c,onValidate:s},null,8,["modelValue","action","accept","max","size-limit","multiple","disabled","request-method"]))]))}}),A=I(Z,[["__scopeId","data-v-a3054ef0"]]),Q={class:"smart-form"},X={key:0,class:"smart-form-footer"},ee={name:"SmartForm"},te=e.defineComponent({...ee,props:{modelValue:{},schemas:{},labelWidth:{default:"80px"},labelAlign:{default:"left"},showFooter:{type:Boolean,default:!0},submitBtnText:{default:"提交"},resetBtnText:{default:"重置"},loading:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},componentMap:{}},emits:["update:modelValue","submit","reset","validate-failed"],setup(u,{expose:b,emit:r}){const p=u,f=r,y=e.ref(),h={input:m.Input,password:m.Input,number:m.Input,textarea:m.Textarea,select:w,cascader:_,"tree-select":R,date:O,time:O,switch:m.Switch,radio:m.RadioGroup,checkbox:m.CheckboxGroup,stepper:m.Stepper,upload:A},g=i=>{const v=i||"input";return p.componentMap?.[v]?p.componentMap[v]:h[v]??null},P=i=>{const v=[];return i.required&&v.push({required:!0,message:`${i.label||"该项"}不能为空`}),i.rules&&v.push(...i.rules),v},S=i=>{const v=p.modelValue;let c={};typeof i.props=="function"?c=i.props(v):i.props&&(c={...i.props});const s=i.type||"input";if(s==="password"?Object.assign(c,{type:"password"}):s==="number"?Object.assign(c,{type:"number"}):s==="date"?Object.assign(c,{mode:"date"}):s==="time"&&Object.assign(c,{mode:["hour","minute"]}),["input","password","number","textarea"].includes(s)&&(!(typeof i.props=="object"&&i.props!==null&&Object.prototype.hasOwnProperty.call(i.props,"borderless"))&&c.borderless===void 0&&(c.borderless=!0),c.type==="file"&&(console.warn(`[SmartForm] Detected type="file" on Input component (field: ${i.name}). Please use type: "upload" in your schema instead.`),delete c.type)),p.disabled)Object.assign(c,{disabled:!0});else if(i.disabled){const l=typeof i.disabled=="function"?i.disabled(v):i.disabled;Object.assign(c,{disabled:l})}return c},x=i=>i.hidden===void 0?!0:typeof i.hidden=="function"?!i.hidden(p.modelValue):!i.hidden,C=i=>{i.validateResult===!0?f("submit",e.toRaw(p.modelValue)):f("validate-failed",i.validateResult)},V=()=>{y.value?.reset(),f("reset")};return b({validate:()=>y.value?.validate(),reset:()=>y.value?.reset(),clearValidate:()=>y.value?.clearValidate()}),(i,v)=>(e.openBlock(),e.createElementBlock("div",Q,[e.createVNode(e.unref(m.Form),{ref_key:"formRef",ref:y,data:u.modelValue,"label-width":u.labelWidth,"label-align":u.labelAlign,onSubmit:C},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(u.schemas,c=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:c.name},[x(c)?(e.openBlock(),e.createBlock(e.unref(m.FormItem),{key:0,name:c.name,label:c.label,rules:P(c),help:c.help},{default:e.withCtx(()=>[c.type==="slot"&&c.slot?e.renderSlot(i.$slots,c.slot,{key:0,model:u.modelValue,schema:c},void 0,!0):(e.openBlock(),e.createBlock(e.resolveDynamicComponent(g(c.type)),e.mergeProps({key:1,modelValue:u.modelValue[c.name],"onUpdate:modelValue":s=>u.modelValue[c.name]=s},{ref_for:!0},S(c)),null,16,["modelValue","onUpdate:modelValue"]))]),_:2},1032,["name","label","rules","help"])):e.createCommentVNode("",!0)],64))),128)),u.showFooter?(e.openBlock(),e.createElementBlock("div",X,[e.createVNode(e.unref(m.Button),{theme:"primary",type:"submit",block:"",loading:u.loading,disabled:u.disabled},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(u.submitBtnText),1)]),_:1},8,["loading","disabled"]),u.resetBtnText?(e.openBlock(),e.createBlock(e.unref(m.Button),{key:0,theme:"default",variant:"outline",block:"",class:"reset-btn",onClick:V,disabled:u.disabled||u.loading},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(u.resetBtnText),1)]),_:1},8,["disabled"])):e.createCommentVNode("",!0)])):e.createCommentVNode("",!0)]),_:3},8,["data","label-width","label-align"])]))}}),le=I(te,[["__scopeId","data-v-88de1754"]]);exports.SmartCascader=_;exports.SmartForm=le;exports.SmartSelect=w;exports.SmartTimePicker=O;exports.SmartTreeSelect=R;exports.SmartUpload=A;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/composables/usePopupField.ts","../src/components/SmartSelect/index.vue","../src/components/SmartTimePicker/index.vue","../src/components/SmartCascader/index.vue","../src/components/SmartTreeSelect/index.vue","../src/components/SmartUpload/index.vue","../src/components/SmartForm/index.vue"],"sourcesContent":["import { ref, computed, type Ref, type ComputedRef } from 'vue'\n\n/**\n * 弹窗字段通用逻辑 Hook\n * 用于 SmartSelect、SmartCascader、SmartTreeSelect、SmartTimePicker 等组件\n */\n\nexport interface UsePopupFieldOptions<T, O> {\n /** 当前选中值 (响应式) */\n modelValue: Ref<T | undefined>\n /** 数据源 (响应式) */\n options: Ref<O[]>\n /** 从选项中查找 label 的函数 */\n findLabel: (options: O[], value: T) => string | null\n /** 用户传入的显示 label (用于异步场景的 fallback) */\n displayLabel?: Ref<string | undefined>\n}\n\nexport interface UsePopupFieldReturn {\n /** 弹窗可见状态 */\n visible: Ref<boolean>\n /** 计算后的显示文本 */\n displayValue: ComputedRef<string>\n /** 打开弹窗 */\n openPopup: () => void\n /** 关闭弹窗 */\n closePopup: () => void\n}\n\nexport function usePopupField<T, O>(options: UsePopupFieldOptions<T, O>): UsePopupFieldReturn {\n const visible = ref(false)\n\n const displayValue = computed(() => {\n const val = options.modelValue.value\n if (val === undefined || val === null || val === '') {\n return ''\n }\n\n // 2. 尝试从 options 中查找 label\n const foundLabel = options.findLabel(options.options.value, val)\n if (foundLabel !== null) {\n return foundLabel\n }\n\n // 3. Fallback: 使用用户传入的 displayLabel\n if (options.displayLabel?.value) {\n return options.displayLabel.value\n }\n\n // 4. 最终 fallback: 直接显示原始值\n return String(val)\n })\n\n const openPopup = () => {\n visible.value = true\n }\n\n const closePopup = () => {\n visible.value = false\n }\n\n return {\n visible,\n displayValue,\n openPopup,\n closePopup,\n }\n}\n","<script lang=\"ts\">\nexport default {\n name: 'SmartSelect',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { h, computed, toRef } from 'vue'\nimport { Input as TInput, Popup as TPopup, Picker as TPicker, Icon } from 'tdesign-mobile-vue'\nimport type { PickerValue, PickerColumnItem } from 'tdesign-mobile-vue'\nimport type { SmartSelectProps } from './props'\nimport { usePopupField } from '../../composables/usePopupField'\n\nconst props = withDefaults(defineProps<SmartSelectProps>(), {\n modelValue: '',\n placeholder: '请选择',\n columns: () => [],\n title: '',\n required: false,\n rules: () => [],\n align: 'left',\n borderless: true,\n clearable: false,\n readonly: false,\n disabled: false,\n cancelBtn: true,\n confirmBtn: true,\n placement: 'bottom',\n showOverlay: true,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string | number | boolean): void\n (e: 'change', value: PickerValue[], context: unknown): void\n (e: 'confirm', value: PickerValue[], context: unknown): void\n (e: 'cancel', context: unknown): void\n}>()\n\nconst findLabelFromColumns = (\n cols: PickerColumnItem[] | PickerColumnItem[][],\n value: string | number | boolean,\n): string | null => {\n let flatColumns: PickerColumnItem[] = []\n if (Array.isArray(cols)) {\n if (cols.length > 0 && Array.isArray(cols[0])) {\n flatColumns = (cols as PickerColumnItem[][]).flat()\n } else {\n flatColumns = cols as PickerColumnItem[]\n }\n }\n const selectedItem = flatColumns.find((item) => item.value === value)\n return selectedItem ? selectedItem.label : null\n}\n\nconst { visible, displayValue, openPopup, closePopup } = usePopupField({\n modelValue: toRef(() => props.modelValue),\n options: toRef(() => props.columns as PickerColumnItem[]),\n findLabel: (cols, val) => findLabelFromColumns(cols, val as string | number | boolean),\n displayLabel: toRef(() => props.displayLabel),\n})\n\nconst POPUP_KEYS = ['attach', 'closeBtn', 'closeOnOverlayClick', 'destroyOnClose', 'duration', 'overlayProps', 'placement', 'preventScrollThrough', 'showOverlay', 'transitionName', 'zIndex'] as const\n\nconst popupBindProps = computed(() => {\n const result: Record<string, unknown> = {}\n const propsAny = props as Record<string, unknown>\n POPUP_KEYS.forEach((key) => {\n if (propsAny[key] !== undefined) {\n result[key] = propsAny[key]\n }\n })\n return result\n})\n\nconst INPUT_KEYS = ['align', 'autofocus', 'borderless', 'clearable', 'clearTrigger', 'disabled', 'label', 'layout', 'maxcharacter', 'maxlength', 'name', 'placeholder', 'prefixIcon', 'readonly', 'size', 'status', 'suffix', 'suffixIcon', 'tips', 'type'] as const\n\nconst inputBindProps = computed(() => {\n const result: Record<string, unknown> = {}\n const propsAny = props as Record<string, unknown>\n INPUT_KEYS.forEach((key) => {\n if (propsAny[key] !== undefined) {\n result[key] = propsAny[key]\n }\n })\n return result\n})\n\nconst onConfirm = (value: PickerValue[], context: unknown) => {\n const selectedValue = value[0]\n if (selectedValue !== undefined && typeof selectedValue !== 'object') {\n emit('update:modelValue', selectedValue)\n }\n emit('confirm', value, context)\n closePopup()\n}\n\nconst onCancel = (context: unknown) => {\n emit('cancel', context)\n closePopup()\n}\n\nconst onChange = (value: PickerValue[], context: unknown) => {\n emit('change', value, context)\n}\n</script>\n\n<template>\n <div class=\"smart-select\">\n <t-input\n :value=\"displayValue\"\n @click=\"openPopup\"\n v-bind=\"{ ...$attrs, ...inputBindProps }\"\n readonly\n :suffix-icon=\"() => h(Icon, { name: 'chevron-right' })\"\n >\n <template v-for=\"(_, slotName) in $slots\" #[slotName]=\"slotData\">\n <slot :name=\"slotName\" v-bind=\"slotData || {}\" />\n </template>\n </t-input>\n\n <t-popup v-model=\"visible\" placement=\"bottom\" v-bind=\"popupBindProps\">\n <t-picker\n :value=\"[modelValue as PickerValue]\"\n :columns=\"columns\"\n :title=\"title\"\n :cancel-btn=\"cancelBtn\"\n :confirm-btn=\"confirmBtn\"\n @confirm=\"onConfirm\"\n @cancel=\"onCancel\"\n @change=\"onChange\"\n />\n </t-popup>\n </div>\n</template>\n\n<style lang=\"scss\" scoped>\n.smart-select {\n width: 100%;\n}\n</style>\n","<script lang=\"ts\">\nexport default {\n name: 'SmartTimePicker',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { ref, h, computed } from 'vue'\nimport {\n Input as TInput,\n Popup as TPopup,\n DateTimePicker as TDateTimePicker,\n Icon,\n} from 'tdesign-mobile-vue'\nimport type { SmartTimePickerProps } from './props'\n\nconst props = withDefaults(defineProps<SmartTimePickerProps>(), {\n modelValue: '',\n // label: '', // 移除默认 label,避免在 SmartForm 中出现双重 label\n placeholder: '请选择时间',\n required: false,\n rules: () => [],\n\n // Input Defaults\n align: 'left',\n clearTrigger: 'always',\n cursorColor: '#0052d9',\n layout: 'horizontal',\n name: '',\n status: 'default',\n type: 'text',\n autofocus: false,\n borderless: true, // 默认无边框,避免与 FormItem 双下划线\n clearable: false,\n readonly: false,\n disabled: false,\n\n // DateTimePicker Defaults\n cancelBtn: '',\n confirmBtn: '',\n format: 'YYYY-MM-DD HH:mm:ss',\n mode: 'date',\n showWeek: false,\n steps: () => ({}),\n title: '',\n\n // Popup Defaults\n attach: 'body',\n closeOnOverlayClick: true,\n duration: 240,\n overlayProps: () => ({}),\n placement: 'bottom',\n preventScrollThrough: true,\n showOverlay: true,\n transitionName: '',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string | number): void\n (e: 'change', value: string | number): void\n (e: 'confirm', value: string | number): void\n (e: 'cancel', context: { e: MouseEvent }): void\n (e: 'pick', value: string | number): void\n}>()\n\nconst visible = ref(false)\n// const displayValue = ref('')\n\nconst POPUP_KEYS = [\n 'attach',\n 'closeBtn',\n 'closeOnOverlayClick',\n 'destroyOnClose',\n 'duration',\n 'overlayProps',\n 'placement',\n 'preventScrollThrough',\n 'showOverlay',\n 'transitionName',\n 'zIndex',\n 'onClose',\n 'onClosed',\n 'onOpen',\n 'onOpened',\n] as const\n\nconst propsAny = props as Record<string, unknown>\n\nconst popupBindProps = computed(() => {\n const result: Record<string, unknown> = {}\n POPUP_KEYS.forEach((key) => {\n if (propsAny[key] !== undefined) {\n result[key] = propsAny[key]\n }\n })\n return result\n})\n\nconst inputBindProps = computed(() => {\n const { modelValue, title, inputFormat, ...others } = props\n const result = { ...others } as Record<string, unknown>\n POPUP_KEYS.forEach((key) => {\n delete result[key]\n })\n // Omit DateTimePicker specific props that shouldn't be on Input\n const PICKER_KEYS = [\n 'cancelBtn',\n 'confirmBtn',\n 'end',\n 'footer',\n 'header',\n 'mode',\n 'renderLabel',\n 'showWeek',\n 'start',\n 'steps',\n 'format',\n ]\n PICKER_KEYS.forEach((key) => delete result[key])\n\n // Map inputFormat to format for Input component\n if (inputFormat) {\n result.format = inputFormat\n }\n\n return result\n})\n\nconst dateTimePickerBindProps = computed(() => {\n const { label, placeholder, rules, required, modelValue, ...others } = props\n const result = { ...others } as Record<string, unknown>\n POPUP_KEYS.forEach((key) => {\n delete result[key]\n })\n return result\n})\n\n// 使用 computed 替代 watch + ref\nconst displayValue = computed(() => {\n return String(props.modelValue || '')\n})\n\nconst onTriggerClick = () => {\n visible.value = true\n}\n\nconst onConfirm = (value: string | number) => {\n emit('update:modelValue', value)\n emit('confirm', value)\n visible.value = false\n}\n\nconst onCancel = (context: { e: MouseEvent }) => {\n emit('cancel', context)\n visible.value = false\n}\n\nconst onChange = (value: string | number) => {\n emit('change', value)\n}\n\nconst onPick = (value: string | number) => {\n emit('pick', value)\n}\n</script>\n\n<template>\n <div class=\"smart-time-picker\">\n <t-input\n :value=\"displayValue\"\n @click=\"onTriggerClick\"\n v-bind=\"{ ...$attrs, ...inputBindProps }\"\n readonly\n :suffix-icon=\"() => h(Icon, { name: 'chevron-right' })\"\n >\n <template v-for=\"(_, name) in $slots\" #[name]=\"slotData\">\n <slot :name=\"name\" v-bind=\"slotData || {}\" />\n </template>\n </t-input>\n\n <t-popup v-model=\"visible\" placement=\"bottom\" v-bind=\"popupBindProps\">\n <t-date-time-picker\n :value=\"modelValue\"\n v-bind=\"dateTimePickerBindProps\"\n @confirm=\"onConfirm\"\n @cancel=\"onCancel\"\n @change=\"onChange\"\n @pick=\"onPick\"\n />\n </t-popup>\n </div>\n</template>\n\n<style lang=\"scss\" scoped>\n.smart-time-picker {\n width: 100%;\n}\n</style>\n","<script lang=\"ts\">\nexport default {\n name: 'SmartCascader',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { h, computed, toRef } from 'vue'\nimport { Input as TInput, Cascader as TCascader, Icon } from 'tdesign-mobile-vue'\nimport type { SmartCascaderProps } from './props'\nimport { usePopupField } from '../../composables/usePopupField'\n\ninterface CascaderOption {\n label?: unknown\n value?: string | number\n children?: CascaderOption[]\n [key: string]: unknown\n}\n\nconst props = withDefaults(defineProps<SmartCascaderProps>(), {\n modelValue: '',\n placeholder: '请选择',\n options: () => [],\n title: '',\n required: false,\n rules: () => [],\n align: 'left',\n borderless: true,\n clearable: false,\n readonly: false,\n disabled: false,\n checkStrictly: false,\n theme: 'step',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string | number): void\n (e: 'change', value: string | number, context: unknown): void\n (e: 'pick', value: string | number, context: unknown): void\n (e: 'close', trigger: unknown): void\n}>()\n\nconst findLabelPath = (\n options: CascaderOption[],\n targetValue: string | number,\n path: string[] = [],\n): string[] | null => {\n for (const option of options) {\n const value = option.value ?? (option.id as string | number)\n const label = option.label ?? option.name\n if (value === targetValue) {\n return [...path, String(label)]\n }\n if (option.children && option.children.length > 0) {\n const found = findLabelPath(option.children, targetValue, [...path, String(label)])\n if (found) return found\n }\n }\n return null\n}\n\nconst findLabelFromOptions = (options: CascaderOption[], value: string | number): string | null => {\n const path = findLabelPath(options, value)\n return path ? path.join(' / ') : null\n}\n\nconst { visible, displayValue, openPopup, closePopup } = usePopupField({\n modelValue: toRef(() => props.modelValue),\n options: toRef(() => (props.options || []) as CascaderOption[]),\n findLabel: findLabelFromOptions,\n displayLabel: toRef(() => props.displayLabel),\n})\n\nconst INPUT_KEYS = ['align', 'autofocus', 'borderless', 'clearable', 'clearTrigger', 'disabled', 'label', 'layout', 'maxcharacter', 'maxlength', 'name', 'placeholder', 'prefixIcon', 'readonly', 'size', 'status', 'suffix', 'suffixIcon', 'tips', 'type'] as const\n\nconst inputBindProps = computed(() => {\n const result: Record<string, unknown> = {}\n const propsAny = props as Record<string, unknown>\n INPUT_KEYS.forEach((key) => {\n if (propsAny[key] !== undefined) {\n result[key] = propsAny[key]\n }\n })\n return result\n})\n\nconst CASCADER_KEYS = ['checkStrictly', 'closeBtn', 'header', 'keys', 'load', 'middleContent', 'options', 'overlayProps', 'subTitles', 'theme', 'title'] as const\n\nconst cascaderBindProps = computed(() => {\n const result: Record<string, unknown> = {}\n const propsAny = props as Record<string, unknown>\n CASCADER_KEYS.forEach((key) => {\n if (propsAny[key] !== undefined) {\n result[key] = propsAny[key]\n }\n })\n return result\n})\n\nconst onChange = (value: string | number, context: unknown) => {\n emit('update:modelValue', value)\n emit('change', value, context)\n closePopup()\n}\n\nconst onPick = (context: { value: string | number; label: string; index: number; level: number }) => {\n emit('pick', context.value, context)\n}\n\nconst onClose = (trigger: unknown) => {\n emit('close', trigger)\n closePopup()\n}\n</script>\n\n<template>\n <div class=\"smart-cascader\">\n <t-input\n :value=\"displayValue\"\n @click=\"openPopup\"\n v-bind=\"{ ...$attrs, ...inputBindProps }\"\n readonly\n :suffix-icon=\"() => h(Icon, { name: 'chevron-right' })\"\n >\n <template v-for=\"(_, slotName) in $slots\" #[slotName]=\"slotData\">\n <slot :name=\"slotName\" v-bind=\"slotData || {}\" />\n </template>\n </t-input>\n\n <t-cascader\n v-model:visible=\"visible\"\n :value=\"modelValue\"\n v-bind=\"cascaderBindProps\"\n @change=\"onChange\"\n @pick=\"onPick\"\n @close=\"onClose\"\n />\n </div>\n</template>\n\n<style lang=\"scss\" scoped>\n.smart-cascader {\n width: 100%;\n}\n</style>\n","<script lang=\"ts\">\nexport default {\n name: 'SmartTreeSelect',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { ref, h, computed, watch } from 'vue'\nimport {\n Input as TInput,\n Popup as TPopup,\n TreeSelect as TTreeSelect,\n Icon,\n} from 'tdesign-mobile-vue'\nimport type { TreeSelectValue, TreeLevel } from 'tdesign-mobile-vue'\nimport type { SmartTreeSelectProps } from './props'\n\nconst props = withDefaults(defineProps<SmartTreeSelectProps>(), {\n placeholder: '请选择',\n options: () => [],\n title: '',\n required: false,\n rules: () => [],\n // Input Defaults\n align: 'left',\n clearTrigger: 'always',\n cursorColor: '#0052d9',\n layout: 'horizontal',\n name: '',\n status: 'default',\n type: 'text',\n autofocus: false,\n borderless: true, // 默认无边框,避免与 FormItem 双下划线\n clearable: false,\n readonly: false,\n disabled: false,\n // TreeSelect Defaults\n height: 336,\n multiple: false,\n filterable: false,\n customStyle: '',\n // Popup Defaults\n attach: 'body',\n closeOnOverlayClick: true,\n duration: 240,\n overlayProps: () => ({}),\n placement: 'bottom',\n preventScrollThrough: true,\n showOverlay: true,\n transitionName: '',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: TreeSelectValue): void\n (e: 'change', value: TreeSelectValue, level: TreeLevel): void\n (e: 'close', trigger: unknown): void\n}>()\n\nconst visible = ref(false)\n\n// Popup 属性 keys 列表\nconst POPUP_KEYS = [\n 'attach',\n 'closeBtn',\n 'closeOnOverlayClick',\n 'destroyOnClose',\n 'duration',\n 'overlayProps',\n 'placement',\n 'preventScrollThrough',\n 'showOverlay',\n 'transitionName',\n 'zIndex',\n 'onClose',\n 'onClosed',\n 'onOpen',\n 'onOpened',\n] as const\n\n// TreeSelect 专属属性 keys 列表\nconst TREE_SELECT_KEYS = [\n 'customStyle',\n 'filterable',\n 'height',\n 'keys',\n 'multiple',\n 'options',\n] as const\n\nconst propsAny = props as Record<string, unknown>\n\nconst popupBindProps = computed(() => {\n const result: Record<string, unknown> = {}\n POPUP_KEYS.forEach((key) => {\n if (propsAny[key] !== undefined) {\n result[key] = propsAny[key]\n }\n })\n return result\n})\n\nconst inputBindProps = computed(() => {\n const { modelValue, ...others } = props\n const result = { ...others } as Record<string, unknown>\n // 移除 Popup 属性\n POPUP_KEYS.forEach((key) => {\n delete result[key]\n })\n // 移除 TreeSelect 属性\n TREE_SELECT_KEYS.forEach((key) => {\n delete result[key]\n })\n // 移除自定义属性\n delete result['rules']\n delete result['required']\n delete result['title']\n return result\n})\n\nconst treeSelectBindProps = computed(() => {\n const result: Record<string, unknown> = {}\n TREE_SELECT_KEYS.forEach((key) => {\n if (key !== 'options' && propsAny[key] !== undefined) {\n result[key] = propsAny[key]\n }\n })\n // 添加 keys 属性\n if (propsAny['keys'] !== undefined) {\n result['keys'] = propsAny['keys']\n }\n return result\n})\n\n// 递归查找 Label\ninterface TreeOption {\n label?: string\n value?: string | number\n children?: TreeOption[]\n [key: string]: unknown\n}\n\n/**\n * 判断某个值是否是叶子节点\n */\nconst isLeafValue = (options: TreeOption[], targetValue: string | number): boolean => {\n for (const option of options) {\n if (option.value === targetValue) {\n // 如果没有 children 或 children 为空,则是叶子节点\n return !option.children || option.children.length === 0\n }\n if (option.children && option.children.length > 0) {\n const found = isLeafValue(option.children, targetValue)\n if (found !== false) return found\n }\n }\n return false\n}\n\n/**\n * 根据叶子节点值,构建完整的路径值数组\n * TDesign TreeSelect 需要 [level0Value, level1Value, level2Value] 格式的 value\n */\nconst buildValuePath = (\n options: TreeOption[],\n targetValue: string | number,\n path: (string | number)[] = [],\n): (string | number)[] | null => {\n for (const option of options) {\n if (option.value === targetValue) {\n return [...path, option.value]\n }\n if (option.children && option.children.length > 0) {\n const found = buildValuePath(option.children, targetValue, [...path, option.value!])\n if (found) return found\n }\n }\n return null\n}\n\n/**\n * 内部使用的数组格式 value(TDesign TreeSelect 需要)\n */\nconst internalValue = ref<Array<string | number>>([])\n\nconst normalizePathValue = (value: TreeSelectValue): Array<string | number> => {\n if (Array.isArray(value)) {\n return value\n .flatMap((item) => (Array.isArray(item) ? item : [item]))\n .filter(\n (item): item is string | number => typeof item === 'string' || typeof item === 'number',\n )\n }\n if (value === null || value === undefined || value === '') {\n return []\n }\n return [value as string | number]\n}\n\n// 监听外部 modelValue 变化,转换为内部数组格式\nwatch(\n () => props.modelValue,\n (newVal) => {\n if (Array.isArray(newVal)) {\n internalValue.value = normalizePathValue(newVal)\n } else if (newVal !== undefined && newVal !== '' && newVal !== null) {\n // 单个值,需要构建完整路径\n const opts = (props.options || []) as TreeOption[]\n const path = buildValuePath(opts, newVal as string | number)\n internalValue.value = path || []\n } else {\n internalValue.value = []\n }\n },\n { immediate: true },\n)\n\n// 监听 options 变化时也需要重新计算\nwatch(\n () => props.options,\n () => {\n const val = props.modelValue\n if (!Array.isArray(val) && val !== undefined && val !== '' && val !== null) {\n const opts = (props.options || []) as TreeOption[]\n const path = buildValuePath(opts, val as string | number)\n internalValue.value = path || []\n }\n },\n { deep: true },\n)\n\nconst findLabel = (options: TreeOption[], targetValue: string | number): string | null => {\n for (const option of options) {\n if (option.value === targetValue) {\n return option.label || String(option.value)\n }\n if (option.children && option.children.length > 0) {\n const found = findLabel(option.children, targetValue)\n if (found) return found\n }\n }\n return null\n}\n\nconst displayValue = computed(() => {\n const val = props.modelValue\n if (!val && val !== 0) {\n return ''\n }\n\n const opts = (props.options || []) as TreeOption[]\n\n if (Array.isArray(val)) {\n // 取最后一个值作为显示值\n const lastVal = val[val.length - 1]\n if (lastVal !== undefined) {\n return findLabel(opts, lastVal as string | number) || String(lastVal)\n }\n return ''\n }\n\n return findLabel(opts, val as string | number) || String(val)\n})\n\nconst onTriggerClick = () => {\n if (props.disabled) return\n visible.value = true\n}\n\n// 修复 onChange 事件签名,与组件库保持一致\nconst onChange = (value: TreeSelectValue, level: TreeLevel) => {\n // TDesign TreeSelect 返回的是数组格式 [level0, level1, level2, ...]\n // 我们需要取最后一级的值作为最终值传给父组件\n if (Array.isArray(value)) {\n const normalizedValue = normalizePathValue(value)\n const leafValue = normalizedValue[normalizedValue.length - 1]\n internalValue.value = normalizedValue\n\n // 判断是否选中了叶子节点\n const opts = (props.options || []) as TreeOption[]\n const isLeaf = isLeafValue(opts, leafValue as string | number)\n\n if (isLeaf) {\n // 只有选中叶子节点才更新外部值并关闭弹窗\n emit('update:modelValue', leafValue as TreeSelectValue)\n emit('change', leafValue as TreeSelectValue, level)\n // 单选时关闭弹窗\n if (!props.multiple) {\n visible.value = false\n }\n }\n } else {\n internalValue.value = normalizePathValue(value)\n emit('update:modelValue', value)\n emit('change', value, level)\n }\n}\n</script>\n\n<template>\n <div class=\"smart-tree-select\">\n <t-input\n :value=\"displayValue\"\n @click=\"onTriggerClick\"\n v-bind=\"{ ...$attrs, ...inputBindProps }\"\n readonly\n :suffix-icon=\"() => h(Icon, { name: 'chevron-right' })\"\n >\n <template v-for=\"(_, name) in $slots\" #[name]=\"slotData\">\n <slot :name=\"name\" v-bind=\"slotData || {}\" />\n </template>\n </t-input>\n\n <t-popup v-model=\"visible\" placement=\"bottom\" v-bind=\"popupBindProps\">\n <div class=\"tree-select-wrapper\">\n <t-tree-select\n v-if=\"visible\"\n :value=\"internalValue\"\n :options=\"options\"\n v-bind=\"treeSelectBindProps\"\n @change=\"onChange\"\n />\n </div>\n </t-popup>\n </div>\n</template>\n\n<style lang=\"scss\" scoped>\n.smart-tree-select {\n width: 100%;\n}\n.tree-select-wrapper {\n height: 50vh; // TreeSelect 需要高度\n display: flex;\n flex-direction: column;\n}\n</style>\n","<script lang=\"ts\">\nexport default {\n name: 'SmartUpload',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch, nextTick } from 'vue'\nimport {\n Upload as TUpload,\n Message,\n type SuccessContext,\n type UploadFailContext,\n type UploadRemoveContext,\n} from 'tdesign-mobile-vue'\nimport type { SmartUploadProps, SmartUploadFile, FileItem, FileListValue, FileListValueType } from './props'\n\nconst props = withDefaults(defineProps<SmartUploadProps>(), {\n modelValue: '',\n separator: ',',\n urlPrefix: '',\n urlKey: 'fileUrl',\n nameKey: 'filename',\n mode: 'image',\n accept: 'image/*',\n max: 9,\n maxSize: 10 * 1024,\n multiple: true,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: FileListValue): void\n (e: 'success', context: SuccessContext): void\n (e: 'fail', context: UploadFailContext): void\n (e: 'remove', file: SmartUploadFile, index: number): void\n}>()\n\n\n// 内部文件列表(完全由 t-upload 控制)\nconst internalFiles = ref<SmartUploadFile[]>([])\n\n// 计算 accept 字符串\nconst acceptString = computed(() => {\n if (Array.isArray(props.accept)) {\n return props.accept.join(',')\n }\n return props.accept\n})\n\n/**\n * 判断值类型\n * 优先使用 props.valueType,否则自动推断\n */\nconst valueType = computed<FileListValueType>(() => {\n if (props.valueType) return props.valueType\n\n const list = props.modelValue\n if (typeof list === 'string') return 'string'\n if (Array.isArray(list)) {\n if (list.length === 0) return 'stringArray'\n if (typeof list[0] === 'object' && list[0] !== null) return 'objectArray'\n return 'stringArray'\n }\n return 'string'\n})\n\n/**\n * 判断是否为完整 URL\n */\nconst isFullUrl = (url: string) => /^(https?:|blob:|data:)/i.test(url)\n\n/**\n * 从对象或字符串中提取 URL\n */\nconst extractUrl = (item: string | FileItem): string => {\n if (typeof item === 'string') return item\n const url = item[props.urlKey]\n return typeof url === 'string' ? url : ''\n}\n\n/**\n * 从对象中提取 name\n */\nconst extractName = (item: string | FileItem, url: string): string => {\n if (typeof item === 'object' && item[props.nameKey]) {\n const name = item[props.nameKey]\n return typeof name === 'string' ? name : String(name)\n }\n return url.split('/').pop() || ''\n}\n\n/**\n * 从 URL 中去除前缀\n */\nconst removeUrlPrefix = (url: string): string => {\n if (!props.urlPrefix || !url) return url\n if (url.startsWith(props.urlPrefix)) {\n return url.slice(props.urlPrefix.length)\n }\n return url\n}\n\n/**\n * 将外部值转换为 UploadFile[] 用于显示\n */\nconst parseModelValue = (value: FileListValue): SmartUploadFile[] => {\n if (!value) return []\n\n // 解析为项目数组\n let items: (string | FileItem)[] = []\n if (typeof value === 'string') {\n items = value.split(props.separator).filter(Boolean)\n } else {\n items = value.filter(Boolean) as (string | FileItem)[]\n }\n\n // 转换为 SmartUploadFile[]\n return items.map((item, index) => {\n const url = extractUrl(item)\n if (!url) return null\n\n // 拼接前缀\n const displayUrl = isFullUrl(url) ? url : (props.urlPrefix + url)\n\n const fileInfo: SmartUploadFile = {\n url: displayUrl,\n name: extractName(item, url),\n status: 'success' as const,\n originalPath: url,\n }\n\n // 对象数组模式下,保存原始对象\n if (typeof item === 'object') {\n fileInfo.__rawResult__ = item as Record<string, unknown>\n }\n\n return fileInfo\n }).filter(Boolean) as SmartUploadFile[]\n}\n\n// 初始化/同步外部值到内部文件列表\nwatch(\n () => props.modelValue,\n (newVal) => {\n const parsed = parseModelValue(newVal)\n const currentSuccessUrls = internalFiles.value\n .filter((f) => f.status === 'success')\n .map((f) => f.originalPath || f.url)\n const parsedUrls = parsed.map((f) => f.originalPath || f.url)\n\n if (JSON.stringify(currentSuccessUrls) !== JSON.stringify(parsedUrls)) {\n const uploadingFiles = internalFiles.value.filter((f) => f.status !== 'success')\n internalFiles.value = [...parsed, ...uploadingFiles]\n }\n },\n { immediate: true },\n)\n\n\n/**\n * 同步内部文件列表到 modelValue\n */\nconst syncModelValue = () => {\n const finishedFiles = internalFiles.value.filter((f) => f.status === 'success')\n\n let result: FileListValue\n\n switch (valueType.value) {\n case 'string':\n result = finishedFiles\n .map((f) => removeUrlPrefix(f.originalPath || f.url || ''))\n .filter(Boolean)\n .join(props.separator)\n break\n\n case 'objectArray':\n result = finishedFiles.map((f) => {\n // 优先使用保存的完整返回对象\n if (f.__rawResult__ && typeof f.__rawResult__ === 'object') {\n const rawUrl = f.__rawResult__[props.urlKey]\n return {\n ...f.__rawResult__,\n [props.urlKey]: removeUrlPrefix(typeof rawUrl === 'string' ? rawUrl : (f.originalPath || f.url || '')),\n }\n }\n // 否则构建基础对象\n const baseObj: Record<string, unknown> = {\n [props.urlKey]: removeUrlPrefix(f.originalPath || f.url || ''),\n }\n if (props.nameKey) {\n baseObj[props.nameKey] = f.name\n }\n return baseObj\n })\n break\n\n case 'stringArray':\n default:\n result = finishedFiles\n .map((f) => removeUrlPrefix(f.originalPath || f.url || ''))\n .filter(Boolean)\n break\n }\n\n emit('update:modelValue', result)\n}\n\n// 上传成功处理\nconst onSuccess = (context: SuccessContext) => {\n const { file, response } = context\n\n console.log('[SmartUpload] onSuccess', { file, response })\n\n if (file) {\n // 使用 formatResponse 提取数据\n let uploadResult: string | Record<string, unknown> = ''\n if (props.formatResponse) {\n uploadResult = props.formatResponse(response)\n } else if (response && typeof response === 'object') {\n // TDesign 会把 response 包装成数组\n const data = Array.isArray(response) ? response[0] : response\n if (data && 'url' in data) {\n uploadResult = (data as { url: string }).url\n }\n }\n\n console.log('[SmartUpload] uploadResult:', uploadResult)\n\n if (uploadResult) {\n const isResultObject = typeof uploadResult === 'object'\n // 根据结果类型提取 fileUrl\n let fileUrl = ''\n let rawResultObj: Record<string, unknown> | undefined\n\n if (isResultObject) {\n const urlValue = (uploadResult as Record<string, unknown>)[props.urlKey]\n fileUrl = typeof urlValue === 'string' ? urlValue : ''\n rawResultObj = uploadResult as Record<string, unknown>\n } else {\n fileUrl = uploadResult as string\n }\n\n const displayUrl = isFullUrl(fileUrl) ? fileUrl : props.urlPrefix + fileUrl\n\n nextTick(() => {\n const fileIndex = internalFiles.value.findIndex((f) => f.name === file.name)\n console.log('[SmartUpload] fileIndex:', fileIndex)\n\n if (fileIndex >= 0) {\n internalFiles.value[fileIndex] = {\n ...internalFiles.value[fileIndex],\n status: 'success',\n url: displayUrl,\n originalPath: fileUrl,\n // 对象数组模式下保存完整返回\n __rawResult__: rawResultObj,\n }\n }\n\n syncModelValue()\n })\n }\n }\n\n emit('success', context)\n}\n\n// 上传失败处理\nconst onFail = (context: UploadFailContext) => {\n const { file } = context\n\n if (file) {\n const fileIndex = internalFiles.value.findIndex((f) => f.name === file.name)\n if (fileIndex >= 0) {\n internalFiles.value.splice(fileIndex, 1)\n }\n }\n\n Message.error('上传失败,请重试')\n emit('fail', context)\n}\n\n// 删除文件\nconst onRemove = (context: UploadRemoveContext) => {\n const { index = 0, file } = context\n emit('remove', file as SmartUploadFile, index)\n nextTick(() => {\n syncModelValue()\n })\n}\n\n// 校验处理:显示重复文件、文件过大等提示\nconst onValidate = (context: { type: string; files: unknown[] }) => {\n const { type, files } = context\n console.log('[SmartUpload] onValidate', { type, files })\n\n switch (type) {\n case 'FILTER_FILE_SAME_NAME':\n Message.info('请勿上传重复的文件')\n break\n case 'FILE_OVER_SIZE_LIMIT':\n Message.error(`文件大小超过限制(最大 ${Math.round(props.maxSize / 1024)}MB)`)\n break\n case 'FILES_OVER_LENGTH_LIMIT':\n Message.info(`最多只能上传 ${props.max} 个文件`)\n break\n }\n}\n\n</script>\n\n<template>\n <div class=\"smart-upload\">\n <!-- 图片模式 - 使用 TDesign 默认样式 -->\n <template v-if=\"mode === 'image'\">\n <t-upload\n v-model=\"internalFiles\"\n :action=\"action\"\n :accept=\"acceptString\"\n :max=\"max\"\n :size-limit=\"{ size: maxSize, unit: 'KB' }\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n :request-method=\"requestMethod\"\n @success=\"onSuccess\"\n @fail=\"onFail\"\n @remove=\"onRemove\"\n @validate=\"onValidate\"\n />\n </template>\n\n <!-- 文件模式 - 使用 TDesign 默认样式 -->\n <template v-else>\n <t-upload\n v-model=\"internalFiles\"\n :action=\"action\"\n :accept=\"acceptString\"\n :max=\"max\"\n :size-limit=\"{ size: maxSize, unit: 'KB' }\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n :request-method=\"requestMethod\"\n @success=\"onSuccess\"\n @fail=\"onFail\"\n @remove=\"onRemove\"\n @validate=\"onValidate\"\n />\n </template>\n </div>\n</template>\n\n<style lang=\"scss\" scoped>\n.smart-upload {\n width: 100%;\n :deep(.t-upload__item) {\n &:has(.t-image){\n background-color: #F1F1F1;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n }\n :deep(.t-upload){\n grid-template-columns: repeat(3,1fr);\n row-gap: 8px;\n }\n}\n</style>\n\n","<script lang=\"ts\">\nexport default {\n name: 'SmartForm',\n}\n</script>\n\n<script setup lang=\"ts\" generic=\"T extends Record<string, unknown>\">\nimport { ref, toRaw, type Component } from 'vue'\nimport {\n Form as TForm,\n FormItem as TFormItem,\n Button as TButton,\n Input as TInput,\n Textarea as TTextarea,\n Switch as TSwitch,\n RadioGroup as TRadioGroup,\n CheckboxGroup as TCheckboxGroup,\n Stepper as TStepper,\n type SubmitContext,\n} from 'tdesign-mobile-vue'\nimport SmartSelect from '../SmartSelect/index.vue'\nimport SmartTimePicker from '../SmartTimePicker/index.vue'\nimport SmartCascader from '../SmartCascader/index.vue'\nimport SmartTreeSelect from '../SmartTreeSelect/index.vue'\nimport SmartUpload from '../SmartUpload/index.vue'\nimport type { FormSchema, SmartFormProps } from './types'\n\ninterface FormInstance {\n validate: (params?: {\n fields?: string[]\n trigger?: 'blur' | 'change' | 'all'\n showErrorMessage?: boolean\n }) => Promise<unknown>\n validateOnly: (params?: { fields?: string[]; trigger?: 'change' }) => Promise<unknown>\n submit: (params?: { showErrorMessage?: boolean }) => Promise<unknown>\n reset: (params?: { type?: 'initial' | 'empty'; fields?: string[] }) => void\n clearValidate: (fields?: string[]) => void\n setValidateMessage: (message: Record<string, unknown>) => void\n}\n\nconst props = withDefaults(defineProps<SmartFormProps<T>>(), {\n labelWidth: '80px',\n labelAlign: 'left',\n showFooter: true,\n submitBtnText: '提交',\n resetBtnText: '重置',\n loading: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: Record<string, unknown>): void\n (e: 'submit', value: Record<string, unknown>): void\n (e: 'reset'): void\n (e: 'validate-failed', errors: unknown): void\n}>()\n\nconst formRef = ref<FormInstance>()\n\n// 内置组件映射\nconst builtInComponentMap: Record<string, Component> = {\n input: TInput,\n password: TInput,\n number: TInput,\n textarea: TTextarea,\n select: SmartSelect,\n cascader: SmartCascader,\n 'tree-select': SmartTreeSelect,\n date: SmartTimePicker,\n time: SmartTimePicker,\n switch: TSwitch,\n radio: TRadioGroup,\n checkbox: TCheckboxGroup,\n stepper: TStepper,\n upload: SmartUpload,\n}\n\n// 获取组件映射(优先使用用户自定义的 componentMap)\nconst getComponent = (type?: string): Component | null => {\n const targetType = type || 'input'\n\n // 1. 优先从用户自定义 componentMap 中查找\n if (props.componentMap?.[targetType]) {\n return props.componentMap[targetType]\n }\n\n // 2. 回退到内置组件映射\n return builtInComponentMap[targetType] ?? null\n}\n\n// 获取验证规则\nconst getRules = (schema: FormSchema<T>) => {\n const rules = []\n if (schema.required) {\n rules.push({ required: true, message: `${schema.label || '该项'}不能为空` })\n }\n if (schema.rules) {\n rules.push(...schema.rules)\n }\n return rules\n}\n\n// 处理表单项属性\nconst getItemProps = (schema: FormSchema<T>) => {\n const model = props.modelValue\n let itemProps: Record<string, unknown> = {}\n\n // 1. 获取基础 props\n if (typeof schema.props === 'function') {\n itemProps = schema.props(model)\n } else if (schema.props) {\n itemProps = { ...schema.props }\n }\n\n // 2. 处理特殊类型属性\n const type = schema.type || 'input'\n if (type === 'password') {\n Object.assign(itemProps, { type: 'password' })\n } else if (type === 'number') {\n Object.assign(itemProps, { type: 'number' })\n } else if (type === 'date') {\n Object.assign(itemProps, { mode: 'date' })\n } else if (type === 'time') {\n Object.assign(itemProps, { mode: ['hour', 'minute'] })\n }\n\n // 3. 对于 Input/Textarea 类型,仅在用户未显式设置时才应用默认 borderless\n // 这样用户可以通过 schema.props.borderless = false 来覆盖默认行为\n if (['input', 'password', 'number', 'textarea'].includes(type)) {\n // 检查用户是否显式设置了 borderless(包括设置为 false 的情况)\n const userExplicitlySetBorderless =\n typeof schema.props === 'object' &&\n schema.props !== null &&\n Object.prototype.hasOwnProperty.call(schema.props, 'borderless')\n if (!userExplicitlySetBorderless && itemProps.borderless === undefined) {\n itemProps.borderless = true\n }\n\n // 安全防护:防止 Input 组件接收 type=\"file\"\n // 如果 schema.type 默认为 input,但 props 中传入了 type: 'file',会导致 v-model 绑定失败引发 InvalidStateError\n if (itemProps.type === 'file') {\n console.warn(\n `[SmartForm] Detected type=\"file\" on Input component (field: ${schema.name}). Please use type: \"upload\" in your schema instead.`,\n )\n delete itemProps.type\n }\n }\n\n // 4. 全局禁用状态传递\n if (props.disabled) {\n Object.assign(itemProps, { disabled: true })\n } else if (schema.disabled) {\n // 字段级禁用控制\n const isDisabled =\n typeof schema.disabled === 'function' ? schema.disabled(model) : schema.disabled\n Object.assign(itemProps, { disabled: isDisabled })\n }\n\n return itemProps\n}\n\n// 判断表单项是否可见\nconst isVisible = (schema: FormSchema<T>) => {\n if (schema.hidden === undefined) return true\n if (typeof schema.hidden === 'function') {\n return !schema.hidden(props.modelValue)\n }\n return !schema.hidden\n}\n\nconst onSubmit = (context: SubmitContext) => {\n if (context.validateResult === true) {\n emit('submit', toRaw(props.modelValue))\n } else {\n emit('validate-failed', context.validateResult)\n }\n}\n\n// 重置处理\nconst onReset = () => {\n formRef.value?.reset()\n emit('reset')\n}\n\n// 暴露方法\ndefineExpose({\n validate: () => formRef.value?.validate(),\n reset: () => formRef.value?.reset(),\n clearValidate: () => formRef.value?.clearValidate(),\n})\n</script>\n\n<template>\n <div class=\"smart-form\">\n <t-form\n ref=\"formRef\"\n :data=\"modelValue\"\n :label-width=\"labelWidth\"\n :label-align=\"labelAlign\"\n @submit=\"onSubmit\"\n >\n <template v-for=\"schema in schemas\" :key=\"schema.name\">\n <t-form-item\n v-if=\"isVisible(schema)\"\n :name=\"schema.name\"\n :label=\"schema.label\"\n :rules=\"getRules(schema)\"\n :help=\"schema.help\"\n >\n <!-- 自定义插槽 -->\n <template v-if=\"schema.type === 'slot' && schema.slot\">\n <slot :name=\"schema.slot\" :model=\"modelValue\" :schema=\"schema\" />\n </template>\n\n <!-- 动态组件渲染 -->\n <component\n :is=\"getComponent(schema.type)\"\n v-else\n v-model=\"(modelValue as Record<string, any>)[schema.name]\"\n v-bind=\"getItemProps(schema)\"\n />\n </t-form-item>\n </template>\n\n <!-- 底部按钮 -->\n <div v-if=\"showFooter\" class=\"smart-form-footer\">\n <t-button theme=\"primary\" type=\"submit\" block :loading=\"loading\" :disabled=\"disabled\">\n {{ submitBtnText }}\n </t-button>\n <t-button\n v-if=\"resetBtnText\"\n theme=\"default\"\n variant=\"outline\"\n block\n class=\"reset-btn\"\n @click=\"onReset\"\n :disabled=\"disabled || loading\"\n >\n {{ resetBtnText }}\n </t-button>\n </div>\n </t-form>\n </div>\n</template>\n\n<style lang=\"scss\" scoped>\n.smart-form {\n .smart-form-footer {\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n\n .reset-btn {\n margin-top: 0; // 覆盖 tdesign 默认样式\n }\n }\n}\n</style>\n"],"names":["usePopupField","options","visible","ref","displayValue","computed","val","foundLabel","__default__","props","__props","emit","__emit","findLabelFromColumns","cols","value","flatColumns","selectedItem","item","openPopup","closePopup","toRef","POPUP_KEYS","popupBindProps","result","propsAny","key","INPUT_KEYS","inputBindProps","onConfirm","context","selectedValue","onCancel","onChange","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","_mergeProps","$attrs","h","Icon","$slots","_","slotName","_withCtx","slotData","_renderSlot","_ctx","_normalizeProps","_guardReactiveProps","$event","TPicker","modelValue","title","inputFormat","others","dateTimePickerBindProps","label","placeholder","rules","required","onTriggerClick","onPick","name","findLabelPath","targetValue","path","option","found","findLabelFromOptions","CASCADER_KEYS","cascaderBindProps","onClose","trigger","TREE_SELECT_KEYS","treeSelectBindProps","isLeafValue","buildValuePath","internalValue","normalizePathValue","watch","newVal","opts","findLabel","lastVal","level","normalizedValue","leafValue","_createElementVNode","_hoisted_2","_createBlock","internalFiles","acceptString","valueType","list","isFullUrl","url","extractUrl","extractName","removeUrlPrefix","parseModelValue","items","index","fileInfo","parsed","currentSuccessUrls","f","parsedUrls","uploadingFiles","syncModelValue","finishedFiles","rawUrl","baseObj","onSuccess","file","response","uploadResult","data","isResultObject","fileUrl","rawResultObj","urlValue","displayUrl","nextTick","fileIndex","onFail","Message","onRemove","onValidate","type","files","TUpload","formRef","builtInComponentMap","TInput","TTextarea","SmartSelect","SmartCascader","SmartTreeSelect","SmartTimePicker","TSwitch","TRadioGroup","TCheckboxGroup","TStepper","SmartUpload","getComponent","targetType","getRules","schema","getItemProps","model","itemProps","isDisabled","isVisible","onSubmit","toRaw","onReset","__expose","TForm","_Fragment","_renderList","TFormItem","_resolveDynamicComponent","TButton"],"mappings":"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"}