Ant Design Vue 4常用组件

前言

https://www.antdv.com/components/overview-cn/

主题配置

1
2
3
4
5
6
7
8
9
10
11
<template>
<a-config-provider
:theme="{
token: {
colorPrimary: '#FA7E31',
},
}"
>
<RouterView/>
</a-config-provider>
</template>

中文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script setup lang="ts">
import { RouterView } from 'vue-router'
import zhCN from 'ant-design-vue/es/locale/zh_CN'
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
dayjs.locale('cn')
</script>
<template>
<a-config-provider
:locale="zhCN"
:theme="{
token: {
colorPrimary: '#FA7E31'
}
}"
>
<RouterView />
</a-config-provider>
</template>

其中下面的部分是设置日期选择为中文

否则日期选择弹出框还是英文,设置一次就行。

1
2
3
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
dayjs.locale('cn')

布局类

栅格化

https://www.antdv.com/components/grid-cn

份数

1
2
3
4
5
6
7
<a-row>
<a-col :span="6" class="z_tb_title">商品信息</a-col>
<a-col :span="4" class="z_tb_title">押金/租金/数量</a-col>
<a-col :span="6" class="z_tb_title">实付款</a-col>
<a-col :span="4" class="z_tb_title">用户地址</a-col>
<a-col :span="4" class="z_tb_title">交易状态</a-col>
</a-row>

填充

1
2
3
4
<a-row>
<a-col flex="100px">100px</a-col>
<a-col flex="auto">auto</a-col>
</a-row>

填充2

1
2
3
4
5
6
<a-row :wrap="false">
<a-col flex="none">
<div style="padding: 0 16px">none</div>
</a-col>
<a-col flex="auto">auto with no-wrap</a-col>
</a-row>

间距

水平

1
2
3
4
<a-space size="middle">
<a-button type="primary">搜索</a-button>
<a-button>重置</a-button>
</a-space>

垂直

1
<a-space direction="vertical"></a-space>

水平换行

水平的时候如果想换行的时候不要用a-space,这个有BUG,如果有多行的时候底部会生成一个margin-bottom: -16px;的样式导致样式不对。

1
<a-space size="middle" :wrap="true" style="width: 100%">

可以用a-flex替代:

1
<a-flex gap="middle" wrap="wrap" style="width: 100%"></a-flex>

Flex

水平

1
<a-flex gap="middle"></a-flex>

水平自动换行

1
<a-flex gap="middle" wrap="wrap"></a-flex>

垂直

1
<a-flex gap="middle" vertical></a-flex>

消息提示

1
2
3
4
5
6
7
8
9
10
11
12
13
import { message } from 'ant-design-vue';
const info = () => {
message.info('This is a normal message');
};
const success = () => {
message.success('This is a success message');
};
const error = () => {
message.error('This is an error message');
};
const warning = () => {
message.warning('This is a warning message');
};

加载中

1
2
3
4
5
<a-spin tip="加载中..." :spinning="loading">
<div>
页面内容
</div>
</a-spin>

TS

1
const loading = ref<boolean>(false);

注意

这个加载中会在抽屉下面

抽屉中建议加载按钮上

1
<a-button type="primary" :loading="loading">保存</a-button>

展示类

文字提示

1
2
3
4
<a-tooltip>
<template #title> 气泡中的文字 </template>
<div class="z_ellipsis_item"> 文字 </div>
</a-tooltip>

让div中的文字超出的显示省略号

1
2
3
4
5
6
7
.z_ellipsis_item {
width: 100%;
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

图片

https://www.antdv.com/components/image-cn

相比于HTML的img,能够预览图片

1
2
3
4
<a-image
:width="260"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>

走马灯

1
2
<a-carousel :autoplay="true">
</a-carousel>

二维码

https://www.antdv.com/components/qrcode-cn

基本示例

1
<a-qrcode :value="qrCodeUrl" />

默认渲染方式是canvas,会有点糊,可以改为svg,就会很清晰

1
<a-qrcode :value="qrCodeUrl" type="svg" />

调整大小

1
<a-qrcode :value="qrCodeUrl" type="svg" size="180" />

无边框

1
<a-qrcode :value="qrCodeUrl" type="svg" size="180" :bordered="false" />

TS

1
const qrCodeUrl = ref('https://www.psvmc.cn')

拼接URL

1
2
const { protocol, host } = window.location
qrCodeUrl.value = `${protocol}//${host}/mp.html?id=${item.goodsId}`

标签

基本

image-20240811175745927

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<h4 style="margin-bottom: 16px">Presets:</h4>
<div>
<a-tag color="pink">pink</a-tag>
<a-tag color="red">red</a-tag>
<a-tag color="orange">orange</a-tag>
<a-tag color="green">green</a-tag>
<a-tag color="cyan">cyan</a-tag>
<a-tag color="blue">blue</a-tag>
<a-tag color="purple">purple</a-tag>
</div>
<h4 style="margin: 16px 0">Custom:</h4>
<div>
<a-tag color="#f50">#f50</a-tag>
<a-tag color="#2db7f5">#2db7f5</a-tag>
<a-tag color="#87d068">#87d068</a-tag>
<a-tag color="#108ee9">#108ee9</a-tag>
</div>
</template>

无边框Tag

image-20240811175905832

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
<a-space :size="[0, 'small']" wrap>
<a-tag :bordered="false">Tag 1</a-tag>
<a-tag :bordered="false">Tag 2</a-tag>
<a-tag :bordered="false" closable>Tag 3</a-tag>
<a-tag :bordered="false" closable>Tag 4</a-tag>
</a-space>
<a-divider />
<a-space :size="[0, 'small']" wrap>
<a-tag :bordered="false" color="processing">processing</a-tag>
<a-tag :bordered="false" color="success">success</a-tag>
<a-tag :bordered="false" color="error">error</a-tag>
<a-tag :bordered="false" color="warning">warning</a-tag>
<a-tag :bordered="false" color="magenta">magenta</a-tag>
<a-tag :bordered="false" color="red">red</a-tag>
<a-tag :bordered="false" color="volcano">volcano</a-tag>
<a-tag :bordered="false" color="orange">orange</a-tag>
<a-tag :bordered="false" color="gold">gold</a-tag>
<a-tag :bordered="false" color="lime">lime</a-tag>
<a-tag :bordered="false" color="green">green</a-tag>
<a-tag :bordered="false" color="cyan">cyan</a-tag>
<a-tag :bordered="false" color="blue">blue</a-tag>
<a-tag :bordered="false" color="geekblue">geekblue</a-tag>
<a-tag :bordered="false" color="purple">purple</a-tag>
</a-space>
</template>

标签删除

1
2
3
4
5
6
7
8
<a-tag
:bordered="false"
closable
v-for="(item, index) in allSelectList"
:key="index"
@close.prevent="closeTag(item, index)"
>{{ item.name }}
</a-tag>

进度条

圆形进度条

1
2
3
4
5
6
7
8
9
10
<a-progress
type="circle"
:stroke-color="{
'0%': '#FFA05C',
'100%': '#FA7E31'
}"
:percent="75"
:size="80"
class="z_margin_t_m"
/>

步骤条

1
<a-steps :current="currStepIndex" :items="stepItems"></a-steps>

TS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const currStepIndex = ref(0)
const stepItems = ref([
{
title: '类目信息'
},
{
title: '商品信息'
},
{
title: '销售信息'
},
{
title: '物流信息'
}
])

Card

1
2
<a-card style="margin: 0.8rem">
</a-card>

按钮类

Button

1
2
3
4
5
6
7
8
9
10
<a-space>
<a-button type="primary">搜索</a-button>
<a-button>重置</a-button>
<a-button type="dashed">虚线按钮</a-button>
<a-button type="text">文本按钮</a-button>
<a-button type="link" danger>删除</a-button>
<a-popconfirm title="确定要删除吗?" ok-text="Yes" cancel-text="No">
<a-button type="link" danger>删除</a-button>
</a-popconfirm>
</a-space>

确认

1
2
3
4
5
6
7
8
9
10
<a-popconfirm
title="确定要删除吗?"
ok-text="是"
cancel-text="否"
@confirm="delItemClick(sort_item)"
>
<a-button type="link" danger size="small">
<comp-iconfont icon-name="icon-shanchu" />
</a-button>
</a-popconfirm>

气泡卡片

1
2
3
4
5
6
7
8
9
10
11
12
13
<a-popover placement="bottomRight">
<template #title></template>
<template #content>
<a-space direction="vertical" :size="0">
<a-button type="text">用户信息</a-button>
<a-button type="text">退出登录</a-button>
</a-space>
</template>

<div>
管理员
</div>
</a-popover>

也可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<a-dropdown>
<div>
管理员
</div>
<template #overlay>
<a-menu>
<a-menu-item>
<a href="javascript:;">用户信息</a>
</a-menu-item>
<a-menu-item>
<a href="javascript:;">退出登录</a>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>

注意

a-popover中使用a-menu,右侧会多一个竖线。

但是在a-dropdown中使用a-menu就没有。

Tab

1
2
3
4
5
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="全部"></a-tab-pane>
<a-tab-pane key="2" tab="上架中"></a-tab-pane>
<a-tab-pane key="3" tab="审核中"></a-tab-pane>
</a-tabs>

TS

1
const activeKey = ref('1');

弹窗类

抽屉

基本

1
2
3
4
5
6
7
8
9
<a-drawer
width="60%"
title="添加"
placement="right"
:open="showAddDrawer"
@close="onAddDrawerClose"
>
<p>页面内容</p>
</a-drawer>

TS

1
2
3
4
const showAddDrawer = ref(false)
const onAddDrawerClose = () => {
showAddDrawer.value = false
}

顶部按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
<a-drawer
:width="500"
title="添加"
placement="right"
:open="showAddDrawer"
@close="onAddDrawerClose"
>
<template #extra>
<a-button style="margin-right: 8px" @click="onAddDrawerClose">取消</a-button>
<a-button type="primary" @click="onAddDrawerClose">添加</a-button>
</template>
<p>页面内容</p>
</a-drawer>

TS

1
2
3
4
const showAddDrawer = ref(false)
const onAddDrawerClose = () => {
showAddDrawer.value = false
}

项目示例

1
2
3
4
5
6
7
8
9
10
11
12
13
<a-drawer
width="60%"
title="订单分配"
placement="right"
:open="showXxDrawer"
@close="onXxDrawerClose"
>
<comp-order-assign
:order-id="formState.orderId"
v-if="showXxDrawer"
@drawer-close="showXxCloseAction"
/>
</a-drawer>

TS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//订单分配
const showXxDrawer = ref(false)

function showXxClick(item: TOrder) {
formState.orderId = item.orderId
showXxDrawer.value = true
}

const onXxDrawerClose = () => {
showXxDrawer.value = false
}

function showXxCloseAction() {
showXxDrawer.value = false
actionList()
}

组件内

1
2
3
4
5
6
7
8
9
10
11
const props = defineProps({
orderId: { type: Number, required: true }
})

const emit = defineEmits<{
(e: 'drawerClose'): void
}>()


//关闭时调用
emit('drawerClose')

弹窗/对话框

https://www.antdv.com/components/modal-cn

弹窗和抽屉后出现的在上层

1
2
3
4
5
6
7
8
9
10
<a-modal
v-model:open="showConfirmModal"
title="修改单号"
ok-text="确认"
cancel-text="取消"
width="800px"
@ok="modalOkClick"
>
<div>弹窗内容</div>
</a-modal>

TS

1
2
3
4
5
6
7
8
const showConfirmModal = ref(false)
function editFormCodeClick() {
showConfirmModal.value = true
resetModalAttr()
}
function modalOkClick() {
showConfirmModal.value = false
}

注意,最新Vue版本4.2.5依旧会出现下面的报错,这个在React的版本已经解决了,但是Vue版本依旧有这个问题

Blocked aria-hidden on an element because its descendant retained focus.

解决方法

a-modal打开后调用resetModalAttr

1
2
3
4
5
6
7
8
9
10
11
12
13
async function resetModalAttr(){
await nextTick();
const domArr = document.getElementsByClassName('ant-modal')
if (domArr && domArr.length > 0) {
Array.from(domArr)
.forEach(item => {
const elements = item.querySelectorAll('[aria-hidden]');
elements.forEach(element => {
element.setAttribute('aria-hidden', 'false')
});
})
}
}

树组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<template>
<div class="SelectTypeSingleView">
<a-card>
<a-flex vertical gap="small">
<a-flex gap="small" wrap="wrap">
已选择商品:
<a-tag :bordered="false" color="orange" v-if="model" closable @close.prevent="closeTag()">
{{ model.sortName }}
</a-tag>
</a-flex>
<a-tree
:default-expand-all="true"
:blockNode="false"
v-if="treeData"
v-model:selectedKeys="selectedKeys"
:height="600"
:fieldNames="{ children: 'childList', title: 'sortName', key: 'sortId' }"
:tree-data="treeData"
@select="onSelect"
>
</a-tree>
</a-flex>
</a-card>
</div>
</template>

<script lang="ts" setup>
import { onMounted, ref, watch } from 'vue'
import { message, type TreeProps, type TreeSelectProps } from 'ant-design-vue'
import { apiSortListSel } from '@/assets/api/common_api'
const model = defineModel<TSort | null>()
const selectedKeys = ref<number[]>([])

watch(model, () => {
if (model.value) {
selectedKeys.value = [model.value.sortId]
}
})

const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => {
if (selectedKeys.length > 0) {
let { sortName, sortId, sortImg, rversion, sortPid } = info.node
model.value = { sortName, sortId, sortImg, rversion, sortPid }
} else {
model.value = null
}
}
const treeData = ref<TreeSelectProps['treeData']>()
async function getTreeData() {
let result = await apiSortListSel()
if (result.code == 0) {
treeData.value = result.obj
} else {
message.warning(result.msg)
}
}
function closeTag() {
model.value = null
selectedKeys.value.length = 0
}

onMounted(() => {
getTreeData()
})
</script>

<style src="./SelectTypeSingleView.less" lang="less" scoped></style>

下拉树菜单

https://www.antdv.com/components/tree-select-cn

基本

1
2
3
4
5
6
7
8
9
10
11
12
<a-tree-select
v-model:value="value"
show-search
style="width: 300px"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择"
allow-clear
tree-default-expand-all
:tree-data="treeData"
tree-node-filter-prop="label"
>
</a-tree-select>

TS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import type { TreeSelectProps } from 'ant-design-vue'
const value = ref<string>()
const treeData = ref<TreeSelectProps['treeData']>([
{
label: '分类1',
value: '分类1',
children: [
{
label: '子分类1',
value: '子分类1',
children: []
},
{
label: '子分类2',
value: '子分类2'
}
]
},
{
label: '分类2',
value: '分类2',
children: []
}
])

自定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
<a-tree-select
v-model:value="value"
show-search
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择"
allow-clear
tree-default-expand-all
:fieldNames="{ children: 'nodeSonList', label: 'nodeName', value: 'nodeValue' }"
:tree-data="treeData"
tree-node-filter-prop="nodeName"
>
</a-tree-select>

TS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import type { TreeSelectProps } from 'ant-design-vue'
const value = ref<string>()
const treeData = ref<TreeSelectProps['treeData']>([
{
nodeName: '分类1',
nodeValue: '分类1',
nodeSonList: [
{
nodeName: '子分类1',
nodeValue: '子分类1',
nodeSonList: []
},
{
nodeName: '子分类2',
nodeValue: '子分类2'
}
]
},
{
nodeName: '分类2',
nodeValue: '分类2',
nodeSonList: []
}
])

禁止节点选择

对应的节点添加disabled: true即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const treeData = ref<TreeSelectProps['treeData']>([
{
label: '分类1',
value: '分类1',
disabled: true,
children: [
{
label: '子分类1',
value: '子分类1',
children: []
},
{
label: '子分类2',
value: '子分类2'
}
]
},
{
label: '分类2',
value: '分类2',
children: []
}
])

下拉菜单

基本

1
2
3
4
<a-select v-model:value="formState.couponType" style="width: 120px">
<a-select-option :value="1">抵扣券</a-select-option>
<a-select-option :value="2">折扣券</a-select-option>
</a-select>

多选

1
2
3
4
5
6
7
<a-select
v-model:value="tagValues"
:options="options"
mode="multiple"
placeholder="选择标签"
>
</a-select>

TS

1
2
const tagValues = ref<string[]>([])
let options = [{value:"香蕉"},{value:"苹果"},{value:"菠萝"}]

或者

1
2
3
4
<a-select v-model:value="formState.couponType" mode="multiple" style="width: 120px">
<a-select-option :value="1">抵扣券</a-select-option>
<a-select-option :value="2">折扣券</a-select-option>
</a-select>

表单 Form

https://www.antdv.com/components/form-cn

基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<a-form
:model="formState"
name="basic"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 20 }"
autocomplete="off"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="热词"
name="hotWord"
:rules="[{ required: true, message: '不能为空!' }]"
>
<a-input v-model:value="formState.hotWord" />
</a-form-item>

<a-form-item
label="热度"
name="hotNum"
:rules="[{ required: true, message: '不能为空!' }]"
>
<a-input-number v-model:value="formState.hotNum" style="width: 100%" />
</a-form-item>

<a-form-item :wrapper-col="{ offset: 10, span: 14 }">
<a-button type="primary" html-type="submit">添加</a-button>
</a-form-item>
</a-form>

TS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface FormState {
hotWord: string
hotNum: number
}

const formState = reactive<FormState>({
hotWord: '',
hotNum: 0
})
const onFinish = (values: any) => {
console.log('Success:', values)
}

const onFinishFailed = (errorInfo: any) => {
console.log('Failed:', errorInfo)
}

JS提交

1
2
3
4
5
6
7
8
9
10
11
12
13
<a-form ref="formRef"
:label-col="{ span: 24 }"
:wrapper-col="{ span: 24 }"
autocomplete="off"
:model="formState">
<a-form-item
label="快递单号"
name="code"
:rules="[{ required: true, message: '不能为空!' }]"
>
<a-input v-model:value="formState.code" />
</a-form-item>
</a-form>

TS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import type { FormInstance } from 'ant-design-vue'
const formRef = ref<FormInstance>() // 3 定义变量
const formState = reactive<any>({
code: '',
})
formRef.value
?.validate()
.then(
(result) => {
console.info('result', result)
})
.catch(
(err) => {
console.info('err', err)
})

如果校验通过会进入then回调,返回的数据是表单中所有属性的对象,不是表单绑定的对象,只有内部有的属性才会返回。

1
{"bannerImg":"https://www.psvmc.cn/head.jpg","sortType":1}

校验失败则会进入catch

1
2
3
4
5
{
"values":{"bannerImg":"","sortType":1},
"errorFields":[{"name":["bannerImg"],"errors":["不能为空!"],"warnings":[]}],
"outOfDate":false
}

标题和输入上下分布

1
2
3
4
5
6
7
8
9
<a-form
:model="formState"
name="basic"
:label-col="{ span: 24 }"
:wrapper-col="{ span: 10 }"
autocomplete="off"
@finish="onFinish"
>
</a-form>

就是设置label-col占一行即可。

单行输入

1
2
3
<a-form-item label="优惠券名称" name="hotWord" :rules="[{ required: true, message: '不能为空!' }]">
<a-input v-model:value="formState.hotWord" />
</a-form-item>

数字输入

1
2
3
<a-form-item label="热度" name="hotNum" :rules="[{ required: true, message: '不能为空!' }]">
<a-input-number v-model:value="formState.hotNum" style="width: 100%" />
</a-form-item>

设置最大最小

1
2
3
4
5
6
<a-input-number
:min="0"
:max="100"
v-model:value="formState.settlementRatio"
style="width: 160px"
/>

多行输入

1
2
3
<a-form-item label="规则说明" name="couponType" :rules="[{ required: true, message: '不能为空!' }]">
<a-textarea v-model:value="formState.hotWord" placeholder="请输入使用规则" />
</a-form-item>

设置行数

1
2
3
4
<a-textarea
v-model:value="formState.orderCode"
:autoSize="{ minRows: 6, maxRows: 10 }"
/>

下拉菜单

1
2
3
4
5
6
<a-form-item label="优惠券类型" name="couponType" :rules="[{ required: true, message: '不能为空!' }]">
<a-select v-model:value="formState.couponType">
<a-select-option :value="1">抵扣券</a-select-option>
<a-select-option :value="2">折扣券</a-select-option>
</a-select>
</a-form-item>

开关

1
<a-switch v-model:checked="checked" />

设置选中时的值

1
2
3
4
5
6
<a-switch
v-model:checked="formState.isWhite"
:checkedValue="1"
:unCheckedValue="0"
@change="goodsWhiteChange(record)"
/>

单选按钮

1
2
3
4
5
6
<a-form-item label="优惠券类型" name="couponType" :rules="[{ required: true, message: '不能为空!' }]">
<a-radio-group v-model:value="formState.couponType">
<a-radio :value="1">平台</a-radio>
<a-radio :value="2">商家</a-radio>
</a-radio-group>
</a-form-item>

样式2

image-20240810155645638

1
2
3
4
5
<a-radio-group v-model:value="dateType" button-style="solid">
<a-radio-button :value="1"></a-radio-button>
<a-radio-button :value="2"></a-radio-button>
<a-radio-button :value="3">自定义</a-radio-button>
</a-radio-group>

TS

1
let dateType = ref(1)

多选

基本

1
<a-checkbox v-model:checked="checked">Checkbox</a-checkbox>

多选按钮组

1
<a-checkbox-group v-model:value="cbValue" name="checkboxgroup" :options="cbItemList" />

TS

1
2
3
4
5
6
const cbValue = reactive([1])
const cbItemList = [
{ label: '90天质保', value: 1 },
{ label: '质量保修', value: 2 },
{ label: '按时发货', value: 3 }
]

一行多个

1
2
3
4
5
6
7
8
9
10
11
12
<a-form-item label="使用条件" name="couponType" :rules="[{ required: true, message: '不能为空!' }]">
<a-space>
<a-select v-model:value="formState.couponType" style="width: 120px">
<a-select-option :value="1">签约价值</a-select-option>
<a-select-option :value="2">总租金</a-select-option>
<a-select-option :value="3">月租金</a-select-option>
</a-select>

<a-input v-model:value="formState.hotWord" />
元可使用
</a-space>
</a-form-item>

可输入可选择

和下拉菜单的区别是它可以输入也可以单选

1
2
3
4
5
6
<a-auto-complete
v-model:value="formState.groupName"
:options="options"
placeholder="选择分组"
>
</a-auto-complete>

报错

You can set not need to be collected fields into a-form-item-rest

原因是一个表单项下出现了多个输入项

第一种,使用多个 a-form-item

1
2
3
4
5
6
<a-form-item>
<a-input name="a"></a-input>
<a-form-item>
<a-input name="b"></a-input>
</a-form-item>
</a-form-item>

第二种,使用 a-form-item-rest 组件:

它会阻止数据的收集,你可以将不需要收集校验的表单项放到这个组件中即可,它和第一种方式很类似,但它不会产生额外的 dom 节点。

1
2
3
4
5
6
<a-form-item>
<a-input name="a"></a-input>
<a-form-item-rest>
<a-input name="b"></a-input>
</a-form-item-rest>
</a-form-item>

日期选择

https://www.antdv.com/components/date-picker-cn

单个日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<a-space direction="vertical" :size="12">
<a-date-picker v-model:value="value1" />
<a-date-picker v-model:value="value2" picker="week" />
<a-date-picker v-model:value="value3" picker="month" />
<a-date-picker v-model:value="value4" picker="quarter" />
<a-date-picker v-model:value="value5" picker="year" />
</a-space>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import type { Dayjs } from 'dayjs';
const value1 = ref<Dayjs>();
const value2 = ref<Dayjs>();
const value3 = ref<Dayjs>();
const value4 = ref<Dayjs>();
const value5 = ref<Dayjs>();
</script>

注意绑定的日期类型不是字符串

1
2
3
4
5
6
7
8
9
10
import type { Dayjs } from 'dayjs'

const beginDateObj = ref<Dayjs>()
watch(beginDateObj, () => {
if (beginDateObj.value) {
filterObj.begindate = beginDateObj.value.format('YYYY-MM-DD')
} else {
filterObj.begindate = ''
}
})

禁止选择

1
2
3
4
5
<a-date-picker
v-model:value="endDateObj"
:disabled-date="disabledDate"
placeholder="结束日期"
/>

TS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const endDateObj = ref<Dayjs>()

const props = defineProps({
realEnd: { type: String, required: true }
})

watch(endDateObj, () => {
if (endDateObj.value) {
formState.endTime = endDateObj.value.format('YYYY-MM-DD')
} else {
formState.endTime = ''
}
})

endDateObj.value = dayjs(props.realEnd).startOf('day').add(30, 'day')

const disabledDate = (current: Dayjs) => {
return current && current < dayjs(props.realEnd).endOf('day')
}

这里是时间选择必须在props.realEnd之后。

日期和时间

示例1

1
<a-date-picker show-time placeholder="选择日期时间"  v-model:value="beginDateObj"/>

TS

1
2
3
4
5
6
7
8
const beginDateObj = ref<Dayjs>()
watch(beginDateObj, () => {
if (beginDateObj.value) {
formState.useTimeBegin = beginDateObj.value.format('YYYY-MM-DD HH:mm:ss')
} else {
formState.useTimeBegin = ''
}
})

示例2

1
<a-date-picker show-time placeholder="选择日期时间" format="YYYY-MM-DD HH:mm:ss" @change="onChange" @ok="onOk" />

日期也可以控制不选秒

1
<a-date-picker show-time placeholder="选择日期时间" format="YYYY-MM-DD HH:mm" @change="onChange" @ok="onOk" />

TS

1
2
3
4
5
6
7
const onChange = (value: Dayjs, dateString: string) => {
console.log('Selected Time: ', value);
console.log('Formatted Selected Time: ', dateString);
};
const onOk = (value: Dayjs) => {
console.log('onOk: ', value);
};

日期段

单个组件

1
<a-range-picker v-model:value="dateRange" />

TS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import type { Dayjs } from 'dayjs'
type RangeValue = [Dayjs, Dayjs]
const dateRange = ref<RangeValue>()
watch(dateRange, () => {
if (dateRange.value) {
if (dateRange.value[0]) {
filterObj.begindate = dateRange.value[0].format('YYYY-MM-DD')
} else {
filterObj.begindate = ''
}

if (dateRange.value[1]) {
filterObj.enddate = dateRange.value[1].format('YYYY-MM-DD')
} else {
filterObj.enddate = ''
}
}
})

重置的时候

1
2
3
dateRange.value = undefined
filterObj.begindate = ''
filterObj.enddate = ''

多个组件

1
2
3
4
5
6
7
8
9
<a-space align="center">
<span>开始日期:</span>
<a-date-picker v-model:value="beginDateObj" placeholder="开始日期" />
</a-space>

<a-space align="center">
<span>结束日期:</span>
<a-date-picker v-model:value="endDateObj" placeholder="结束日期" />
</a-space>

TS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import type { Dayjs } from 'dayjs'

const beginDateObj = ref<Dayjs>()
watch(beginDateObj, () => {
if (beginDateObj.value) {
filterObj.begindate = beginDateObj.value.format('YYYY-MM-DD')
} else {
filterObj.begindate = ''
}
})

const endDateObj = ref<Dayjs>()
watch(endDateObj, () => {
if (endDateObj.value) {
filterObj.enddate = endDateObj.value.format('YYYY-MM-DD')
} else {
filterObj.enddate = ''
}
})