logo 范 · 拾光录
网址收集 关于作者 Github Gitee
杂文随笔5
Hexo博客:基础使用Hexo博客:Next主题Hexo博客:Next进阶使用Hexo博客:Next高级配置基于Node的WIKI管理
前端知识16
HTML常用知识CSS常用知识CSS美化checkbox复选框JavaScript常用知识JavaScript格式化时间戳JavaScript窗口宽高处理JavaScript黑夜主题切换实现方案JavaScript数字转大写简易图片查看器TypeScript基础知识Threejs基础三要素Threejs网格辅助和轨道控制器Threejs物体绘制Electron基础使用Nodejs基础知识animate.css页面动画
Vue框架19
Vite的使用及扩展Vue3父子组件Vue3使用Marked解析MarkdownMermaid图表生成库初始化页面加载动画Axios表单提交二维码解决方案NProgress加载进度条Vue3动态菜单实现Vue3使用ECharts图表Vue3处理Excel导入导出keep-alive页面缓存及setup问题Element:文件上传Element:结合Pinia实现动态菜单Element:图片上传组件Element:自定义统一弹窗组件Element:表格自定义指令控制按钮显示(鉴权)可视化大屏使用缩放适配分辨率
UniApp15
UniApp的基础使用封装网络请求工具及文件上传uni-app的开发记录微信小程序分享原生文件上传Pinia取消滚动条(兼容小程序)tabbar消息数量显示scroll-view上滑到底部加载数据状态栏高度动态设配数据共享与传递uview-plus导航栏实现背景融合Wot UIWot UI实现顶部背景图融合uni-app x
Java基础知识10
基础知识面向对象Lambda表达式常用API常用知识积累try-with-resource注解反射多线程经纬度距离计算
SpringBoot31
application配置Maven创建聚合项目全局异常处理锁机制项目启动初始化数据方式邮件功能集成原生定时任务异步集成阿里云OSS阿里OSS预签名上传基于hutool读excelJSR303WebSocketWebSocket版AI接口流式调用Smart-Doc接口文档生成器application配置信息加密雪花算法工具AOP实现请求参数脱敏思路JWT生成Token及工具类SpringBoot默认JSON与对象转换若依框架:安装使用若依框架:优化和调整文件上传若依框架:管理后台页面优化若依框架:后端接口代码优化SpringAISpringBoot实现AI接口流式调用服务启动时创建MySQL连接自建项目工程树形结构处理工具微信支付代码微信手机号登录
SpringMVC14
跨域处理拦截器RESTful风格伪前后端分离Jackson转换器调整Thymeleaf基于拦截器做权限校验AOP打印接口请求响应日志AOP打印接口请求响应耗时文件上传和回显POST请求加解密实现(AES)POST请求加解密实现(RSA+AES)参数动态校验实现方案真实IP和归属地
MyBatis8
MyBatis基本使用与配置Mapper使用相关MaBatis多数据源配置MyBatisPlus数据统计类处理方案MyBatisPlus条件查询正向工程的实现(H2)mybatis-plus-join
SpringCloud15
Netflix:微服务与搭建Netflix:服务的消费与提供Netflix:EurekaNetflix:ActuatorNetflix:RibbonNetflix:FeignNetflix:HystrixNetflix:ZuulAlibaba:简介与搭建Alibaba:Nacos注册中心Alibaba:RibbonAlibaba:OpenFeignAlibaba:Nacos配置中心Alibaba:GetewayAlibaba:Sentinel
MySQL6
MySQL基础知识MySQL多表查询与事务MySQL常用函数及解决方案MySQL视图MySQL索引安装MySQL
Redis7
Redis介绍和安装Redis配置文件Redis持久化Redis集群Redis语法基础Redis相关问题及解决方案SpringBoot集成Redis使用记录
MongoDB10
Linux安装MongoDBMongoDB基础语法MongoTemplate及SpringBoot配置MongoTemplate中Update操作MongoTemplate中聚合查询MongoTemplate日期归档示例项目使用相关知识归纳地理位置存储与距离查询MongoDB副本集与事务获取类名和属性名工具类
其他数据库1
H2数据库
Python编程6
Python基础知识Python语法yolo目标检测OpenCV的使用及树莓派平台condauv
工具集合13
IDEAMavenGradleGitNginx安装Nginx配置VSCodeJMeter压测DockerOllamaRustFSPicGoObs录制
Linux知识11
Linux常用命令Jar启动脚本VirtualBox安装CentOSVirtualBox安装Ubuntu树莓派安装及使用frp内网穿透ArchLinux:基础系统安装ArchLInux:图形化界面安装ArchLinux:常用软件ArchLinux:深度优化ArchLinux:Niri
创意设计2
Blender:入门知识UI设计基础知识
AI相关9
Claude CodeHermes AgentOpenAI基本使用OpenAI工具调用OpenAI记忆管理OpenAI推理执行OpenAI开发框架Langchainllama.cpp

父组件向子组件传递数据

定义子组件:

<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是对传入数据的拷贝,原始值的改变并不影响

解决办法:使用roReftoRefs,其本质是对传入数据的引用

// 单个数据引用
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>
父组件向子组件传递数据
子组件向父组件传值
单图片上传示例
父数据改变子组件不变问题
v-model子组件定义
v-model常用组件
单图片上传
Excel解析
分页组件
地区级联