mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-07 01:28:30 +08:00
feat: ✨ 新增 Form 表单组件
This commit is contained in:
parent
ed56bcdb03
commit
c8086d624c
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: weisheng
|
* @Author: weisheng
|
||||||
* @Date: 2023-07-27 10:26:09
|
* @Date: 2023-07-27 10:26:09
|
||||||
* @LastEditTime: 2023-12-07 15:08:32
|
* @LastEditTime: 2023-12-21 22:27:02
|
||||||
* @LastEditors: weisheng
|
* @LastEditors: weisheng
|
||||||
* @Description:
|
* @Description:
|
||||||
* @FilePath: \wot-design-uni\docs\.vitepress\config.ts
|
* @FilePath: \wot-design-uni\docs\.vitepress\config.ts
|
||||||
@ -231,10 +231,13 @@ export default defineConfig({
|
|||||||
text: "DatetimePickerView 时间选择器视图"
|
text: "DatetimePickerView 时间选择器视图"
|
||||||
}, {
|
}, {
|
||||||
link: "/component/form",
|
link: "/component/form",
|
||||||
text: "Form 表单组合"
|
text: "Form 表单"
|
||||||
}, {
|
}, {
|
||||||
link: "/component/input",
|
link: "/component/input",
|
||||||
text: "Input 输入框"
|
text: "Input 输入框"
|
||||||
|
}, {
|
||||||
|
link: "/component/textarea",
|
||||||
|
text: "Textarea 文本域"
|
||||||
}, {
|
}, {
|
||||||
link: "/component/input-number",
|
link: "/component/input-number",
|
||||||
text: "InputNumber 计数器"
|
text: "InputNumber 计数器"
|
||||||
|
|||||||
@ -397,6 +397,17 @@ function handleConfirm4({ value }) {
|
|||||||
| close-on-click-modal | 点击遮罩是否关闭 | boolean | - | true | - |
|
| close-on-click-modal | 点击遮罩是否关闭 | boolean | - | true | - |
|
||||||
| z-index | 弹窗层级 | number | - | 15 | - |
|
| z-index | 弹窗层级 | number | - | 15 | - |
|
||||||
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离(iphone X 类型的机型) | boolean | - | true | - |
|
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离(iphone X 类型的机型) | boolean | - | true | - |
|
||||||
|
| prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - |
|
||||||
|
| rules | 表单验证规则 | `FormItemRule []` | - | `[]` | - |
|
||||||
|
|
||||||
|
### FormItemRule 数据结构
|
||||||
|
|
||||||
|
| 键名 | 说明 | 类型 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| required | 是否为必选字段 | `boolean` |
|
||||||
|
| message | 错误提示文案 | `string` |
|
||||||
|
| validator | 通过函数进行校验,可以返回一个 `Promise` 来进行异步校验 | `(value, rule) => boolean \| Promise` |
|
||||||
|
| pattern | 通过正则表达式进行校验,正则无法匹配表示校验不通过 | `RegExp` |
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
|
|||||||
@ -35,10 +35,10 @@
|
|||||||
```scss
|
```scss
|
||||||
.cell-icon {
|
.cell-icon {
|
||||||
display: block;
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
margin-top: 2px;
|
margin-right: 4px;
|
||||||
margin-right: 15px;
|
|
||||||
background: url('https://img10.360buyimg.com/jmadvertisement/jfs/t1/71075/7/3762/1820/5d1f26d1E0d600b9e/a264c901943080ac.png') no-repeat;
|
background: url('https://img10.360buyimg.com/jmadvertisement/jfs/t1/71075/7/3762/1820/5d1f26d1E0d600b9e/a264c901943080ac.png') no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
@ -198,10 +198,11 @@ function handleSwitchChange({ value }) {
|
|||||||
```scss
|
```scss
|
||||||
.cell-icon {
|
.cell-icon {
|
||||||
display: block;
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 4px 0;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 24px;
|
||||||
margin-top: 2px;
|
margin-right: 4px;
|
||||||
margin-right: 15px;
|
|
||||||
background: url('https://img10.360buyimg.com/jmadvertisement/jfs/t1/71075/7/3762/1820/5d1f26d1E0d600b9e/a264c901943080ac.png') no-repeat;
|
background: url('https://img10.360buyimg.com/jmadvertisement/jfs/t1/71075/7/3762/1820/5d1f26d1E0d600b9e/a264c901943080ac.png') no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
@ -251,6 +252,18 @@ function handleSwitchChange({ value }) {
|
|||||||
| center | 是否垂直居中,默认顶部居中 | boolean | - | false | - |
|
| center | 是否垂直居中,默认顶部居中 | boolean | - | false | - |
|
||||||
| required | 表单属性,必填 | boolean | - | false | - |
|
| required | 表单属性,必填 | boolean | - | false | - |
|
||||||
| vertical | 表单属性,上下结构 | boolean | - | false | - |
|
| vertical | 表单属性,上下结构 | boolean | - | false | - |
|
||||||
|
| prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - |
|
||||||
|
| rules | 表单验证规则 | `FormItemRule []` | - | `[]` | - |
|
||||||
|
|
||||||
|
### FormItemRule 数据结构
|
||||||
|
|
||||||
|
| 键名 | 说明 | 类型 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| required | 是否为必选字段 | `boolean` |
|
||||||
|
| message | 错误提示文案 | `string` |
|
||||||
|
| validator | 通过函数进行校验,可以返回一个 `Promise` 来进行异步校验 | `(value, rule) => boolean \| Promise` |
|
||||||
|
| pattern | 通过正则表达式进行校验,正则无法匹配表示校验不通过 | `RegExp` |
|
||||||
|
|
||||||
|
|
||||||
## Cell Events
|
## Cell Events
|
||||||
|
|
||||||
|
|||||||
@ -525,6 +525,17 @@ const columnChange = ({ selectedItem, resolve, finish }) => {
|
|||||||
| z-index | 弹窗层级 | number | - | 15 | - |
|
| z-index | 弹窗层级 | number | - | 15 | - |
|
||||||
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离(iphone X 类型的机型) | boolean | - | true | - |
|
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离(iphone X 类型的机型) | boolean | - | true | - |
|
||||||
| ellipsis | 是否超出隐藏 | boolean | - | false | - |
|
| ellipsis | 是否超出隐藏 | boolean | - | false | - |
|
||||||
|
| prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - |
|
||||||
|
| rules | 表单验证规则 | `FormItemRule []` | - | `[]` | - |
|
||||||
|
|
||||||
|
### FormItemRule 数据结构
|
||||||
|
|
||||||
|
| 键名 | 说明 | 类型 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| required | 是否为必选字段 | `boolean` |
|
||||||
|
| message | 错误提示文案 | `string` |
|
||||||
|
| validator | 通过函数进行校验,可以返回一个 `Promise` 来进行异步校验 | `(value, rule) => boolean \| Promise` |
|
||||||
|
| pattern | 通过正则表达式进行校验,正则无法匹配表示校验不通过 | `RegExp` |
|
||||||
|
|
||||||
## 选项数据结构
|
## 选项数据结构
|
||||||
|
|
||||||
|
|||||||
@ -284,6 +284,17 @@ const displayFormatTabLabel = (items) => {
|
|||||||
| z-index | 弹窗层级 | number | - | 15 | - |
|
| z-index | 弹窗层级 | number | - | 15 | - |
|
||||||
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离(iphone X 类型的机型) | boolean | - | true | - |
|
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离(iphone X 类型的机型) | boolean | - | true | - |
|
||||||
| ellipsis | 是否超出隐藏 | boolean | - | false | - |
|
| ellipsis | 是否超出隐藏 | boolean | - | false | - |
|
||||||
|
| prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - |
|
||||||
|
| rules | 表单验证规则 | `FormItemRule []` | - | `[]` | - |
|
||||||
|
|
||||||
|
### FormItemRule 数据结构
|
||||||
|
|
||||||
|
| 键名 | 说明 | 类型 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| required | 是否为必选字段 | `boolean` |
|
||||||
|
| message | 错误提示文案 | `string` |
|
||||||
|
| validator | 通过函数进行校验,可以返回一个 `Promise` 来进行异步校验 | `(value, rule) => boolean \| Promise` |
|
||||||
|
| pattern | 通过正则表达式进行校验,正则无法匹配表示校验不通过 | `RegExp` |
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
|
|||||||
@ -1,141 +1,727 @@
|
|||||||
<frame/>
|
<frame/>
|
||||||
|
|
||||||
# Form 表单组合
|
# Form 表单 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.0</el-tag>
|
||||||
|
|
||||||
本章节主要讲如何将多个 form 表单组件进行组合,形成一个完整的表单页面。
|
用于数据录入、校验,支持输入框、单选框、复选框、文件上传等类型,常见的 form 表单为`单元格`形式的展示,即左侧为表单的标题描述,右侧为表单的输入。
|
||||||
|
|
||||||
常见的 form 表单为`单元格`形式的展示,即左侧为表单的标题描述,右侧为表单的输入。
|
其中,`Input 输入框`、`Textarea 输入框`、`Picker 选择器`、 `Calendar 日历选择器`、 `ColPicker 多列选择器`、`SelectPicker 单复选选择器`、`Cell 单元格` 和 `DatetimePicker 日期时间选择器`具有`单元格`的展示形式,同时也支持 `prop` 和 `rules` 属性,我们称之为`表单项组件`,而 `InputNumber 计数器` 、 `Switch 开关` 和 `Upload 上传` 等组件则需要使用 `Cell 单元格` 进行包裹使用。
|
||||||
|
|
||||||
其中,`Input 输入框`、`Picker 选择器`、 `Calendar 日历选择器`, `ColPicker 多列选择器`、`SelectPicker 单复选选择器` 和 `DatetimePicker 日期时间选择器`具有`单元格`的展示形式,而 `InputNumber 计数器`和 `Switch 开关`需要使用 `Cell 单元格`进行包裹使用。
|
结合 `wd-form` 组件,可以实现对以上组件的规则校验。
|
||||||
|
|
||||||
所有的表单组件都支持 `name` 属性,可以结合小程序原生的 `form` 组件,监听 `submit` 事件,统一获取到所有表单组件的 `value`,也可以单独对每个表单组件监听 `change` 事件来获取单个表单组件的 `value`。
|
|
||||||
|
|
||||||
> 对于表单组件,建议对 wd-cell-group 开启 border 属性,这样每条 cell 就会有边框线隔离开,这样表单的划分比较清晰。
|
> 对于表单组件,建议对 wd-cell-group 开启 border 属性,这样每条 cell 就会有边框线隔离开,这样表单的划分比较清晰。
|
||||||
|
|
||||||
下面是 Demo 示例:
|
## 基础用法
|
||||||
|
|
||||||
html 文件代码:
|
在表单中,使用 `model` 指定表单数据对象,每个 `表单项组件` 代表一个表单项,使用 `prop` 指定表单项字段 ,使用 `rules` 属性定义校验规则。
|
||||||
|
|
||||||
```html
|
::: details 查看基础用法示例
|
||||||
|
::: code-group
|
||||||
|
|
||||||
|
```html [vue]
|
||||||
|
<wd-form ref="form" :model="model">
|
||||||
|
<wd-cell-group border>
|
||||||
|
<wd-input
|
||||||
|
label="用户名"
|
||||||
|
label-width="100px"
|
||||||
|
prop="value1"
|
||||||
|
clearable
|
||||||
|
v-model="model.value1"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
:rules="[{ required: true, message: '请填写用户名' }]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
label="密码"
|
||||||
|
label-width="100px"
|
||||||
|
prop="value2"
|
||||||
|
show-password
|
||||||
|
clearable
|
||||||
|
v-model="model.value2"
|
||||||
|
placeholder="请输入密码"
|
||||||
|
:rules="[{ required: true, message: '请填写密码' }]"
|
||||||
|
/>
|
||||||
|
</wd-cell-group>
|
||||||
|
<view class="footer">
|
||||||
|
<wd-button type="primary" size="large" @click="handleSubmit" block>提交</wd-button>
|
||||||
|
</view>
|
||||||
|
</wd-form>
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript [typescript]
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const { success: showSuccess } = useToast()
|
||||||
|
|
||||||
|
const model = reactive<{
|
||||||
|
value1: string
|
||||||
|
value2: string
|
||||||
|
}>({
|
||||||
|
value1: '',
|
||||||
|
value2: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const form = ref()
|
||||||
|
|
||||||
|
function handleSubmit1() {
|
||||||
|
form.value
|
||||||
|
.validate()
|
||||||
|
.then(({ valid, errors }) => {
|
||||||
|
if (valid) {
|
||||||
|
showSuccess({
|
||||||
|
msg: '校验通过'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error, 'error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
```css [css]
|
||||||
|
.footer {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 校验规则
|
||||||
|
|
||||||
|
本章节演示四种自定义校验及提示规则:`正则校验`、`函数校验`、`函数返回错误提示`和`异步函数校验`。
|
||||||
|
|
||||||
|
::: details 查看校验规则示例
|
||||||
|
::: code-group
|
||||||
|
|
||||||
|
```html [vue]
|
||||||
|
<wd-form ref="form2" :model="model">
|
||||||
|
<wd-cell-group border>
|
||||||
|
<wd-input
|
||||||
|
label="校验"
|
||||||
|
label-width="100px"
|
||||||
|
prop="value1"
|
||||||
|
clearable
|
||||||
|
v-model="model.value1"
|
||||||
|
placeholder="正则校验"
|
||||||
|
:rules="[{ required: false, pattern: /\d{6}/, message: '请输入6位字符' }]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
label="校验"
|
||||||
|
label-width="100px"
|
||||||
|
prop="value2"
|
||||||
|
clearable
|
||||||
|
v-model="model.value2"
|
||||||
|
placeholder="函数校验"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: false,
|
||||||
|
validator: validatorMessage,
|
||||||
|
message: '请输入正确的手机号'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
label="校验"
|
||||||
|
label-width="100px"
|
||||||
|
prop="value3"
|
||||||
|
clearable
|
||||||
|
v-model="model.value3"
|
||||||
|
placeholder="校验函数返回错误提示"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: false,
|
||||||
|
message: '请输入内容',
|
||||||
|
validator: validator
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
label="校验"
|
||||||
|
label-width="100px"
|
||||||
|
prop="value4"
|
||||||
|
clearable
|
||||||
|
v-model="model.value4"
|
||||||
|
placeholder="异步函数校验"
|
||||||
|
:rules="[{ required: false, validator: asyncValidator, message: '请输入1234' }]"
|
||||||
|
/>
|
||||||
|
</wd-cell-group>
|
||||||
|
<view class="footer">
|
||||||
|
<wd-button type="primary" size="large" @click="handleSubmit" block>提交</wd-button>
|
||||||
|
</view>
|
||||||
|
</wd-form>
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript [typescript]
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const model = reactive<{
|
||||||
|
value1: string
|
||||||
|
value2: string
|
||||||
|
value3: string
|
||||||
|
value4: string
|
||||||
|
}>({
|
||||||
|
value1: '',
|
||||||
|
value2: '',
|
||||||
|
value3: '',
|
||||||
|
value4: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const { success: showSuccess } = useToast()
|
||||||
|
|
||||||
|
const form = ref()
|
||||||
|
|
||||||
|
const validatorMessage = (val) => {
|
||||||
|
return /1\d{10}/.test(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validator = (val) => {
|
||||||
|
if (String(val).length >= 4) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('长度不得小于4')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验函数可以返回 Promise,实现异步校验
|
||||||
|
const asyncValidator = (val) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
showLoading('验证中...')
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
closeToast()
|
||||||
|
resolve(val === '1234')
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
form.value
|
||||||
|
.validate()
|
||||||
|
.then(({ valid, errors }) => {
|
||||||
|
if (valid) {
|
||||||
|
showSuccess({
|
||||||
|
msg: '提交成功'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error, 'error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
```css [css]
|
||||||
|
.footer {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 动态表单
|
||||||
|
|
||||||
|
表单项动态增减。
|
||||||
|
|
||||||
|
::: details 查看动态表单示例
|
||||||
|
::: code-group
|
||||||
|
|
||||||
|
```html [vue]
|
||||||
|
<wd-form ref="form" :model="model">
|
||||||
|
<wd-cell-group border>
|
||||||
|
<wd-input
|
||||||
|
label="用户名"
|
||||||
|
label-width="100px"
|
||||||
|
prop="name"
|
||||||
|
clearable
|
||||||
|
v-model="model.name"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
:rules="[{ required: true, message: '请填写用户名' }]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
v-for="(item, index) in model.phoneNumbers"
|
||||||
|
:key="item.key"
|
||||||
|
:label="'联系方式' + index"
|
||||||
|
:prop="'phoneNumbers.' + index + '.value'"
|
||||||
|
label-width="100px"
|
||||||
|
clearable
|
||||||
|
v-model="item.value"
|
||||||
|
placeholder="联系方式"
|
||||||
|
:rules="[{ required: true, message: '请填写联系方式' + index }]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<wd-cell title-width="0px">
|
||||||
|
<view class="footer">
|
||||||
|
<wd-button size="small" type="info" plain @click="addPhone">添加</wd-button>
|
||||||
|
<wd-button size="small" type="info" plain @click="removePhone">删除</wd-button>
|
||||||
|
<wd-button size="small" type="info" plain @click="reset">重置</wd-button>
|
||||||
|
<wd-button type="primary" size="small" @click="submit">提交</wd-button>
|
||||||
|
</view>
|
||||||
|
</wd-cell>
|
||||||
|
</wd-cell-group>
|
||||||
|
</wd-form>
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript [typescript]
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useToast } from '@/uni_modules/wot-design-uni'
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
interface PhoneItem {
|
||||||
|
key: number
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = reactive<{
|
||||||
|
name: string
|
||||||
|
phoneNumbers: PhoneItem[]
|
||||||
|
}>({
|
||||||
|
name: '',
|
||||||
|
phoneNumbers: [
|
||||||
|
{
|
||||||
|
key: Date.now(),
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const { success: showSuccess } = useToast()
|
||||||
|
const form = ref()
|
||||||
|
|
||||||
|
const removePhone = () => {
|
||||||
|
model.phoneNumbers.splice(model.phoneNumbers.length - 1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addPhone = () => {
|
||||||
|
model.phoneNumbers.push({
|
||||||
|
key: Date.now(),
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
form.value.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
form.value.validate().then(({ valid, errors }) => {
|
||||||
|
if (valid) {
|
||||||
|
showSuccess('校验通过')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
```css [css]
|
||||||
|
.footer {
|
||||||
|
text-align: left;
|
||||||
|
:deep(.wd-button) {
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 指定字段校验
|
||||||
|
|
||||||
|
`validate` 方法可以传入一个 `prop` 参数,指定校验的字段,可以实现在表单组件的`blur`、`change`等事件触发时对该字段的校验。
|
||||||
|
|
||||||
|
::: details 查看指定字段校验示例
|
||||||
|
::: code-group
|
||||||
|
|
||||||
|
```html [vue]
|
||||||
|
<wd-form ref="form" :model="model">
|
||||||
|
<wd-cell-group border>
|
||||||
|
<wd-input
|
||||||
|
label="用户名"
|
||||||
|
label-width="100px"
|
||||||
|
prop="name"
|
||||||
|
clearable
|
||||||
|
v-model="model.name"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
@blur="handleBlur('name')"
|
||||||
|
:rules="[{ required: true, message: '请填写用户名' }]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
label="联系方式"
|
||||||
|
prop="phoneNumber"
|
||||||
|
label-width="100px"
|
||||||
|
clearable
|
||||||
|
@blur="handleBlur('phoneNumber')"
|
||||||
|
v-model="model.phoneNumber"
|
||||||
|
placeholder="联系方式"
|
||||||
|
:rules="[{ required: true, message: '请填写联系方式' }]"
|
||||||
|
/>
|
||||||
|
</wd-cell-group>
|
||||||
|
</wd-form>
|
||||||
|
|
||||||
|
<view class="footer">
|
||||||
|
<wd-button type="primary" size="large" block @click="handleSubmit">提交</wd-button>
|
||||||
|
</view>
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript [typescript]
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useToast } from '@/uni_modules/wot-design-uni'
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
const model = reactive<{
|
||||||
|
name: string
|
||||||
|
phoneNumber: string
|
||||||
|
}>({
|
||||||
|
name: '',
|
||||||
|
phoneNumber: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const { success: showSuccess } = useToast()
|
||||||
|
const form = ref()
|
||||||
|
|
||||||
|
function handleBlur(prop: string) {
|
||||||
|
form.value.validate(prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
form.value
|
||||||
|
.validate()
|
||||||
|
.then(({ valid }) => {
|
||||||
|
if (valid) {
|
||||||
|
showSuccess('校验通过')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error, 'error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
```css [css]
|
||||||
|
.footer {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 复杂表单
|
||||||
|
|
||||||
|
结合`Input 输入框`、`Textarea 输入框`、`Picker 选择器`、 `Calendar 日历选择器`、 `ColPicker 多列选择器`、`SelectPicker 单复选选择器`、`Cell 单元格` 和 `DatetimePicker 日期时间选择器`实现一个复杂表单。
|
||||||
|
|
||||||
|
::: details 查看复杂表单示例
|
||||||
|
::: code-group
|
||||||
|
|
||||||
|
```html [vue]
|
||||||
|
<view>
|
||||||
<wd-message-box />
|
<wd-message-box />
|
||||||
<wd-toast />
|
<wd-toast />
|
||||||
<form @submit="formSubmit">
|
<wd-form ref="form" :model="model" :rules="rules">
|
||||||
<wd-cell-group custom-class="group" title="基础信息" border>
|
<wd-cell-group custom-class="group" title="基础信息" border>
|
||||||
<wd-input
|
<wd-input
|
||||||
label="优惠券名称"
|
label="优惠券名称"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
:maxlength="20"
|
:maxlength="20"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
name="couponName"
|
prop="couponName"
|
||||||
required
|
required
|
||||||
suffix-icon="warn-bold"
|
suffix-icon="warn-bold"
|
||||||
clearable
|
clearable
|
||||||
v-model="couponName"
|
v-model="model.couponName"
|
||||||
placeholder="请输入优惠券名称"
|
placeholder="请输入优惠券名称"
|
||||||
@change="handleCouponName"
|
|
||||||
@clicksuffixicon="handleIconClick"
|
@clicksuffixicon="handleIconClick"
|
||||||
/>
|
/>
|
||||||
<wd-select-picker
|
<wd-select-picker
|
||||||
label="推广平台"
|
label="推广平台"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
name="platform"
|
prop="platform"
|
||||||
v-model="platform"
|
v-model="model.platform"
|
||||||
:columns="platformList"
|
:columns="platformList"
|
||||||
placeholder="请选择推广平台"
|
placeholder="请选择推广平台"
|
||||||
@confirm="handlePlatform"
|
|
||||||
/>
|
/>
|
||||||
<wd-picker label="优惠方式" label-width="100px" name="promotion" align-right v-model="promotion" :columns="promotionlist" />
|
<wd-picker
|
||||||
<wd-cell title="券面额" required title-width="100px" custom-value-class="cell-left">
|
label="优惠方式"
|
||||||
|
placeholder="请选择优惠方式"
|
||||||
|
label-width="100px"
|
||||||
|
prop="promotion"
|
||||||
|
v-model="model.promotion"
|
||||||
|
:columns="promotionlist"
|
||||||
|
/>
|
||||||
|
<wd-cell prop="threshold" title="券面额" required title-width="100px" custom-value-class="cell-left">
|
||||||
<view style="text-align: left">
|
<view style="text-align: left">
|
||||||
<view class="inline-txt" style="margin-left: 0">满</view>
|
<view class="inline-txt" style="margin-left: 0">满</view>
|
||||||
<wd-input
|
<wd-input
|
||||||
no-border
|
no-border
|
||||||
custom-style="display: inline-block; width: 70px; vertical-align: middle"
|
custom-style="display: inline-block; width: 70px; vertical-align: middle"
|
||||||
placeholder="请输入金额"
|
placeholder="请输入金额"
|
||||||
v-model="threshold"
|
v-model="model.threshold"
|
||||||
name="threshold"
|
|
||||||
@change="handleThreshold"
|
|
||||||
/>
|
/>
|
||||||
<view class="inline-txt">减</view>
|
<view class="inline-txt">减</view>
|
||||||
<wd-input
|
<wd-input
|
||||||
no-border
|
no-border
|
||||||
custom-style="display: inline-block; width: 70px; vertical-align: middle"
|
custom-style="display: inline-block; width: 70px; vertical-align: middle"
|
||||||
placeholder="请输入金额"
|
placeholder="请输入金额"
|
||||||
v-model="price"
|
v-model="model.price"
|
||||||
name="price"
|
|
||||||
@change="handlePrice"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</wd-cell>
|
</wd-cell>
|
||||||
</wd-cell-group>
|
</wd-cell-group>
|
||||||
<wd-cell-group custom-class="group" title="时间和地址" border>
|
<wd-cell-group custom-class="group" title="时间和地址" border>
|
||||||
<wd-datetime-picker label="时间" label-width="100px" name="date" v-model="date" @confirm="handleDate" />
|
<wd-datetime-picker label="时间" label-width="100px" placeholder="请选择时间" prop="time" v-model="model.time" />
|
||||||
|
<wd-calendar label="日期" label-width="100px" placeholder="请选择日期" prop="date" v-model="model.date" />
|
||||||
|
|
||||||
<wd-col-picker
|
<wd-col-picker
|
||||||
label="地址"
|
label="地址"
|
||||||
|
placeholder="请选择地址"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
name="address"
|
prop="address"
|
||||||
v-model="address"
|
v-model="model.address"
|
||||||
:columns="area"
|
:columns="area"
|
||||||
:column-change="areaChange"
|
:column-change="areaChange"
|
||||||
@confirm="handleAddress"
|
|
||||||
/>
|
/>
|
||||||
</wd-cell-group>
|
</wd-cell-group>
|
||||||
<wd-cell-group custom-class="group" title="其他信息" border>
|
<wd-cell-group custom-class="group" title="其他信息" border>
|
||||||
<wd-input
|
<wd-textarea
|
||||||
label="活动细则"
|
label="活动细则"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
v-model="content"
|
v-model="model.content"
|
||||||
:maxlength="300"
|
:maxlength="300"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
placeholder="请输入活动细则信息"
|
placeholder="请输入活动细则信息"
|
||||||
clearable
|
clearable
|
||||||
name="content"
|
prop="content"
|
||||||
@change="handleContent"
|
|
||||||
/>
|
/>
|
||||||
<wd-cell title="发货数量" center>
|
<wd-cell title="发货数量" title-width="100px" prop="count">
|
||||||
<wd-input-number v-model="count" name="count" @change="handleCount" />
|
<view style="text-align: left">
|
||||||
|
<wd-input-number v-model="model.count" />
|
||||||
|
</view>
|
||||||
</wd-cell>
|
</wd-cell>
|
||||||
<wd-cell title="这里显示的是多文字标题包含非常的文字" title-width="240px" center>
|
<wd-cell title="开启折扣" title-width="100px" prop="switchVal" center>
|
||||||
<wd-switch v-model="switchVal" name="switchVal" @change="handleSwitch" />
|
<view style="text-align: left">
|
||||||
|
<wd-switch v-model="model.switchVal" />
|
||||||
|
</view>
|
||||||
|
</wd-cell>
|
||||||
|
<wd-input label="卡号" label-width="100px" prop="cardId" suffix-icon="camera" placeholder="请输入卡号" clearable v-model="model.cardId" />
|
||||||
|
<wd-input label="手机号" label-width="100px" prop="phone" placeholder="请输入手机号" clearable v-model="model.phone" />
|
||||||
|
<wd-cell title="活动图片" title-width="100px" prop="fileList">
|
||||||
|
<wd-upload :file-list="model.fileList" action="https://ftf.jd.com/api/uploadImg" @change="handleFileChange"></wd-upload>
|
||||||
</wd-cell>
|
</wd-cell>
|
||||||
<wd-input
|
|
||||||
label="卡号"
|
|
||||||
label-width="100px"
|
|
||||||
name="cardId"
|
|
||||||
suffix-icon="camera"
|
|
||||||
placeholder="请输入卡号"
|
|
||||||
clearable
|
|
||||||
v-model="cardId"
|
|
||||||
@change="handleCardId"
|
|
||||||
/>
|
|
||||||
<wd-input label="手机号" label-width="100px" name="phone" placeholder="请输入手机号" clearable v-model="phone" @change="handlePhone" />
|
|
||||||
</wd-cell-group>
|
</wd-cell-group>
|
||||||
<view class="tip">
|
<view class="tip">
|
||||||
<wd-checkbox v-model="read" name="read" @change="handleRead" custom-label-class="label-class">
|
<wd-checkbox v-model="model.read" prop="read" custom-label-class="label-class">
|
||||||
已阅读并同意
|
已阅读并同意
|
||||||
<text style="color: #4d80f0">《借款额度合同及相关授权》</text>
|
<text style="color: #4d80f0">《借款额度合同及相关授权》</text>
|
||||||
</wd-checkbox>
|
</wd-checkbox>
|
||||||
</view>
|
</view>
|
||||||
<view class="footer">
|
<view class="footer">
|
||||||
<button class="wd-button is-primary is-block is-round is-large" form-type="submit">提交</button>
|
<wd-button type="primary" size="large" @click="handleSubmit" block>提交</wd-button>
|
||||||
|
</view>
|
||||||
|
</wd-form>
|
||||||
</view>
|
</view>
|
||||||
</form>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> 自定义按钮组件的 form-type 无法触发小程序官方form组件的submit事件,微信要求小程序基础库 2.10.3 才支持,京东小程序的则暂时不支持,因此需要用小程序自带的官方 button 组件,样式上可以引入 wot-design 中 button 的样式文件,使用 `wd-button` , `is-primary` , `is-suck` , `is-block`, `is-plain` , `is-disabled` 等类名进行组合使用来展示 wot-design 组件库的按钮样式。
|
```typescript [typescript]
|
||||||
|
<script lang="ts" setup>
|
||||||
index.js 文件代码:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { useMessage } from '@/uni_modules/wot-design-uni/components/wd-message-box'
|
|
||||||
import { useToast } from '@/uni_modules/wot-design-uni'
|
import { useToast } from '@/uni_modules/wot-design-uni'
|
||||||
|
import { isArray } from '@/uni_modules/wot-design-uni/components/common/util'
|
||||||
|
import { FormRules } from '@/uni_modules/wot-design-uni/components/wd-form/types'
|
||||||
import { areaData } from '@/utils/area'
|
import { areaData } from '@/utils/area'
|
||||||
import { ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
const model = reactive<{
|
||||||
|
couponName: string
|
||||||
|
platform: any[]
|
||||||
|
promotion: string
|
||||||
|
threshold: string
|
||||||
|
price: string
|
||||||
|
time: number | string
|
||||||
|
date: null | number
|
||||||
|
address: string[]
|
||||||
|
count: number
|
||||||
|
content: string
|
||||||
|
switchVal: boolean
|
||||||
|
cardId: string
|
||||||
|
phone: string
|
||||||
|
read: boolean
|
||||||
|
fileList: Record<string, string>[]
|
||||||
|
}>({
|
||||||
|
couponName: '',
|
||||||
|
platform: [],
|
||||||
|
promotion: '',
|
||||||
|
threshold: '',
|
||||||
|
price: '',
|
||||||
|
date: null,
|
||||||
|
time: '',
|
||||||
|
address: [],
|
||||||
|
count: 1,
|
||||||
|
content: '',
|
||||||
|
switchVal: true,
|
||||||
|
cardId: '',
|
||||||
|
phone: '',
|
||||||
|
read: false,
|
||||||
|
fileList: []
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules: FormRules = {
|
||||||
|
couponName: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
pattern: /\d{6}/,
|
||||||
|
message: '优惠券名称6个字以上',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请输入优惠券名称')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入活动细则信息',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value && value.length > 2) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请输入活动细则信息')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
threshold: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入满减金额',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value && model.price) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
platform: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择推广平台',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value && isArray(value) && value.length) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请选择推广平台')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
promotion: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择推广平台',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请选择推广平台')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
time: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择时间',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请选择时间')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
date: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择日期',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
address: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择地址',
|
||||||
|
validator: (value) => {
|
||||||
|
if (isArray(value) && value.length) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请选择地址')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
count: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '发货数量需要大于1',
|
||||||
|
validator: (value) => {
|
||||||
|
if (Number(value) > 1) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('发货数量需要大于1')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
cardId: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入卡号',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请输入卡号')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
phone: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入手机号',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
fileList: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择活动图片',
|
||||||
|
validator: (value) => {
|
||||||
|
if (isArray(value) && value.length) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
const couponName = ref<string>('')
|
|
||||||
const couponNameErr = ref<boolean>(false)
|
|
||||||
const platform = ref<any>([])
|
|
||||||
const platformList = ref<any>([
|
const platformList = ref<any>([
|
||||||
{
|
{
|
||||||
value: '1',
|
value: '1',
|
||||||
@ -166,7 +752,6 @@ const platformList = ref<any>([
|
|||||||
label: '京东极速版'
|
label: '京东极速版'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const promotion = ref<string>('1')
|
|
||||||
const promotionlist = ref<any[]>([
|
const promotionlist = ref<any[]>([
|
||||||
{
|
{
|
||||||
value: '1',
|
value: '1',
|
||||||
@ -177,12 +762,6 @@ const promotionlist = ref<any[]>([
|
|||||||
label: '无门槛'
|
label: '无门槛'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const threshold = ref<string>('')
|
|
||||||
const price = ref<string>('')
|
|
||||||
const date = ref<number>(Date.now())
|
|
||||||
const address = ref<any[]>([])
|
|
||||||
|
|
||||||
const count = ref<number>(1)
|
|
||||||
|
|
||||||
const area = ref<any[]>([
|
const area = ref<any[]>([
|
||||||
Object.keys(areaData[86]).map((key) => {
|
Object.keys(areaData[86]).map((key) => {
|
||||||
@ -206,69 +785,32 @@ const areaChange = ({ selectedItem, resolve, finish }) => {
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const content = ref<string>('')
|
|
||||||
const coun = ref<number>(1)
|
|
||||||
const read = ref<boolean>(false)
|
|
||||||
const switchVal = ref<boolean>(true)
|
|
||||||
const cardId = ref<string>('')
|
|
||||||
const phone = ref<string>('')
|
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const messageBox = useMessage()
|
const form = ref()
|
||||||
|
|
||||||
function handleCouponName({ value }) {
|
function handleFileChange({ fileList }) {
|
||||||
console.log(value)
|
model.fileList = fileList
|
||||||
|
}
|
||||||
|
|
||||||
couponNameErr.value = false
|
function handleSubmit() {
|
||||||
}
|
form.value
|
||||||
function handlePlatform({ value }) {
|
.validate()
|
||||||
console.log(value)
|
.then(({ valid, errors }) => {
|
||||||
}
|
console.log(valid)
|
||||||
function handleThreshold({ value }) {
|
console.log(errors)
|
||||||
console.log(value)
|
})
|
||||||
}
|
.catch((error) => {
|
||||||
function handlePrice({ value }) {
|
console.log(error, 'error')
|
||||||
console.log(value)
|
})
|
||||||
}
|
|
||||||
function handleAddress({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handleContent({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handleCount({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handleSwitch({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handleRead({ value }) {
|
|
||||||
read.value = value
|
|
||||||
}
|
|
||||||
function handleCardId({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handlePhone({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function formSubmit() {
|
|
||||||
if (!couponName.value) {
|
|
||||||
toast.error('请填写优惠券名称')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
messageBox.alert('提交成功')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleIconClick() {
|
function handleIconClick() {
|
||||||
toast.info('优惠券提示信息')
|
toast.info('优惠券提示信息')
|
||||||
}
|
}
|
||||||
function handleDate({ value }) {
|
</script>
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
css 文件代码:
|
```css [css]
|
||||||
|
|
||||||
```scss
|
|
||||||
.inline-txt {
|
.inline-txt {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@ -292,3 +834,34 @@ css 文件代码:
|
|||||||
font-size: 12px !important;
|
font-size: 12px !important;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Attributes
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
||||||
|
| ----- | ------------ | --------------------- | ------ | ------ | -------- |
|
||||||
|
| model | 表单数据对象 | `Record<string, any>` | - | - | 0.2.0 |
|
||||||
|
| rules | 表单验证规则 | `FormRules` | - | - | 0.2.0 |
|
||||||
|
|
||||||
|
### FormItemRule 数据结构
|
||||||
|
|
||||||
|
| 键名 | 说明 | 类型 |
|
||||||
|
| --------- | ------------------------------------------------------- | ------------------------------------- |
|
||||||
|
| required | 是否为必选字段 | `boolean` |
|
||||||
|
| message | 错误提示文案 | `string` |
|
||||||
|
| validator | 通过函数进行校验,可以返回一个 `Promise` 来进行异步校验 | `(value, rule) => boolean \| Promise` |
|
||||||
|
| pattern | 通过正则表达式进行校验,正则无法匹配表示校验不通过 | `RegExp` |
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
| 事件名称 | 说明 | 参数 | 最低版本 |
|
||||||
|
| -------- | ------------------------------------------------------------------------------ | --------------- | -------- |
|
||||||
|
| validate | 验证表单,支持传入一个 prop 来验证单个表单项,不传入 prop 时,会验证所有表单项 | `prop?: string` | 0.2.0 |
|
||||||
|
| reset | 重置校验结果 | - | 0.2.0 |
|
||||||
|
|
||||||
|
## 外部样式类
|
||||||
|
|
||||||
|
| 类名 | 说明 | 最低版本 |
|
||||||
|
| ------------ | ---------- | -------- |
|
||||||
|
| custom-class | 根节点样式 | 0.2.0 |
|
||||||
|
|||||||
@ -69,36 +69,6 @@ function handleChange(event) {
|
|||||||
<wd-input v-model="value" :maxlength="20" show-word-limit @change="handleChange"/>
|
<wd-input v-model="value" :maxlength="20" show-word-limit @change="handleChange"/>
|
||||||
```
|
```
|
||||||
|
|
||||||
## 文本域
|
|
||||||
|
|
||||||
设置 `type` 为 'textarea`。
|
|
||||||
|
|
||||||
:::warning
|
|
||||||
当 `wd-input` 的 `type` 为 'textarea' ,并嵌入 `wd-message-box`、`wd-popup`、`wd-action-sheet` 这类弹层组件时,textarea 的 placeholder 样式会失效,需要手动给 `wd-message-box`、`wd-popup`、`wd-action-sheet` 组件设置 `:lazy-render="false"` 属性,textarea 原生组件在这块实现有些问题,对于页面非立即渲染的 textarea 无法成功设置 placeholder 样式
|
|
||||||
:::
|
|
||||||
|
|
||||||
```html
|
|
||||||
<wd-input type="textarea" v-model="value" placeholder="请输入..." @change="handleChange"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
设置清空,字数限制。
|
|
||||||
|
|
||||||
```html
|
|
||||||
<wd-input
|
|
||||||
type="textarea"
|
|
||||||
v-model="value"
|
|
||||||
placeholder="请输入..."
|
|
||||||
:maxlength="120"
|
|
||||||
clearable
|
|
||||||
show-word-limit
|
|
||||||
@change="handleChange"/>
|
|
||||||
```
|
|
||||||
也可以设置`auto-height`使高度自增加。
|
|
||||||
|
|
||||||
```html
|
|
||||||
<wd-input v-model="value" auto-height @change="handleChange" clearable/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 设置label标题
|
## 设置label标题
|
||||||
|
|
||||||
设置 `label` 标题,可以和 `cell-group` 组合使用,形成 `cell` 展示类型。可以通过 `label-width` 设置标题宽度,默认为 '33%'。
|
设置 `label` 标题,可以和 `cell-group` 组合使用,形成 `cell` 展示类型。可以通过 `label-width` 设置标题宽度,默认为 '33%'。
|
||||||
@ -143,7 +113,7 @@ function handleChange(event) {
|
|||||||
|
|
||||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
||||||
|-----|------|-----|-------|-------|---------|
|
|-----|------|-----|-------|-------|---------|
|
||||||
| type | 类型 | string | text / textarea / number / digit / idcard | text | - |
|
| type | 类型 | string | text / number / digit / idcard | text | - |
|
||||||
| v-model | 绑定值 | string / number | - | - | - |
|
| v-model | 绑定值 | string / number | - | - | - |
|
||||||
| placeholder | 占位文本 | string | - | 请输入... | - |
|
| placeholder | 占位文本 | string | - | 请输入... | - |
|
||||||
| clearable | 显示清空按钮 | boolean | - | false | - |
|
| clearable | 显示清空按钮 | boolean | - | false | - |
|
||||||
@ -157,17 +127,14 @@ function handleChange(event) {
|
|||||||
| confirm-type | 设置键盘右下角按钮的文字,仅在type='text'时生效 | string | done / go / next / search / send | done | - |
|
| confirm-type | 设置键盘右下角按钮的文字,仅在type='text'时生效 | string | done / go / next / search / send | done | - |
|
||||||
| confirm-hold | 点击键盘右下角按钮时是否保持键盘不收起 | Boolean | - | false | - |
|
| confirm-hold | 点击键盘右下角按钮时是否保持键盘不收起 | Boolean | - | false | - |
|
||||||
| always-embed | 微信小程序原生属性,强制 input 处于同层状态,默认 focus 时 input 会切到非同层状态 (仅在 iOS 下生效) | Boolean | - | false | - |
|
| always-embed | 微信小程序原生属性,强制 input 处于同层状态,默认 focus 时 input 会切到非同层状态 (仅在 iOS 下生效) | Boolean | - | false | - |
|
||||||
| placeholderStyle | 原生属,指定 placeholder 的样式,目前仅支持color,font-size和font-weight | string | - | - | - |
|
| placeholderStyle | 原生属性,指定 placeholder 的样式,目前仅支持color,font-size和font-weight | string | - | - | - |
|
||||||
| placeholderClass | textarea指定 placeholder 的样式类 | string | - | textarea-placeholder | - |
|
| placeholderClass | 原生属性,指定 placeholder 的样式类 | string | - | - | - |
|
||||||
| focus | 原生属性,获取焦点 | boolean | - | false | - |
|
| focus | 原生属性,获取焦点 | boolean | - | false | - |
|
||||||
| cursorSpacing | 原生属性,指定光标与键盘的距离。取textarea距离底部的距离和cursor-spacing指定的距离的最小值作为光标与键盘的距离 | number | - | 0 | - |
|
| cursorSpacing | 原生属性,指定光标与键盘的距离。取 input 距离底部的距离和cursor-spacing指定的距离的最小值作为光标与键盘的距离 | number | - | 0 | - |
|
||||||
| fixed | textarea原生属性,如果 textarea 是在一个 position:fixed 的区域,需要显示指定属性 fixed 为 true | boolean | - | false | - |
|
|
||||||
| cursor | 原生属性,指定focus时的光标位置 | number | - | -1 | - |
|
| cursor | 原生属性,指定focus时的光标位置 | number | - | -1 | - |
|
||||||
| showConfirmBar | 原生属性,是否显示键盘上方带有”完成“按钮那一栏 | boolean | - | true | - |
|
|
||||||
| selectionStart | 原生属性,光标起始位置,自动聚集时有效,需与selection-end搭配使用 | number | - | -1 | - |
|
| selectionStart | 原生属性,光标起始位置,自动聚集时有效,需与selection-end搭配使用 | number | - | -1 | - |
|
||||||
| selectionEnd | 原生属性,光标结束位置,自动聚集时有效,需与selection-start搭配使用 | number | - | -1 | - |
|
| selectionEnd | 原生属性,光标结束位置,自动聚集时有效,需与selection-start搭配使用 | number | - | -1 | - |
|
||||||
| adjustPosition | 原生属性,键盘弹起时,是否自动上推页面 | boolean | - | true | - |
|
| adjustPosition | 原生属性,键盘弹起时,是否自动上推页面 | boolean | - | true | - |
|
||||||
| autoHeight | textarea原生属性,textarea 行数自适应,从1行开始显示 | boolean | - | false | - |
|
|
||||||
| label | 设置左侧标题 | string | - | - | - |
|
| label | 设置左侧标题 | string | - | - | - |
|
||||||
| size | 设置输入框大小 | string | - | - | - |
|
| size | 设置输入框大小 | string | - | - | - |
|
||||||
| error | 设置输入框错误状态,错误状态时为红色 | boolean | - | false | - |
|
| error | 设置输入框错误状态,错误状态时为红色 | boolean | - | false | - |
|
||||||
@ -177,8 +144,20 @@ function handleChange(event) {
|
|||||||
| use-suffix-slot | 使用 后置图标 插槽 | boolean | - | false | - |
|
| use-suffix-slot | 使用 后置图标 插槽 | boolean | - | false | - |
|
||||||
| use-prefix-slot | 使用 前置图标 插槽 | boolean | - | false | - |
|
| use-prefix-slot | 使用 前置图标 插槽 | boolean | - | false | - |
|
||||||
| required | cell 类型下必填样式 | boolean | - | false | - |
|
| required | cell 类型下必填样式 | boolean | - | false | - |
|
||||||
| name | form 表单中的字段名 | string | - | - | - |
|
|
||||||
| no-border | 非 cell 类型下是否隐藏下划线 | boolean | - | false | - | - |
|
| no-border | 非 cell 类型下是否隐藏下划线 | boolean | - | false | - | - |
|
||||||
|
| prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - |
|
||||||
|
| rules | 表单验证规则 | `FormItemRule []` | - | `[]` | - |
|
||||||
|
|
||||||
|
|
||||||
|
### FormItemRule 数据结构
|
||||||
|
|
||||||
|
| 键名 | 说明 | 类型 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| required | 是否为必选字段 | `boolean` |
|
||||||
|
| message | 错误提示文案 | `string` |
|
||||||
|
| validator | 通过函数进行校验,可以返回一个 `Promise` 来进行异步校验 | `(value, rule) => boolean \| Promise` |
|
||||||
|
| pattern | 通过正则表达式进行校验,正则无法匹配表示校验不通过 | `RegExp` |
|
||||||
|
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
@ -186,10 +165,9 @@ function handleChange(event) {
|
|||||||
|---------|-----|-----|---------|
|
|---------|-----|-----|---------|
|
||||||
| input | 监听输入框input事件 | ` {value, cursor, keyCode}` | - |
|
| input | 监听输入框input事件 | ` {value, cursor, keyCode}` | - |
|
||||||
| focus | 监听输入框focus事件 | ` { value, height }`, height 为键盘高度 | - |
|
| focus | 监听输入框focus事件 | ` { value, height }`, height 为键盘高度 | - |
|
||||||
| blur | 监听输入框blur事件 | ` { value, cursor }`,仅在type="textarea"时存在cursor | - |
|
| blur | 监听输入框blur事件 | ` { value }` | - |
|
||||||
| change | 监听输入框修改事件 | ` { value }` | - |
|
| change | 监听输入框修改事件 | ` { value }` | - |
|
||||||
| clear | 监听输入框清空按钮事件 | - | - |
|
| clear | 监听输入框清空按钮事件 | - | - |
|
||||||
| linechange | 监听输入框行数变化(仅限textarea) | ` { height: 0, heightRpx: 0, lineCount: 0 }` | - |
|
|
||||||
| confirm | 点击完成时, 触发 confirm 事件 | ` { value }` | - |
|
| confirm | 点击完成时, 触发 confirm 事件 | ` { value }` | - |
|
||||||
| keyboardheightchange | 键盘高度发生变化的时候触发此事件 | ` { height, duration }` | - |
|
| keyboardheightchange | 键盘高度发生变化的时候触发此事件 | ` { height, duration }` | - |
|
||||||
| clickprefixicon | 点击前置图标时触发 | - | - |
|
| clickprefixicon | 点击前置图标时触发 | - | - |
|
||||||
@ -211,7 +189,5 @@ function handleChange(event) {
|
|||||||
| 类名 | 说明 | 最低版本 |
|
| 类名 | 说明 | 最低版本 |
|
||||||
|-----|------|--------|
|
|-----|------|--------|
|
||||||
| custom-class | 根节点样式 | - |
|
| custom-class | 根节点样式 | - |
|
||||||
| custom-textarea-container-class | textarea 容器外部自定义样式 | - |
|
|
||||||
| custom-textarea-class | textarea 外部自定义样式 | - |
|
|
||||||
| custom-input-class | input 外部自定义样式 | - |
|
| custom-input-class | input 外部自定义样式 | - |
|
||||||
| custom-label-class | label 外部自定义样式 | - |
|
| custom-label-class | label 外部自定义样式 | - |
|
||||||
@ -261,6 +261,17 @@ function handleConfirm({ value }) {
|
|||||||
| z-index | 弹窗层级 | number | - | 15 | - |
|
| z-index | 弹窗层级 | number | - | 15 | - |
|
||||||
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离(iphone X 类型的机型) | boolean | - | true | - |
|
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离(iphone X 类型的机型) | boolean | - | true | - |
|
||||||
| ellipsis | 是否超出隐藏 | boolean | - | false | - |
|
| ellipsis | 是否超出隐藏 | boolean | - | false | - |
|
||||||
|
| prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - |
|
||||||
|
| rules | 表单验证规则 | `FormItemRule []` | - | `[]` | - |
|
||||||
|
|
||||||
|
### FormItemRule 数据结构
|
||||||
|
|
||||||
|
| 键名 | 说明 | 类型 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| required | 是否为必选字段 | `boolean` |
|
||||||
|
| message | 错误提示文案 | `string` |
|
||||||
|
| validator | 通过函数进行校验,可以返回一个 `Promise` 来进行异步校验 | `(value, rule) => boolean \| Promise` |
|
||||||
|
| pattern | 通过正则表达式进行校验,正则无法匹配表示校验不通过 | `RegExp` |
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
|
|||||||
@ -342,6 +342,17 @@ function handleConfirm({ value, selectedItems }) {
|
|||||||
| filter-placeholder | 搜索框占位符 | string | - | 搜索 | - |
|
| filter-placeholder | 搜索框占位符 | string | - | 搜索 | - |
|
||||||
| ellipsis | 是否超出隐藏 | boolean | - | false | - |
|
| ellipsis | 是否超出隐藏 | boolean | - | false | - |
|
||||||
| scroll-into-view | 重新打开是否滚动到选中项 | boolean | - | true | 0.1.34 |
|
| scroll-into-view | 重新打开是否滚动到选中项 | boolean | - | true | 0.1.34 |
|
||||||
|
| prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - |
|
||||||
|
| rules | 表单验证规则 | `FormItemRule []` | - | `[]` | - |
|
||||||
|
|
||||||
|
### FormItemRule 数据结构
|
||||||
|
|
||||||
|
| 键名 | 说明 | 类型 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| required | 是否为必选字段 | `boolean` |
|
||||||
|
| message | 错误提示文案 | `string` |
|
||||||
|
| validator | 通过函数进行校验,可以返回一个 `Promise` 来进行异步校验 | `(value, rule) => boolean \| Promise` |
|
||||||
|
| pattern | 通过正则表达式进行校验,正则无法匹配表示校验不通过 | `RegExp` |
|
||||||
|
|
||||||
## 选项数据结构
|
## 选项数据结构
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,6 @@
|
|||||||
<!--
|
|
||||||
* @Author: weisheng
|
|
||||||
* @Date: 2023-08-07 18:49:03
|
|
||||||
* @LastEditTime: 2023-10-17 15:14:09
|
|
||||||
* @LastEditors: weisheng
|
|
||||||
* @Description:
|
|
||||||
* @FilePath: \wot-design-uni\src\components\page-wraper\page-wraper.vue
|
|
||||||
* 记得注释
|
|
||||||
-->
|
|
||||||
<template>
|
<template>
|
||||||
<wd-config-provider :theme="theme">
|
<wd-config-provider :theme="theme">
|
||||||
|
<wd-toast />
|
||||||
<view class="page-wraper">
|
<view class="page-wraper">
|
||||||
<wd-cell title="切换暗黑" title-width="240px" center v-if="showDarkMode">
|
<wd-cell title="切换暗黑" title-width="240px" center v-if="showDarkMode">
|
||||||
<wd-switch v-model="isDark" />
|
<wd-switch v-model="isDark" />
|
||||||
|
|||||||
@ -160,6 +160,16 @@
|
|||||||
"navigationBarTitleText": "Input 输入框"
|
"navigationBarTitleText": "Input 输入框"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/textarea/Index",
|
||||||
|
"name": "textarea",
|
||||||
|
"style": {
|
||||||
|
"mp-alipay": {
|
||||||
|
"allowsBounceVertical": "NO"
|
||||||
|
},
|
||||||
|
"navigationBarTitleText": "Textarea 文本域"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/messageBox/Index",
|
"path": "pages/messageBox/Index",
|
||||||
"name": "messageBox",
|
"name": "messageBox",
|
||||||
@ -528,7 +538,37 @@
|
|||||||
"mp-alipay": {
|
"mp-alipay": {
|
||||||
"allowsBounceVertical": "NO"
|
"allowsBounceVertical": "NO"
|
||||||
},
|
},
|
||||||
"navigationBarTitleText": "Form 表单组合"
|
"navigationBarTitleText": "Form 表单"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/form/demo1",
|
||||||
|
"name": "formDemo1",
|
||||||
|
"style": {
|
||||||
|
"mp-alipay": {
|
||||||
|
"allowsBounceVertical": "NO"
|
||||||
|
},
|
||||||
|
"navigationBarTitleText": "复杂表单"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/form/demo2",
|
||||||
|
"name": "formDemo2",
|
||||||
|
"style": {
|
||||||
|
"mp-alipay": {
|
||||||
|
"allowsBounceVertical": "NO"
|
||||||
|
},
|
||||||
|
"navigationBarTitleText": "失焦校验"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/form/demo3",
|
||||||
|
"name": "formDemo3",
|
||||||
|
"style": {
|
||||||
|
"mp-alipay": {
|
||||||
|
"allowsBounceVertical": "NO"
|
||||||
|
},
|
||||||
|
"navigationBarTitleText": "复杂表单"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -128,10 +128,10 @@ function showToast() {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.cell-icon {
|
.cell-icon {
|
||||||
display: block;
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
margin-top: 2px;
|
margin: 4px 4px 4px 0;
|
||||||
margin-right: 15px;
|
|
||||||
background: url('https://img10.360buyimg.com/jmadvertisement/jfs/t1/71075/7/3762/1820/5d1f26d1E0d600b9e/a264c901943080ac.png') no-repeat;
|
background: url('https://img10.360buyimg.com/jmadvertisement/jfs/t1/71075/7/3762/1820/5d1f26d1E0d600b9e/a264c901943080ac.png') no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,279 +1,211 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
|
||||||
<page-wraper>
|
<page-wraper>
|
||||||
<wd-message-box />
|
<demo-block title="基础表单" transparent>
|
||||||
<wd-toast />
|
<wd-form ref="form1" :model="model1">
|
||||||
<form @submit="formSubmit">
|
<wd-cell-group border>
|
||||||
<wd-cell-group custom-class="group" title="基础信息" border>
|
|
||||||
<wd-input
|
<wd-input
|
||||||
label="优惠券名称"
|
label="用户名"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
:maxlength="20"
|
prop="value1"
|
||||||
show-word-limit
|
|
||||||
name="couponName"
|
|
||||||
required
|
|
||||||
suffix-icon="warn-bold"
|
|
||||||
clearable
|
clearable
|
||||||
v-model="couponName"
|
v-model="model1.value1"
|
||||||
placeholder="请输入优惠券名称"
|
placeholder="请输入用户名"
|
||||||
@change="handleCouponName"
|
:rules="[{ required: true, message: '请填写用户名' }]"
|
||||||
@clicksuffixicon="handleIconClick"
|
|
||||||
/>
|
/>
|
||||||
<wd-select-picker
|
|
||||||
label="推广平台"
|
|
||||||
label-width="100px"
|
|
||||||
name="platform"
|
|
||||||
v-model="platform"
|
|
||||||
:columns="platformList"
|
|
||||||
placeholder="请选择推广平台"
|
|
||||||
@confirm="handlePlatform"
|
|
||||||
/>
|
|
||||||
<wd-picker label="优惠方式" label-width="100px" name="promotion" align-right v-model="promotion" :columns="promotionlist" />
|
|
||||||
<wd-cell title="券面额" required title-width="100px" custom-value-class="cell-left">
|
|
||||||
<view style="text-align: left">
|
|
||||||
<view class="inline-txt" style="margin-left: 0">满</view>
|
|
||||||
<wd-input
|
<wd-input
|
||||||
no-border
|
label="密码"
|
||||||
custom-style="display: inline-block; width: 70px; vertical-align: middle"
|
|
||||||
placeholder="请输入金额"
|
|
||||||
v-model="threshold"
|
|
||||||
name="threshold"
|
|
||||||
@change="handleThreshold"
|
|
||||||
/>
|
|
||||||
<view class="inline-txt">减</view>
|
|
||||||
<wd-input
|
|
||||||
no-border
|
|
||||||
custom-style="display: inline-block; width: 70px; vertical-align: middle"
|
|
||||||
placeholder="请输入金额"
|
|
||||||
v-model="price"
|
|
||||||
name="price"
|
|
||||||
@change="handlePrice"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</wd-cell>
|
|
||||||
</wd-cell-group>
|
|
||||||
<wd-cell-group custom-class="group" title="时间和地址" border>
|
|
||||||
<wd-datetime-picker label="时间" label-width="100px" name="date" v-model="date" @confirm="handleDate" />
|
|
||||||
<wd-col-picker
|
|
||||||
label="地址"
|
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
name="address"
|
prop="value2"
|
||||||
v-model="address"
|
show-password
|
||||||
:columns="area"
|
clearable
|
||||||
:column-change="areaChange"
|
v-model="model1.value2"
|
||||||
@confirm="handleAddress"
|
placeholder="请输入密码"
|
||||||
|
:rules="[{ required: true, message: '请填写密码' }]"
|
||||||
/>
|
/>
|
||||||
</wd-cell-group>
|
</wd-cell-group>
|
||||||
<wd-cell-group custom-class="group" title="其他信息" border>
|
|
||||||
<wd-input
|
|
||||||
label="活动细则"
|
|
||||||
label-width="100px"
|
|
||||||
type="textarea"
|
|
||||||
v-model="content"
|
|
||||||
:maxlength="300"
|
|
||||||
show-word-limit
|
|
||||||
placeholder="请输入活动细则信息"
|
|
||||||
clearable
|
|
||||||
name="content"
|
|
||||||
@change="handleContent"
|
|
||||||
/>
|
|
||||||
<wd-cell title="发货数量" center>
|
|
||||||
<wd-input-number v-model="count" name="count" @change="handleCount" />
|
|
||||||
</wd-cell>
|
|
||||||
<wd-cell title="这里显示的是多文字标题包含非常的文字" title-width="240px" center>
|
|
||||||
<wd-switch v-model="switchVal" name="switchVal" @change="handleSwitch" />
|
|
||||||
</wd-cell>
|
|
||||||
<wd-input
|
|
||||||
label="卡号"
|
|
||||||
label-width="100px"
|
|
||||||
name="cardId"
|
|
||||||
suffix-icon="camera"
|
|
||||||
placeholder="请输入卡号"
|
|
||||||
clearable
|
|
||||||
v-model="cardId"
|
|
||||||
@change="handleCardId"
|
|
||||||
/>
|
|
||||||
<wd-input label="手机号" label-width="100px" name="phone" placeholder="请输入手机号" clearable v-model="phone" @change="handlePhone" />
|
|
||||||
</wd-cell-group>
|
|
||||||
<view class="tip">
|
|
||||||
<wd-checkbox v-model="read" name="read" @change="handleRead" custom-label-class="label-class">
|
|
||||||
已阅读并同意
|
|
||||||
<text style="color: #4d80f0">《借款额度合同及相关授权》</text>
|
|
||||||
</wd-checkbox>
|
|
||||||
</view>
|
|
||||||
<view class="footer">
|
<view class="footer">
|
||||||
<button class="wd-button is-primary is-block is-round is-large" form-type="submit">提交</button>
|
<wd-button type="primary" size="large" @click="handleSubmit1" block>提交</wd-button>
|
||||||
</view>
|
</view>
|
||||||
</form>
|
</wd-form>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="校验规则" transparent>
|
||||||
|
<wd-form ref="form2" :model="model2">
|
||||||
|
<wd-cell-group border>
|
||||||
|
<wd-input
|
||||||
|
label="校验"
|
||||||
|
label-width="100px"
|
||||||
|
prop="value1"
|
||||||
|
clearable
|
||||||
|
v-model="model2.value1"
|
||||||
|
placeholder="正则校验"
|
||||||
|
:rules="[{ required: false, pattern: /\d{6}/, message: '请输入6位字符' }]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
label="校验"
|
||||||
|
label-width="100px"
|
||||||
|
prop="value2"
|
||||||
|
clearable
|
||||||
|
v-model="model2.value2"
|
||||||
|
placeholder="函数校验"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: false,
|
||||||
|
validator: validatorMessage,
|
||||||
|
message: '请输入正确的手机号'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
label="校验"
|
||||||
|
label-width="100px"
|
||||||
|
prop="value3"
|
||||||
|
clearable
|
||||||
|
v-model="model2.value3"
|
||||||
|
placeholder="校验函数返回错误提示"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: false,
|
||||||
|
message: '请输入内容',
|
||||||
|
validator: validator
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
label="校验"
|
||||||
|
label-width="100px"
|
||||||
|
prop="value4"
|
||||||
|
clearable
|
||||||
|
v-model="model2.value4"
|
||||||
|
placeholder="异步函数校验"
|
||||||
|
:rules="[{ required: false, validator: asyncValidator, message: '请输入1234' }]"
|
||||||
|
/>
|
||||||
|
</wd-cell-group>
|
||||||
|
<view class="footer">
|
||||||
|
<wd-button type="primary" size="large" @click="handleSubmit2" block>提交</wd-button>
|
||||||
|
</view>
|
||||||
|
</wd-form>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="动态表单" transparent>
|
||||||
|
<view class="demo-button">
|
||||||
|
<wd-button @click="handleClick1" :round="false" block size="large">动态表单</wd-button>
|
||||||
|
</view>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="失焦校验" transparent>
|
||||||
|
<view class="demo-button">
|
||||||
|
<wd-button @click="handleClick2" :round="false" block size="large">失焦校验</wd-button>
|
||||||
|
</view>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="复杂表单" transparent>
|
||||||
|
<view class="demo-button">
|
||||||
|
<wd-button @click="handleClick3" :round="false" block size="large">复杂表单</wd-button>
|
||||||
|
</view>
|
||||||
|
</demo-block>
|
||||||
</page-wraper>
|
</page-wraper>
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useMessage } from '@/uni_modules/wot-design-uni/components/wd-message-box'
|
|
||||||
import { useToast } from '@/uni_modules/wot-design-uni'
|
import { useToast } from '@/uni_modules/wot-design-uni'
|
||||||
import { areaData } from '@/utils/area'
|
import { reactive, ref } from 'vue'
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const couponName = ref<string>('')
|
const model1 = reactive<{
|
||||||
const couponNameErr = ref<boolean>(false)
|
value1: string
|
||||||
const platform = ref<any>([])
|
value2: string
|
||||||
const platformList = ref<any>([
|
}>({
|
||||||
{
|
value1: '',
|
||||||
value: '1',
|
value2: ''
|
||||||
label: '京东'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '2',
|
|
||||||
label: '开普勒'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '3',
|
|
||||||
label: '手Q'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '4',
|
|
||||||
label: '微信'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '5',
|
|
||||||
label: '1号店'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '6',
|
|
||||||
label: '十元街'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '7',
|
|
||||||
label: '京东极速版'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
const promotion = ref<string>('1')
|
|
||||||
const promotionlist = ref<any[]>([
|
|
||||||
{
|
|
||||||
value: '1',
|
|
||||||
label: '满减'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '2',
|
|
||||||
label: '无门槛'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
const threshold = ref<string>('')
|
|
||||||
const price = ref<string>('')
|
|
||||||
const date = ref<number>(Date.now())
|
|
||||||
const address = ref<any[]>([])
|
|
||||||
|
|
||||||
const count = ref<number>(1)
|
|
||||||
|
|
||||||
const area = ref<any[]>([
|
|
||||||
Object.keys(areaData[86]).map((key) => {
|
|
||||||
return {
|
|
||||||
value: key,
|
|
||||||
label: areaData[86][key]
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
])
|
|
||||||
const areaChange = ({ selectedItem, resolve, finish }) => {
|
const model2 = reactive<{
|
||||||
if (areaData[selectedItem.value]) {
|
value1: string
|
||||||
resolve(
|
value2: string
|
||||||
Object.keys(areaData[selectedItem.value]).map((key) => {
|
value3: string
|
||||||
return {
|
value4: string
|
||||||
value: key,
|
}>({
|
||||||
label: areaData[selectedItem.value][key]
|
value1: '',
|
||||||
}
|
value2: '',
|
||||||
|
value3: '',
|
||||||
|
value4: ''
|
||||||
})
|
})
|
||||||
)
|
|
||||||
|
const { success: showSuccess, loading: showLoading, close: closeToast } = useToast()
|
||||||
|
const form1 = ref()
|
||||||
|
const form2 = ref()
|
||||||
|
|
||||||
|
const validatorMessage = (val) => {
|
||||||
|
return /1\d{10}/.test(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validator = (val) => {
|
||||||
|
if (String(val).length >= 4) {
|
||||||
|
return Promise.resolve()
|
||||||
} else {
|
} else {
|
||||||
finish()
|
return Promise.reject('长度不得小于4')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const content = ref<string>('')
|
|
||||||
const coun = ref<number>(1)
|
|
||||||
const read = ref<boolean>(false)
|
|
||||||
const switchVal = ref<boolean>(true)
|
|
||||||
const cardId = ref<string>('')
|
|
||||||
const phone = ref<string>('')
|
|
||||||
|
|
||||||
const toast = useToast()
|
// 校验函数可以返回 Promise,实现异步校验
|
||||||
const messageBox = useMessage()
|
const asyncValidator = (val) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
showLoading('验证中...')
|
||||||
|
|
||||||
function handleCouponName({ value }) {
|
setTimeout(() => {
|
||||||
console.log(value)
|
closeToast()
|
||||||
|
resolve(val === '1234')
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
|
||||||
couponNameErr.value = false
|
function handleSubmit1() {
|
||||||
|
form1.value
|
||||||
|
.validate()
|
||||||
|
.then(({ valid, errors }) => {
|
||||||
|
if (valid) {
|
||||||
|
showSuccess({
|
||||||
|
msg: '提交成功'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
function handlePlatform({ value }) {
|
})
|
||||||
console.log(value)
|
.catch((error) => {
|
||||||
|
console.log(error, 'error')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
function handleThreshold({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handlePrice({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handleAddress({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handleContent({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handleCount({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handleSwitch({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handleRead({ value }) {
|
|
||||||
read.value = value
|
|
||||||
}
|
|
||||||
function handleCardId({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function handlePhone({ value }) {
|
|
||||||
console.log(value)
|
|
||||||
}
|
|
||||||
function formSubmit(event) {
|
|
||||||
console.log(event)
|
|
||||||
|
|
||||||
if (!couponName.value) {
|
function handleSubmit2() {
|
||||||
toast.error('请填写优惠券名称')
|
form2.value
|
||||||
return
|
.validate()
|
||||||
|
.then(({ valid, errors }) => {
|
||||||
|
if (valid) {
|
||||||
|
showSuccess({
|
||||||
|
msg: '提交成功'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
messageBox.alert('提交成功')
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error, 'error')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
function handleIconClick() {
|
|
||||||
toast.info('优惠券提示信息')
|
function handleClick1() {
|
||||||
|
uni.navigateTo({ url: '/pages/form/demo1' })
|
||||||
}
|
}
|
||||||
function handleDate({ value }) {
|
|
||||||
console.log(value)
|
function handleClick2() {
|
||||||
|
uni.navigateTo({ url: '/pages/form/demo2' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick3() {
|
||||||
|
uni.navigateTo({ url: '/pages/form/demo3' })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.wot-theme-dark {
|
.demo-button {
|
||||||
.inline-txt {
|
width: 100%;
|
||||||
color: $-dark-color3;
|
box-sizing: border-box;
|
||||||
}
|
padding: 0 24rpx;
|
||||||
}
|
|
||||||
.inline-txt {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 0 8px;
|
|
||||||
color: rgba(0, 0, 0, 0.45);
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
:deep(.group) {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
.tip {
|
|
||||||
margin: 10px 15px 21px;
|
|
||||||
color: #999;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
padding: 0 25px 21px;
|
padding: 16px;
|
||||||
}
|
|
||||||
:deep(.label-class) {
|
|
||||||
color: #999 !important;
|
|
||||||
font-size: 12px !important;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
95
src/pages/form/demo1.vue
Normal file
95
src/pages/form/demo1.vue
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<template>
|
||||||
|
<page-wraper>
|
||||||
|
<wd-form ref="form" :model="model">
|
||||||
|
<wd-cell-group border>
|
||||||
|
<wd-input
|
||||||
|
label="用户名"
|
||||||
|
label-width="100px"
|
||||||
|
prop="name"
|
||||||
|
clearable
|
||||||
|
v-model="model.name"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
:rules="[{ required: true, message: '请填写用户名' }]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
v-for="(item, index) in model.phoneNumbers"
|
||||||
|
:key="item.key"
|
||||||
|
:label="'联系方式' + index"
|
||||||
|
:prop="'phoneNumbers.' + index + '.value'"
|
||||||
|
label-width="100px"
|
||||||
|
clearable
|
||||||
|
v-model="item.value"
|
||||||
|
placeholder="联系方式"
|
||||||
|
:rules="[{ required: true, message: '请填写联系方式' + index }]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<wd-cell title-width="0px">
|
||||||
|
<view class="footer">
|
||||||
|
<wd-button size="small" type="info" plain @click="addPhone">添加</wd-button>
|
||||||
|
<wd-button size="small" type="info" plain @click="removePhone">删除</wd-button>
|
||||||
|
<wd-button size="small" type="info" plain @click="reset">重置</wd-button>
|
||||||
|
<wd-button type="primary" size="small" @click="submit">提交</wd-button>
|
||||||
|
</view>
|
||||||
|
</wd-cell>
|
||||||
|
</wd-cell-group>
|
||||||
|
</wd-form>
|
||||||
|
</page-wraper>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useToast } from '@/uni_modules/wot-design-uni'
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
interface PhoneItem {
|
||||||
|
key: number
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = reactive<{
|
||||||
|
name: string
|
||||||
|
phoneNumbers: PhoneItem[]
|
||||||
|
}>({
|
||||||
|
name: '',
|
||||||
|
phoneNumbers: [
|
||||||
|
{
|
||||||
|
key: Date.now(),
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const { success: showSuccess } = useToast()
|
||||||
|
const form = ref()
|
||||||
|
|
||||||
|
const removePhone = () => {
|
||||||
|
model.phoneNumbers.splice(model.phoneNumbers.length - 1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addPhone = () => {
|
||||||
|
model.phoneNumbers.push({
|
||||||
|
key: Date.now(),
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
form.value.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
form.value.validate().then(({ valid, errors }) => {
|
||||||
|
if (valid) {
|
||||||
|
showSuccess('校验通过')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.footer {
|
||||||
|
text-align: left;
|
||||||
|
:deep(.wd-button) {
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
69
src/pages/form/demo2.vue
Normal file
69
src/pages/form/demo2.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<page-wraper>
|
||||||
|
<wd-form ref="form" :model="model">
|
||||||
|
<wd-cell-group border>
|
||||||
|
<wd-input
|
||||||
|
label="用户名"
|
||||||
|
label-width="100px"
|
||||||
|
prop="name"
|
||||||
|
clearable
|
||||||
|
v-model="model.name"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
@blur="handleBlur('name')"
|
||||||
|
:rules="[{ required: true, message: '请填写用户名' }]"
|
||||||
|
/>
|
||||||
|
<wd-input
|
||||||
|
label="联系方式"
|
||||||
|
prop="phoneNumber"
|
||||||
|
label-width="100px"
|
||||||
|
clearable
|
||||||
|
@blur="handleBlur('phoneNumber')"
|
||||||
|
v-model="model.phoneNumber"
|
||||||
|
placeholder="联系方式"
|
||||||
|
:rules="[{ required: true, message: '请填写联系方式' }]"
|
||||||
|
/>
|
||||||
|
</wd-cell-group>
|
||||||
|
</wd-form>
|
||||||
|
|
||||||
|
<view class="footer">
|
||||||
|
<wd-button type="primary" size="large" block @click="handleSubmit">提交</wd-button>
|
||||||
|
</view>
|
||||||
|
</page-wraper>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useToast } from '@/uni_modules/wot-design-uni'
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
const model = reactive<{
|
||||||
|
name: string
|
||||||
|
phoneNumber: string
|
||||||
|
}>({
|
||||||
|
name: '',
|
||||||
|
phoneNumber: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const { success: showSuccess } = useToast()
|
||||||
|
const form = ref()
|
||||||
|
|
||||||
|
function handleBlur(prop: string) {
|
||||||
|
form.value.validate(prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
form.value
|
||||||
|
.validate()
|
||||||
|
.then(({ valid }) => {
|
||||||
|
if (valid) {
|
||||||
|
showSuccess('校验通过')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error, 'error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.footer {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
426
src/pages/form/demo3.vue
Normal file
426
src/pages/form/demo3.vue
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<page-wraper>
|
||||||
|
<wd-message-box />
|
||||||
|
<wd-toast />
|
||||||
|
<wd-form ref="form" :model="model" :rules="rules">
|
||||||
|
<wd-cell-group custom-class="group" title="基础信息" border>
|
||||||
|
<wd-input
|
||||||
|
label="优惠券名称"
|
||||||
|
label-width="100px"
|
||||||
|
:maxlength="20"
|
||||||
|
show-word-limit
|
||||||
|
prop="couponName"
|
||||||
|
required
|
||||||
|
suffix-icon="warn-bold"
|
||||||
|
clearable
|
||||||
|
v-model="model.couponName"
|
||||||
|
placeholder="请输入优惠券名称"
|
||||||
|
@clicksuffixicon="handleIconClick"
|
||||||
|
/>
|
||||||
|
<wd-select-picker
|
||||||
|
label="推广平台"
|
||||||
|
label-width="100px"
|
||||||
|
prop="platform"
|
||||||
|
v-model="model.platform"
|
||||||
|
:columns="platformList"
|
||||||
|
placeholder="请选择推广平台"
|
||||||
|
/>
|
||||||
|
<wd-picker
|
||||||
|
label="优惠方式"
|
||||||
|
placeholder="请选择优惠方式"
|
||||||
|
label-width="100px"
|
||||||
|
prop="promotion"
|
||||||
|
v-model="model.promotion"
|
||||||
|
:columns="promotionlist"
|
||||||
|
/>
|
||||||
|
<wd-cell prop="threshold" title="券面额" required title-width="100px" custom-value-class="cell-left">
|
||||||
|
<view style="text-align: left">
|
||||||
|
<view class="inline-txt" style="margin-left: 0">满</view>
|
||||||
|
<wd-input
|
||||||
|
no-border
|
||||||
|
custom-style="display: inline-block; width: 70px; vertical-align: middle"
|
||||||
|
placeholder="请输入金额"
|
||||||
|
v-model="model.threshold"
|
||||||
|
/>
|
||||||
|
<view class="inline-txt">减</view>
|
||||||
|
<wd-input
|
||||||
|
no-border
|
||||||
|
custom-style="display: inline-block; width: 70px; vertical-align: middle"
|
||||||
|
placeholder="请输入金额"
|
||||||
|
v-model="model.price"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</wd-cell>
|
||||||
|
</wd-cell-group>
|
||||||
|
<wd-cell-group custom-class="group" title="时间和地址" border>
|
||||||
|
<wd-datetime-picker label="时间" label-width="100px" placeholder="请选择时间" prop="time" v-model="model.time" />
|
||||||
|
<wd-calendar label="日期" label-width="100px" placeholder="请选择日期" prop="date" v-model="model.date" />
|
||||||
|
|
||||||
|
<wd-col-picker
|
||||||
|
label="地址"
|
||||||
|
placeholder="请选择地址"
|
||||||
|
label-width="100px"
|
||||||
|
prop="address"
|
||||||
|
v-model="model.address"
|
||||||
|
:columns="area"
|
||||||
|
:column-change="areaChange"
|
||||||
|
/>
|
||||||
|
</wd-cell-group>
|
||||||
|
<wd-cell-group custom-class="group" title="其他信息" border>
|
||||||
|
<wd-textarea
|
||||||
|
label="活动细则"
|
||||||
|
label-width="100px"
|
||||||
|
type="textarea"
|
||||||
|
v-model="model.content"
|
||||||
|
:maxlength="300"
|
||||||
|
show-word-limit
|
||||||
|
placeholder="请输入活动细则信息"
|
||||||
|
clearable
|
||||||
|
prop="content"
|
||||||
|
/>
|
||||||
|
<wd-cell title="发货数量" title-width="100px" prop="count">
|
||||||
|
<view style="text-align: left">
|
||||||
|
<wd-input-number v-model="model.count" />
|
||||||
|
</view>
|
||||||
|
</wd-cell>
|
||||||
|
<wd-cell title="开启折扣" title-width="100px" prop="switchVal" center>
|
||||||
|
<view style="text-align: left">
|
||||||
|
<wd-switch v-model="model.switchVal" />
|
||||||
|
</view>
|
||||||
|
</wd-cell>
|
||||||
|
<wd-input label="卡号" label-width="100px" prop="cardId" suffix-icon="camera" placeholder="请输入卡号" clearable v-model="model.cardId" />
|
||||||
|
<wd-input label="手机号" label-width="100px" prop="phone" placeholder="请输入手机号" clearable v-model="model.phone" />
|
||||||
|
<wd-cell title="活动图片" title-width="100px" prop="fileList">
|
||||||
|
<wd-upload :file-list="model.fileList" action="https://ftf.jd.com/api/uploadImg" @change="handleFileChange"></wd-upload>
|
||||||
|
</wd-cell>
|
||||||
|
</wd-cell-group>
|
||||||
|
<view class="tip">
|
||||||
|
<wd-checkbox v-model="model.read" prop="read" custom-label-class="label-class">
|
||||||
|
已阅读并同意
|
||||||
|
<text style="color: #4d80f0">《借款额度合同及相关授权》</text>
|
||||||
|
</wd-checkbox>
|
||||||
|
</view>
|
||||||
|
<view class="footer">
|
||||||
|
<wd-button type="primary" size="large" @click="handleSubmit" block>提交</wd-button>
|
||||||
|
</view>
|
||||||
|
</wd-form>
|
||||||
|
</page-wraper>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useToast } from '@/uni_modules/wot-design-uni'
|
||||||
|
import { isArray } from '@/uni_modules/wot-design-uni/components/common/util'
|
||||||
|
import { FormRules } from '@/uni_modules/wot-design-uni/components/wd-form/types'
|
||||||
|
import { areaData } from '@/utils/area'
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
const model = reactive<{
|
||||||
|
couponName: string
|
||||||
|
platform: any[]
|
||||||
|
promotion: string
|
||||||
|
threshold: string
|
||||||
|
price: string
|
||||||
|
time: number | string
|
||||||
|
date: null | number
|
||||||
|
address: string[]
|
||||||
|
count: number
|
||||||
|
content: string
|
||||||
|
switchVal: boolean
|
||||||
|
cardId: string
|
||||||
|
phone: string
|
||||||
|
read: boolean
|
||||||
|
fileList: Record<string, string>[]
|
||||||
|
}>({
|
||||||
|
couponName: '',
|
||||||
|
platform: [],
|
||||||
|
promotion: '',
|
||||||
|
threshold: '',
|
||||||
|
price: '',
|
||||||
|
date: null,
|
||||||
|
time: '',
|
||||||
|
address: [],
|
||||||
|
count: 1,
|
||||||
|
content: '',
|
||||||
|
switchVal: true,
|
||||||
|
cardId: '',
|
||||||
|
phone: '',
|
||||||
|
read: false,
|
||||||
|
fileList: []
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules: FormRules = {
|
||||||
|
couponName: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
pattern: /\d{6}/,
|
||||||
|
message: '优惠券名称6个字以上',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请输入优惠券名称')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入活动细则信息',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value && value.length > 2) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请输入活动细则信息')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
threshold: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入满减金额',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value && model.price) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
platform: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择推广平台',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value && isArray(value) && value.length) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请选择推广平台')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
promotion: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择推广平台',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请选择推广平台')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
time: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择时间',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请选择时间')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
date: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择日期',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
address: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择地址',
|
||||||
|
validator: (value) => {
|
||||||
|
if (isArray(value) && value.length) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请选择地址')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
count: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '发货数量需要大于1',
|
||||||
|
validator: (value) => {
|
||||||
|
if (Number(value) > 1) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('发货数量需要大于1')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
cardId: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入卡号',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject('请输入卡号')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
phone: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入手机号',
|
||||||
|
validator: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
fileList: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择活动图片',
|
||||||
|
validator: (value) => {
|
||||||
|
if (isArray(value) && value.length) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const platformList = ref<any>([
|
||||||
|
{
|
||||||
|
value: '1',
|
||||||
|
label: '京东'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '2',
|
||||||
|
label: '开普勒'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '3',
|
||||||
|
label: '手Q'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '4',
|
||||||
|
label: '微信'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '5',
|
||||||
|
label: '1号店'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '6',
|
||||||
|
label: '十元街'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '7',
|
||||||
|
label: '京东极速版'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const promotionlist = ref<any[]>([
|
||||||
|
{
|
||||||
|
value: '1',
|
||||||
|
label: '满减'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '2',
|
||||||
|
label: '无门槛'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const area = ref<any[]>([
|
||||||
|
Object.keys(areaData[86]).map((key) => {
|
||||||
|
return {
|
||||||
|
value: key,
|
||||||
|
label: areaData[86][key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
])
|
||||||
|
const areaChange = ({ selectedItem, resolve, finish }) => {
|
||||||
|
if (areaData[selectedItem.value]) {
|
||||||
|
resolve(
|
||||||
|
Object.keys(areaData[selectedItem.value]).map((key) => {
|
||||||
|
return {
|
||||||
|
value: key,
|
||||||
|
label: areaData[selectedItem.value][key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const toast = useToast()
|
||||||
|
const form = ref()
|
||||||
|
|
||||||
|
function handleFileChange({ fileList }) {
|
||||||
|
model.fileList = fileList
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
form.value
|
||||||
|
.validate()
|
||||||
|
.then(({ valid, errors }) => {
|
||||||
|
console.log(valid)
|
||||||
|
console.log(errors)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error, 'error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleIconClick() {
|
||||||
|
toast.info('优惠券提示信息')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.wot-theme-dark {
|
||||||
|
.inline-txt {
|
||||||
|
color: $-dark-color3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.inline-txt {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0 8px;
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
:deep(.group) {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
.tip {
|
||||||
|
margin: 10px 15px 21px;
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
padding: 0 25px 21px;
|
||||||
|
}
|
||||||
|
:deep(.label-class) {
|
||||||
|
color: #999 !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -151,6 +151,10 @@ const list = ref([
|
|||||||
id: 'input',
|
id: 'input',
|
||||||
name: 'Input 输入框'
|
name: 'Input 输入框'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'textarea',
|
||||||
|
name: 'Textarea 文本域'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'inputNumber',
|
id: 'inputNumber',
|
||||||
name: 'InputNumber 计数器'
|
name: 'InputNumber 计数器'
|
||||||
@ -189,7 +193,7 @@ const list = ref([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'form',
|
id: 'form',
|
||||||
name: 'Form 表单组件组合'
|
name: 'Form 表单'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'upload',
|
id: 'upload',
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
<wd-input type="text" v-model="value4" clearable @change="handleChange1" />
|
<wd-input type="text" v-model="value4" clearable @change="handleChange1" />
|
||||||
</demo-block>
|
</demo-block>
|
||||||
<demo-block title="密码框">
|
<demo-block title="密码框">
|
||||||
<wd-input type="text" v-model="value5" clearable show-password @change="handleChange2" />
|
<wd-input type="text" v-model="value5" disabled clearable show-password @change="handleChange2" />
|
||||||
</demo-block>
|
</demo-block>
|
||||||
<demo-block title="设置前后Icon">
|
<demo-block title="设置前后Icon">
|
||||||
<wd-input type="text" v-model="value6" prefix-icon="dong" suffix-icon="list" clearable @change="handleChange3" />
|
<wd-input type="text" v-model="value6" prefix-icon="dong" suffix-icon="list" clearable @change="handleChange3" />
|
||||||
@ -26,16 +26,7 @@
|
|||||||
</demo-block>
|
</demo-block>
|
||||||
<demo-block title="取消底部边框,自定义使用">
|
<demo-block title="取消底部边框,自定义使用">
|
||||||
<wd-input v-model="value8" no-border placeholder="请输入价格" custom-style="display: inline-block; width: 70px; vertical-align: middle;" />
|
<wd-input v-model="value8" no-border placeholder="请输入价格" custom-style="display: inline-block; width: 70px; vertical-align: middle;" />
|
||||||
<text class="custom-txt" style="display: inline-block; vertical-align: middle; font-size: 14px">元</text>
|
<text class="custom-txt">元</text>
|
||||||
</demo-block>
|
|
||||||
<demo-block title="textarea" transparent>
|
|
||||||
<wd-input type="textarea" v-model="value9" placeholder="请填写评价" @blur="handleBlur" />
|
|
||||||
</demo-block>
|
|
||||||
<demo-block title="textarea 清空按钮 和 字数限制" transparent>
|
|
||||||
<wd-input type="textarea" v-model="value10" :maxlength="120" clearable show-word-limit />
|
|
||||||
</demo-block>
|
|
||||||
<demo-block title="textarea 高度自适应">
|
|
||||||
<wd-input type="textarea" v-model="value11" auto-height clearable></wd-input>
|
|
||||||
</demo-block>
|
</demo-block>
|
||||||
<demo-block title="cell 类型" transparent>
|
<demo-block title="cell 类型" transparent>
|
||||||
<wd-cell-group border>
|
<wd-cell-group border>
|
||||||
@ -45,12 +36,12 @@
|
|||||||
<wd-input type="text" label="错误状态" v-model="value15" placeholder="请输入用户名" error />
|
<wd-input type="text" label="错误状态" v-model="value15" placeholder="请输入用户名" error />
|
||||||
<wd-input type="text" label="必填" v-model="value16" placeholder="请输入用户名" required />
|
<wd-input type="text" label="必填" v-model="value16" placeholder="请输入用户名" required />
|
||||||
<wd-input type="text" label="图标" v-model="value17" placeholder="请输入..." prefix-icon="dong" suffix-icon="list" />
|
<wd-input type="text" label="图标" v-model="value17" placeholder="请输入..." prefix-icon="dong" suffix-icon="list" />
|
||||||
<wd-input type="text" label="自定义插槽" v-model="value18" placeholder="请输入..." use-suffix-slot clearable>
|
<wd-input type="text" label="自定义插槽" center v-model="value18" placeholder="请输入..." use-suffix-slot clearable>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<wd-button size="small" custom-class="button">获取验证码</wd-button>
|
<wd-button size="small" custom-class="button">获取验证码</wd-button>
|
||||||
</template>
|
</template>
|
||||||
</wd-input>
|
</wd-input>
|
||||||
<wd-input type="text" label="大尺寸" size="large" v-model="value19" placeholder="请输入..." />
|
<wd-input type="text" label="大尺寸" clearable size="large" v-model="value19" placeholder="请输入..." />
|
||||||
</wd-cell-group>
|
</wd-cell-group>
|
||||||
</demo-block>
|
</demo-block>
|
||||||
</page-wraper>
|
</page-wraper>
|
||||||
@ -101,6 +92,12 @@ function handleBlur(event) {
|
|||||||
color: $-dark-color;
|
color: $-dark-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.custom-txt {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
@ -1,6 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<page-wraper>
|
<page-wraper>
|
||||||
<wd-toast />
|
<wd-toast />
|
||||||
|
<view class="main">
|
||||||
|
<wd-tabs v-model="tab1" @change="handleChange">
|
||||||
|
<block v-for="item in 4" :key="item">
|
||||||
|
<wd-tab :title="`标签${item}`">
|
||||||
|
<view class="content">
|
||||||
|
内容{{ tab1 + 1 }}
|
||||||
|
<wd-button @click="tab1 < 3 ? tab1++ : (tab1 = 0)">下一个</wd-button>
|
||||||
|
</view>
|
||||||
|
</wd-tab>
|
||||||
|
</block>
|
||||||
|
</wd-tabs>
|
||||||
|
</view>
|
||||||
<demo-block title="基本用法" transparent>
|
<demo-block title="基本用法" transparent>
|
||||||
<wd-tabs v-model="tab1" @change="handleChange">
|
<wd-tabs v-model="tab1" @change="handleChange">
|
||||||
<block v-for="item in 4" :key="item">
|
<block v-for="item in 4" :key="item">
|
||||||
@ -129,4 +141,8 @@ function handleChange(event) {
|
|||||||
line-height: 320px;
|
line-height: 320px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
.main {
|
||||||
|
:deep(.wd-tabs) {
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -52,13 +52,14 @@
|
|||||||
@include e(cell) {
|
@include e(cell) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0 $-cell-padding;
|
padding: $-cell-wrapper-padding $-cell-padding;
|
||||||
|
align-items: flex-start;
|
||||||
background-color: $-color-white;
|
background-color: $-color-white;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: $-cell-title-color;
|
color: $-cell-title-color;
|
||||||
font-size: $-cell-title-fs;
|
font-size: $-cell-title-fs;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
line-height: $-cell-ling-height;
|
line-height: $-cell-line-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(cell) {
|
@include e(cell) {
|
||||||
@ -101,11 +102,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include e(error-message){
|
||||||
|
color: $-form-item-error-message-color;
|
||||||
|
font-size: $-form-item-error-message-font-size;
|
||||||
|
line-height: $-form-item-error-message-line-height;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
@include e(label) {
|
@include e(label) {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: $-input-cell-label-width;
|
width: $-input-cell-label-width;
|
||||||
padding: $-cell-wrapper-padding 0;
|
|
||||||
margin-right: $-cell-padding;
|
margin-right: $-cell-padding;
|
||||||
|
color: $-cell-title-color;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
@include when(required) {
|
@include when(required) {
|
||||||
@ -114,7 +123,7 @@
|
|||||||
&::after {
|
&::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: calc($-cell-wrapper-padding + 2px);
|
top: 2px;
|
||||||
content: '*';
|
content: '*';
|
||||||
font-size: $-cell-required-size;
|
font-size: $-cell-required-size;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
@ -123,12 +132,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include e(value-wraper) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
@include e(value) {
|
@include e(value) {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: $-cell-wrapper-padding 0;
|
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
color: $-cell-value-color;
|
color: $-cell-value-color;
|
||||||
white-space: pre-wrap;
|
|
||||||
|
|
||||||
@include when(ellipsis) {
|
@include when(ellipsis) {
|
||||||
@include lineEllipsis;
|
@include lineEllipsis;
|
||||||
@ -139,12 +150,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include e(body) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
@include edeep(arrow) {
|
@include edeep(arrow) {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: $-cell-wrapper-padding;
|
|
||||||
font-size: $-cell-icon-size;
|
font-size: $-cell-icon-size;
|
||||||
color: $-cell-arrow-color;
|
color: $-cell-arrow-color;
|
||||||
line-height: 1.25;
|
line-height: $-cell-line-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(header) {
|
@include e(header) {
|
||||||
|
|||||||
@ -10,12 +10,14 @@
|
|||||||
>
|
>
|
||||||
<view
|
<view
|
||||||
v-if="label || useLabelSlot"
|
v-if="label || useLabelSlot"
|
||||||
:class="`wd-calendar__label ${required ? 'is-required' : ''} ${customLabelClass}`"
|
:class="`wd-calendar__label ${isRequired ? 'is-required' : ''} ${customLabelClass}`"
|
||||||
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
|
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
|
||||||
>
|
>
|
||||||
<block v-if="label">{{ label }}</block>
|
<block v-if="label">{{ label }}</block>
|
||||||
<slot v-else name="label"></slot>
|
<slot v-else name="label"></slot>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="wd-calendar__body">
|
||||||
|
<view class="wd-calendar__value-wraper">
|
||||||
<view
|
<view
|
||||||
:class="`wd-calendar__value ${ellipsis ? 'is-ellipsis' : ''} ${customValueClass} ${showValue ? '' : 'wd-calendar__value--placeholder'}`"
|
:class="`wd-calendar__value ${ellipsis ? 'is-ellipsis' : ''} ${customValueClass} ${showValue ? '' : 'wd-calendar__value--placeholder'}`"
|
||||||
>
|
>
|
||||||
@ -23,6 +25,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<wd-icon v-if="!disabled && !readonly" custom-class="wd-calendar__arrow" name="arrow-right" />
|
<wd-icon v-if="!disabled && !readonly" custom-class="wd-calendar__arrow" name="arrow-right" />
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="errorMessage" class="wd-calendar__error-message">{{ errorMessage }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<wd-action-sheet
|
<wd-action-sheet
|
||||||
v-model="pickerShow"
|
v-model="pickerShow"
|
||||||
@ -109,9 +114,11 @@ export default {
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { dayjs } from '../common/dayjs'
|
import { dayjs } from '../common/dayjs'
|
||||||
import { debounce, deepClone, isArray, isEqual, padZero } from '../common/util'
|
import { deepClone, isArray, isEqual, padZero } from '../common/util'
|
||||||
import { getWeekNumber, isRange } from '../wd-calendar-view/utils'
|
import { getWeekNumber, isRange } from '../wd-calendar-view/utils'
|
||||||
import { useCell } from '../composables/useCell'
|
import { useCell } from '../composables/useCell'
|
||||||
|
import { FORM_KEY, FormItemRule } from '../wd-form/types'
|
||||||
|
import { useParent } from '../composables/useParent'
|
||||||
|
|
||||||
const defaultDisplayFormat = (value, type) => {
|
const defaultDisplayFormat = (value, type) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -229,6 +236,8 @@ interface Props {
|
|||||||
safeAreaInsetBottom?: boolean
|
safeAreaInsetBottom?: boolean
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
beforeConfirm?: Function
|
beforeConfirm?: Function
|
||||||
|
prop?: string
|
||||||
|
rules?: FormItemRule[]
|
||||||
}
|
}
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
customClass: '',
|
customClass: '',
|
||||||
@ -255,7 +264,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
ellipsis: false,
|
ellipsis: false,
|
||||||
showTypeSwitch: false,
|
showTypeSwitch: false,
|
||||||
shortcuts: () => [],
|
shortcuts: () => [],
|
||||||
safeAreaInsetBottom: true
|
safeAreaInsetBottom: true,
|
||||||
|
rules: () => []
|
||||||
})
|
})
|
||||||
|
|
||||||
const pickerShow = ref<boolean>(false)
|
const pickerShow = ref<boolean>(false)
|
||||||
@ -329,6 +339,31 @@ watch(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { parent: form } = useParent(FORM_KEY)
|
||||||
|
|
||||||
|
// 表单校验错误信息
|
||||||
|
const errorMessage = computed(() => {
|
||||||
|
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
|
||||||
|
return form.errorMessages[props.prop]
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 是否展示必填
|
||||||
|
const isRequired = computed(() => {
|
||||||
|
let formRequired = false
|
||||||
|
if (form && form.rules) {
|
||||||
|
const rules = form.rules
|
||||||
|
for (const key in rules) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
|
||||||
|
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.required || props.rules.some((rule) => rule.required) || formRequired
|
||||||
|
})
|
||||||
|
|
||||||
const range = computed(() => {
|
const range = computed(() => {
|
||||||
return (type) => {
|
return (type) => {
|
||||||
return isRange(type)
|
return isRange(type)
|
||||||
|
|||||||
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Author: weisheng
|
|
||||||
* @Date: 2023-08-01 11:12:05
|
|
||||||
* @LastEditTime: 2023-08-15 16:12:36
|
|
||||||
* @LastEditors: weisheng
|
|
||||||
* @Description:
|
|
||||||
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-cell-group\wd-cell-group.vue
|
|
||||||
* 记得注释
|
|
||||||
-->
|
|
||||||
<template>
|
<template>
|
||||||
<view :class="['wd-cell-group', border ? 'is-border' : '', customClass]">
|
<view :class="['wd-cell-group', border ? 'is-border' : '', customClass]">
|
||||||
<view v-if="title || value || useSlot" class="wd-cell-group__title">
|
<view v-if="title || value || useSlot" class="wd-cell-group__title">
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
background-color: $-color-white;
|
background-color: $-color-white;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: $-cell-title-color;
|
color: $-cell-title-color;
|
||||||
line-height: $-cell-ling-height;
|
line-height: $-cell-line-height;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
|
||||||
@include when(border) {
|
@include when(border) {
|
||||||
@ -62,6 +62,10 @@
|
|||||||
.wd-cell__value {
|
.wd-cell__value {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wd-cell__left {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include when(label) {
|
@include when(label) {
|
||||||
@ -73,10 +77,9 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
margin-right: $-cell-padding;
|
|
||||||
font-size: $-cell-title-fs;
|
font-size: $-cell-title-fs;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
margin-right: $-cell-padding;
|
||||||
|
|
||||||
@include when(required) {
|
@include when(required) {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
@ -94,7 +97,6 @@
|
|||||||
|
|
||||||
@include e(right) {
|
@include e(right) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +104,6 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: $-cell-title-fs;
|
font-size: $-cell-title-fs;
|
||||||
margin-right: $-cell-padding;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(label) {
|
@include e(label) {
|
||||||
@ -115,11 +116,14 @@
|
|||||||
@include edeep(icon) {
|
@include edeep(icon) {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: $-cell-icon-size;
|
|
||||||
height: $-cell-icon-size;
|
|
||||||
line-height: 1.25;
|
|
||||||
margin-right: $-cell-icon-right;
|
margin-right: $-cell-icon-right;
|
||||||
font-size: $-cell-icon-size;
|
font-size: $-cell-icon-size;
|
||||||
|
height: $-cell-line-height;
|
||||||
|
line-height: $-cell-line-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include e(body){
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(value) {
|
@include e(value) {
|
||||||
@ -128,18 +132,25 @@
|
|||||||
font-size: $-cell-value-fs;
|
font-size: $-cell-value-fs;
|
||||||
color: $-cell-value-color;
|
color: $-cell-value-color;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
line-height: $-cell-value-line-height;
|
vertical-align: middle;
|
||||||
vertical-align: top;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include edeep(arrow-right) {
|
@include edeep(arrow-right) {
|
||||||
display: inline-block;
|
display: block;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
width: $-cell-arrow-size;
|
width: $-cell-arrow-size;
|
||||||
line-height: 1.22;
|
|
||||||
font-size: $-cell-arrow-size;
|
font-size: $-cell-arrow-size;
|
||||||
color: $-cell-arrow-color;
|
color: $-cell-arrow-color;
|
||||||
vertical-align: top;
|
height: $-cell-line-height;
|
||||||
|
line-height: $-cell-line-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include e(error-message){
|
||||||
|
color: $-form-item-error-message-color;
|
||||||
|
font-size: $-form-item-error-message-font-size;
|
||||||
|
line-height: $-form-item-error-message-line-height;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include when(link) {
|
@include when(link) {
|
||||||
@ -155,14 +166,17 @@
|
|||||||
font-size: $-cell-title-fs-large;
|
font-size: $-cell-title-fs-large;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wd-cell__wrapper {
|
||||||
|
padding-top: $-cell-wrapper-padding-large;
|
||||||
|
padding-bottom: $-cell-wrapper-padding-large;
|
||||||
|
}
|
||||||
|
|
||||||
.wd-cell__label {
|
.wd-cell__label {
|
||||||
font-size: $-cell-label-fs-large;
|
font-size: $-cell-label-fs-large;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-cell__icon {
|
:deep(.wd-cell__icon) {
|
||||||
font-size: $-cell-icon-size-large;
|
font-size: $-cell-icon-size-large;
|
||||||
width: $-cell-icon-size-large;
|
|
||||||
height: $-cell-icon-size-large;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<view
|
<view
|
||||||
:class="['wd-cell', cell.border.value ? 'is-border' : '', size ? 'is-' + size : '', center ? 'is-center' : '', customClass]"
|
:class="['wd-cell', isBorder ? 'is-border' : '', size ? 'is-' + size : '', center ? 'is-center' : '', customClass]"
|
||||||
:hover-class="isLink || clickable ? 'is-hover' : 'none'"
|
:hover-class="isLink || clickable ? 'is-hover' : 'none'"
|
||||||
hover-stay-time="70"
|
hover-stay-time="70"
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
>
|
>
|
||||||
<view :class="['wd-cell__wrapper', vertical ? 'is-vertical' : '']">
|
<view :class="['wd-cell__wrapper', vertical ? 'is-vertical' : '']">
|
||||||
<view
|
<view
|
||||||
:class="['wd-cell__left', required ? 'is-required' : '']"
|
:class="['wd-cell__left', isRequired ? 'is-required' : '']"
|
||||||
:style="titleWidth ? 'min-width:' + titleWidth + ';max-width:' + titleWidth + ';' : ''"
|
:style="titleWidth ? 'min-width:' + titleWidth + ';max-width:' + titleWidth + ';' : ''"
|
||||||
>
|
>
|
||||||
<!--左侧icon部位-->
|
<!--左侧icon部位-->
|
||||||
@ -16,22 +16,19 @@
|
|||||||
|
|
||||||
<view class="wd-cell__title">
|
<view class="wd-cell__title">
|
||||||
<!--title BEGIN-->
|
<!--title BEGIN-->
|
||||||
<view>
|
|
||||||
<view v-if="title" :class="customTitleClass">{{ title }}</view>
|
<view v-if="title" :class="customTitleClass">{{ title }}</view>
|
||||||
<slot v-else name="title"></slot>
|
<slot v-else name="title"></slot>
|
||||||
</view>
|
|
||||||
<!--title END-->
|
<!--title END-->
|
||||||
|
|
||||||
<!--label BEGIN-->
|
<!--label BEGIN-->
|
||||||
<view>
|
|
||||||
<view v-if="label" :class="`wd-cell__label ${customLabelClass}`">{{ label }}</view>
|
<view v-if="label" :class="`wd-cell__label ${customLabelClass}`">{{ label }}</view>
|
||||||
<slot v-else name="label" />
|
<slot v-else name="label" />
|
||||||
</view>
|
|
||||||
<!--label END-->
|
<!--label END-->
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!--right content BEGIN-->
|
<!--right content BEGIN-->
|
||||||
<view class="wd-cell__right">
|
<view class="wd-cell__right">
|
||||||
|
<view class="wd-cell__body">
|
||||||
<!--文案内容-->
|
<!--文案内容-->
|
||||||
<view :class="`wd-cell__value ${customValueClass}`">
|
<view :class="`wd-cell__value ${customValueClass}`">
|
||||||
<template v-if="value">{{ value }}</template>
|
<template v-if="value">{{ value }}</template>
|
||||||
@ -39,6 +36,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<!--箭头-->
|
<!--箭头-->
|
||||||
<wd-icon v-if="isLink" custom-class="wd-cell__arrow-right" name="arrow-right" />
|
<wd-icon v-if="isLink" custom-class="wd-cell__arrow-right" name="arrow-right" />
|
||||||
|
<slot v-else name="right-icon" />
|
||||||
|
</view>
|
||||||
|
<view v-if="errorMessage" class="wd-cell__error-message">{{ errorMessage }}</view>
|
||||||
</view>
|
</view>
|
||||||
<!--right content END-->
|
<!--right content END-->
|
||||||
</view>
|
</view>
|
||||||
@ -57,7 +57,10 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
import { useCell } from '../composables/useCell'
|
import { useCell } from '../composables/useCell'
|
||||||
|
import { useParent } from '../composables/useParent'
|
||||||
|
import { FORM_KEY, FormItemRule } from '../wd-form/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string
|
title?: string
|
||||||
@ -69,10 +72,13 @@ interface Props {
|
|||||||
replace?: boolean
|
replace?: boolean
|
||||||
clickable?: boolean
|
clickable?: boolean
|
||||||
size?: string
|
size?: string
|
||||||
|
border?: boolean
|
||||||
titleWidth?: string
|
titleWidth?: string
|
||||||
center?: boolean
|
center?: boolean
|
||||||
required?: boolean
|
required?: boolean
|
||||||
vertical?: boolean
|
vertical?: boolean
|
||||||
|
prop?: string
|
||||||
|
rules?: FormItemRule[]
|
||||||
customClass?: string
|
customClass?: string
|
||||||
customIconClass?: string
|
customIconClass?: string
|
||||||
customLabelClass?: string
|
customLabelClass?: string
|
||||||
@ -91,11 +97,40 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
replace: false,
|
replace: false,
|
||||||
center: false,
|
center: false,
|
||||||
required: false,
|
required: false,
|
||||||
vertical: false
|
vertical: false,
|
||||||
|
rules: () => []
|
||||||
})
|
})
|
||||||
|
|
||||||
const cell = useCell()
|
const cell = useCell()
|
||||||
|
|
||||||
|
const isBorder = computed(() => {
|
||||||
|
return cell.border.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const { parent: form } = useParent(FORM_KEY)
|
||||||
|
|
||||||
|
const errorMessage = computed(() => {
|
||||||
|
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
|
||||||
|
return form.errorMessages[props.prop]
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 是否展示必填
|
||||||
|
const isRequired = computed(() => {
|
||||||
|
let formRequired = false
|
||||||
|
if (form && form.rules) {
|
||||||
|
const rules = form.rules
|
||||||
|
for (const key in rules) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
|
||||||
|
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.required || props.rules.some((rule) => rule.required) || formRequired
|
||||||
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['click'])
|
const emit = defineEmits(['click'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -52,14 +52,14 @@
|
|||||||
@include e(cell) {
|
@include e(cell) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0 $-cell-padding;
|
padding: $-cell-wrapper-padding $-cell-padding;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
background-color: $-color-white;
|
background-color: $-color-white;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: $-cell-title-color;
|
color: $-cell-title-color;
|
||||||
font-size: $-cell-title-fs;
|
font-size: $-cell-title-fs;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
line-height: $-cell-ling-height;
|
line-height: $-cell-line-height;
|
||||||
}
|
}
|
||||||
@include e(cell) {
|
@include e(cell) {
|
||||||
@include when(disabled) {
|
@include when(disabled) {
|
||||||
@ -88,11 +88,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@include e(error-message){
|
||||||
|
color: $-form-item-error-message-color;
|
||||||
|
font-size: $-form-item-error-message-font-size;
|
||||||
|
line-height: $-form-item-error-message-line-height;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
@include e(label) {
|
@include e(label) {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: $-input-cell-label-width;
|
width: $-input-cell-label-width;
|
||||||
padding: $-cell-wrapper-padding 0;
|
|
||||||
margin-right: $-cell-padding;
|
margin-right: $-cell-padding;
|
||||||
|
color: $-cell-title-color;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
@include when(required) {
|
@include when(required) {
|
||||||
@ -101,7 +108,7 @@
|
|||||||
&::after {
|
&::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: calc($-cell-wrapper-padding + 2px);
|
top: 2px;
|
||||||
content: '*';
|
content: '*';
|
||||||
font-size: $-cell-required-size;
|
font-size: $-cell-required-size;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
@ -109,9 +116,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@include e(value-wraper) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
@include e(value) {
|
@include e(value) {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: $-cell-wrapper-padding 0;
|
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
color: $-cell-value-color;
|
color: $-cell-value-color;
|
||||||
|
|
||||||
@ -122,12 +131,14 @@
|
|||||||
color: $-input-placeholder-color;
|
color: $-input-placeholder-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@include e(body) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
@include edeep(arrow) {
|
@include edeep(arrow) {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: $-cell-wrapper-padding;
|
|
||||||
font-size: $-cell-icon-size;
|
font-size: $-cell-icon-size;
|
||||||
color: $-cell-arrow-color;
|
color: $-cell-arrow-color;
|
||||||
line-height: 1.25;
|
line-height: $-cell-line-height;
|
||||||
}
|
}
|
||||||
@include e(selected) {
|
@include e(selected) {
|
||||||
height: $-col-picker-selected-height;
|
height: $-col-picker-selected-height;
|
||||||
|
|||||||
@ -10,12 +10,14 @@
|
|||||||
>
|
>
|
||||||
<view
|
<view
|
||||||
v-if="label || useLabelSlot"
|
v-if="label || useLabelSlot"
|
||||||
:class="`wd-col-picker__label ${required && 'is-required'} ${customLabelClass}`"
|
:class="`wd-col-picker__label ${isRequired && 'is-required'} ${customLabelClass}`"
|
||||||
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
|
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
|
||||||
>
|
>
|
||||||
<block v-if="label">{{ label }}</block>
|
<block v-if="label">{{ label }}</block>
|
||||||
<slot v-else name="label"></slot>
|
<slot v-else name="label"></slot>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="wd-col-picker__body">
|
||||||
|
<view class="wd-col-picker__value-wraper">
|
||||||
<view
|
<view
|
||||||
:class="`wd-col-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${showValue ? '' : 'wd-col-picker__value--placeholder'}`"
|
:class="`wd-col-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${showValue ? '' : 'wd-col-picker__value--placeholder'}`"
|
||||||
>
|
>
|
||||||
@ -23,6 +25,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<wd-icon v-if="!disabled && !readonly" custom-class="wd-col-picker__arrow" name="arrow-right" />
|
<wd-icon v-if="!disabled && !readonly" custom-class="wd-col-picker__arrow" name="arrow-right" />
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="errorMessage" class="wd-col-picker__error-message">{{ errorMessage }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<wd-action-sheet
|
<wd-action-sheet
|
||||||
v-model="pickerShow"
|
v-model="pickerShow"
|
||||||
@ -89,9 +94,11 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getCurrentInstance, onMounted, ref, watch } from 'vue'
|
import { computed, getCurrentInstance, onMounted, ref, watch } from 'vue'
|
||||||
import { debounce, getRect, getType } from '../common/util'
|
import { debounce, getRect, getType } from '../common/util'
|
||||||
import { useCell } from '../composables/useCell'
|
import { useCell } from '../composables/useCell'
|
||||||
|
import { FORM_KEY, FormItemRule } from '../wd-form/types'
|
||||||
|
import { useParent } from '../composables/useParent'
|
||||||
|
|
||||||
const $container = '.wd-col-picker__selected-container'
|
const $container = '.wd-col-picker__selected-container'
|
||||||
const $item = '.wd-col-picker__selected-item'
|
const $item = '.wd-col-picker__selected-item'
|
||||||
@ -132,6 +139,8 @@ interface Props {
|
|||||||
zIndex?: number
|
zIndex?: number
|
||||||
safeAreaInsetBottom?: boolean
|
safeAreaInsetBottom?: boolean
|
||||||
ellipsis?: boolean
|
ellipsis?: boolean
|
||||||
|
prop?: string
|
||||||
|
rules?: FormItemRule[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@ -157,7 +166,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
zIndex: 15,
|
zIndex: 15,
|
||||||
safeAreaInsetBottom: true,
|
safeAreaInsetBottom: true,
|
||||||
ellipsis: false,
|
ellipsis: false,
|
||||||
labelWidth: '33%'
|
labelWidth: '33%',
|
||||||
|
rules: () => []
|
||||||
})
|
})
|
||||||
|
|
||||||
const pickerShow = ref<boolean>(false)
|
const pickerShow = ref<boolean>(false)
|
||||||
@ -268,6 +278,31 @@ watch(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { parent: form } = useParent(FORM_KEY)
|
||||||
|
|
||||||
|
// 表单校验错误信息
|
||||||
|
const errorMessage = computed(() => {
|
||||||
|
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
|
||||||
|
return form.errorMessages[props.prop]
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 是否展示必填
|
||||||
|
const isRequired = computed(() => {
|
||||||
|
let formRequired = false
|
||||||
|
if (form && form.rules) {
|
||||||
|
const rules = form.rules
|
||||||
|
for (const key in rules) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
|
||||||
|
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.required || props.rules.some((rule) => rule.required) || formRequired
|
||||||
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['close', 'update:modelValue', 'confirm'])
|
const emit = defineEmits(['close', 'update:modelValue', 'confirm'])
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
.wot-theme-dark {
|
.wot-theme-dark {
|
||||||
@include b(picker) {
|
@include b(picker) {
|
||||||
@include e(field) {
|
@include e(cell) {
|
||||||
background-color: $-dark-background2;
|
background-color: $-dark-background2;
|
||||||
color: $-dark-color;
|
color: $-dark-color;
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
|
|
||||||
@include when(border) {
|
@include when(border) {
|
||||||
.wd-picker__field {
|
.wd-picker__cell {
|
||||||
@include halfPixelBorder('top', $-cell-padding, $-dark-border-color);
|
@include halfPixelBorder('top', $-cell-padding, $-dark-border-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,17 +53,17 @@
|
|||||||
border-radius: 16px 16px 0px 0px;
|
border-radius: 16px 16px 0px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(body) {
|
@include e(wraper) {
|
||||||
padding-bottom: var(--window-bottom);
|
padding-bottom: var(--window-bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
@include when(border) {
|
@include when(border) {
|
||||||
.wd-picker__field {
|
.wd-picker__cell {
|
||||||
@include halfPixelBorder("top", $-cell-padding);
|
@include halfPixelBorder("top", $-cell-padding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@include when(large) {
|
@include when(large) {
|
||||||
.wd-picker__field {
|
.wd-picker__cell {
|
||||||
font-size: $-cell-title-fs-large;
|
font-size: $-cell-title-fs-large;
|
||||||
}
|
}
|
||||||
:deep(.wd-picker__arrow) {
|
:deep(.wd-picker__arrow) {
|
||||||
@ -85,17 +85,17 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@include e(field) {
|
@include e(cell) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0 $-cell-padding;
|
padding: $-cell-wrapper-padding $-cell-padding;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
background-color: $-color-white;
|
background-color: $-color-white;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: $-cell-title-color;
|
color: $-cell-title-color;
|
||||||
font-size: $-cell-title-fs;
|
font-size: $-cell-title-fs;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
line-height: $-cell-ling-height;
|
line-height: $-cell-line-height;
|
||||||
|
|
||||||
@include when(disabled) {
|
@include when(disabled) {
|
||||||
.wd-picker__value {
|
.wd-picker__value {
|
||||||
@ -110,10 +110,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include e(error-message){
|
||||||
|
color: $-form-item-error-message-color;
|
||||||
|
font-size: $-form-item-error-message-font-size;
|
||||||
|
line-height: $-form-item-error-message-line-height;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
@include e(label) {
|
@include e(label) {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: $-input-cell-label-width;
|
width: $-input-cell-label-width;
|
||||||
padding: $-cell-wrapper-padding 0;
|
|
||||||
margin-right: $-cell-padding;
|
margin-right: $-cell-padding;
|
||||||
color: $-cell-title-color;
|
color: $-cell-title-color;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -124,7 +131,7 @@
|
|||||||
&::after {
|
&::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: calc($-cell-wrapper-padding + 2px);
|
top: 2px;
|
||||||
content: "*";
|
content: "*";
|
||||||
font-size: $-cell-required-size;
|
font-size: $-cell-required-size;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
@ -133,10 +140,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include e(value-wraper) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
@include e(value) {
|
@include e(value) {
|
||||||
width: 0;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: $-cell-wrapper-padding 0;
|
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
color: $-cell-value-color;
|
color: $-cell-value-color;
|
||||||
|
|
||||||
@ -145,16 +154,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include e(body) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
@include e(placeholder) {
|
@include e(placeholder) {
|
||||||
color: $-input-placeholder-color;
|
color: $-input-placeholder-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include edeep(arrow) {
|
@include edeep(arrow) {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: $-cell-wrapper-padding;
|
|
||||||
font-size: $-cell-icon-size;
|
font-size: $-cell-icon-size;
|
||||||
color: $-cell-arrow-color;
|
color: $-cell-arrow-color;
|
||||||
line-height: 1.25;
|
line-height: $-cell-line-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(toolbar) {
|
@include e(toolbar) {
|
||||||
|
|||||||
@ -5,17 +5,19 @@
|
|||||||
} ${error ? 'is-error' : ''} ${customClass}`"
|
} ${error ? 'is-error' : ''} ${customClass}`"
|
||||||
>
|
>
|
||||||
<!--文案-->
|
<!--文案-->
|
||||||
<view @click="showPopup">
|
<view class="wd-picker__field" @click="showPopup">
|
||||||
<slot v-if="useDefaultSlot"></slot>
|
<slot v-if="useDefaultSlot"></slot>
|
||||||
<view v-else class="wd-picker__field">
|
<view v-else class="wd-picker__cell">
|
||||||
<view
|
<view
|
||||||
v-if="label || useLabelSlot"
|
v-if="label || useLabelSlot"
|
||||||
:class="`wd-picker__label ${customLabelClass} ${required ? 'is-required' : ''}`"
|
:class="`wd-picker__label ${customLabelClass} ${isRequired ? 'is-required' : ''}`"
|
||||||
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
|
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
|
||||||
>
|
>
|
||||||
<block v-if="label">{{ label }}</block>
|
<block v-if="label">{{ label }}</block>
|
||||||
<slot v-else name="label"></slot>
|
<slot v-else name="label"></slot>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="wd-picker__body">
|
||||||
|
<view class="wd-picker__value-wraper">
|
||||||
<view :class="`wd-picker__value ${customValueClass}`">
|
<view :class="`wd-picker__value ${customValueClass}`">
|
||||||
<view v-if="region">
|
<view v-if="region">
|
||||||
<text :class="showValue[0] ? '' : 'wd-picker__placeholder'">{{ showValue[0] ? showValue[0] : placeholder }}</text>
|
<text :class="showValue[0] ? '' : 'wd-picker__placeholder'">{{ showValue[0] ? showValue[0] : placeholder }}</text>
|
||||||
@ -28,6 +30,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<wd-icon v-if="!disabled && !readonly" custom-class="wd-picker__arrow" name="arrow-right" />
|
<wd-icon v-if="!disabled && !readonly" custom-class="wd-picker__arrow" name="arrow-right" />
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="errorMessage" class="wd-picker__error-message">{{ errorMessage }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!--弹出层,picker-view 在隐藏时修改值,会触发多次change事件,从而导致所有列选中第一项,因此picker在关闭时不隐藏 -->
|
<!--弹出层,picker-view 在隐藏时修改值,会触发多次change事件,从而导致所有列选中第一项,因此picker在关闭时不隐藏 -->
|
||||||
<wd-popup
|
<wd-popup
|
||||||
@ -40,7 +45,7 @@
|
|||||||
@close="onCancel"
|
@close="onCancel"
|
||||||
custom-class="wd-picker__popup"
|
custom-class="wd-picker__popup"
|
||||||
>
|
>
|
||||||
<view class="wd-picker__body">
|
<view class="wd-picker__wraper">
|
||||||
<!--toolBar-->
|
<!--toolBar-->
|
||||||
<view class="wd-picker__toolbar" @touchmove="noop">
|
<view class="wd-picker__toolbar" @touchmove="noop">
|
||||||
<!--取消按钮-->
|
<!--取消按钮-->
|
||||||
@ -135,10 +140,12 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getCurrentInstance, nextTick, onBeforeMount, onMounted, ref, watch } from 'vue'
|
import { computed, getCurrentInstance, nextTick, onBeforeMount, onMounted, ref, watch } from 'vue'
|
||||||
import { deepClone, getType, isArray, isDef, isEqual, padZero } from '../common/util'
|
import { deepClone, getType, isArray, isDef, isEqual, padZero } from '../common/util'
|
||||||
import { useCell } from '../composables/useCell'
|
import { useCell } from '../composables/useCell'
|
||||||
import { type DateTimeType, getPickerValue } from '../wd-datetime-picker-view/type'
|
import { type DateTimeType, getPickerValue } from '../wd-datetime-picker-view/type'
|
||||||
|
import { FORM_KEY, FormItemRule } from '../wd-form/types'
|
||||||
|
import { useParent } from '../composables/useParent'
|
||||||
interface Props {
|
interface Props {
|
||||||
customClass?: string
|
customClass?: string
|
||||||
customViewClass?: string
|
customViewClass?: string
|
||||||
@ -211,6 +218,8 @@ interface Props {
|
|||||||
displayFormatTabLabel?: Function
|
displayFormatTabLabel?: Function
|
||||||
defaultValue?: string | number | Date | Array<string | number | Date>
|
defaultValue?: string | number | Date | Array<string | number | Date>
|
||||||
zIndex?: number
|
zIndex?: number
|
||||||
|
prop?: string
|
||||||
|
rules?: FormItemRule[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@ -262,7 +271,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
minMinute: 0,
|
minMinute: 0,
|
||||||
// 最大分钟
|
// 最大分钟
|
||||||
maxMinute: 59,
|
maxMinute: 59,
|
||||||
zIndex: 15
|
zIndex: 15,
|
||||||
|
rules: () => []
|
||||||
})
|
})
|
||||||
const datetimePickerView = ref()
|
const datetimePickerView = ref()
|
||||||
const datetimePickerView1 = ref()
|
const datetimePickerView1 = ref()
|
||||||
@ -383,6 +393,31 @@ watch(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { parent: form } = useParent(FORM_KEY)
|
||||||
|
|
||||||
|
// 表单校验错误信息
|
||||||
|
const errorMessage = computed(() => {
|
||||||
|
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
|
||||||
|
return form.errorMessages[props.prop]
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 是否展示必填
|
||||||
|
const isRequired = computed(() => {
|
||||||
|
let formRequired = false
|
||||||
|
if (form && form.rules) {
|
||||||
|
const rules = form.rules
|
||||||
|
for (const key in rules) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
|
||||||
|
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.required || props.rules.some((rule) => rule.required) || formRequired
|
||||||
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['change', 'open', 'toggle', 'cancel', 'confirm', 'update:modelValue'])
|
const emit = defineEmits(['change', 'open', 'toggle', 'cancel', 'confirm', 'update:modelValue'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
@import '../common/abstracts/variable';
|
||||||
|
@import '../common/abstracts/mixin';
|
||||||
|
|
||||||
|
.wot-theme-dark {
|
||||||
|
@include b(form-item) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@include bdeep(form-item) {
|
||||||
|
@include e(error-message){
|
||||||
|
color: $-form-item-error-message-color;
|
||||||
|
font-size: $-form-item-error-message-font-size;
|
||||||
|
line-height: $-form-item-error-message-line-height;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: weisheng
|
||||||
|
* @Date: 2023-12-14 11:21:58
|
||||||
|
* @LastEditTime: 2023-12-17 15:16:03
|
||||||
|
* @LastEditors: weisheng
|
||||||
|
* @Description:
|
||||||
|
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-form-item\wd-form-item.vue
|
||||||
|
* 记得注释
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<wd-cell
|
||||||
|
custom-class="wd-form-item"
|
||||||
|
:required="required"
|
||||||
|
:title="label"
|
||||||
|
:center="center"
|
||||||
|
:border="border"
|
||||||
|
:title-width="labelWidth"
|
||||||
|
:is-link="isLink"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
<view v-if="errorMessage" class="wd-form-item__error-message">{{ errorMessage }}</view>
|
||||||
|
</wd-cell>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'wd-form-item',
|
||||||
|
options: {
|
||||||
|
addGlobalClass: true,
|
||||||
|
virtualHost: true,
|
||||||
|
styleIsolation: 'shared'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
import { useParent } from '../composables/useParent'
|
||||||
|
import WdCell from '../wd-cell/wd-cell.vue'
|
||||||
|
import { FORM_KEY, FormItemRule } from '../wd-form/types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
prop: string
|
||||||
|
rules?: FormItemRule[]
|
||||||
|
required?: boolean
|
||||||
|
center?: boolean
|
||||||
|
label?: string
|
||||||
|
labelWidth?: string
|
||||||
|
isLink?: boolean
|
||||||
|
customClass?: string
|
||||||
|
customStyle?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
rules: () => [],
|
||||||
|
center: false,
|
||||||
|
labelWidth: '100px',
|
||||||
|
customClass: '',
|
||||||
|
customStyle: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const { parent: form, index } = useParent(FORM_KEY)
|
||||||
|
|
||||||
|
const errorMessage = computed(() => {
|
||||||
|
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
|
||||||
|
return form.errorMessages[props.prop]
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const border = computed(() => {
|
||||||
|
if (index.value > 0 && form && form.border) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './index.scss';
|
||||||
|
</style>
|
||||||
10
src/uni_modules/wot-design-uni/components/wd-form/index.scss
Normal file
10
src/uni_modules/wot-design-uni/components/wd-form/index.scss
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
@import "../common/abstracts/variable";
|
||||||
|
@import "../common/abstracts/mixin";
|
||||||
|
|
||||||
|
.wot-theme-dark {
|
||||||
|
@include b(form) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include b(form) {
|
||||||
|
}
|
||||||
38
src/uni_modules/wot-design-uni/components/wd-form/types.ts
Normal file
38
src/uni_modules/wot-design-uni/components/wd-form/types.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* @Author: weisheng
|
||||||
|
* @Date: 2023-12-14 11:21:58
|
||||||
|
* @LastEditTime: 2023-12-23 16:20:20
|
||||||
|
* @LastEditors: weisheng
|
||||||
|
* @Description:
|
||||||
|
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-form\types.ts
|
||||||
|
* 记得注释
|
||||||
|
*/
|
||||||
|
import { type InjectionKey } from 'vue'
|
||||||
|
|
||||||
|
export type FormProvide = {
|
||||||
|
model: Record<string, any>
|
||||||
|
rules?: FormRules
|
||||||
|
border?: boolean
|
||||||
|
errorMessages?: Record<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FORM_KEY: InjectionKey<FormProvide> = Symbol('wd-form')
|
||||||
|
|
||||||
|
export type FormRules = {
|
||||||
|
[key: string]: FormItemRule[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ErrorMessage = {
|
||||||
|
prop: string
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FormItemRule {
|
||||||
|
[key: string]: any
|
||||||
|
required: boolean
|
||||||
|
message: string
|
||||||
|
pattern?: RegExp
|
||||||
|
validator?: (value: any, rule: FormItemRuleWithoutValidator) => boolean | Promise<string> | Promise<boolean> | Promise<void> | Promise<unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FormItemRuleWithoutValidator = Omit<FormItemRule, 'validator'>
|
||||||
174
src/uni_modules/wot-design-uni/components/wd-form/wd-form.vue
Normal file
174
src/uni_modules/wot-design-uni/components/wd-form/wd-form.vue
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<template>
|
||||||
|
<view :class="`wd-form ${customClass}`" :style="customStyle">
|
||||||
|
<slot></slot>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'wd-form',
|
||||||
|
options: {
|
||||||
|
addGlobalClass: true,
|
||||||
|
virtualHost: true,
|
||||||
|
styleIsolation: 'shared'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, watch } from 'vue'
|
||||||
|
import { deepClone, getPropByPath, isDef, isPromise } from '../common/util'
|
||||||
|
import { useChildren } from '../composables/useChildren'
|
||||||
|
import { FormRules, FORM_KEY, ErrorMessage, FormItemRule } from './types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
// 表单数据对象
|
||||||
|
model: Record<string, any>
|
||||||
|
// 表单验证规则
|
||||||
|
rules?: FormRules
|
||||||
|
customClass?: string
|
||||||
|
customStyle?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
rules: () => ({}),
|
||||||
|
customClass: '',
|
||||||
|
customStyle: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const { children, linkChildren } = useChildren(FORM_KEY)
|
||||||
|
let errorMessages = reactive<Record<string, string>>({})
|
||||||
|
|
||||||
|
linkChildren({ ...props, errorMessages: errorMessages })
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.model,
|
||||||
|
() => {
|
||||||
|
clearMessage()
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单校验
|
||||||
|
* @param prop 指定校验字段
|
||||||
|
*/
|
||||||
|
async function validate(prop?: string): Promise<{ valid: boolean; errors: ErrorMessage[] }> {
|
||||||
|
const errors: ErrorMessage[] = []
|
||||||
|
let valid: boolean = true
|
||||||
|
const promises: Promise<void>[] = []
|
||||||
|
const formRules: FormRules = getMergeRules()
|
||||||
|
const rulesToValidate: FormRules = prop ? { [prop]: formRules[prop] } : formRules
|
||||||
|
for (const prop in rulesToValidate) {
|
||||||
|
const rules = rulesToValidate[prop]
|
||||||
|
const value = getPropByPath(props.model, prop)
|
||||||
|
if (rules && rules.length > 0) {
|
||||||
|
for (const rule of rules) {
|
||||||
|
if (rule.required && (!isDef(value) || value === '')) {
|
||||||
|
errors.push({
|
||||||
|
prop,
|
||||||
|
message: rule.message
|
||||||
|
})
|
||||||
|
valid = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (rule.pattern && !rule.pattern.test(props.model[prop])) {
|
||||||
|
errors.push({
|
||||||
|
prop,
|
||||||
|
message: rule.message
|
||||||
|
})
|
||||||
|
valid = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
const { validator, ...ruleWithoutValidator } = rule
|
||||||
|
if (validator) {
|
||||||
|
const result = validator(props.model[prop], ruleWithoutValidator)
|
||||||
|
if (isPromise(result)) {
|
||||||
|
promises.push(
|
||||||
|
result
|
||||||
|
.then((res: any) => {
|
||||||
|
if (typeof res === 'string') {
|
||||||
|
errors.push({
|
||||||
|
prop,
|
||||||
|
message: res
|
||||||
|
})
|
||||||
|
valid = false
|
||||||
|
} else if (typeof res === 'boolean' && !res) {
|
||||||
|
errors.push({
|
||||||
|
prop,
|
||||||
|
message: rule.message
|
||||||
|
})
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
errors.push({
|
||||||
|
prop,
|
||||||
|
message: error || rule.message
|
||||||
|
})
|
||||||
|
valid = false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if (!result) {
|
||||||
|
errors.push({
|
||||||
|
prop,
|
||||||
|
message: rule.message
|
||||||
|
})
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
|
||||||
|
errors.forEach((error) => {
|
||||||
|
showMessage(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid,
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并子组件的rules到父组件的rules
|
||||||
|
function getMergeRules() {
|
||||||
|
const mergedRules: FormRules = deepClone(props.rules)
|
||||||
|
children.forEach((item) => {
|
||||||
|
if (isDef(item.prop) && isDef(item.rules) && item.rules.length) {
|
||||||
|
if (mergedRules[item.prop]) {
|
||||||
|
mergedRules[item.prop] = [...mergedRules[item.prop], ...item.rules]
|
||||||
|
} else {
|
||||||
|
mergedRules[item.prop] = item.rules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return mergedRules
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMessage(errorMsg: ErrorMessage) {
|
||||||
|
if (errorMsg.message) {
|
||||||
|
errorMessages[errorMsg.prop] = errorMsg.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearMessage() {
|
||||||
|
Object.keys(errorMessages).forEach((key) => {
|
||||||
|
errorMessages[key] = ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
clearMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ validate, reset })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './index.scss';
|
||||||
|
</style>
|
||||||
@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
@include b(input-number) {
|
@include b(input-number) {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 0;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
line-height: 1.15;
|
line-height: 1.15;
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
.wot-theme-dark {
|
.wot-theme-dark {
|
||||||
@include b(input) {
|
@include b(input) {
|
||||||
|
background: $-dark-background2;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
background: $-dark-color-gray;
|
background: $-dark-color-gray;
|
||||||
@ -16,9 +17,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(textarea) {
|
|
||||||
background: $-dark-background2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include e(inner) {
|
@include e(inner) {
|
||||||
color: $-dark-color;
|
color: $-dark-color;
|
||||||
@ -28,14 +26,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(textarea-inner) {
|
|
||||||
color: $-dark-color;
|
|
||||||
|
|
||||||
&::-webkit-input-placeholder {
|
|
||||||
color: $-dark-color3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include e(placeholder) {
|
@include e(placeholder) {
|
||||||
color: $-dark-color3;
|
color: $-dark-color3;
|
||||||
}
|
}
|
||||||
@ -49,19 +39,8 @@
|
|||||||
color: $-dark-color;
|
color: $-dark-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(textarea-count) {
|
|
||||||
color: $-dark-color3;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include e(textarea-count-current) {
|
|
||||||
color: $-dark-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
:deep(.wd-input__icon),
|
:deep(.wd-input__icon),
|
||||||
:deep(.wd-input__clear),
|
:deep(.wd-input__clear) {
|
||||||
:deep(.wd-input__textarea-icon) {
|
|
||||||
color: $-dark-color;
|
color: $-dark-color;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
@ -76,8 +55,7 @@
|
|||||||
|
|
||||||
@include when(disabled) {
|
@include when(disabled) {
|
||||||
|
|
||||||
.wd-input__inner,
|
.wd-input__inner {
|
||||||
.wd-input__textarea-inner {
|
|
||||||
color: $-dark-color-gray;
|
color: $-dark-color-gray;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
@ -94,6 +72,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
background: $-input-bg;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -115,12 +94,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include when(textarea) {
|
|
||||||
&::after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include e(label) {
|
@include e(label) {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: $-input-cell-label-width;
|
width: $-input-cell-label-width;
|
||||||
@ -129,7 +102,6 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-size: $-input-fs;
|
font-size: $-input-fs;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
line-height: $-cell-ling-height;
|
|
||||||
|
|
||||||
@include when(required) {
|
@include when(required) {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
@ -137,7 +109,7 @@
|
|||||||
&::after {
|
&::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: calc($-cell-wrapper-padding + 2px);
|
top: 2px;
|
||||||
content: "*";
|
content: "*";
|
||||||
font-size: $-cell-required-size;
|
font-size: $-cell-required-size;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
@ -148,17 +120,22 @@
|
|||||||
|
|
||||||
@include e(label-inner) {
|
@include e(label-inner) {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: $-cell-wrapper-padding 0;
|
|
||||||
font-size: $-input-fs;
|
font-size: $-input-fs;
|
||||||
|
line-height: $-cell-line-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(block) {
|
@include e(body) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include e(value) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@include e(prefix) {
|
@include e(prefix) {
|
||||||
margin-right: $-input-icon-margin;
|
margin-right: $-input-icon-margin;
|
||||||
font-size: $-input-fs;
|
font-size: $-input-fs;
|
||||||
@ -172,12 +149,20 @@
|
|||||||
@include e(suffix) {
|
@include e(suffix) {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-left: $-input-icon-margin;
|
margin-left: $-input-icon-margin;
|
||||||
|
line-height: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include e(error-message){
|
||||||
|
color: $-form-item-error-message-color;
|
||||||
|
font-size: $-form-item-error-message-font-size;
|
||||||
|
line-height: $-form-item-error-message-line-height;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include when(disabled) {
|
@include when(disabled) {
|
||||||
|
|
||||||
.wd-input__inner,
|
.wd-input__inner {
|
||||||
.wd-input__textarea-inner {
|
|
||||||
color: $-input-disabled-color;
|
color: $-input-disabled-color;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
@ -185,8 +170,7 @@
|
|||||||
|
|
||||||
@include when(error) {
|
@include when(error) {
|
||||||
|
|
||||||
.wd-input__inner,
|
.wd-input__inner {
|
||||||
.wd-input__textarea-inner {
|
|
||||||
color: $-input-error-color;
|
color: $-input-error-color;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
@ -207,18 +191,13 @@
|
|||||||
@include when(cell) {
|
@include when(cell) {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
padding: 0 $-input-cell-padding;
|
padding: $-input-cell-padding $-input-padding;
|
||||||
background-color: $-input-cell-bg;
|
background-color: $-input-cell-bg;
|
||||||
|
|
||||||
&.is-error::after {
|
&.is-error::after {
|
||||||
background: $-input-cell-border-color;
|
background: $-input-cell-border-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-input__textarea,
|
|
||||||
.wd-input__block {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.wd-input__icon),
|
:deep(.wd-input__icon),
|
||||||
:deep(.wd-input__clear) {
|
:deep(.wd-input__clear) {
|
||||||
height: $-input-cell-height;
|
height: $-input-cell-height;
|
||||||
@ -238,17 +217,6 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-input__textarea {
|
|
||||||
padding: 13px 0 36px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-input__textarea-inner {
|
|
||||||
padding-right: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-input__textarea-suffix {
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include when(center) {
|
@include when(center) {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -260,6 +228,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include when(large) {
|
@include when(large) {
|
||||||
|
padding: $-input-cell-padding-large;
|
||||||
|
|
||||||
.wd-input__prefix {
|
.wd-input__prefix {
|
||||||
font-size: $-input-fs-large;
|
font-size: $-input-fs-large;
|
||||||
}
|
}
|
||||||
@ -270,47 +240,20 @@
|
|||||||
|
|
||||||
.wd-input__inner {
|
.wd-input__inner {
|
||||||
font-size: $-input-fs-large;
|
font-size: $-input-fs-large;
|
||||||
height: $-input-cell-height-large;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-input__textarea-inner {
|
|
||||||
font-size: $-input-fs-large;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-input__count {
|
.wd-input__count {
|
||||||
font-size: $-input-count-fs-large;
|
font-size: $-input-count-fs-large;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-input__textarea-count {
|
|
||||||
font-size: $-input-count-fs-large;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.wd-input__icon),
|
:deep(.wd-input__icon),
|
||||||
:deep(.wd-input__clear) {
|
:deep(.wd-input__clear) {
|
||||||
height: $-input-cell-height-large;
|
|
||||||
font-size: $-input-icon-size-large;
|
|
||||||
line-height: $-input-cell-height-large;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.wd-input__textarea-icon) {
|
|
||||||
font-size: $-input-icon-size-large;
|
font-size: $-input-icon-size-large;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include when(auto-height) {
|
|
||||||
&::after {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-input__textarea {
|
|
||||||
padding: $-input-inner-padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-input__textarea-suffix {
|
|
||||||
top: 8px;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include e(inner) {
|
@include e(inner) {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@ -330,16 +273,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(readonly) {
|
@include e(readonly-mask) {
|
||||||
padding: $-input-inner-padding;
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(textarea-inner) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include edeep(icon) {
|
@include edeep(icon) {
|
||||||
margin-left: 8px;
|
margin-left: $-input-icon-margin;
|
||||||
font-size: $-input-icon-size;
|
font-size: $-input-icon-size;
|
||||||
color: $-input-icon-color;
|
color: $-input-icon-color;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@ -347,7 +292,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include edeep(clear) {
|
@include edeep(clear) {
|
||||||
margin-left: 8px;
|
margin-left: $-input-icon-margin;
|
||||||
font-size: $-input-icon-size;
|
font-size: $-input-icon-size;
|
||||||
color: $-input-clear-color;
|
color: $-input-clear-color;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@ -370,79 +315,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(textarea) {
|
|
||||||
position: relative;
|
|
||||||
padding: $-input-textarea-padding;
|
|
||||||
font-size: 0;
|
|
||||||
display: flex;
|
|
||||||
background: $-input-bg;
|
|
||||||
|
|
||||||
@include when(show-limit) {
|
|
||||||
padding-bottom: 36px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include edeep(textarea-icon) {
|
|
||||||
margin-left: 8px;
|
|
||||||
font-size: $-input-icon-size;
|
|
||||||
line-height: 20px;
|
|
||||||
color: $-input-icon-color;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include e(textarea-inner) {
|
|
||||||
flex: 1;
|
|
||||||
// width: 1px;
|
|
||||||
padding: 0;
|
|
||||||
font-size: $-input-fs;
|
|
||||||
line-height: 1.43;
|
|
||||||
color: $-input-color;
|
|
||||||
outline: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: none;
|
|
||||||
word-break: break-word;
|
|
||||||
|
|
||||||
&::-webkit-input-placeholder {
|
|
||||||
color: $-input-placeholder-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include when(suffix) {
|
|
||||||
padding-right: calc($-input-textarea-padding + $-input-icon-size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include e(textarea-suffix) {
|
|
||||||
position: absolute;
|
|
||||||
right: 14px;
|
|
||||||
top: $-input-textarea-padding;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include edeep(textarea-icon) {
|
|
||||||
margin-left: 8px;
|
|
||||||
font-size: $-input-icon-size;
|
|
||||||
line-height: 20px;
|
|
||||||
color: $-input-clear-color;
|
|
||||||
background: $-input-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include e(textarea-count) {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 8px;
|
|
||||||
right: 0;
|
|
||||||
font-size: $-input-count-fs;
|
|
||||||
color: $-input-count-color;
|
|
||||||
background: $-input-bg;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include e(textarea-count-current) {
|
|
||||||
color: $-input-count-current-color;
|
|
||||||
|
|
||||||
@include when(error) {
|
|
||||||
color: $-input-error-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include e(placeholder) {
|
@include e(placeholder) {
|
||||||
color: $-input-placeholder-color;
|
color: $-input-placeholder-color;
|
||||||
@ -452,18 +324,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.wd-input__textarea-count) {
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-input__count,
|
.wd-input__count,
|
||||||
.wd-input__count-current {
|
.wd-input__count-current {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(textarea-map) {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,74 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<view :class="rootClass" :style="customStyle">
|
<view :class="rootClass" :style="customStyle" @click="handleClick">
|
||||||
<view v-if="label || useLabelSlot" :class="labelClass" :style="labelStyle">
|
<view v-if="label || useLabelSlot" :class="labelClass" :style="labelStyle">
|
||||||
<view v-if="prefixIcon || usePrefixSlot" class="wd-input__prefix">
|
<view v-if="prefixIcon || usePrefixSlot" class="wd-input__prefix">
|
||||||
<wd-icon v-if="prefixIcon && !usePrefixSlot" custom-class="wd-input__icon" :name="prefixIcon" @click="onClickPrefixIcon" />
|
<wd-icon v-if="prefixIcon && !usePrefixSlot" custom-class="wd-input__icon" :name="prefixIcon" @click="onClickPrefixIcon" />
|
||||||
<slot v-else name="prefix"></slot>
|
<slot v-else name="prefix"></slot>
|
||||||
</view>
|
</view>
|
||||||
<view style="display: inline-flex">
|
|
||||||
<view class="wd-input__label-inner">
|
<view class="wd-input__label-inner">
|
||||||
<template v-if="label">{{ label }}</template>
|
<template v-if="label">{{ label }}</template>
|
||||||
<slot v-else name="label"></slot>
|
<slot v-else name="label"></slot>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
<!-- 文本域 -->
|
|
||||||
<view v-if="type === 'textarea'" :class="`wd-input__textarea ${customTextareaContainerClass} ${showWordCount ? 'is-show-limit' : ''}`">
|
|
||||||
<!-- readonly -->
|
|
||||||
<view v-if="readonly" class="wd-input__textarea-inner">{{ inputValue }}</view>
|
|
||||||
<template v-else>
|
|
||||||
<textarea
|
|
||||||
:class="`wd-input__textarea-inner ${showClear ? 'is-suffix' : ''} ${customTextareaClass}`"
|
|
||||||
v-model="inputValue"
|
|
||||||
:show-count="false"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:disabled="disabled"
|
|
||||||
:minlength="minlength"
|
|
||||||
:maxlength="maxlength"
|
|
||||||
:focus="isFocus"
|
|
||||||
:placeholder-style="placeholderStyle"
|
|
||||||
:placeholder-class="inputPlaceholderClass"
|
|
||||||
:auto-height="autoHeight"
|
|
||||||
:cursor-spacing="cursorSpacing"
|
|
||||||
:fixed="fixed"
|
|
||||||
:cursor="cursor"
|
|
||||||
:show-confirm-bar="showConfirmBar"
|
|
||||||
:selection-start="selectionStart"
|
|
||||||
:selection-end="selectionEnd"
|
|
||||||
:adjust-position="adjustPosition"
|
|
||||||
:hold-keyboard="holdKeyboard"
|
|
||||||
@input="handleInput"
|
|
||||||
@focus="handleFocus"
|
|
||||||
@blur="handleBlur"
|
|
||||||
@confirm="handleConfirm"
|
|
||||||
@linechange="handleLineChange"
|
|
||||||
@keyboardheightchange="handleKeyboardheightchange"
|
|
||||||
/>
|
|
||||||
<view class="wd-input__textarea-suffix">
|
|
||||||
<wd-icon v-if="showClear" custom-class="wd-input__textarea-icon" name="error-fill" @click="clear" />
|
|
||||||
<view v-if="showWordCount" class="wd-input__textarea-count">
|
|
||||||
<text
|
|
||||||
:class="[
|
|
||||||
inputValue && String(inputValue).length > 0 ? 'wd-input__textarea-count-current' : '',
|
|
||||||
String(inputValue).length > parseInt(String(maxlength)) ? 'is-error' : ''
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
{{ String(inputValue).length }}
|
|
||||||
</text>
|
|
||||||
/{{ maxlength }}
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
</view>
|
|
||||||
<!-- 输入域 -->
|
<!-- 输入域 -->
|
||||||
<view v-else class="wd-input__block">
|
<view class="wd-input__body">
|
||||||
|
<view class="wd-input__value">
|
||||||
<view v-if="(prefixIcon || usePrefixSlot) && !label" class="wd-input__prefix">
|
<view v-if="(prefixIcon || usePrefixSlot) && !label" class="wd-input__prefix">
|
||||||
<wd-icon v-if="prefixIcon" custom-class="wd-input__icon" :name="prefixIcon" @click="onClickPrefixIcon" />
|
<wd-icon v-if="prefixIcon" custom-class="wd-input__icon" :name="prefixIcon" @click="onClickPrefixIcon" />
|
||||||
<slot name="prefix"></slot>
|
<slot name="prefix"></slot>
|
||||||
</view>
|
</view>
|
||||||
<!-- readonly -->
|
|
||||||
<view v-if="readonly" class="wd-input__inner wd-input__readonly">{{ inputValue }}</view>
|
|
||||||
<template v-else>
|
|
||||||
<input
|
<input
|
||||||
:class="[
|
:class="[
|
||||||
'wd-input__inner',
|
'wd-input__inner',
|
||||||
@ -81,9 +29,7 @@
|
|||||||
:password="showPassword && !isPwdVisible"
|
:password="showPassword && !isPwdVisible"
|
||||||
v-model="inputValue"
|
v-model="inputValue"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:minlength="minlength"
|
|
||||||
:maxlength="maxlength"
|
:maxlength="maxlength"
|
||||||
:focus="isFocus"
|
:focus="isFocus"
|
||||||
:confirm-type="confirmType"
|
:confirm-type="confirmType"
|
||||||
@ -103,6 +49,7 @@
|
|||||||
@confirm="handleConfirm"
|
@confirm="handleConfirm"
|
||||||
@keyboardheightchange="handleKeyboardheightchange"
|
@keyboardheightchange="handleKeyboardheightchange"
|
||||||
/>
|
/>
|
||||||
|
<view v-if="readonly" class="wd-input__readonly-mask" />
|
||||||
<view v-if="showClear || showPassword || suffixIcon || showWordCount || useSuffixSlot" class="wd-input__suffix">
|
<view v-if="showClear || showPassword || suffixIcon || showWordCount || useSuffixSlot" class="wd-input__suffix">
|
||||||
<wd-icon v-if="showClear" custom-class="wd-input__clear" name="error-fill" @click="clear" />
|
<wd-icon v-if="showClear" custom-class="wd-input__clear" name="error-fill" @click="clear" />
|
||||||
<wd-icon v-if="showPassword" custom-class="wd-input__icon" :name="isPwdVisible ? 'view' : 'eye-close'" @click="togglePwdVisible" />
|
<wd-icon v-if="showPassword" custom-class="wd-input__icon" :name="isPwdVisible ? 'view' : 'eye-close'" @click="togglePwdVisible" />
|
||||||
@ -120,7 +67,8 @@
|
|||||||
<wd-icon v-if="suffixIcon" custom-class="wd-input__icon" :name="suffixIcon" @click="onClickSuffixIcon" />
|
<wd-icon v-if="suffixIcon" custom-class="wd-input__icon" :name="suffixIcon" @click="onClickSuffixIcon" />
|
||||||
<slot name="suffix"></slot>
|
<slot name="suffix"></slot>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</view>
|
||||||
|
<view v-if="errorMessage" class="wd-input__error-message">{{ errorMessage }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -140,10 +88,10 @@ export default {
|
|||||||
import { computed, onBeforeMount, ref, watch } from 'vue'
|
import { computed, onBeforeMount, ref, watch } from 'vue'
|
||||||
import { objToStyle, requestAnimationFrame } from '../common/util'
|
import { objToStyle, requestAnimationFrame } from '../common/util'
|
||||||
import { useCell } from '../composables/useCell'
|
import { useCell } from '../composables/useCell'
|
||||||
|
import { FORM_KEY, FormItemRule } from '../wd-form/types'
|
||||||
|
import { useParent } from '../composables/useParent'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
customTextareaContainerClass?: string
|
|
||||||
customTextareaClass?: string
|
|
||||||
customInputClass?: string
|
customInputClass?: string
|
||||||
customLabelClass?: string
|
customLabelClass?: string
|
||||||
customClass?: string
|
customClass?: string
|
||||||
@ -152,11 +100,8 @@ interface Props {
|
|||||||
placeholder?: string
|
placeholder?: string
|
||||||
placeholderStyle?: string
|
placeholderStyle?: string
|
||||||
placeholderClass?: string
|
placeholderClass?: string
|
||||||
autoHeight?: boolean
|
|
||||||
fixed?: boolean
|
|
||||||
cursorSpacing?: number
|
cursorSpacing?: number
|
||||||
cursor?: number
|
cursor?: number
|
||||||
showConfirmBar?: boolean
|
|
||||||
selectionStart?: number
|
selectionStart?: number
|
||||||
selectionEnd?: number
|
selectionEnd?: number
|
||||||
adjustPosition?: boolean
|
adjustPosition?: boolean
|
||||||
@ -167,11 +112,10 @@ interface Props {
|
|||||||
type?: string
|
type?: string
|
||||||
maxlength?: number
|
maxlength?: number
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
alignRight?: boolean
|
|
||||||
alwaysEmbed?: boolean
|
alwaysEmbed?: boolean
|
||||||
// 原生属性结束
|
// 原生属性结束
|
||||||
|
alignRight?: boolean
|
||||||
modelValue: string | number
|
modelValue: string | number
|
||||||
minlength?: number
|
|
||||||
showPassword?: boolean
|
showPassword?: boolean
|
||||||
clearable?: boolean
|
clearable?: boolean
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
@ -180,8 +124,6 @@ interface Props {
|
|||||||
prefixIcon?: string
|
prefixIcon?: string
|
||||||
suffixIcon?: string
|
suffixIcon?: string
|
||||||
showWordLimit?: boolean
|
showWordLimit?: boolean
|
||||||
suffix?: string
|
|
||||||
suffixCount?: number
|
|
||||||
label?: string
|
label?: string
|
||||||
labelWidth?: string
|
labelWidth?: string
|
||||||
useLabelSlot?: boolean
|
useLabelSlot?: boolean
|
||||||
@ -190,11 +132,11 @@ interface Props {
|
|||||||
center?: boolean
|
center?: boolean
|
||||||
noBorder?: boolean
|
noBorder?: boolean
|
||||||
required?: boolean
|
required?: boolean
|
||||||
|
prop?: string
|
||||||
|
rules?: FormItemRule[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
customTextareaContainerClass: '',
|
|
||||||
customTextareaClass: '',
|
|
||||||
customInputClass: '',
|
customInputClass: '',
|
||||||
customLabelClass: '',
|
customLabelClass: '',
|
||||||
customClass: '',
|
customClass: '',
|
||||||
@ -203,7 +145,6 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
maxlength: -1,
|
maxlength: -1,
|
||||||
modelValue: '',
|
modelValue: '',
|
||||||
placeholder: '请输入...',
|
placeholder: '请输入...',
|
||||||
autoHeight: false,
|
|
||||||
clearable: false,
|
clearable: false,
|
||||||
showPassword: false,
|
showPassword: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
@ -215,12 +156,10 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
showWordLimit: false,
|
showWordLimit: false,
|
||||||
confirmType: 'done',
|
confirmType: 'done',
|
||||||
confirmHold: false,
|
confirmHold: false,
|
||||||
placeholderClass: 'textarea-placeholder',
|
placeholderClass: '',
|
||||||
focus: false,
|
focus: false,
|
||||||
cursorSpacing: 0,
|
cursorSpacing: 0,
|
||||||
fixed: false,
|
|
||||||
cursor: -1,
|
cursor: -1,
|
||||||
showConfirmBar: true,
|
|
||||||
selectionStart: -1,
|
selectionStart: -1,
|
||||||
selectionEnd: -1,
|
selectionEnd: -1,
|
||||||
adjustPosition: true,
|
adjustPosition: true,
|
||||||
@ -230,7 +169,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
labelWidth: '33%',
|
labelWidth: '33%',
|
||||||
useLabelSlot: false,
|
useLabelSlot: false,
|
||||||
required: false,
|
required: false,
|
||||||
noBorder: false
|
noBorder: false,
|
||||||
|
rules: () => []
|
||||||
})
|
})
|
||||||
|
|
||||||
const showClear = ref<boolean>(false)
|
const showClear = ref<boolean>(false)
|
||||||
@ -263,18 +203,40 @@ watch(
|
|||||||
{ immediate: true, deep: true }
|
{ immediate: true, deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { parent: form } = useParent(FORM_KEY)
|
||||||
|
|
||||||
|
const errorMessage = computed(() => {
|
||||||
|
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
|
||||||
|
return form.errorMessages[props.prop]
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 是否展示必填
|
||||||
|
const isRequired = computed(() => {
|
||||||
|
let formRequired = false
|
||||||
|
if (form && form.rules) {
|
||||||
|
const rules = form.rules
|
||||||
|
for (const key in rules) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
|
||||||
|
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.required || props.rules.some((rule) => rule.required) || formRequired
|
||||||
|
})
|
||||||
|
|
||||||
const rootClass = computed(() => {
|
const rootClass = computed(() => {
|
||||||
return `wd-input ${props.type === 'textarea' ? 'is-textarea' : ''} ${props.label || props.useLabelSlot ? 'is-cell' : ''} ${
|
return `wd-input ${props.label || props.useLabelSlot ? 'is-cell' : ''} ${props.center ? 'is-center' : ''} ${
|
||||||
props.center ? 'is-center' : ''
|
cell.border.value ? 'is-border' : ''
|
||||||
} ${cell.border.value ? 'is-border' : ''} ${props.size ? 'is-' + props.size : ''} ${props.error ? 'is-error' : ''} ${
|
} ${props.size ? 'is-' + props.size : ''} ${props.error ? 'is-error' : ''} ${props.disabled ? 'is-disabled' : ''} ${
|
||||||
props.disabled ? 'is-disabled' : ''
|
inputValue.value && String(inputValue.value).length > 0 ? 'is-not-empty' : ''
|
||||||
} ${props.autoHeight ? 'is-auto-height' : ''} ${inputValue.value && String(inputValue.value).length > 0 ? 'is-not-empty' : ''} ${
|
} ${props.noBorder ? 'is-no-border' : ''} ${props.customClass}`
|
||||||
props.noBorder ? 'is-no-border' : ''
|
|
||||||
} ${props.customClass}`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const labelClass = computed(() => {
|
const labelClass = computed(() => {
|
||||||
return `wd-input__label ${props.customLabelClass} ${props.required ? 'is-required' : ''}`
|
return `wd-input__label ${props.customLabelClass} ${isRequired.value ? 'is-required' : ''}`
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputPlaceholderClass = computed(() => {
|
const inputPlaceholderClass = computed(() => {
|
||||||
@ -301,7 +263,8 @@ const emit = defineEmits([
|
|||||||
'confirm',
|
'confirm',
|
||||||
'linechange',
|
'linechange',
|
||||||
'clicksuffixicon',
|
'clicksuffixicon',
|
||||||
'clickprefixicon'
|
'clickprefixicon',
|
||||||
|
'click'
|
||||||
])
|
])
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
@ -346,9 +309,7 @@ function handleBlur({ detail }) {
|
|||||||
})
|
})
|
||||||
emit('update:modelValue', inputValue.value)
|
emit('update:modelValue', inputValue.value)
|
||||||
emit('blur', {
|
emit('blur', {
|
||||||
value: inputValue.value,
|
value: inputValue.value
|
||||||
// textarea 有 cursor
|
|
||||||
cursor: detail.cursor ? detail.cursor : null
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function handleFocus({ detail }) {
|
function handleFocus({ detail }) {
|
||||||
@ -370,15 +331,15 @@ function handleKeyboardheightchange(event) {
|
|||||||
function handleConfirm({ detail }) {
|
function handleConfirm({ detail }) {
|
||||||
emit('confirm', detail)
|
emit('confirm', detail)
|
||||||
}
|
}
|
||||||
function handleLineChange(event) {
|
|
||||||
emit('linechange', event.detail)
|
|
||||||
}
|
|
||||||
function onClickSuffixIcon() {
|
function onClickSuffixIcon() {
|
||||||
emit('clicksuffixicon')
|
emit('clicksuffixicon')
|
||||||
}
|
}
|
||||||
function onClickPrefixIcon() {
|
function onClickPrefixIcon() {
|
||||||
emit('clickprefixicon')
|
emit('clickprefixicon')
|
||||||
}
|
}
|
||||||
|
function handleClick(event: MouseEvent) {
|
||||||
|
emit('click', event)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
.wot-theme-dark {
|
.wot-theme-dark {
|
||||||
@include b(picker) {
|
@include b(picker) {
|
||||||
@include when(border) {
|
@include when(border) {
|
||||||
.wd-picker__field {
|
.wd-picker__cell {
|
||||||
@include halfPixelBorder('top', $-cell-padding, $-dark-border-color);
|
@include halfPixelBorder('top', $-cell-padding, $-dark-border-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@
|
|||||||
color: $-dark-color;
|
color: $-dark-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(field) {
|
@include e(cell) {
|
||||||
background-color: $-dark-background2;
|
background-color: $-dark-background2;
|
||||||
color: $-dark-color;
|
color: $-dark-color;
|
||||||
@include when(disabled) {
|
@include when(disabled) {
|
||||||
@ -67,12 +67,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include when(border) {
|
@include when(border) {
|
||||||
.wd-picker__field {
|
.wd-picker__cell {
|
||||||
@include halfPixelBorder('top', $-cell-padding);
|
@include halfPixelBorder('top', $-cell-padding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@include when(large) {
|
@include when(large) {
|
||||||
.wd-picker__field {
|
.wd-picker__cell {
|
||||||
font-size: $-cell-title-fs-large;
|
font-size: $-cell-title-fs-large;
|
||||||
}
|
}
|
||||||
:deep(.wd-picker__arrow) {
|
:deep(.wd-picker__arrow) {
|
||||||
@ -90,17 +90,17 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@include e(field) {
|
@include e(cell) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0 $-cell-padding;
|
padding: $-cell-wrapper-padding $-cell-padding;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
background-color: $-color-white;
|
background-color: $-color-white;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: $-cell-title-color;
|
color: $-cell-title-color;
|
||||||
font-size: $-cell-title-fs;
|
font-size: $-cell-title-fs;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
line-height: $-cell-ling-height;
|
line-height: $-cell-line-height;
|
||||||
|
|
||||||
@include when(disabled) {
|
@include when(disabled) {
|
||||||
.wd-picker__value {
|
.wd-picker__value {
|
||||||
@ -115,10 +115,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include e(error-message){
|
||||||
|
color: $-form-item-error-message-color;
|
||||||
|
font-size: $-form-item-error-message-font-size;
|
||||||
|
line-height: $-form-item-error-message-line-height;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
@include e(label) {
|
@include e(label) {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: $-input-cell-label-width;
|
width: $-input-cell-label-width;
|
||||||
padding: $-cell-wrapper-padding 0;
|
|
||||||
margin-right: $-cell-padding;
|
margin-right: $-cell-padding;
|
||||||
color: $-cell-title-color;
|
color: $-cell-title-color;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -129,7 +136,7 @@
|
|||||||
&::after {
|
&::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: calc($-cell-wrapper-padding + 2px);
|
top: 2px;
|
||||||
content: '*';
|
content: '*';
|
||||||
font-size: $-cell-required-size;
|
font-size: $-cell-required-size;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
@ -138,10 +145,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include e(value-wraper) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
@include e(value) {
|
@include e(value) {
|
||||||
width: 0;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: $-cell-wrapper-padding 0;
|
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
color: $-cell-value-color;
|
color: $-cell-value-color;
|
||||||
|
|
||||||
@ -150,19 +159,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include e(body) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
@include e(placeholder) {
|
@include e(placeholder) {
|
||||||
color: $-input-placeholder-color;
|
color: $-input-placeholder-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include edeep(arrow) {
|
@include edeep(arrow) {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: $-cell-wrapper-padding;
|
|
||||||
font-size: $-cell-icon-size;
|
font-size: $-cell-icon-size;
|
||||||
color: $-cell-arrow-color;
|
color: $-cell-arrow-color;
|
||||||
line-height: 1.25;
|
line-height: $-cell-line-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include e(body) {
|
@include e(wraper) {
|
||||||
padding-bottom: var(--window-bottom);
|
padding-bottom: var(--window-bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,22 +5,27 @@
|
|||||||
} ${error ? 'is-error' : ''} ${customClass}`"
|
} ${error ? 'is-error' : ''} ${customClass}`"
|
||||||
>
|
>
|
||||||
<!--文案-->
|
<!--文案-->
|
||||||
<view @click="showPopup">
|
<view class="wd-picker__field" @click="showPopup">
|
||||||
<slot v-if="useDefaultSlot"></slot>
|
<slot v-if="useDefaultSlot"></slot>
|
||||||
<view v-else class="wd-picker__field">
|
<view v-else class="wd-picker__cell">
|
||||||
<view
|
<view
|
||||||
v-if="label || useLabelSlot"
|
v-if="label || useLabelSlot"
|
||||||
:class="`wd-picker__label ${customLabelClass} ${required ? 'is-required' : ''}`"
|
:class="`wd-picker__label ${customLabelClass} ${isRequired ? 'is-required' : ''}`"
|
||||||
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
|
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
|
||||||
>
|
>
|
||||||
<template v-if="label">{{ label }}</template>
|
<template v-if="label">{{ label }}</template>
|
||||||
<slot v-else name="label"></slot>
|
<slot v-else name="label"></slot>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="wd-picker__body">
|
||||||
|
<view class="wd-picker__value-wraper">
|
||||||
<view :class="`wd-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${showValue ? '' : 'wd-picker__placeholder'}`">
|
<view :class="`wd-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${showValue ? '' : 'wd-picker__placeholder'}`">
|
||||||
{{ showValue ? showValue : placeholder }}
|
{{ showValue ? showValue : placeholder }}
|
||||||
</view>
|
</view>
|
||||||
<wd-icon v-if="!disabled && !readonly" custom-class="wd-picker__arrow" name="arrow-right" />
|
<wd-icon v-if="!disabled && !readonly" custom-class="wd-picker__arrow" name="arrow-right" />
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="errorMessage" class="wd-picker__error-message">{{ errorMessage }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!--弹出层,picker-view 在隐藏时修改值,会触发多次change事件,从而导致所有列选中第一项,因此picker在关闭时不隐藏 -->
|
<!--弹出层,picker-view 在隐藏时修改值,会触发多次change事件,从而导致所有列选中第一项,因此picker在关闭时不隐藏 -->
|
||||||
<wd-popup
|
<wd-popup
|
||||||
@ -33,7 +38,7 @@
|
|||||||
@close="onCancel"
|
@close="onCancel"
|
||||||
custom-class="wd-picker__popup"
|
custom-class="wd-picker__popup"
|
||||||
>
|
>
|
||||||
<view class="wd-picker__body">
|
<view class="wd-picker__wraper">
|
||||||
<!--toolBar-->
|
<!--toolBar-->
|
||||||
<view class="wd-picker__toolbar" @touchmove="noop">
|
<view class="wd-picker__toolbar" @touchmove="noop">
|
||||||
<!--取消按钮-->
|
<!--取消按钮-->
|
||||||
@ -84,6 +89,8 @@ import { getCurrentInstance, onBeforeMount, ref, watch, computed, onMounted, nex
|
|||||||
import { deepClone, defaultDisplayFormat, getType } from '../common/util'
|
import { deepClone, defaultDisplayFormat, getType } from '../common/util'
|
||||||
import { useCell } from '../composables/useCell'
|
import { useCell } from '../composables/useCell'
|
||||||
import { type ColumnItem, formatArray } from '../wd-picker-view/type'
|
import { type ColumnItem, formatArray } from '../wd-picker-view/type'
|
||||||
|
import { FORM_KEY, FormItemRule } from '../wd-form/types'
|
||||||
|
import { useParent } from '../composables/useParent'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
customClass?: string
|
customClass?: string
|
||||||
@ -139,6 +146,8 @@ interface Props {
|
|||||||
displayFormat?: Function
|
displayFormat?: Function
|
||||||
// 自定义层级
|
// 自定义层级
|
||||||
zIndex?: number
|
zIndex?: number
|
||||||
|
prop?: string
|
||||||
|
rules?: FormItemRule[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@ -168,12 +177,12 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
closeOnClickModal: true,
|
closeOnClickModal: true,
|
||||||
safeAreaInsetBottom: true,
|
safeAreaInsetBottom: true,
|
||||||
ellipsis: false,
|
ellipsis: false,
|
||||||
|
|
||||||
columnsHeight: 217,
|
columnsHeight: 217,
|
||||||
valueKey: 'value',
|
valueKey: 'value',
|
||||||
labelKey: 'label',
|
labelKey: 'label',
|
||||||
columns: () => [],
|
columns: () => [],
|
||||||
zIndex: 15
|
zIndex: 15,
|
||||||
|
rules: () => []
|
||||||
})
|
})
|
||||||
|
|
||||||
const pickerViewWd = ref<any>(null)
|
const pickerViewWd = ref<any>(null)
|
||||||
@ -275,6 +284,31 @@ watch(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { parent: form } = useParent(FORM_KEY)
|
||||||
|
|
||||||
|
// 表单校验错误信息
|
||||||
|
const errorMessage = computed(() => {
|
||||||
|
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
|
||||||
|
return form.errorMessages[props.prop]
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 是否展示必填
|
||||||
|
const isRequired = computed(() => {
|
||||||
|
let formRequired = false
|
||||||
|
if (form && form.rules) {
|
||||||
|
const rules = form.rules
|
||||||
|
for (const key in rules) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
|
||||||
|
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.required || props.rules.some((rule) => rule.required) || formRequired
|
||||||
|
})
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance() as any
|
const { proxy } = getCurrentInstance() as any
|
||||||
|
|
||||||
const emit = defineEmits(['confirm', 'open', 'cancel', 'update:modelValue'])
|
const emit = defineEmits(['confirm', 'open', 'cancel', 'update:modelValue'])
|
||||||
|
|||||||
@ -47,14 +47,14 @@
|
|||||||
@include e(cell) {
|
@include e(cell) {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0 $-cell-padding;
|
padding: $-cell-wrapper-padding $-cell-padding;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
background-color: $-color-white;
|
background-color: $-color-white;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: $-cell-title-color;
|
color: $-cell-title-color;
|
||||||
font-size: $-cell-title-fs;
|
font-size: $-cell-title-fs;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
line-height: $-cell-ling-height;
|
line-height: $-cell-line-height;
|
||||||
}
|
}
|
||||||
@include e(cell) {
|
@include e(cell) {
|
||||||
@include when(disabled) {
|
@include when(disabled) {
|
||||||
@ -83,10 +83,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@include e(error-message){
|
||||||
|
color: $-form-item-error-message-color;
|
||||||
|
font-size: $-form-item-error-message-font-size;
|
||||||
|
line-height: $-form-item-error-message-line-height;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
@include e(label) {
|
@include e(label) {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: $-input-cell-label-width;
|
width: $-input-cell-label-width;
|
||||||
padding: $-cell-wrapper-padding 0;
|
color: $-cell-title-color;
|
||||||
margin-right: $-cell-padding;
|
margin-right: $-cell-padding;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
@ -96,7 +103,7 @@
|
|||||||
&::after {
|
&::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: calc($-cell-wrapper-padding + 2px);
|
top: 2px;
|
||||||
content: '*';
|
content: '*';
|
||||||
font-size: $-cell-required-size;
|
font-size: $-cell-required-size;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
@ -104,9 +111,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@include e(value-wraper) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
@include e(value) {
|
@include e(value) {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: $-cell-wrapper-padding 0;
|
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
color: $-cell-value-color;
|
color: $-cell-value-color;
|
||||||
|
|
||||||
@ -117,12 +126,14 @@
|
|||||||
color: $-input-placeholder-color;
|
color: $-input-placeholder-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@include e(body) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
@include edeep(arrow) {
|
@include edeep(arrow) {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: $-cell-wrapper-padding;
|
|
||||||
font-size: $-cell-icon-size;
|
font-size: $-cell-icon-size;
|
||||||
color: $-cell-arrow-color;
|
color: $-cell-arrow-color;
|
||||||
line-height: 1.25;
|
line-height: $-cell-line-height;
|
||||||
}
|
}
|
||||||
@include e(selected) {
|
@include e(selected) {
|
||||||
height: $-col-picker-selected-height;
|
height: $-col-picker-selected-height;
|
||||||
|
|||||||
@ -10,12 +10,14 @@
|
|||||||
>
|
>
|
||||||
<view
|
<view
|
||||||
v-if="label || useLabelSlot"
|
v-if="label || useLabelSlot"
|
||||||
:class="`wd-select-picker__label ${required && 'is-required'} ${customLabelClass}`"
|
:class="`wd-select-picker__label ${isRequired && 'is-required'} ${customLabelClass}`"
|
||||||
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
|
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
|
||||||
>
|
>
|
||||||
<block v-if="label">{{ label }}</block>
|
<block v-if="label">{{ label }}</block>
|
||||||
<slot v-else name="label"></slot>
|
<slot v-else name="label"></slot>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="wd-select-picker__body">
|
||||||
|
<view class="wd-select-picker__value-wraper">
|
||||||
<view
|
<view
|
||||||
:class="`wd-select-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${
|
:class="`wd-select-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${
|
||||||
showValue ? '' : 'wd-select-picker__value--placeholder'
|
showValue ? '' : 'wd-select-picker__value--placeholder'
|
||||||
@ -25,6 +27,10 @@
|
|||||||
</view>
|
</view>
|
||||||
<wd-icon v-if="!disabled && !readonly" custom-class="wd-select-picker__arrow" name="arrow-right" />
|
<wd-icon v-if="!disabled && !readonly" custom-class="wd-select-picker__arrow" name="arrow-right" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view v-if="errorMessage" class="wd-select-picker__error-message">{{ errorMessage }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<wd-action-sheet
|
<wd-action-sheet
|
||||||
v-model="pickerShow"
|
v-model="pickerShow"
|
||||||
@ -102,9 +108,11 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getCurrentInstance, onBeforeMount, ref, watch, nextTick } from 'vue'
|
import { getCurrentInstance, onBeforeMount, ref, watch, nextTick, computed } from 'vue'
|
||||||
import { useCell } from '../composables/useCell'
|
import { useCell } from '../composables/useCell'
|
||||||
import { getRect, getType, isArray, isDef, requestAnimationFrame } from '../common/util'
|
import { getRect, getType, isArray, isDef, requestAnimationFrame } from '../common/util'
|
||||||
|
import { useParent } from '../composables/useParent'
|
||||||
|
import { FORM_KEY, FormItemRule } from '../wd-form/types'
|
||||||
|
|
||||||
type SelectPickerType = 'checkbox' | 'radio'
|
type SelectPickerType = 'checkbox' | 'radio'
|
||||||
|
|
||||||
@ -149,6 +157,8 @@ interface Props {
|
|||||||
filterPlaceholder?: string
|
filterPlaceholder?: string
|
||||||
ellipsis?: boolean
|
ellipsis?: boolean
|
||||||
scrollIntoView?: boolean
|
scrollIntoView?: boolean
|
||||||
|
prop?: string
|
||||||
|
rules?: FormItemRule[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@ -181,7 +191,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
filterable: false,
|
filterable: false,
|
||||||
filterPlaceholder: '搜索',
|
filterPlaceholder: '搜索',
|
||||||
ellipsis: false,
|
ellipsis: false,
|
||||||
scrollIntoView: true
|
scrollIntoView: true,
|
||||||
|
rules: () => []
|
||||||
})
|
})
|
||||||
|
|
||||||
const pickerShow = ref<boolean>(false)
|
const pickerShow = ref<boolean>(false)
|
||||||
@ -249,6 +260,31 @@ watch(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { parent: form } = useParent(FORM_KEY)
|
||||||
|
|
||||||
|
// 表单校验错误信息
|
||||||
|
const errorMessage = computed(() => {
|
||||||
|
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
|
||||||
|
return form.errorMessages[props.prop]
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 是否展示必填
|
||||||
|
const isRequired = computed(() => {
|
||||||
|
let formRequired = false
|
||||||
|
if (form && form.rules) {
|
||||||
|
const rules = form.rules
|
||||||
|
for (const key in rules) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
|
||||||
|
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.required || props.rules.some((rule) => rule.required) || formRequired
|
||||||
|
})
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
selectList.value = valueFormat(props.modelValue)
|
selectList.value = valueFormat(props.modelValue)
|
||||||
filterColumns.value = props.columns
|
filterColumns.value = props.columns
|
||||||
|
|||||||
4
src/uni_modules/wot-design-uni/global.d.ts
vendored
4
src/uni_modules/wot-design-uni/global.d.ts
vendored
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: weisheng
|
* @Author: weisheng
|
||||||
* @Date: 2023-09-25 17:28:12
|
* @Date: 2023-09-25 17:28:12
|
||||||
* @LastEditTime: 2023-12-06 16:42:32
|
* @LastEditTime: 2023-12-23 16:26:58
|
||||||
* @LastEditors: weisheng
|
* @LastEditors: weisheng
|
||||||
* @Description:
|
* @Description:
|
||||||
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\global.d.ts
|
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\global.d.ts
|
||||||
@ -93,6 +93,8 @@ declare module '@vue/runtime-core' {
|
|||||||
WdCountDown: typeof import('./components/wd-count-down/wd-count-down.vue')['default']
|
WdCountDown: typeof import('./components/wd-count-down/wd-count-down.vue')['default']
|
||||||
WdNumberKeyboard: typeof import('./components/wd-number-keyboard/wd-number-keyboard.vue')['default']
|
WdNumberKeyboard: typeof import('./components/wd-number-keyboard/wd-number-keyboard.vue')['default']
|
||||||
WdGap: typeof import('./components/wd-gap/wd-gap.vue')['default']
|
WdGap: typeof import('./components/wd-gap/wd-gap.vue')['default']
|
||||||
|
WdForm: typeof import('./components/wd-form/wd-form.vue')['default']
|
||||||
|
WdTextarea: typeof import('./components/wd-textarea/wd-textarea.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user