位置: IT常识 - 正文

vue里面使用pdfjs-dist+fabric实现pdf电子签章!!!(vue使用pdf.js)

编辑:rootadmin
vue里面使用pdfjs-dist+fabric实现pdf电子签章!!!

推荐整理分享vue里面使用pdfjs-dist+fabric实现pdf电子签章!!!(vue使用pdf.js),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:Vue里面使用定时器尖头函数,vue3.0 pdf,vue3 pdf,vue3 pdf,vue3 pdf,vue pdf,Vue里面使用定时器尖头函数,Vue里面使用定时器尖头函数,内容如对您有帮助,希望把文章链接给更多的朋友!

2022.9.6

一、需求

最近领导提了一个新需求:仿照e签宝,实现pdf电子签章!

最终实现效果图

这是做出来的效果图,当然还有很多待修改

二、思路

然后我就去看了下人家e签宝的操作界面,左侧是印章,右侧是pdf,然后拖拽印章到pdf上面,点击保存,下次打开时显示印章的位置。 思路:我首先想到了拖拽、pdf预览、坐标;分工明确,前端来实现拖拽,pdf预览及把印章信息和坐标传给后端,后端只需要把信息和坐标保存下来就可以了。

三、使用插件

之前实现pdf预览就是通过window.open,打开一个窗口,显示pdf,功能很多,但是和需求不符,需要做的事是把pdf显示出来,同时可以可以拖拽印章到上面去,也不要放大与缩小及其他的功能。百度下了,说是用pdfjs-dist,这个pdf插件可以自定义很多的功能,但是实际用起来,发现好坑。。最后去百度了下,vue实现pdf电子签章, 看有没有现成的,然后还真找到了一个。js处理pdf展示、分页和签章等功能,下载到本地(只许查看index.html文件即可)后发现大佬用的不是vue-cli脚手架,是引用的cdn链接,然后就cv到项目里面了,跟着步骤,安装了pdfjs-dist插件(pdf插件)和fabric插件(专门处理印章的插件)这两个插件,但是项目本地运行后,报错了。。

四、遇到的问题vue里面使用pdfjs-dist+fabric实现pdf电子签章!!!(vue使用pdf.js)

1.TypeError: Cannot read properties of undefined( reading 'Globalworkeroptions ')

百思不得其解啊,照着步骤来的啊,为啥呢,然后又回去看了下大佬的代码,发现他的pdf.js不是用的pdfjs-dist,而是引入的pdf的cdn链接 然后我就在项目的public/index.html下面引入这个链接 pdf路径则是使用的一个在线的pdf链接,https://www.gjtool.cn/pdfh5/git.pdf,发现可以打开了(样式做了些修改) 2.Dev Tools failed to load source map: Could not load content for https //mozilla github.ia/pdf js/build/pdf js map: Load canceled due to loadtimeout 开始觉得似乎已经大功告成了,到时候和后端商量下返回数据的格式就完事了的,谁知道还是有问题的。。多次打开关闭pdf后,有时候pdf会不加载出来了。人麻了,然后看了下提示,加载超时了,取消加载。 明显是cdn链接的问题,那就把pdf.js文件下载到本地呗,本地加载快,应该不会出现加载超时的问题,结果还是有问题。 唉,真的是服了,使用cdn链接吧,会加载超时,下载到本地引用吧,又会报这么个莫名其妙的问题,然后今天浏览博客时,发现一个兄弟碰到了一样的问题,哈哈,发现还是引入pdf方式的问题

/* 引用cdn链接,可以使用但会加载超时 */// let pdfjsLib = window["pdfjs-dist/build/pdf"];// pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://mozilla.github.io/pdf.js/build/pdf.worker.js';/* 下载到本地,看着官方文档引用,报个莫名其妙的错 */// import pdfjsLib from 'pdfjs-dist';// pdfjsLib.GlobalWorkerOptions.workerSrc='pdfjs-dist/build/pdf.worker.js';/* 下载到本地,照着大佬的方式引用,完美! */let pdfjsLib =require("pdfjs-dist/legacy/build/pdf.js");import workerSrc from "pdfjs-dist/legacy/build/pdf.worker.entry";pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc;

这是大佬的博客链接pdf.js 使用攻略及错误集合

不过这项目的电子签章有些与众不同,用户打开的pdf,是可以自定义的,即用户打开弹窗,在tinymac编辑器里面输入内容,然后切换tab,会立即生成一个pdf,接下来才是用户使用电子签章的过程

以下是电子签章的主要代码,和大佬的index.html的代码差不多,就是做了点修改(ps:目前印章的位置和坐标保存,使用的得本地缓存,便于调试,后期会保存到接口里面!)

代码部分

首先 引入pdfjs-dist插件和fabric插件

npm install pdfjs-distnpm i fabric --save

html部分

<div id="elesign" class="elesign"><el-row> <el-col :span="4" style="margin-top:1%;"> <div class="left-title">我的印章</div> <draggable v-model="mainImagelist" :group="{ name: 'itext', pull: 'clone' }" :sort="false" @end="end"> <transition-group type="transition"> <li v-for="item in mainImagelist" :key="item" class="item" style="text-align:center;"> <img :src="item" width="100%;" height="100%" class="imgstyle" /> </li> </transition-group> </draggable> </el-col> <el-col :span="16" style="text-align:center;" class="pCenter"> <div class="page"> <!-- <el-button class="btn-outline-dark" @click="zoomIn">-</el-button> <span style="color:red;">{{(percentage*100).toFixed(0)+'%'}}</span> <el-button class="btn-outline-dark" @click="zoomOut">+</el-button> --> <el-button class="btn-outline-dark" @click="prevPage">上一页</el-button> <el-button class="btn-outline-dark" @click="nextPage">下一页</el-button> <el-button class="btn-outline-dark">{{ pageNum }}/{{ numPages }}页</el-button> <el-input-number style="margin:0 5px;border-radius:5px;" class="btn-outline-dark" v-model="pageNum" :min="1" :max="numPages" label="输入页码"></el-input-number> <el-button class="btn-outline-dark" @click="cutover">跳转</el-button> </div> <canvas id="the-canvas" /> <!-- 盖章部分 --> <canvas id="ele-canvas"></canvas> <div class="ele-control" style="margin-bottom:2%;"> <el-button class="btn-outline-dark" @click="removeSignature"> 删除签章</el-button> <el-button class="btn-outline-dark" @click="clearSignature"> 清除所有签章</el-button> <el-button class="btn-outline-dark" @click="submitSignature">提交所有签章信息</el-button> </div> </el-col> <el-col :span="4" style="margin-top:1%;"> <div class="left-title">任务信息</div> <div style="text-align:center;"> <div> <div class="right-item"> <div class="right-item-title">文件主题</div> <div class="detail-item-desc">{{ taskInfo.title }}</div> </div> <div class="right-item"> <div class="right-item-title">发起方</div> <div class="detail-item-desc">{{ taskInfo.uname }}</div> </div> <div class="right-item"> <div class="right-item-title">截止时间</div> <div class="detail-item-desc">{{ taskInfo.endtime }}</div> </div> </div> </div> </el-col> </el-row></div>

js部分

import {fabric} from 'fabric';let pdfjsLib =require("pdfjs-dist/legacy/build/pdf.js");import workerSrc from "pdfjs-dist/legacy/build/pdf.worker.entry";pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc;import draggable from "vuedraggable";export default { components: {draggable}, data() { return { //pdf预览 pdfUrl: '', pdfDoc: null, numPages: 1, pageNum: 1, scale: 2.2, pageRendering: false, pageNumPending: null, sealUrl: '', signUrl: '', canvas: null, ctx: null, canvasEle: null, whDatas: null, mainImagelist: [], taskInfo: {}, } }, computed: { hasSigna() { return this.canvasEle && this.canvasEle.getObjects()[0] ? true : false; }, }, created(){ var that = this; that.mainImagelist = [require('./sign.png'),require('./seal.png')]; that.taskInfo = {'title':'测试盖章', uname:'张三', endtime:'2021-09-01 17:59:59'}; }, methods: { //pdf预览 // zoomIn() { // console.log("缩小"); // if(this.scale<=0.5){ // this.$message.error("已经显示最小比例") // }else{ // this.scale-=0.1; // this.percentage-=0.1; // this.renderPage(this.pageNum); // this.renderFabric(); // } // }, // zoomOut() { // console.log("放大") // if(this.scale>=2.2){ // this.$message.error('已经显示最大比例') // }else{ // this.scale+=0.1; // this.percentage+=0.1; // this.renderPage(this.pageNum); // this.renderFabric(); // } // }, renderPage(num) { let _this = this; this.pageRendering = true; return this.pdfDoc.getPage(num).then((page) => { let viewport = page.getViewport({ scale: _this.scale });//设置视口大小 _this.canvas.height = viewport.height; _this.canvas.width = viewport.width; // Render PDF page into canvas context let renderContext = { canvasContext: _this.ctx, viewport: viewport, }; let renderTask = page.render(renderContext); // Wait for rendering to finish renderTask.promise.then(() => { _this.pageRendering = false; if (_this.pageNumPending !== null) { // New page rendering is pending this.renderPage(_this.pageNumPending); _this.pageNumPending = null; } }); }); }, queueRenderPage(num) { if (this.pageRendering) { this.pageNumPending = num; } else { this.renderPage(num); } }, prevPage() { this.confirmSignature(); if (this.pageNum <= 1) { return; } this.pageNum--; }, nextPage() { this.confirmSignature(); if (this.pageNum >= this.numPages) { return; } this.pageNum++; }, cutover() { this.confirmSignature(); }, //渲染pdf,到时还会盖章信息,在渲染时,同时显示出来,不应该在切换页码时才显示印章信息 showpdf(pdfUrl) { let caches = JSON.parse(localStorage.getItem('signs')); //获取缓存字符串后转换为对象 console.log(caches); if(caches == null) return false; let datas = caches[this.pageNum]; if(datas != null && datas != undefined) { for (let index in datas) { this.addSeal(datas[index].sealUrl, datas[index].left, datas[index].top, datas[index].index); } } this.canvas = document.getElementById("the-canvas"); this.ctx = this.canvas.getContext("2d"); pdfjsLib.getDocument({url:pdfUrl, rangeChunkSize:65536, disableAutoFetch:false}).promise.then((pdfDoc_) => { this.pdfDoc = pdfDoc_; this.numPages = this.pdfDoc.numPages; this.renderPage(this.pageNum).then(() => { this.renderPdf({ width: this.canvas.width, height: this.canvas.height, }); }); this.commonSign(this.pageNum, true); }); }, /** * 盖章部分开始 */ // 设置绘图区域宽高 renderPdf(data) { this.whDatas = data; // document.querySelector("#elesign").style.width = data.width + "px"; }, // 生成绘图区域 renderFabric() { let canvaEle = document.querySelector("#ele-canvas"); let pCenter=document.querySelector(".pCenter"); canvaEle.width = pCenter.clientWidth; // canvaEle.height = (this.whDatas.height)*(this.scale); canvaEle.height = this.whDatas.height; this.canvasEle = new fabric.Canvas(canvaEle); let container = document.querySelector(".canvas-container"); container.style.position = "absolute"; container.style.top = "50px"; // container.style.left = "30%"; }, // 相关事件操作哟 canvasEvents() { // 拖拽边界 不能将图片拖拽到绘图区域外 this.canvasEle.on("object:moving", function (e) { var obj = e.target; // if object is too big ignore if(obj.currentHeight > obj.canvas.height || obj.currentWidth > obj.canvas.width){ return; } obj.setCoords(); // top-left corner if(obj.getBoundingRect().top < 0 || obj.getBoundingRect().left < 0){ obj.top = Math.max(obj.top, obj.top-obj.getBoundingRect().top); obj.left = Math.max(obj.left, obj.left-obj.getBoundingRect().left); } // bot-right corner if(obj.getBoundingRect().top+obj.getBoundingRect().height > obj.canvas.height || obj.getBoundingRect().left+obj.getBoundingRect().width > obj.canvas.width){ obj.top = Math.min(obj.top, obj.canvas.height-obj.getBoundingRect().height+obj.top-obj.getBoundingRect().top); obj.left = Math.min(obj.left, obj.canvas.width-obj.getBoundingRect().width+obj.left-obj.getBoundingRect().left); } }); }, // 添加公章 addSeal(sealUrl, left, top, index) { fabric.Image.fromURL( sealUrl, (oImg) => { oImg.set({ left: left, top: top, // angle: 10, scaleX: 0.8, scaleY: 0.8, index:index, }); // oImg.scale(0.5); //图片缩小一 this.canvasEle.add(oImg); } ); }, // 删除签章 removeSignature() { this.canvasEle.remove(this.canvasEle.getActiveObject()) }, //翻页展示盖章信息 commonSign(pageNum, isFirst = false) { if(isFirst == false) this.canvasEle.remove(this.canvasEle.clear()); //清空页面所有签章 let caches = JSON.parse(localStorage.getItem('signs')); //获取缓存字符串后转换为对象 console.log(caches); if(caches == null) return false; let datas = caches[this.pageNum]; if(datas != null && datas != undefined) { for (let index in datas) { this.addSeal(datas[index].sealUrl, datas[index].left, datas[index].top, datas[index].index); } } }, //确认签章位置并保存到缓存 confirmSignature() { let data = this.canvasEle.getObjects(); //获取当前页面内的所有签章信息 let caches = JSON.parse(localStorage.getItem('signs')); //获取缓存字符串后转换为对象 let signDatas = {}; //存储当前页的所有签章信息 let i = 0; // let sealUrl = ''; for(var val of data) { signDatas[i] = { width: val.width, height: val.height, top: val.top, left: val.left, angle: val.angle, translateX: val.translateX, translateY: val.translateY, scaleX: val.scaleX, scaleY: val.scaleY, pageNum: this.pageNum, sealUrl: this.mainImagelist[val.index], index:val.index } i++; } if(caches == null) { caches = {}; caches[this.pageNum] = signDatas; } else { caches[this.pageNum] = signDatas; } localStorage.setItem('signs', JSON.stringify(caches)); //对象转字符串后存储到缓存 }, //提交数据 submitSignature() { this.confirmSignature(); let caches = localStorage.getItem('signs'); console.log(JSON.parse(caches)); return false }, //清空数据 clearSignature() { this.canvasEle.remove(this.canvasEle.clear()); //清空页面所有签章 localStorage.removeItem('signs'); //清除缓存 }, end(e){ this.addSeal(this.mainImagelist[e.newDraggableIndex], e.originalEvent.layerX, e.originalEvent.layerY, e.newDraggableIndex) }, //设置PDF预览区域高度 setPdfArea(){ this.pdfUrl = 'https://www.gjtool.cn/pdfh5/git.pdf'; this.pdfurl=res.data.data.pdfurl; this.$nextTick(() => { this.showpdf(this.pdfUrl);//接口返回的应该还有盖章信息,不只是pdf }); }, }, watch: { whDatas: { handler() { const loading = this.$loading({ lock: true, text: 'Loading', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' }); if (this.whDatas) { console.log(this.whDatas) loading.close(); this.renderFabric(); this.canvasEvents(); let eleCanvas=document.querySelector("#ele-canvas"); eleCanvas.style="border:1px solid #5ea6ef"; } }, }, pageNum: function() { this.commonSign(this.pageNum); this.queueRenderPage(this.pageNum); } }},

css部分

<style scoped>/*pdf部分*/.pCenter{ overflow-x: hidden;}#the-canvas{ margin-top:10px;}html:fullscreen { background: white;}.elesign { display: flex; flex: 1; flex-direction: column; position: relative; /* padding-left: 180px; */ margin: auto; /* width:600px; */}.page { text-align:center; margin:0 auto; margin-top: 1%;}#ele-canvas { /* border: 1px solid #5ea6ef; */ overflow: hidden;}.ele-control { text-align: center; margin-top: 3%;}#page-input { width: 7%;}@keyframes ani-demo-spin { from { transform: rotate(0deg);} 50% { transform: rotate(180deg);} to { transform: rotate(360deg);}}/* .loadingclass{ position: absolute; top:30%; left:49%; z-index: 99;} */.left { position: absolute; top: 42px; left: -5px; padding: 5px 5px; /*border: 1px solid #eee;*/ /*border-radius: 4px;*/}.left-title { text-align:center; padding-bottom: 10px; border-bottom: 1px solid #eee;}li { list-style-type:none; padding: 10px;}.imgstyle{ vertical-align: middle; width: 130px; border: solid 1px #e8eef2; background-image: url("tuo.png"); background-repeat:no-repeat;}.right { position: absolute; top: 7px; right: -177px; margin-top: 34px; padding-top: 10px; padding-bottom: 20px; width: 152px; /*border: 1px solid #eee;*/ /*border-radius: 4px;*/}.right-item { margin-bottom: 15px; margin-left: 10px;}.right-item-title { color: #777; height: 20px; line-height: 20px; font-size: 12px; font-weight: 400; text-align: left !important;}.detail-item-desc { color: #333; line-height: 20px; width: 100%; font-size: 12px; display: inline-block; text-align: left;}.btn-outline-dark { color: #0f1531; background-color: transparent; background-image: none; border:1px solid #3e4b5b;}.btn-outline-dark:hover { color: #fff; background-color: #3e4b5b; border-color: #3e4b5b;}

2022.9.13修改 使用的时候,发现在pdf第一页添加的印章,下次再打开时,不在显示,本地缓存的也是显示{},所以琢磨了下,应该是每次打开pdf页面重置了,代码做了以下修改

将选中部分改为以下代码

本文链接地址:https://www.jiuchutong.com/zhishi/283804.html 转载请保留说明!

上一篇:windows7旗舰版系统注册表被篡改的解决方法(win7旗舰版叫啥)

下一篇:若依框架图片上传、富文本框编辑器功能(若依框架讲解)

  • bilibili查看已删除视频(b站查看已删除视频)

    bilibili查看已删除视频(b站查看已删除视频)

  • 腾讯会议视频黑屏的原因(腾讯会议视频黑色)

    腾讯会议视频黑屏的原因(腾讯会议视频黑色)

  • qplay找不到设备(qplay 找不到)

    qplay找不到设备(qplay 找不到)

  • 荣耀8x和8xmax怎么选(荣耀8x和8x max)

    荣耀8x和8xmax怎么选(荣耀8x和8x max)

  • 华为bac一al00是什么型号(华为bac一al00什么型号)

    华为bac一al00是什么型号(华为bac一al00什么型号)

  • 苹果xmax电池百分比怎么显示(苹果xmax电池百分比怎么调出来)

    苹果xmax电池百分比怎么显示(苹果xmax电池百分比怎么调出来)

  • 光猫可以连接两个路由器吗(光猫可以连接两台电视吗)

    光猫可以连接两个路由器吗(光猫可以连接两台电视吗)

  • 华为小艺怎么改名字(华为小艺怎么改语言)

    华为小艺怎么改名字(华为小艺怎么改语言)

  • 快手发说说仅24小时怎么修改(快手说说最多能发多少字)

    快手发说说仅24小时怎么修改(快手说说最多能发多少字)

  • 微信京东怎么退款流程(微信京东怎么退货退款京东申请退款流程)

    微信京东怎么退款流程(微信京东怎么退货退款京东申请退款流程)

  • 为什么电脑手写板用不了(为什么电脑手写输入不能用)

    为什么电脑手写板用不了(为什么电脑手写输入不能用)

  • 苹果相机声音怎么设置(iphone相机声音怎么调整大小)

    苹果相机声音怎么设置(iphone相机声音怎么调整大小)

  • oppo手机怎么设置定位(oppo手机怎么设置锁屏)

    oppo手机怎么设置定位(oppo手机怎么设置锁屏)

  • 如何保存哔哩哔哩音频(如何保存哔哩哔哩视频封面)

    如何保存哔哩哔哩音频(如何保存哔哩哔哩视频封面)

  • 红米k20会加入dc调光吗(red米k20)

    红米k20会加入dc调光吗(red米k20)

  • 无线网需认证什么意思(无线网需要认证是什么)

    无线网需认证什么意思(无线网需要认证是什么)

  • 皮皮搞笑怎么去水印(皮皮搞笑怎么去掉IP地址)

    皮皮搞笑怎么去水印(皮皮搞笑怎么去掉IP地址)

  • 抖音里面人消失是用的什么软件(抖音里的人突然消失)

    抖音里面人消失是用的什么软件(抖音里的人突然消失)

  • 闲鱼的钱怎么提现(闲鱼的钱怎么提现到支付宝)

    闲鱼的钱怎么提现(闲鱼的钱怎么提现到支付宝)

  • 怎样导出手机中的QQ聊天记录(怎样导出手机中的照片视频教程)

    怎样导出手机中的QQ聊天记录(怎样导出手机中的照片视频教程)

  • vivox27电池容量(vivox27属于什么档次)

    vivox27电池容量(vivox27属于什么档次)

  • ipad教育优惠送耳机(ipad教育优惠送的耳机是几代)

    ipad教育优惠送耳机(ipad教育优惠送的耳机是几代)

  • hd2是什么意思(hd2是什么意思怎么关vivo的手机)

    hd2是什么意思(hd2是什么意思怎么关vivo的手机)

  • 红米note5a高配和标配的区别(红米note5a高配和标准版屏幕通用吗)

    红米note5a高配和标配的区别(红米note5a高配和标准版屏幕通用吗)

  • uniapp及uniCloud开发中经常会出现的问题汇总(uni-app开发教程)

    uniapp及uniCloud开发中经常会出现的问题汇总(uni-app开发教程)

  • 增值税的账务处理办法
  • 员工买东西自己垫付的钱怎么做账
  • 比较优先股和普通股
  • 为客户购买的机票怎么退
  • 与企业日常无关的政府补服属于利得吗
  • 代订餐如何赚钱
  • 社保计入主营业务成本
  • 资本公积和实收资本是什么意思
  • 个税换电脑了还能查到之前的申报记录吗?
  • 劳务派遣怎么做起来
  • 服务业如何确定收入
  • 1元换购怎么做账
  • 研发费用加计扣除留存备查资料
  • 进口免税店的东西都是正品吗
  • 预缴增值税为什么记借方
  • 联营商品如何做账
  • 发行股票购买资产并募集是利好吗
  • 缴纳企业所得税怎么计算
  • 企业所得税收入大于增值税收入的原因
  • 印花税必须每个月零申报吗
  • 天猫开发票需要确认收货吗?
  • 工程多少金额需要投标
  • 购买国债逆回购有股东限制
  • 员工差旅补贴是否需要发票
  • 工程施工纳税
  • 发票开了收不到钱
  • linux 界面卡死
  • 无法访问移动网络怎么办
  • 调整会计分录是什么
  • 施工企业工程结算
  • 委托代销委托方需要开发票吗
  • 定期支票丢了能取钱吗
  • php foo
  • 开发产品完工结转
  • 贝纳克波斯图干红葡萄酒
  • php将数据导出到excel
  • 公司股东年底分红怎么做账
  • 支付宝提现到对公账户怎么做账
  • less中使用js变量
  • 【K210】K210学习笔记七——使用K210拍摄照片并在MaixHub上进行训练
  • 帝国cms怎么上传图片
  • 应该如何打造自己
  • 法定的盈余公积是什么
  • 公司垫付的工伤医药费怎么报销
  • sql执行顺序优先级
  • mysql索引和主键
  • 营改增小规模纳税人认定标准
  • 个人所得缴税租金怎么算
  • 用友应收系统凭证冲销后查询不到怎么办
  • access 替换
  • 跨年发票能不能用
  • 货物出口销售确认流程
  • 银行承兑汇票到期未承兑怎么办
  • 营业外收入在汇算清缴里填哪张表
  • 一般纳税人增值税减免政策2023
  • 国外工程合同类型
  • 公司固定资产报废申请范文
  • 结转费用时财务费用为负
  • 待抵扣进项税额什么意思
  • 销项发票怎么导出的是压缩包怎么打开
  • 销售方运输收入分录
  • 小规模纳税人季报网上申报流程
  • xbox无法连接无线网络
  • win7资源管理器怎么打开
  • windows8使用教程
  • linux修改时间和日期的方法
  • windows中创建的任务计划可以多久执行一次任务?
  • win10天气系统怎么弄掉
  • 铁嘴银牙剧情简介
  • node.js入门
  • javascript运用
  • android floatbutton
  • angular script
  • jquery加载图片
  • npm与nodejs
  • 原生js常用的方法
  • 北京电子税务
  • 税务系统运维应急预案范文
  • 云南省国家税务局网上办税服务厅
  • 小规模税控盘全额抵扣怎么做分录
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设