Vue3封装ant-design-vue表格的详细代码
Vue3封装ant-design-vue表格
说明
该文仅仅用来记录自己使用Vue3基于ant-design-vue开发的时候对Table组建的二次封装,因为在vue中,组件不能像react中那样作为一种JS数据类型随意传递,需要使用slot,因此个人认为不够灵活,但是为了解决这个问题就是用插槽对ant-design-vue的Table做了二次封装,封装后使用非常灵活:
封装代码
<template> <Table :row-key="rowKey" :row-selection="rowSelection" :pagination="pagination" :scroll="{ x: 'auto', y: scrollY }" :columns="columns" :loading="loading" :data-source="dataSource" > <template v-slot:headerCell="{ column }"> <span class="title2" :style="{ whiteSpace: 'nowrap', fontWeight: 'normal' }" >{{ $t(column.title as string) }}</span > </template> <template v-slot:bodyCell="{ column, record }"> <template v-for="slotName of slots"> <slot v-if="slotName === column.dataIndex" :name="slotName" :column="column" :record="record" ></slot> </template> <span v-if="!slots.includes(column.dataIndex as string)">{{ parseDefaultValue(record, column.dataIndex as string) }}</span> </template> </Table> </template> <script lang="ts" setup> import { Table, TablePaginationConfig } from "ant-design-vue"; import { TableColumns } from "@/models/base.model"; import { isNull } from "@/util"; import { TableRowSelection } from "ant-design-vue/es/table/interface"; const { columns, dataSource, slots = [], pagination = false, scrollY, rowSelection = undefined, rowKey, loading, } = defineProps<{ columns: TableColumns[]; pagination?: false | TablePaginationConfig; dataSource: any[]; slots?: string[]; scrollY?: number; rowSelection?: TableRowSelection; rowKey?: string; loading?: boolean; }>(); const parseDefaultValue = (record: Record<string, any>, dataIndex: string) => { const dataIndexs = dataIndex.split("."); let result = record; dataIndexs.forEach((element) => { if (result) { result = result[element]; } }); if (isNull(result)) { return "-"; } return result; }; </script>
使用
<script lang="ts" setup> import VlanConfig from "@/views/VlanConfig.vue"; import { Button, Input, InputNumber, message, Modal } from "ant-design-vue"; import { reactive, watch } from "vue"; import vlanonfigController from "@/controllers/vlanConfig.controller"; import useDataFetch from "@/hooks/useDataFetch"; import CommonTable from "../common/CommonTable.vue"; import { NoPaddingTableColumns, TableColumns } from "@/models/base.model"; import Iconfont from "../layouts/Iconfont.vue"; import { TableRowSelection } from "ant-design-vue/es/table/interface"; import { VlanInfo } from "@/models/vlanConfig.model"; import { $deleteConfirm } from "@/util"; import EditButtons from "../common/EditButtons.vue"; import { useI18n } from "vue-i18n"; enum OperationType { EDIT, DELETE, } const { t } = useI18n(); const data = useDataFetch(vlanonfigController.getVlanList, true); const state = reactive({ selectedVlanIds: [], onOperationVlan: undefined as VlanInfo, operationType: null as OperationType, addModalOpen: false, confirmLoading: false, editData: { vlan_id: undefined, vlan_name: undefined, }, }); const handleDelete = (vlan: VlanInfo) => { state.onOperationVlan = vlan; state.operationType = OperationType.DELETE; $deleteConfirm({ title: t("vlan.deleteVlanTitle", { name: vlan.vlan_name }), content: t("deleteContent"), onOk() { return new Promise<void>((resolve) => { setTimeout(() => { resolve(); }, 1000); }); }, }); }; const restoreEditData = () => { state.editData = { vlan_id: undefined, vlan_name: undefined }; }; const handleEdit = (vlan: VlanInfo) => { state.operationType = OperationType.EDIT; restoreEditData(); state.onOperationVlan = vlan; }; const handleSelectChange: TableRowSelection["onSelect"] = (ids: number[]) => { console.log("in"); state.selectedVlanIds = ids; }; const columns = [ new TableColumns("vlan.vlanId", "vlan_id"), new TableColumns("vlan.vlanName", "vlan_name"), new NoPaddingTableColumns("", "operation", undefined, 240), ]; const deleteDisabled = $computed(() => !state.selectedVlanIds.length); const handleMultiDelete = () => { console.log(state.selectedVlanIds); }; const handleEditOk = (vlan: VlanInfo) => { state.onOperationVlan = undefined; data.setEditDataToOriginData(); }; const handleEditCancel = (vlan: VlanInfo) => { state.onOperationVlan = undefined; data.resetData(); console.log("cancel"); }; const checkOperationDisabled = (id: string) => { return ( state.onOperationVlan?.nanoid === id && state.operationType === OperationType.EDIT ); }; const handleAddVlanClick = () => { state.addModalOpen = true; }; const hanldeAddVlanCancel = () => { state.addModalOpen = false; restoreEditData(); }; const hanldeAddVlanOk = () => { const { vlan_id, vlan_name } = state.editData; if (!vlan_id || !vlan_name) { message.error(t("vlan.addError")); return; } state.confirmLoading = true; return new Promise<void>((resolve) => { setTimeout(() => { resolve(); data.refresh(); hanldeAddVlanCancel(); state.confirmLoading = false; }, 1000); }); }; const isEdit = $computed(() => state.operationType === OperationType.EDIT); </script> <template> <VlanConfig> <template #rightExtra> <div style="bottom: 12px" class="flex-start relative-position"> <Button :disabled="deleteDisabled" @click="handleMultiDelete" danger type="primary" >{{ $t("vlan.deleteVlan") }}</Button > <Button @click="handleAddVlanClick" :style="{ marginLeft: '16px' }" type="primary" >{{ $t("vlan.addVlan") }}</Button > </div> </template> <CommonTable :row-key="'nanoid'" :rowSelection="{ selectedRowKeys: state.selectedVlanIds, onChange: handleSelectChange, }" :slots="['operation', 'vlan_id', 'vlan_name']" :dataSource="data.clonedData.value" :columns="columns" > <template #vlan_id="{ record }"> <div v-if="isEdit && record.nanoid === state.onOperationVlan?.nanoid" class="flex-start" > <InputNumber style="margin-right: 8px" v-model:value="record.vlan_id" ></InputNumber> <EditButtons @ok="handleEditOk(record as VlanInfo)" @cancel="handleEditCancel(record as VlanInfo)" /> </div> </template> <template #vlan_name="{ record }"> <div v-if="isEdit && record.nanoid === state.onOperationVlan?.nanoid" class="flex-start" > <Input style="width: 200px; margin-right: 8px" v-model:value="record.vlan_name" ></Input> <EditButtons @ok="handleEditOk(record as VlanInfo)" @cancel="handleEditCancel(record as VlanInfo)" /> </div> </template> <template #operation="{ record }"> <div class="flex-start flex-nowrap"> <Button type="text" :disabled="checkOperationDisabled(record.nanoid)" @click="() => handleEdit(record as VlanInfo)" class="flex" > <Iconfont icon="ic_edit" :disabled="checkOperationDisabled(record.nanoid)" :primary="!checkOperationDisabled(record.nanoid)" ></Iconfont> <span :class="{ 'primary-color': !checkOperationDisabled(record.nanoid), }" >{{ $t("edit") }}</span > </Button> <Button type="text" @click="() => handleDelete(record as VlanInfo)" class="flex" > <Iconfont icon="ic_edit" primary></Iconfont> <span class="primary-color">{{ $t("delete") }}</span> </Button> </div> </template> </CommonTable> </VlanConfig> <!-- 添加vlan对话框 --> <Modal :width="400" @cancel="hanldeAddVlanCancel" @ok="hanldeAddVlanOk" centered :title="$t('vlan.addVlan')" :confirmLoading="state.confirmLoading" :open="state.addModalOpen" > <div style="padding: 24px 0 16px"> <div class="flex-btw flex-nowrap" :style="{ marginBottom: '24px' }"> <span class="title1 white-s-nowrap">{{ $t("vlan.vlanId") }}</span> <InputNumber :min="1" :max="4093" v-model:value="state.editData.vlan_id" style="width: 264px" ></InputNumber> </div> <div class="flex-btw flex-nowrap"> <span class="title1 white-s-nowrap">{{ $t("vlan.vlanName") }}</span> <Input v-model:value="state.editData.vlan_name" style="width: 264px" ></Input> </div> </div> </Modal> </template> <style scoped lang="scss"></style>
ant-design-vue Table封装
封装表格主要功能:
1、表格加载(源数据支持数组和接口方法传递)
2、表格分页
3、表格伸缩列
4、支持单击选中行
5、表格支持列显示和隐藏(同时也可以查看AVue,具有相同的功能,AVue 组件已经封装,可直接使用;此处仅供需要情景使用)
第一步
安装支持vue的可拖动控件
npm install vue-draggable-resizable -S
第二步
利用ant和vue-draggable-resizeable封装自己的表格
Table.js如下
import { Table } from 'ant-design-vue' import Vue from 'vue' // 引入vue-draggable-resizable,用于表格列伸缩 import VueDraggableResizable from 'vue-draggable-resizable' // TableOption用于表格列显示或隐藏 import TableOption from './TableOption' // 注册组件 Vue.component('vue-draggable-resizable', VueDraggableResizable) Vue.component('table-option', TableOption) const componentName = 'drag-table' const DragTable = { name: componentName, props: Object.assign({}, Table.props, { // 返回 Promise<{ currPage, totalCount, list: any[] }> 的获取数据的函数,用于内部管理数据加载 data: { type: Function }, // 是否开启:单击行则选中行 selectOnClick: { type: Boolean, default: true }, // 默认翻到第 1 页 pageNum: { type: Number, default: 1 }, // 默认分页大小 10 行 pageSize: { type: Number, default: 10 }, // 是否显示分页大小切换下拉框 showSizeChanger: { type: Boolean, default: true }, // 是否显示分页器 showPagination: { type: [String, Boolean], default: 'auto' }, // 指定表格当前页数的url 例如:/users/2 pageURI: { type: Boolean, default: false }, // 是否展示序号列 showIndex: { type: Boolean, default: true }, customCell: { type: Function } }), data() { return { localLoading: false, // 加载标识 localDataSource: [], // 表格源数据 localPagination: Object.assign({}, this.pagination), // 分页对象,合并ant默认分页数据 localScroll: {}, // 表格列显隐 filterValue: [], originColumns: [] } }, computed: { localKeys() { return [...Object.keys(this.$data), ...Object.keys(this._computedWatchers), ...Object.keys(this).filter(k => k.startsWith('local'))] }, // 处理最大显示长度后的列 localColumns(){ return this.originColumns.filter(col => !this.filterValue.includes(col.dataIndex || col.key || col.title)) }, // 表格伸缩列(该属性是ant表格中覆盖默认的 table 元素的属性components) localComponents(){ const headerComponent = {} headerComponent.header ={} headerComponent.header.cell = (h, props, children) => { const { key, ...restProps } = props const col = this.columns.find(col => { const k = col.dataIndex || col.key return k === key }) if (!col) { return h('th', { ...restProps }, [...children]) } const dragProps = { key: col.dataIndex || col.key, class: 'table-draggable-handle', attrs: { w: 8, x: parseFloat(col.width), z: 1, axis: 'x', draggable: true, resizable: false, onDragStart: (e) => { e.stopPropagation() } }, on: { dragging: (x) => { col.width = Math.max(x, 35) this.computeWidth() } } } const drag = h('vue-draggable-resizable', { ...dragProps }) return <th {...restProps} title={col.title} width={col.width} class="resize-table-th"> {children} { drag } </th> } return headerComponent } }, watch: { loading(val) { this.localLoading = val }, // 表格源数据 dataSource: { handler(val) { this.localDataSource = val }, immediate: true }, 'localPagination.current'(val) { this.pageURI && this.$router.push({ ...this.$route, params: Object.assign({}, this.$route.params, { pageNo: val }) }) }, pageNum(val) { Object.assign(this.localPagination, { current: val }) }, pageSize(val) { Object.assign(this.localPagination, { pageSize: val }) }, showSizeChanger(val) { Object.assign(this.localPagination, { showSizeChanger: val }) }, scroll() { this.calcLocalScroll() }, columns: { handler(val) { const data = [] // 表格添加序号列 if (this.showIndex) { data.push({ title: '序号', dataIndex: 'sort', width: 50, customRender: (text, record, index) => record.total ? record.total : `${(this.localPagination.current - 1) * (this.localPagination.pageSize) + (index + 1) || (index + 1)}` }) } this.originColumns = data.concat(val) // 超出后显示省略号 不支持操作列、和排序一并使用 this.originColumns.forEach((col)=>{ if(col.dataIndex || col.key) { col.ellipsis = true } }) }, immediate: true } }, created() { // 判断格是传进数据源还是远程数据接口方法 if (this.data) { // 合并分页数据 const { pageNo } = this.$route.params const localPageNum = this.pageURI ? (pageNo && parseInt(pageNo)) : this.pageNum this.localPagination = ['auto', true].includes(this.showPagination) ? Object.assign({}, this.localPagination, { showQuickJumper: true, current: localPageNum, pageSize: this.pageSize, showSizeChanger: this.showSizeChanger, pageSizeOptions: ['10', '20', '40', '80', '120'] }) : false // 调用接口获得数据 this.loadData() } else { // 源数据传入不支持分页 this.localPagination = false } window.addEventListener('resize', this.calcLocalScroll) }, mounted() { setTimeout(() => { this.calcLocalScroll() this.resetColumns() }) }, destroyed() { window.removeEventListener('resize', this.calcLocalScroll) }, methods: { /** * 表格限制最大宽度/高度计算,用于滾动显示 */ calcLocalScroll() { const localScroll = { ...(this.scroll || {}) } // 根据自己的页面计算除表格外其他组件占据的高度,从而得出表格最大高度,也可自适应显示表格 const extraDis = (this.$store.getters.multiTab ? 40 : 0 ) + 56 + 104 + (this.pagination ? 6 : 0) + (this.$scopedSlots.footer ? 33 : 0) localScroll.x = localScroll.x || this.$el.offsetWidth - 20 localScroll.y = localScroll.y || document.body.clientHeight - ((this.$el || {}).offsetTop || 128) - extraDis this.localScroll = localScroll // 计算表格列宽度 this.computeWidth() }, /** * 表格重新加载方法 *@param {object} option 对象属性: {boolean} isBackToFirstPage 如果参数为 true, 则强制刷新到第一页 * 对象属性: {boolean} isResetOption 如果参数为 true, 则重置显隐配置项 * 对象属性: {boolean} layoutTag 如果参数为 true, 则重新计算表格限制最大宽度/高度 */ refresh({ isBackToFirstPage = false, isResetOption = false, layoutTag = false } = { isBackToFirstPage: false, isResetOption: false, layoutTag: false }) { if(layoutTag) { this.calcLocalScroll() } else { isResetOption && this.resetColumns() isBackToFirstPage && (this.localPagination = Object.assign({}, { current: 1, pageSize: this.pageSize })) this.loadData() } }, /** * 加载数据方法 * @param {{ page: number, limit: number }} pagination 分页选项器 * @param {{ [field: string]: string }} filters 过滤条件 * @param {{ field: string, order: 'asc' | 'desc' }} sorter 排序条件 */ loadData(pagination, filters, sorter = {}) { this.localLoading = true const result = this.data({ page: (pagination && pagination.current) || this.showPagination && this.localPagination.current || this.pageNum, limit: (pagination && pagination.pageSize) || this.showPagination && this.localPagination.pageSize || this.pageSize, sidx: sorter.field, order: sorter.order && sorter.order.slice(0, sorter.order.length - 3), ...filters }) // 对接自己的通用数据接口需要修改下方代码中的 r.currPage, r.totalCount, r.list if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') { result.then(r => { r = r || { currPage: 1, totalCount: 0, list: [] } this.localPagination = this.showPagination ? Object.assign({}, this.localPagination, { showQuickJumper: true, current: r.currPage, // 返回结果中的当前分页数 total: r.totalCount, // 返回结果中的总记录数 showSizeChanger: this.showSizeChanger, pageSize: (pagination && pagination.pageSize) || this.localPagination.pageSize }) : false // 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页 if (r.list.length === 0 && this.showPagination && this.localPagination.current > 1) { this.localPagination.current-- this.loadData() return } // 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小 // 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能 try { if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.pageNo * this.localPagination.pageSize))) { this.localPagination.hideOnSinglePage = true } } catch (e) { this.localPagination = false } this.localDataSource = r.list // 返回结果中的数组数据 this.localLoading = false }) } }, /** * 自定义行。可以配置表格行的相关事件,此处主要定义表格单击选中行,没有复选框或者单选框得表格可以屏蔽该功能 * @param {*} record */ localCustomRow(record) { const rowCustomer = this.customRow ? this.customRow(record) : {} if (!this.selectOnClick || !this.rowSelection) { return rowCustomer } if (!rowCustomer.on) { rowCustomer.on = {} } // 单击选中行需要判断是单选框还是多选框,表格多选或单选框得使用会在后续发文章补充。 const selectOnClickHandler = () => { const { type, selectedRowKeys } = this.rowSelection if (selectedRowKeys.includes(record[this.rowKey]) && !type) { this.rowSelection.selections.splice(this.rowSelection.selections.findIndex(r => r === record), 1) selectedRowKeys.splice(selectedRowKeys.findIndex(r => r === record[this.rowKey]), 1) } else if(!type) { this.rowSelection.selections.push(record) selectedRowKeys.push(record[this.rowKey]) } else { this.rowSelection.selectedRow = record selectedRowKeys.splice(0, 1, record[this.rowKey]) } } if (rowCustomer.on.click) { const originalClickHandler = rowCustomer.on.click rowCustomer.on.click = e => { originalClickHandler(e) selectOnClickHandler(e, record) } } else { rowCustomer.on.click = selectOnClickHandler } return rowCustomer }, /** * 对表格设置width(来避免表头和内容的错位) */ computeWidth() { const fullWidth = (this.localScroll.x || this.$el?.offsetWidth) - (this.rowSelection ? 60 : 0) if(!isNaN(fullWidth) && fullWidth >= 0) { const remain = this.originColumns.reduce((obj, col) => { if(!this.filterValue.includes(col.dataIndex || col.key)) { if(col.width) { obj.colWidthSum =obj.colWidthSum - (typeof width === 'string' && col.width.endsWith('%') ? parseFloat(col.width) * fullWidth / 100 : parseFloat(col.width)) } else { obj.noWidthColCount += 1 } } return obj }, {colWidthSum: fullWidth, noWidthColCount: 0}) // 平均宽度 const averageWidth = remain.colWidthSum / remain.noWidthColCount const lastIndex = this.originColumns.length - ( this.originColumns[this.originColumns.length-1].fixed ? 2 : 1) // 设置默认列宽,最少显示为7个字符 // 最后一列默认不设置宽度,避免列宽改变时影响其他列 this.originColumns.forEach((col,index) => { if(index !== lastIndex && averageWidth !== Infinity && !col.width) { Vue.set(col, 'width', averageWidth > 150 ? averageWidth : 150) remain.colWidthSum = remain.colWidthSum - (averageWidth > 150 ? averageWidth : 150) } else if(index === lastIndex) { const minWidth = col.width || 150 remain.colWidthSum = (remain.colWidthSum + (col.width || 0)).toFixed() Vue.set(col, 'width', remain.colWidthSum < minWidth ? minWidth : undefined) } }) } }, /** * 表格列重置,主要使用在数据使用数据数组的表格 */ resetColumns() { this.filterValue = [] this.filterShow = !this.filterShow } }, // 渲染表格方法 render(h) { const props = {} // 表格属性 Object.keys(Table.props).forEach(k => { const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}` // if(k === 'columns'){} if (this.localKeys.includes(localKey)) { props[k] = this[localKey] } else if (this[k] != null) { props[k] = this[k] } }) const on = { ...this.$listeners } this.data && (on.change = this.loadData) return ( <div class={`${componentName}-wrapper`} style="position: relative;">{[ props.showHeader && h('table-option', { ref: 'tableOption', style: { float: 'right', marginTop:'-25px', marginRight: '5px' }, props: { columns: this.originColumns, noCheckedValues: this.filterValue }, on: { filter: (noCheckedValues) => this.filterValue = noCheckedValues } }), h('a-table', { props, on, scopedSlots: { ...this.$scopedSlots } }, Object.keys(this.$slots).map(name => ( <template slot={name}>{this.$slots[name]}</template> )) ) ]} </div> ) } } export default DragTable
TableOption.vue组件
<template> <div> <a-tooltip placement="leftTop" title="表格列显示配置"> <a-button @click="handleClick" class="optionBtn"><a-icon type="table" /></a-button> </a-tooltip> <div v-if="visible" class="table-select"> <a-checkbox :checked="options.length === checkedValues.length" @change="onCheckAllChange" > 全选/反选 </a-checkbox> <a-checkbox-group :options="options" v-model="checkedValues" @change="selectChange" /> </div> </div> </template> <script> const componentName = 'ebig-table-option' const TableOption= { name: componentName, props: { columns: { type: Array, default: ()=>([])}, noCheckedValues: { type:Array, default: ()=>([]) } }, data() { return { checkedValues: [], visible: false } }, watch: { noCheckedValues: { handler(val) { this.checkedValues = this.options.filter(col => !val.includes(col.value)).map(c => c.value) }, immediate: true } }, computed: { options() { return this.columns.map(col => { const key = col.dataIndex || col.key return { label: col.title || col.slots.title, value: key + '' } }) }, allKeys: vm => vm.options.map(o => o.value) }, methods: { handleClick() { this.visible = !this.visible }, onCheckAllChange(e) { this.checkedValues = e.target.checked ? this.allKeys : [] const noCheckedValues = e.target.checked ? [] : this.allKeys this.$emit('filter', noCheckedValues) }, selectChange(checkedValues) { const noCheckedValues = this.allKeys.filter(key => !checkedValues.includes(key)) this.$emit('filter', noCheckedValues) } } } export default TableOption </script> <style lang="less"> .optionBtn { padding: 0 4px !important; height: auto !important; opacity: 0.4; &:hover { opacity: 1; } } .ant-checkbox-group-item + .ant-checkbox-group-item { display: block } .table-select { position: absolute; background:#fff; border:1px solid #ecedef; top: 5px; right: 0; z-index: 100000; padding: 10px 0 10px 10px; width: 180px; max-height: 100%; overflow: auto; &::-webkit-scrollbar { width: 5px; } &::-webkit-scrollbar-thumb { border-radius: 8px; background-color: rgb(177, 175, 175); } &::-webkit-scrollbar-thumb:hover { border-radius: 10px; background-color: #22212177; } } </style>
使用
表格使用案例
<template> <a-card style="paddingTop: 50px"> <drag-table ref="table" size="small" row-key="id" :columns="columns" :data="loadData" /> </a-card> </template> <script> import DragTable from './Table' export default { name: 'drag-table-example', components: { DragTable }, data() { return { columns: [ { dataIndex: 'name', title: '姓名' }, { dataIndex: 'sex', title: '性别' }, { dataIndex: 'age', title: '年龄' }, { dataIndex: 'school', title: '学校' } ], dataSource: [] } }, methods: { loadData() { return Promise.resolve({ currPage: 1, pageSize: 10, totalCount: 9, totalPage: 1, list: [ { id: 1, name: '张三', sex: '男', age: 18, school: '测试高级学校1'}, { id: 2, name: '李四', sex: '女', age: 16, school: '测试高级学校2'}, { id: 3, name: '王五', sex: '男', age: 15, school: '测试高级学校3'}, { id: 4, name: '张红', sex: '女', age: 17, school: '测试高级学校4'}, { id: 5, name: '陈平', sex: '男', age: 20, school: '测试高级学校5'}, ] }) } } } </script>
antdv-基础表格拖拽
到此这篇关于Vue3封装ant-design-vue表格的文章就介绍到这了,更多相关Vue3封装ant-design-vue内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
vue打包上传服务器加载提示错误Loading chunk {n} failed
这篇文章主要为大家介绍了vue打包上传服务器加载提示错误Loading chunk {n} failed解决方法,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2023-08-08vue中的attribute和property的具体使用及区别
本文主要介绍了vue中的attribute和property的具体使用及区别,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2021-09-09Vue+Element-ui弹窗 this.$alert is not a function问题
这篇文章主要介绍了Vue+Element-ui弹窗 this.$alert is not a function问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-10-10Vue报错:TypeError:Cannot create property '
这篇文章主要介绍了Vue报错:TypeError:Cannot create property 'xxx' on string 'xxxx'问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2024-08-08Vue报错Component name"Home"should always be mult
这篇文章主要介绍了Vue报错Component name"Home"should always be multi问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-09-09
最新评论