位置: IT常识 - 正文

Vue3.0实现图片预览组件(媒体查看器)(vue如何预加载图片)

编辑:rootadmin
Vue3.0实现图片预览组件(媒体查看器)

推荐整理分享Vue3.0实现图片预览组件(媒体查看器)(vue如何预加载图片),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:vue实现图片下载,vue预加载项目所有图片资源,vue图片展示,vue如何设置图片的出现方式,vue图片上传并预览,vue 图片编辑插件,vue图片展示,vue预加载项目所有图片资源,内容如对您有帮助,希望把文章链接给更多的朋友!

前言:最近项目中有个场景,一组图片、视频、音频、文件数据,要求点击图片可以放大预览,左右可以切换音视频、文件,支持鼠标及各种键控制 缩放,左右旋转,移动等功能,整理了一下,封了个组件,注释很全面,每块地方都有讲解,可以直接拿到项目中使用

先看下效果:

clg

关于传值:

(必传)传入url数组urlList,传入图片所处index,也就是在数组中的索引

(非必传)是否支持无限滚动?是否支持ESC键退出?是否支持点击遮罩层退出?是否需要工具栏?

关于图片的相关特效:

定义一个transform样式对象,包含缩放scale、旋转deg、移动offsetX|offsetY、动画enableTransition,可在computed计算中返回由此对象组成的css样式对象,在模板中对图片绑定,当去触发特效相关各类事件时,改变对象里某个值,则会重新捕获对象的改变执行computed,去实时更新图片样式

Vue3.0实现图片预览组件(媒体查看器)(vue如何预加载图片)

  1. 缩放操作: 可以通过鼠标滚动上下滑动、键盘上下键up/down、组件内部工具栏按钮这三种方式去控制此特效,定义一个缩放比,即每次缩放的程度,可以根据项目场景自行定义,我这里键盘控制、按钮点击为1.4,鼠标滚轮偏小为1.2,  每次缩放让初始化的样式对象的scale每次乘或除以这个值即可,当然无限缩小无限放大肯定不行,需要定义一个最小最大值控制

   2. 旋转操作: 定义一个旋转常量为90度,顺时针旋转让初始化样式对象deg累加这个值,逆时针相反即可

   3. 移动操作: 顾名思义,也就是在遮罩层内可以通过鼠标对图片进行移动,在鼠标按下事件内,监听鼠标移动事件,每次移动记录下当前鼠标位置,计算offsetX也就是图片要移动的translateX为:移动前的距页面左侧距离offsetX加上当前的鼠标位置event.pageX - 移动前的鼠标位置; 上下移动同理

   4. 动画过渡: 根据特效的触发方式决定是否需要过渡,我这里对通过鼠标操作缩放时没有定义动画,其余操作方式建议都要加上

关于图片初始化展示:

如果想要图片能够自适应在遮罩层的容器内,并且保证图片不变形且宽或高不溢出容器,那么就不应该定死宽高或者是不去定义宽高,我这里解决办法是对图片进行等比例的缩放,具体算法就不在这里过多讲解了,详情:https://blog.csdn.net/dabaooooq/article/details/128852363

关于音视频展示:

视频我这里用的是 vue3-video-play 这个插件,ui和功能各方面整体感觉很棒,算是对Vue 3.0支持比较好的一个吧,详情可以参考:https://codelife.cc/vue3-video-play/

音频用的是原生audio,用法很方便,没什么可讲的,具体看代码

关于抛出数据:

顶部中间一般为图片当前索引index/总长度,当然默认的为这样,抛出index,可以自定义这块地方插槽的使用,顶部左侧插槽也暴露当前index,其实当前组件最需要的数据也莫过于index,暴露方法中也基本都有抛出

附上完整代码

<template> <teleport to='body'> <transition> <div class="el-image-viewer__wrapper" :tabindex="-1" ref="imageViewer" :style="{ zIndex: computedZIndex }"> <!-- MASK --> <div class="el-image-viewer__mask" @click.self="isClickToDisappear && hide"></div> <!-- CLOSE --> <span class="el-image-viewer__btn el-image-viewer__close" @click="hide"> <el-icon> <Close /> </el-icon> </span> <!-- HEADER --> <div class="image-viewer-header" v-if="isTools"> <div class="header-left"> <span class="header-file-name"> <slot name="title" :index="index"></slot> </span> <slot name="left" :index="index"></slot> </div> <div class="header-center"> <slot name="content" :index="index"> {{ index + 1 }} / {{ urlList.length }} </slot> </div> </div> <!-- ACTIONS --> <div class="el-image-viewer__btn el-image-viewer__actions" v-if="isFileType(urlList[index]) === 'img' && isTools"> <div class="el-image-viewer__actions__inner"> <el-icon @click="handleActions('zoomOut')"> <ZoomOut /> </el-icon> <el-icon @click="handleActions('zoomIn')"> <ZoomIn /> </el-icon> <el-icon @click="toggleMode"> <component :is="icons.default[mode.icon]"></component> </el-icon> <el-icon @click="handleActions('anticlockwise')"> <RefreshLeft /> </el-icon> <el-icon @click="handleActions('clockwise')"> <RefreshRight /> </el-icon> </div> </div> <!-- ARROW --> <template v-if="!isSingle"> <span class="el-image-viewer__btn el-image-viewer__prev" @click="prev"> <el-icon> <ArrowLeftBold /> </el-icon> </span> <span class="el-image-viewer__btn el-image-viewer__next" @click="next"> <el-icon> <ArrowRightBold /> </el-icon> </span> </template> <!-- CANVAS --> <div id="image-viewer-canvas" class="el-image-viewer__canvas"> <div v-for="(url, i) in urlList" :key="url" style="display: flex" :data-id="url"> <!-- 图片显示 --> <img v-if="i === index && isFileType(url) === 'img'" v-loading="loading" :src="url" :style="[mediaStyle, { width: 'auto', height: imgHeight }]" class="el-image-viewer__img" @load="handleImgLoad" @error="handleMediaError" @mousedown="handleMouseDown" /> <!-- 视频显示 --> <videoPlay v-if="(i === index && isFileType(url) === 'video')" :src="url" class="el-image-viewer__img" @load="handleMediaLoad" @error="handlePlayError(i)" @mousedown="handleMouseDown"> </videoPlay> <!-- 音频显示 --> <audio controls="controls" v-if="i === index && isFileType(url) === 'audio'" :src="url" class="el-image-viewer__img" @load="handleMediaLoad" @error="handleMediaError"></audio> <!-- 文本文件显示 --> <div v-if="i === index && ['file', 'text'].includes(isFileType(url))" class="image-viewer-tips"> <span class="image-unknown-file-type-view"></span> <p> 我们不能预览该文件。<br> 您要先下载文件以查看。 </p> <div class="image-viewer-download" @click="download(url)"> <span class="icon-download"></span> 下载 </div> </div> </div> </div> </div> </transition> </teleport></template><script lang="ts">import 'element-plus/es/components/image-viewer/style/css'import "vue3-video-play/dist/style.css";import * as icons from './mediaIcons'import { videoPlay } from "vue3-video-play/dist/index.es";import { fileType, transformImgRatio } from "@/utils/dataUtils"; // fileType 判断文件后缀方法; transformImgRatio 等比例计算图片宽高方法import { isNumber, useEventListener } from '@vueuse/core'import { PropType } from 'vue';import { useZIndex } from 'element-plus';export default defineComponent({ name: 'imageViewerUtil', props: { urlList: { // url数组 type: Array as PropType<string[]>, default: () => [] }, imgIndex: { // 当前文件所处位置,也是在数组中的索引 type: Number, default: 0 }, isTools: { // 是否需要工具栏 type: Boolean, default: true }, isInfinite: { // 是否支持无限循环滚动 type: Boolean, default: true }, zIndex: { // 层级 type: Number }, closeOnPressEscape: { // 是否支持ESC键退出 type: Boolean, default: true }, isClickToDisappear: { // 是否支持通过点击遮罩层关闭 type: Boolean, default: false } }, emits: ['close', 'download', 'prevIndex', 'nextIndex'], setup(props, { emit }) { const global = getCurrentInstance().appContext.config.globalProperties const { nextZIndex } = useZIndex() const modes = { // 模式对象 CONTAIN: { name: 'contain', icon: 'IconEpFullScreen', }, ORIGINAL: { name: 'original', icon: 'IconEpScaleToOriginal', }, } const mode = shallowRef(modes.ORIGINAL) // 模式 const EVENT_CODE = { // 按钮对象 left: 'ArrowLeft', // 37 up: 'ArrowUp', // 38 right: 'ArrowRight', // 39 down: 'ArrowDown', // 40 esc: 'Escape', space: 'Backspace' } const imageViewer = ref<HTMLDivElement>() const data = reactive({ index: 0, // 图片索引,也是在数组中的位置 loading: true, // 处理加载 imgHeight: '', // 处理图片高 transform: { scale: 1, // 缩放比 deg: 0, // 旋转角度 offsetX: 0, offsetY: 0, enableTransition: false // 是否需要过渡 } }) // 是否是火狐 const isFirefox = (): boolean => /firefox/i.test(window.navigator.userAgent) // 鼠标滚轮事件,火狐的不同 const mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel' const computedZIndex = computed(() => { // 计算z-index值 return isNumber(props.zIndex) ? props.zIndex : nextZIndex() }) const isSingle = computed(() => { // 是否是单张 return props.urlList.length <= 1 }) const isFirst = computed(() => { // 是否是第一张 return data.index === 0 }) const isLast = computed(() => { // 是否是最后一张 return data.index === props.urlList.length - 1 }) const isFileType = computed(() => { // 判断文件类型 return (url: string) => { return fileType(url.split('?')[0]) } }) const mediaStyle = computed(() => { // 图片css特效对象 const { scale, deg, offsetX, offsetY, enableTransition } = data.transform let translateX = offsetX / scale let translateY = offsetY / scale switch (deg % 360) { case 90: case -270: ;[translateX, translateY] = [translateY, -translateX] break case 180: case -180: ;[translateX, translateY] = [-translateX, -translateY] break case 270: case -90: ;[translateX, translateY] = [-translateY, translateX] break } return { transform: `scale(${scale}) rotate(${deg}deg) translate(${translateX}px, ${translateY}px)`, transition: enableTransition ? 'transform .3s' : '' } }) const handleImgLoad = (e) => { // 处理加载图片后的操作 // 计算图片等比例缩放后的宽高 const { clientWidth, clientHeight } = document.querySelector('#image-viewer-canvas') // 当前遮罩容器 const { width, height } = transformImgRatio(e.target.width, e.target.height, clientWidth, clientHeight - 40) // 计算等比例缩放后的图片宽高 data.imgHeight = height + 'px' data.loading = false } const hide = () => { // 关闭 emit('close') } const prev = () => { // 上一张 if (!props.isInfinite && !data.index) return ElMessage({ type: 'info', message: '已经是第一张了!' }) data.index = (data.index - 1 + props.urlList.length) % props.urlList.length resetStyle() emit('prevIndex', data.index) } const next = () => { // 下一张 if (!props.isInfinite && data.index === props.urlList.length - 1) return ElMessage({ type: 'info', message: '已经是最后一张了!' }) data.index = (data.index + 1 + props.urlList.length) % props.urlList.length resetStyle() emit('nextIndex', data.index) } const keydownHandler = (e: event) => { // 键盘事件 switch (e.code) { case EVENT_CODE.esc: // Escape props.closeOnPressEscape && hide() break; case EVENT_CODE.left: // ArrowLeft prev() break; case EVENT_CODE.right: // ArrowRight next() break; case EVENT_CODE.up: // ArrowUp handleActions('zoomIn') break; case EVENT_CODE.down: // ArrowDown handleActions('zoomOut') break; case EVENT_CODE.space: // Backspace toggleMode() break } e.preventDefault() } const mousewheelHandler = (e: WheelEvent | any /* TODO: wheelDelta is deprecated */) => { // 鼠标滚轮事件 const delta = e.wheelDelta ? e.wheelDelta : -e.detail // 考虑Firefox if (delta > 0) { // 向上 handleActions('zoomIn', { zooRate: 1.2, enableTransition: false }) } else { // 向下 handleActions('zoomOut', { zooRate: 1.2, enableTransition: false }) } } const handleActions = (action: any, option = {}) => { // 各类指令操作 const { zoomRate, rotateDeg, enableTransition } = { // 定义常规特效 zoomRate: 1.4, rotateDeg: 90, enableTransition: true, ...option, } switch (action) { case 'zoomOut': // 缩小 if (data.transform.scale > 0.2) data.transform.scale = parseFloat((data.transform.scale / zoomRate).toFixed(3)) break; case 'zoomIn': // 放大 if (data.transform.scale < 6) data.transform.scale = parseFloat((data.transform.scale * zoomRate).toFixed(3)) break; case 'anticlockwise': // 逆时针旋转 data.transform.deg -= rotateDeg break; case 'clockwise': // 顺时针旋转 data.transform.deg += rotateDeg break; } data.transform.enableTransition = enableTransition } const resetStyle = () => { // 左右切换重置transform对象 data.transform = { scale: 1, deg: 0, enableTransition: false } } const handleMediaLoad = (e: event) => { // 加载处理 data.loading = false } const handleMediaError = () => { // 图片 音频失败处理 data.loading = false } const handlePlayError = (index: number) => { // 视频失败处理 props.urlList[index] = props.urlList[index].split('?')[0] + '?v=' + new Date().getTime() } const download = (url: string) => { // 文件类型抛出url emit('download', url) } const toggleMode = () => { if (data.loading) return const modeNames = Object.keys(modes) const modeValues = Object.values(modes) const currentMode = mode.value.name const index = modeValues.findIndex((i) => i.name === currentMode) const nextIndex = (index + 1) % modeNames.length mode.value = modes[modeNames[nextIndex]] resetStyle() } const handleMouseDown = (e: MouseEvent) => { // 处理鼠标按下事件 data.transform.enableTransition = false const { offsetX, offsetY } = data.transform const startX = e.pageX const startY = e.pageY // 拖拽事件 const dragHandler = (ev: MouseEvent) => { data.transform = { ...data.transform, offsetX: offsetX + ev.pageX - startX, offsetY: offsetY + ev.pageY - startY, } } // 添加鼠标移动事件监听 const removeMousemove = useEventListener(document, 'mousemove', dragHandler) useEventListener(document, 'mouseup', () => { removeMousemove() }) e.preventDefault() } watch(() => props.imgIndex, () => { data.index = props.imgIndex }, { immediate: true }) onMounted(() => { // 优化注册事件监听 useEventListener(document, 'keydown', keydownHandler) useEventListener(document, mousewheelEventName, mousewheelHandler) imageViewer.value?.focus?.() }) return { ...toRefs(data), computedZIndex, mediaStyle, isFileType, isSingle, isFirst, isLast, mode, icons, hide, prev, next, handleImgLoad, handleActions, handleMediaLoad, handlePlayError, handleMediaError, toggleMode, download, handleMouseDown } }})</script><style lang="scss" scoped>:deep(.el-image-viewer__canvas) { //height: calc(100% - 180px); align-items: center; height: calc(100% - 40px);}:deep(.el-image-viewer__actions) { height: 40px; right: 50px; top: 0; left: auto; z-index: inherit; transform: none; padding: 0; background: none; border-radius: 0;}:deep(.el-image-viewer__actions__divider) { display: none;}:deep(.el-image-viewer__next) { width: 64px; height: 100px; background: transparent; border-radius: 6px 0px 0px 6px; opacity: 0.8; right: 0; top: 45%; .el-icon { color: #999999; font-size: 50px; }}:deep(.el-image-viewer__prev) { width: 64px; height: 100px; background: transparent; border-radius: 0px 6px 6px 0px; opacity: 0.8; left: 0; top: 45%; .el-icon { color: #999999; font-size: 50px; }}.el-image-viewer__next:hover,.el-image-viewer__prev:hover { background: #000000; opacity: 1;}:deep(.el-image-viewer__mask) { opacity: 0.8;}:deep(.el-image-viewer__close) { height: 40px; z-index: 56; top: 0; right: 10px; background: transparent;}.image-viewer-carousel { height: 140px; background: #000000; z-index: 50; padding: 20px; position: relative; display: flex; flex-wrap: wrap;}.image-viewer-header { background-color: #000000; box-sizing: border-box; border-spacing: 0; width: 100%; height: 40px; z-index: 55; position: relative; z-index: 55; color: #FFFFFF; text-align: center; line-height: 40px; .header-left { position: absolute; }}.header-file-icon { width: 24px; height: 50px; display: inline-block; margin-left: 20px; background-image: url(../../../../assets/svg/image-icon.svg); background-repeat: no-repeat; background-position: 50% 50%; /*这个是按从左往右,从上往下的百分比位置进行调整*/ background-size: 100% 60%; /*按比例缩放*/}.header-file-name { overflow: hidden; text-overflow: ellipsis; display: inline-block; vertical-align: top; margin: 0 20px;}.image-viewer-tips { background-color: rgba(0, 0, 0, 0.8); color: #fff; box-sizing: border-box; display: inline-block; vertical-align: middle; text-align: center; min-width: 490px; padding: 35px 100px; line-height: 2em; border-radius: 5px; z-index: 56;}.image-unknown-file-type-view { display: inline-block; width: 96px; height: 96px; background-size: contain; background: url(../../../../assets/svg/file-icon.svg); background-repeat: no-repeat; background-position: center center; background-size: contain;}.image-viewer-download { box-sizing: border-box; background: #f5f5f5; border: 1px solid #ccc; border-radius: 3.01px; color: #333; cursor: pointer; display: inline-block; font-family: inherit; font-size: 14px; font-variant: normal; font-weight: 400; height: 2.14285714em; line-height: 1.42857143; margin: 0; padding: 4px 10px; vertical-align: baseline; white-space: nowrap; text-decoration: none; margin: 20px 10px 0 10px; .icon-download { position: relative; top: 4px; background: url(../../../../assets/svg/download.svg) no-repeat 0 0; border: none; margin: 0; padding: 0; text-indent: -999em; vertical-align: text-bottom; display: inline-block; text-align: left; line-height: 0; position: relative; vertical-align: text-top; height: 16px; width: 16px; }}</style>
本文链接地址:https://www.jiuchutong.com/zhishi/288086.html 转载请保留说明!

上一篇:正在宣泄不满的两只巴布亚企鹅,南极洲 (© Grafissimo/Getty Images)(宣泄不能消除不健康的情绪)

下一篇:前端男神尤雨溪传奇

  • 电脑密码忘记了怎么解开(电脑密码忘记了找人解开要多少钱?)

    电脑密码忘记了怎么解开(电脑密码忘记了找人解开要多少钱?)

  • 联通手机上网慢解决(联通手机上网慢)(联通手机上网网速慢)

    联通手机上网慢解决(联通手机上网慢)(联通手机上网网速慢)

  • 苹果11nfc感应在哪唤醒(iphone11 nfc感应)

    苹果11nfc感应在哪唤醒(iphone11 nfc感应)

  • 支付宝怎么买电影票(支付宝怎么买电话卡)

    支付宝怎么买电影票(支付宝怎么买电话卡)

  • 淘宝发货不揽收怎么办(淘宝发货不揽收会自动收货吗)

    淘宝发货不揽收怎么办(淘宝发货不揽收会自动收货吗)

  • 笔记本电脑一直自动输入点点(笔记本电脑一直在开机界面进不去)

    笔记本电脑一直自动输入点点(笔记本电脑一直在开机界面进不去)

  • 小米mdt6是什么型号

    小米mdt6是什么型号

  • 对方通过名片分享添加 查谁分享的(对方通过名片分享添加我微信)

    对方通过名片分享添加 查谁分享的(对方通过名片分享添加我微信)

  • 键盘音效怎么设置(键盘音效怎么设置小米)

    键盘音效怎么设置(键盘音效怎么设置小米)

  • 什么软件可以去视频水印(什么软件可以去掉卷子上的答案)

    什么软件可以去视频水印(什么软件可以去掉卷子上的答案)

  • oppor11系统升级后没声音(oppor11手机升级)

    oppor11系统升级后没声音(oppor11手机升级)

  • 小米10x和10xpro区别(小米10x和小米10xpro区别)

    小米10x和10xpro区别(小米10x和小米10xpro区别)

  • 华为mate10怎么隐藏应用(华为mate10怎么隐藏桌面图标)

    华为mate10怎么隐藏应用(华为mate10怎么隐藏桌面图标)

  • 双十一用红包的钱可以退款吗(双十一用红包的订单退款了怎么办)

    双十一用红包的钱可以退款吗(双十一用红包的订单退款了怎么办)

  • 数据线越长充电越慢是真的吗(数据线越长越耗电吗)

    数据线越长充电越慢是真的吗(数据线越长越耗电吗)

  • ipadpro2020发布时间(ipadpro2020发布时多少钱)

    ipadpro2020发布时间(ipadpro2020发布时多少钱)

  • 微信发30秒视频怎么发朋友圈(微信发30秒视频不用微视)

    微信发30秒视频怎么发朋友圈(微信发30秒视频不用微视)

  • pcle固态和ssd什么区别(ssd pcle区别)

    pcle固态和ssd什么区别(ssd pcle区别)

  • 三星evo橙卡和红卡区别(三星橙卡为什么贵)

    三星evo橙卡和红卡区别(三星橙卡为什么贵)

  • 苹果5sa1533支持什么网络(iphone5sa1533可以4g吗)

    苹果5sa1533支持什么网络(iphone5sa1533可以4g吗)

  • 开机自启项在哪里设置(开机自启动怎么打开)

    开机自启项在哪里设置(开机自启动怎么打开)

  • 手机变听筒模式怎么办

    手机变听筒模式怎么办

  • 什么是pathon语言(pathonize)

    什么是pathon语言(pathonize)

  • lgv30美版便宜的原因

    lgv30美版便宜的原因

  • m和mb流量哪个大(m和mb流量哪个大些)

    m和mb流量哪个大(m和mb流量哪个大些)

  • 铃声多多怎么弄到本地(铃声多多怎么弄视频铃声)

    铃声多多怎么弄到本地(铃声多多怎么弄视频铃声)

  • struts2漏洞(struts2漏洞检测工具下载)

    struts2漏洞(struts2漏洞检测工具下载)

  • python中使用slice函数(python slic)

    python中使用slice函数(python slic)

  • 公司车保险费用高么
  • 外省人员收入怎么查
  • 出口货物退免税管理办法
  • 发票没金额能开吗
  • 住宿报销是开普票还是专票
  • 失业保险稳岗返还是政府补助吗
  • 暂估资产会计处理
  • 商品房的销售方式有哪些
  • 无形资产应当按照公允价值进行初始计量吗
  • 现金折扣退回会计分录
  • 车辆报废收入如何处理
  • 单位租用个人住房合法吗
  • 个体工商户给员工交社保流程
  • 营改增的增值税税率
  • 劳务费税率公式是什么
  • 开发经济适用房是否需要缴纳土增税
  • 企业支付在哪里
  • 应计入增值税一般纳税人认定标准的“年应税销售额”
  • 进口增值税内销可以抵扣吗
  • 建筑公司一般纳税人增值税税率
  • 装修改造空调尾板多少钱
  • 资本公积金转增资本属于所有者权益吗
  • 全年物业费需要待摊吗
  • 增值税逾期申报流程
  • 其他应付款调整到其他应收款
  • 增值税专用发票有几联?
  • 20个健康生活常识
  • 苹果14出来13会下架吗
  • 深度linux使用入门教程
  • 存货盘盈如何记账
  • uni-app开发教程
  • 商品流通企业的经营过程分为
  • 退回以前年度多交的附加税怎么做分录
  • php备份mysql数据库
  • 员工多交的个人社保
  • 申请退税怎么算
  • vue角色管理
  • HTML+CSS+JS+Jquery+练手项目+...合集(前端学习必备,持续更新中...)
  • ai训练流程
  • 前端开发工程师是干嘛的
  • 如何防止sql注入 java
  • hexdump windows
  • 燃气管道安装费和暖气管道安装费两个的欠条怎么写
  • 债权转让收益需缴税吗
  • wordpress技巧
  • python insert方法
  • 公司给员工交社保对公司有什么好处
  • 换电脑了怎么办
  • Win10 64位安装个人版SQL2000图文教程
  • 补提去年企业所得税会计分录
  • 预收账款是怎么算的
  • 小规模企业房产税减按50%
  • 低值易耗品摊销借贷方向
  • 预付在建工程款的会计分录
  • 用于出租的设备属于什么资产
  • 预收货款转营业外收入
  • 新成立的公司需要年报吗
  • centos7.6怎么安装
  • 数据库left joins
  • mysql分页性能
  • 清华同方bios通用密码(thtfpc)
  • usbmmkbd.exe - usbmmkbd是什么进程
  • 如何ie8升级到10
  • qtask.exe - qtask是什么进程 有什么用
  • win8windows设置在哪里
  • win102021年1月大更新
  • 横版格斗rpg手游
  • Node.js中的什么模块是用于处理文件和目录的
  • Linux修改tomcat默认端口号8080
  • vue请求数据再赋值data
  • jquery取消单选框选中
  • 出口退税全流程
  • 山西省国家税务局王旭斌局长
  • 装卸搬运服务是什么服务
  • 顺丰快递的开票历史如何删除
  • 进口完税价格包括哪些部分
  • 广东省税务机关规定小额零售需开发票吗
  • 四川省地方税务局关于城镇土地使用税困难减免
  • 国,地税纳税申报表是什么
  • pcie3.0*4接口
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设