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 +330 -0
- package/dist/favicon.ico +0 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +659 -0
- package/dist/index.js +969 -0
- package/dist/index.js.map +1 -0
- package/dist/resolver.cjs +2 -0
- package/dist/resolver.cjs.map +1 -0
- package/dist/resolver.d.ts +41 -0
- package/dist/resolver.js +24 -0
- package/dist/resolver.js.map +1 -0
- package/dist/style.css +1 -0
- package/package.json +89 -0
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
|
+
[](https://www.npmjs.com/package/h5-tdsign-for-vue)
|
|
6
|
+
[](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)
|
package/dist/favicon.ico
ADDED
|
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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"}
|