父组件向子组件传递数据
定义子组件:
<template>
<h1>{{ msg }}</h1>
</template>
<script setup>
defineProps({ // 使用defineEmits和defineProps不需要导入
msg: String
})
</script>
注意:defineProps里的数据是只读的,无法进行更改,如需修改需要重新指定
const props = defineProps({
imageUrl: String
})
let imgUrl = ref(props.imageUrl)
// 图片上传成功
function imageUploadSuccess(res) {
imgUrl.value = res.url
}
在父组件中使用:
<template>
<HelloWorld msg="Hello Vue 3 + Vite" />
</template>
<script setup>
import HelloWorld from './components/HelloWorld.vue'
</script>
子组件向父组件传值
子组件无法直接修改父组件的变量,只能利用通知父组件,让父组件协助修改相应的值
子组件:
<template>
<button @click="$emit('changeContentEmit', '内容已发生更改')">更改内容</button>
</template>
<script setup>
defineEmits(['changeContentEmit']) // 使用defineEmits和defineProps不需要导入
</script>
父组件:
<template>
<h1>{{ content }}</h1>
<HelloWorld @changeContentEmit="changeContent" />
</template>
<script setup>
import { ref } from 'vue';
import HelloWorld from './components/HelloWorld.vue'
let content = ref('这里是内容')
function changeContent(value) {
content.value = value
}
</script>
在方法中调用
const emit = defineEmits(['uploadSuccess'])
// 图片上传成功
function imageUploadSuccess(res) {
imgUrl.value = res.url
// 通知父组件
emit('uploadSuccess', imgUrl)
loadClose()
}
单图片上传示例
<!-- 单图片上传 -->
<template>
<div>
<el-upload
class="avatar-uploader"
:action="api.file_upload"
:show-file-list="false"
:on-success="imageUploadSuccess"
:before-upload="imageUploadBefore">
<img v-if="imgUrl" :src="imgUrl" class="avatar"/>
<el-icon v-else class="avatar-uploader-icon">
<Plus/>
</el-icon>
</el-upload>
</div>
</template>
<script setup>
import api from '../../http/api'
import {loadClose, loadOpen} from '../../utils/elementUtil'
import {ref} from 'vue'
const props = defineProps({
imageUrl: String
})
// 用变量接收props的值,便于在下面的代码中进行操作
let imgUrl = ref(props.imageUrl)
// 图片上传前
function imageUploadBefore() {
loadOpen('上传中')
}
const emit = defineEmits(['uploadSuccess'])
// 图片上传成功
function imageUploadSuccess(res) {
imgUrl.value = res.url
emit('uploadSuccess', imgUrl)
loadClose()
}
</script>
<style scoped>
</style>
父数据改变子组件不变问题
import {ref} from 'vue'
const props = defineProps({
imageUrl: String
})
// 用变量接收props,便于在下面的代码中进行操作
let imgUrl = ref(props.imageUrl)
// 图片上传成功
function imageUploadSuccess(res) {
imgUrl.value = res.url
}
父组件向子组件传值不是响应式的,绑定的值在子组件创建后再更不对子组件没有影响。原因是ref
是对传入数据的拷贝,原始值的改变并不影响
解决办法:使用roRef
或toRefs
,其本质是对传入数据的引用
// 单个数据引用
let value = toRef(props, 'value')
// 对象引用
let {data} = toRefs(props)
v-model子组件定义
<!--区域级联选择,任意选一个的情况-->
<template>
<el-cascader
v-model="mv"
ref="areaCascadeRef"
placeholder="请选择地区"
:options="areaData" :props="{label:'name',value:'code',children:'child',checkStrictly: true}"
@change="areaCascadeChange(areaCascadeRef)"
clearable/>
</template>
<script setup>
import {ref, getCurrentInstance, watch} from 'vue'
import areaData from '../../utils/areaData'
const props = defineProps({
modelValue: {
type: [Number, String],
default: '',
},
})
// 上面模板的值使用该数据
const mv = ref(props.modelValue)
// 该ref用于组件,无关因素
let areaCascadeRef = ref()
// 获取emit
const {emit} = getCurrentInstance()
// 如果父组件传过来的数据是异步获取的,则需要进行监听,解决父数据更改子数据不响应问题
watch(() => props.modelValue, () => {
mv.value = props.modelValue
})
function areaCascadeChange(refEl) {
let selectValue = ''
if (refEl.getCheckedNodes().length > 0) {
selectValue = refEl.getCheckedNodes()[0].value
}
// 通过update更新值
emit('update:modelValue', selectValue)
// 选中后关闭
refEl.togglePopperVisible()
}
</script>
<style scoped>
</style>
v-model常用组件
单图片上传
<!-- 单图片上传 -->
<template>
<div>
<el-upload
class="avatar-uploader"
:action="api.file_upload"
:show-file-list="false"
:on-success="imageUploadSuccess"
:before-upload="imageUploadBefore">
<img v-if="mv" :src="mv" class="avatar"/>
<el-icon v-else class="avatar-uploader-icon">
<Plus/>
</el-icon>
</el-upload>
</div>
</template>
<script setup>
import {ref, getCurrentInstance, watch} from 'vue'
import api from '../../http/api'
import {loadClose, loadOpen} from '../../utils/elementUtil'
const props = defineProps({
modelValue: {
type: String,
default: '',
},
})
const mv = ref(props.modelValue)
const {emit} = getCurrentInstance()
watch(() => props.modelValue, () => {
mv.value = props.modelValue
})
// 图片上传前
function imageUploadBefore() {
loadOpen('上传中')
}
// 图片上传成功
function imageUploadSuccess(res) {
emit('update:modelValue', res.data)
loadClose()
}
</script>
<style scoped>
</style>
Excel解析
需要npm install xlsx
支持
<!-- 表格解析 -->
<template>
<div>
<el-upload
action=""
:file-list="fileList"
:http-request="uploadHttp">
<el-button type="primary">请选择上传文件</el-button>
</el-upload>
</div>
</template>
<script setup>
import {ref, getCurrentInstance, watch} from 'vue'
import {read, utils} from 'xlsx'
const props = defineProps({
modelValue: {
type: Array,
default: [],
},
})
const mv = ref(props.modelValue)
const {emit} = getCurrentInstance()
watch(() => props.modelValue, () => {
mv.value = props.modelValue
})
// 文件列表,在上次第二个时删除第一个,该方式能解决覆盖不刷新问题
let fileList = ref([])
// 上传时读取表格
async function uploadHttp(file) {
let dataBinary = await readFile(file.file)
let workBook = read(dataBinary, {type: 'binary', cellDates: true})
let workSheet = workBook.Sheets[workBook.SheetNames[0]]
const data = utils.sheet_to_json(workSheet)
emit('update:modelValue', data)
// 保留最新的显示
if (fileList.value.length > 1) {
fileList.value.splice(0, 1)
}
}
// 读取文件
const readFile = (file) => {
return new Promise(resolve => {
let reader = new FileReader()
reader.readAsBinaryString(file)
reader.onload = ev => {
resolve(ev.target.result)
}
})
}
</script>
<style scoped>
</style>
分页组件
<!-- 分页组件 -->
<template>
<div class="page-container">
<el-pagination
v-model:currentPage="pageIndexPro"
v-model:page-size="pageSizePro"
:total="countPro"
@current-change="pageIndexChange"
@size-change="pageSizeChange"
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[15, 30, 50, 100]"
background/>
</div>
</template>
<script setup>
import {ref, getCurrentInstance, watch} from 'vue'
const props = defineProps({
pageIndex: {
type: Number,
default: 1,
},
pageSize: {
type: Number,
default: 15,
},
count: {
type: Number,
default: 0,
},
})
const pageIndexPro = ref(props.pageIndex)
const pageSizePro = ref(props.pageSize)
const countPro = ref(props.count)
const {emit} = getCurrentInstance()
watch(() => props.pageIndex, () => {
pageIndexPro.value = props.pageIndex
})
watch(() => props.pageSize, () => {
pageSizePro.value = props.pageSize
})
watch(() => props.count, () => {
countPro.value = props.count
})
// 分页页码改变
function pageIndexChange(value) {
emit('update:pageIndex', value)
emit('change', '')
}
// 分页页数改变
function pageSizeChange(value) {
emit('update:pageSize', value)
emit('change', '')
}
</script>
<style scoped>
</style>
使用方式:
<!--分页-->
<pageComponent
v-model:pageIndex="listData.req.pageIndex"
v-model:pageSize="listData.req.pageSize"
v-model:count="listData.count"
@change="getList()"/>
地区级联
注意一定要保证value
的值唯一,否则控件会出现问题
<!--区域级联选择,任意选一个的情况-->
<template>
<el-cascader
v-model="mv"
ref="areaCascadeRef"
placeholder="请选择地区"
:options="areaData" :props="{label:'name',value:'name',children:'child',checkStrictly: true}"
@change="areaCascadeChange(areaCascadeRef)"
popper-class="cascade-click"
clearable/>
</template>
<script setup>
import {ref} from 'vue'
import areaData from './areaAll.json'
let mv = ref()
let areaCascadeRef = ref()
const emit = defineEmits(['changeContentEmit'])
function areaCascadeChange(refEl) {
let labels = []
let values = []
if (refEl.getCheckedNodes().length > 0) {
labels = refEl.getCheckedNodes()[0].pathLabels
values = refEl.getCheckedNodes()[0].pathValues
} else {
console.log('清空')
}
emit('changeContentEmit', labels, values)
}
</script>
<style scoped>
</style>