diff --git a/project.private.config.json b/project.private.config.json index 7eb50fb..7c2b919 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -23,13 +23,27 @@ "miniprogram": { "list": [ { - "name": "患者-我的复诊记录", - "pathName": "patient/pages/revisitRecord/index", + "name": "医生-海报", + "pathName": "doctor/pages/poster/index", "query": "", "scene": null, "launchMode": "default" }, { + "name": "医生-计算器", + "pathName": "doctor/pages/calculator/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "患者-我的复诊记录", + "pathName": "patient/pages/revisitRecord/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { "name": "医生-修改手机号", "pathName": "doctor/pages/changeTel/index", "query": "", diff --git a/src/app.json b/src/app.json index c96a7f5..0fffc79 100644 --- a/src/app.json +++ b/src/app.json @@ -61,8 +61,11 @@ "pages/login/index", "pages/index/index", "pages/my/index", + "pages/patientDetail/index", "pages/userInfo/index", - "pages/changeTel/index" + "pages/changeTel/index", + "pages/calculator/index", + "pages/poster/index" ] }, { diff --git a/src/components/calendar/index.wxml b/src/components/calendar/index.wxml index 3bf1244..186b758 100644 --- a/src/components/calendar/index.wxml +++ b/src/components/calendar/index.wxml @@ -45,8 +45,8 @@ +``` + +### 参数示例 + +```js +const data = { + params: { + info: { id: 'circle-note_mp', title: '动态页', desc: '指定动态的页面,mp' }, + body: { + bgImg: + 'https://picsissiok-10049618.cos.ap-shanghai.myqcloud.com/a74018e725861534f1644cff04780f4e_16273677601587.png', + width: '750', + height: '1254', + }, + elements: [ + { + id: 'circle-name', + desc: '圈名称', + type: 1, + text: '治疗讨论', + font: '宋体', + 'font-size': '42', + color: '#FFFFFF', + 'single-line': true, + halign: 'center', + top: '106', + }, + { + id: 'master-name', + desc: '专家名称', + type: 1, + text: '健康小贴士', + font: '宋体', + 'font-size': '38', + color: '#FFFFFF', + 'single-line': true, + halign: 'center', + top: '237', + }, + { + id: 'master-avatar', + desc: '专家头像', + type: 0, + picType: 'avatar', + src: 'https://picsissiok-10049618.cos.ap-shanghai.myqcloud.com/ceb31ac6137e873225b8cc826389a267_16655329078962.jpg', + width: '120', + height: '120', + halign: 'center', + shape: 'circle', + top: '328', + }, + { + id: 'note-content', + desc: '动态内容', + type: 1, + text: '11月5日上海组织进口博览会CIIE大会,邀请大家来我们的展馆现场交流。', + font: '宋体', + 'font-size': '45', + color: '#18191A', + width: '630', + height: '348', + left: '60', + top: '495', + }, + { + id: 'note-title', + desc: '长文标题', + type: 1, + text: '', + font: '宋体', + 'font-size': '42', + color: '#18191A', + width: '659', + height: '116', + left: '46', + top: '495', + 'font-weight': 'bold', + }, + { + id: 'note-summary', + desc: '长文摘要 ', + type: 1, + text: '', + font: '宋体', + 'font-size': '42', + color: '#18191A', + width: '672', + height: '232', + left: '39', + top: '646', + }, + { + id: 'note-more', + desc: '还有', + type: 1, + text: '还有3条精彩附件>', + font: '宋体', + 'font-size': '31', + color: '#666666', + 'single-line': true, + width: '292', + height: '46', + left: '60', + top: '1024', + }, + { + id: 'scanText', + desc: '扫描二维码查看详情', + type: 1, + text: '扫描二维码查看详情', + font: '宋体', + 'font-size': '31', + color: '#666666', + 'single-line': true, + width: '279', + height: '43', + left: '60', + top: '1087', + }, + { + id: 'note-qrcode', + desc: '专家列表的二维码', + type: 0, + picType: 'qrcode', + src: 'https://family.devred.hbraas.com/proxy/mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQHJ8TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyUWhDdGhTWVNlTjMxMDAwMDAwN3gAAgSonPxjAwQAAAAA', + width: '130', + height: '130', + left: '554', + top: '1012', + }, + { + id: 'brand', + desc: 'brand×华秉科技', + type: 1, + text: '做企业数字化转型的亲密伙伴×华秉科技', + font: '宋体', + 'font-size': '24', + color: '#B2B3B4', + 'single-line': true, + halign: 'center', + top: '1192', + }, + ], + }, +} +``` diff --git a/src/components/customPoster/index.js b/src/components/customPoster/index.js new file mode 100644 index 0000000..7344bf8 --- /dev/null +++ b/src/components/customPoster/index.js @@ -0,0 +1,101 @@ +import Wxml2Canvas from "./wxml2canvas/index.js"; // 根据具体路径修改,node_modules会被忽略 + +Component({ + properties: { + params: { + type: Object, + observer(newVal, _olVal) { + if (Object.keys(newVal).length > 0) { + this.paramsFormat(newVal); + this.setData({ + width: newVal.body.width, + height: newVal.body.height, + bgImg: newVal.body.bgImg, + elementsMp: newVal.elements, + }); + this.drawImage1(); + } + }, + }, + }, + data: { + imgUrl: "", + width: "", + height: "", + imgheight: "", + bgImg: "", + elementsMp: [], + }, + lifetimes: { + attached() {}, + }, + methods: { + paramsFormat(params) { + params.elements.forEach((item) => { + if (item.type === 0) { + // 图片居中 + if (item.halign === "center") { + item.left = (params.body.width - item.width) / 2; + } + } else { + item.text = item.text.replace(/[\r\n]/g, ""); + // 文字居中(使文字标签宽度等于画布宽度,文字加上居中的className) + if (item.halign === "center") { + item.width = item.width || params.body.width; + item.left = (params.body.width - item.width) / 2; + } + if (item.halign === "right") { + item.width = params.body.width; + } + if (item.id === "shareText" && item.text.length > 30) { + item.text = item.text.slice(0, 30) + "..."; + } + if (item.id === "note-title" && item.text.length > 28) { + item.text = item.text.slice(0, 28) + "..."; + } + if (item.id === "note-content" && item.text.length > 90) { + item.text = item.text.slice(0, 90) + "..."; + } + let len = params.elements.some((x) => x.id === "note-title" && x.text.length); + if (len && item.id === "note-summary" && item.text.length > 62) { + item.text = item.text.slice(0, 62) + "..."; + } else if (item.id === "note-summary" && item.text.length > 76) { + item.text = item.text.slice(0, 76) + "..."; + } + } + }); + }, + drawImage1() { + let self = this; + this.drawImage1 = new Wxml2Canvas({ + obj: self, + width: this.data.width, // 宽, 以iphone6为基准,传具体数值,其它机型自动适配 + height: this.data.height, // 高 + element: "canvas1", + background: "transparent", + progress(percent) {}, + finish(url) { + self.setData({ + imgUrl: url, + }); + self.triggerEvent("finish", url); + }, + error(res) {}, + }); + + let data = { + list: [ + { + type: "wxml", + class: "#canvas-bill-body-mp .draw_canvas", + limit: "#canvas-bill-body-mp", + x: 0, + y: 0, + }, + ], + }; + + this.drawImage1.draw(data); + }, + }, +}); diff --git a/src/components/customPoster/index.json b/src/components/customPoster/index.json new file mode 100644 index 0000000..a97367d --- /dev/null +++ b/src/components/customPoster/index.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} diff --git a/src/components/customPoster/index.scss b/src/components/customPoster/index.scss new file mode 100644 index 0000000..5dce05f --- /dev/null +++ b/src/components/customPoster/index.scss @@ -0,0 +1,53 @@ +.canvas-box { + position: fixed; + left: 1000000rpx; + top: -1000000rpx; +} +.canvas-body { + background-position: center; + background-repeat: no-repeat; + background-size: cover; + background-color: #fff; +} +.draw_canvas { + position: absolute; +} +.canvas-bill { + position: fixed; + width: 750rpx; + left: 1000000rpx; + top: -1000000rpx; + visibility: hidden; +} +.lineOne { + // overflow: hidden; + // text-overflow: ellipsis; + // white-space: nowrap; +} +.textCenter { + text-align: center; +} +.textRight { + text-align: right; +} +.billImg-box { + position: fixed; + left: 0; + top: 0; + z-index: 10; + width: 750rpx; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + background-color: rgba(0, 0, 0, 0.75); +} +.image-load-text { + width: 750rpx; + text-align: center; + color: #fff; + line-height: 50rpx; + margin-top: 20rpx; + font-size: 28rpx; + letter-spacing: 2rpx; +} diff --git a/src/components/customPoster/index.wxml b/src/components/customPoster/index.wxml new file mode 100644 index 0000000..511c91a --- /dev/null +++ b/src/components/customPoster/index.wxml @@ -0,0 +1,43 @@ + + + + + + {{item.text}} + + + + + + + + + + + + + + diff --git a/src/components/customPoster/wxml2canvas/README.md b/src/components/customPoster/wxml2canvas/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/components/customPoster/wxml2canvas/index.js b/src/components/customPoster/wxml2canvas/index.js new file mode 100644 index 0000000..97b0ab6 --- /dev/null +++ b/src/components/customPoster/wxml2canvas/index.js @@ -0,0 +1,1692 @@ +module.exports = (function () { + var __MODS__ = {}; + var __DEFINE__ = function (modId, func, req) { + var m = { exports: {}, _tempexports: {} }; + __MODS__[modId] = { status: 0, func: func, req: req, m: m }; + }; + var __REQUIRE__ = function (modId, source) { + if (!__MODS__[modId]) return require(source); + if (!__MODS__[modId].status) { + var m = __MODS__[modId].m; + m._exports = m._tempexports; + var desp = Object.getOwnPropertyDescriptor(m, "exports"); + if (desp && desp.configurable) + Object.defineProperty(m, "exports", { + set: function (val) { + if (typeof val === "object" && val !== m._exports) { + m._exports.__proto__ = val.__proto__; + Object.keys(val).forEach(function (k) { + m._exports[k] = val[k]; + }); + } + m._tempexports = val; + }, + get: function () { + return m._tempexports; + }, + }); + __MODS__[modId].status = 1; + __MODS__[modId].func(__MODS__[modId].req, m, m.exports); + } + return __MODS__[modId].m.exports; + }; + var __REQUIRE_WILDCARD__ = function (obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {}; + if (obj != null) { + for (var k in obj) { + if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; + } + } + newObj.default = obj; + return newObj; + } + }; + var __REQUIRE_DEFAULT__ = function (obj) { + return obj && obj.__esModule ? obj.default : obj; + }; + __DEFINE__( + 1685064731946, + function (require, module, exports) { + var __TEMP__ = require("./src/index"); + var Wxml2Canvas = __REQUIRE_DEFAULT__(__TEMP__); + + if (!exports.__esModule) Object.defineProperty(exports, "__esModule", { value: true }); + exports.default = Wxml2Canvas; + }, + function (modId) { + var map = { "./src/index": 1685064731947 }; + return __REQUIRE__(map[modId], modId); + }, + ); + __DEFINE__( + 1685064731947, + function (require, module, exports) { + var __TEMP__ = require("./util"); + var Util = __REQUIRE_DEFAULT__(__TEMP__); + + const imageMode = [ + "scaleToFill", + "aspectFit", + "aspectFill", + "widthFix", + "top", + "bottom", + "center", + "left", + "right", + "top left", + "top right", + "bottom left", + "bottom right", + ]; + + class Wxml2Canvas { + constructor(options = {}) { + this.device = (wx.getSystemInfoSync && wx.getSystemInfoSync()) || {}; + + if (!options.zoom) { + this.zoom = this.device.windowWidth / 375; + } else { + this.zoom = options.zoom || 1; + } + + this.element = options.element; + this.object = options.obj; + this.width = options.width * this.zoom || 0; + this.height = options.height * this.zoom || 0; + this.destZoom = options.destZoom || 3; + this.destWidth = this.width * this.destZoom; + this.destHeight = this.height * this.destZoom; + this.translateX = options.translateX * this.zoom || 0; + this.translateY = options.translateY * this.zoom || 0; + this.gradientBackground = options.gradientBackground || null; + this.background = options.background || "#ffffff"; + this.finishDraw = options.finish || function finish(params) {}; + this.errorHandler = options.error || function error(params) {}; + this.progress = options.progress || function progress(params) {}; + this.textAlign = options.textAlign || "left"; + this.fullText = options.fullText || false; + this.font = options.font || "14px PingFang SC"; + + this._init(); + } + + draw(data = {}, that) { + let self = this; + this.data = data; + this.fef = that; + + this.progress(10); + this._preloadImage(data.list) + .then((result) => { + this.progress(30); + self._draw(); + }) + .catch((res) => { + self.errorHandler(res); + }); + } + + measureWidth(text, font) { + if (font) { + this.ctx.font = font; + } + let res = this.ctx.measureText(text) || {}; + return res.width || 0; + } + + _init() { + this.progressPercent = 0; // 绘制进度百分比 + this.data = null; + this.ref = null; + this.allPic = []; + this.screenList = []; + this.asyncList = []; + this.imgUrl = ""; + this.progressPercent = 0; + this.distance = 0; + this.progress(0); + + this.ctx = wx.createCanvasContext(this.element, this.object); + this.ctx.font = this.font; + this.ctx.setTextBaseline("top"); + this.ctx.setStrokeStyle("white"); + + this.debug = this.device.platform === "devtools" ? true : false; + + this._drawBakcground(); + } + + _drawBakcground() { + if (this.gradientBackground) { + let line = this.gradientBackground.line || [0, 0, 0, this.height]; + let color = this.gradientBackground.color || ["#fff", "#fff"]; + let style = { fill: { line, color } }; + this._drawRectToCanvas(0, 0, this.width, this.height, style); + } else { + let style = { fill: this.background }; + this._drawRectToCanvas(0, 0, this.width, this.height, style); + } + } + + _draw() { + let self = this; + let list = this.data.list || []; + let index = 0; + let all = []; + let count = 0; + + list.forEach((item) => { + if (item.type === "wxml") { + count += 3; + } else { + count += 1; + } + }); + + this.distance = 60 / (count || 1); // 进度条的间距 + this.progressPercent = 30; + this.asyncList = list.filter((item) => item.delay == true); + list = list.filter((item) => item.delay != true); + drawList(list); + + Promise.all(all) + .then((results) => { + index = 0; + drawList(self.asyncList, true); + + Promise.all(all).then((results) => { + self.progress(90); + self._saveCanvasToImage(); + }); + }) + .catch((e) => { + console.log(e); + self.errorHandler(e); + }); + + function drawList(list = [], noDelay) { + list.forEach((item, i) => { + all[index++] = new Promise((resolve, reject) => { + let attr = item.style; + item.progress = self.distance; + if (noDelay) { + item.delay = 0; + } + if (item.type === "radius-image") { + self._drawCircle(item, attr, resolve, reject, "image"); + } else if (item.type === "text") { + self._drawText(item, attr, resolve, reject); + } else if (item.type === "line") { + self._drawLine(item, attr, resolve, reject); + } else if (item.type === "circle") { + self._drawCircle(item, attr, resolve, reject); + } else if (item.type === "rect") { + self._drawRect(item, attr, resolve, reject); + } else if (item.type === "image") { + self._drawRect(item, attr, resolve, reject, "image"); + } else if (item.type === "wxml") { + self._drawWxml(item, attr, resolve, reject); + } else { + resolve(); + } + }); + }); + } + } + + _saveCanvasToImage() { + let self = this; + + // 延时保存有两个原因,一个是等待绘制delay的元素,另一个是安卓上样式会错乱 + setTimeout( + () => { + self.progress(95); + + let obj = { + x: 0, + y: 0, + width: self.width, + height: self.height, + canvasId: self.element, + success: function (res) { + self.progress(100); + self.imgUrl = res.tempFilePath; + self.finishDraw(self.imgUrl); + }, + fail: function (res) { + self.errorHandler({ errcode: 1000, errmsg: "save canvas error", e: res }); + }, + }; + + if (self.destZoom !== 3) { + obj.destWidth = self.destWidth; + obj.destHeight = self.destHeight; + } + + wx.canvasToTempFilePath(obj, self.object); + }, + self.device.system.indexOf("iOS") === -1 ? 300 : 100, + ); + } + + _preloadImage(list = []) { + let self = this; + let all = []; + let count = 0; + + list.forEach((item, i) => { + if (item.url && self._findPicIndex(item.url) === -1) { + // 避免重复下载同一图片 + self.allPic.push({ + url: item.url, + local: "", + }); + all[count++] = new Promise((resolve, reject) => { + // 非http(s)域名的就不下载了 + if ( + !/^http/.test(item.url) || + /^http:\/\/(tmp)|(usr)\//.test(item.url) || + /^http:\/\/127\.0\.0\.1/.test(item.url) + ) { + if (item.isBase64) { + let fileManager = wx.getFileSystemManager(); + + fileManager.writeFile({ + filePath: item.url, + data: item.isBase64.replace(/data:image\/(.*);base64,/, ""), + encoding: "base64", + success(res) { + imageInfo(item.url); + }, + fail(res) { + reject(res); + }, + }); + } else { + imageInfo(item.url); + } + + function imageInfo(url) { + wx.getImageInfo({ + src: url, + success(res) { + let index = self._findPicIndex(url); + if (index > -1) { + self.allPic[index].local = url; + self.allPic[index].width = res.width; + self.allPic[index].height = res.height; + } + resolve({ tempFilePath: url }); + }, + fail(res) { + reject(res); + }, + }); + } + } else { + wx.downloadFile({ + url: item.url.replace(/^https?/, "https"), + success: function (res) { + wx.getImageInfo({ + src: res.tempFilePath, + success(img) { + let index = self._findPicIndex(item.url); + if (index > -1) { + self.allPic[index].local = res.tempFilePath; + self.allPic[index].width = img.width; + self.allPic[index].height = img.height; + } + resolve(res); + }, + fail(res) { + reject(res); + }, + }); + }, + fail: (res) => { + reject({ errcode: 1001, errmsg: "download pic error" }); + }, + }); + } + }); + } + }); + + return Promise.all(all) + .then((results) => { + return new Promise((resolve) => { + resolve(); + }); + }) + .catch((results) => { + return new Promise((resolve, reject) => { + reject(results); + }); + }); + } + + _findPicIndex(url) { + let index = this.allPic.findIndex((pic) => pic.url === url); + return index; + } + + _drawRect(item, style, resolve, reject, isImage, isWxml) { + let zoom = this.zoom; + let leftOffset = 0; + let topOffset = 0; + let width = style.width; + let height = style.height; + let imgWidth = style.width; + let imgHeight = style.height; + let mode = null; + + try { + item.x = this._resetPositionX(item, style); + item.y = this._resetPositionY(item, style); + + let url; + if (isImage) { + let index = this._findPicIndex(item.url); + if (index > -1) { + url = this.allPic[index].local; + imgWidth = this.allPic[index].width; + imgHeight = this.allPic[index].height; + } else { + url = item.url; + } + } + + style.padding = style.padding || []; + if (isWxml === "inline-wxml") { + item.x = item.x + ((style.padding[3] && style.padding[3]) || 0); + item.y = item.y + ((style.padding[0] && style.padding[0]) || 0); + } + + leftOffset = item.x + style.width + ((style.padding[1] && style.padding[1]) || 0); + + if (!isWxml) { + width = width * zoom; + height = height * zoom; + } + + if (style.dataset && style.dataset.mode && imageMode.indexOf(style.dataset.mode) > -1) { + mode = { + type: style.dataset.mode, + width: imgWidth, + height: imgHeight, + }; + } + + this._drawRectToCanvas(item.x, item.y, width, height, style, url, mode); + this._updateProgress(item.progress); + + if (resolve) { + resolve(); + } else { + return { + leftOffset, + topOffset, + }; + } + } catch (e) { + reject && + reject({ errcode: isImage ? 1003 : 1002, errmsg: isImage ? "drawImage error" : "drawRect error", e }); + } + } + + _drawRectToCanvas(x, y, width, height, style, url, mode) { + let { fill, border, boxShadow } = style; + this.ctx.save(); + this._drawBoxShadow(boxShadow, (res) => { + // 真机上填充渐变色时,没有阴影,先画个相等大小的纯色矩形来实现阴影 + if (fill && typeof fill !== "string" && !this.debug) { + this.ctx.setFillStyle(res.color || "#ffffff"); + this.ctx.fillRect(x, y, width, height); + } + }); + + if (url) { + // 开发者工具有bug,先不裁剪 + if (mode) { + this._resetImageByMode(url, x, y, width, height, mode); + } else { + this.ctx.drawImage(url, x, y, width, height); + } + } else { + this._setFill(fill, () => { + this.ctx.fillRect(x, y, width, height); + }); + } + + this._drawBorder(border, style, (border) => { + let fixBorder = border.width; + this.ctx.strokeRect(x - fixBorder / 2, y - fixBorder / 2, width + fixBorder, height + fixBorder); + }); + + this.ctx.draw(true); + this.ctx.restore(); + this.ctx.strokeStyle = "#fff"; + } + + _resetImageByMode(url, x, y, width, height, mode) { + let self = this; + let offsetX = 0; + let offsetY = 0; + let imgWidth = mode.width; + let imgHeight = mode.height; + + switch (mode.type) { + case "scaleToFill": + imgWidth = width; + imgHeight = height; + self.ctx.drawImage(url, x, y, width, height); + break; + case "widthFix": + height = width / ((imgWidth || 1) / (imgHeight || 1)); + self.ctx.drawImage(url, x, y, width, height); + break; + case "aspectFit": + if (imgWidth > imgHeight) { + let realHeight = width / ((imgWidth || 1) / (imgHeight || 1)); + offsetY = -(height - realHeight) / 2; + imgWidth = width; + imgHeight = realHeight; + } else { + let realWidth = height / ((imgHeight || 1) / (imgWidth || 1)); + offsetX = -(width - realWidth) / 2; + imgWidth = realWidth; + imgHeight = height; + } + + _clip(); + break; + case "aspectFill": + if (imgWidth > imgHeight) { + let realWidth = imgWidth / ((imgHeight || 1) / (height || 1)); + offsetX = (realWidth - width) / 2; + imgWidth = realWidth; + imgHeight = height; + } else { + let realHeight = imgHeight / ((imgWidth || 1) / (width || 1)); + offsetY = (realHeight - height) / 2; + imgWidth = width; + imgHeight = realHeight; + } + + _clip(); + break; + case "top left": + _clip(); + break; + case "top": + offsetX = (mode.width - width) / 2; + _clip(); + break; + case "top right": + offsetX = mode.width - width; + _clip(); + break; + case "left": + offsetY = (mode.height - height) / 2; + _clip(); + break; + case "center": + offsetX = (mode.width - width) / 2; + offsetY = (mode.height - height) / 2; + _clip(); + break; + case "right": + offsetX = mode.width - width; + offsetY = (mode.height - height) / 2; + _clip(); + break; + case "bottom left": + offsetY = mode.height - height; + _clip(); + break; + case "bottom": + offsetX = (mode.width - width) / 2; + offsetY = mode.height - height; + _clip(); + break; + case "bottom right": + offsetX = mode.width - width; + offsetY = mode.height - height; + _clip(); + break; + default: + imgWidth = width; + imgHeight = height; + break; + } + + function _clip() { + self.ctx.save(); + self.ctx.beginPath(); + self.ctx.rect(x, y, width, height); + self.ctx.clip(); + self.ctx.drawImage(url, x - offsetX, y - offsetY, imgWidth, imgHeight); + self.ctx.closePath(); + self.ctx.restore(); + } + } + + _drawText(item, style, resolve, reject, type, isWxml) { + let zoom = this.zoom; + let leftOffset = 0; + let topOffset = 0; + + try { + style.fontSize = this._parseNumber(style.fontSize); + let fontSize = Math.ceil((style.fontSize || 14) * zoom); + this.ctx.setTextBaseline("top"); + this.ctx.font = `${style.fontWeight ? style.fontWeight : "normal"} ${fontSize}px ${ + style.fontFamily || "PingFang SC" + }`; + this.ctx.setFillStyle(style.color || "#454545"); + + let text = item.text || ""; + let textWidth = Math.floor(this.measureWidth(text, style.font || this.ctx.font)); + let lineHeight = this._getLineHeight(style); + let textHeight = Math.ceil(textWidth / (style.width || textWidth)) * lineHeight; + let width = Math.ceil((style.width || textWidth) * (!isWxml ? zoom : 1)); + let whiteSpace = style.whiteSpace || "wrap"; + let x = 0; + let y = 0; + + if (typeof style.padding === "string") { + style.padding = Util.transferPadding(style.padding); + } + item.x = this._resetPositionX(item, style); + item.y = this._resetPositionY(item, style, textHeight); + this._drawBoxShadow(style.boxShadow); + + if (style.background || style.border) { + this._drawTextBackgroud(item, style, textWidth, textHeight, isWxml); + } + + // 行内文本 + if (type === "inline-text") { + width = item.maxWidth; + if (item.leftOffset + textWidth > width) { + // 如果上一个行内元素换行了,这个元素要继续在后面补足一行 + let lineNum = Math.max(Math.floor(textWidth / width), 1); + let length = text.length; + let singleLength = Math.floor(length / lineNum); + let widthOffset = item.leftOffset ? item.leftOffset - item.originX : 0; + let { + endIndex: currentIndex, + single, + singleWidth, + } = this._getTextSingleLine(text, width, singleLength, 0, widthOffset); + x = this._resetTextPositionX(item, style, singleWidth); + y = this._resetTextPositionY(item, style); + this.ctx.fillText(single, x, y); + leftOffset = x + singleWidth; + topOffset = y; + + // 去除第一行补的内容,然后重置 + text = text.substring(currentIndex, text.length); + currentIndex = 0; + lineNum = Math.max(Math.floor(textWidth / width), 1); + textWidth = Math.floor(this.measureWidth(text, style.font || this.ctx.font)); + item.x = item.originX; // 还原换行后的x + for (let i = 0; i < lineNum; i++) { + let { endIndex, single, singleWidth } = this._getTextSingleLine( + text, + width, + singleLength, + currentIndex, + ); + currentIndex = endIndex; + if (single) { + x = this._resetTextPositionX(item, style, singleWidth, width); + y = this._resetTextPositionY(item, style, i + 1); + this.ctx.fillText(single, x, y); + if (i === lineNum - 1) { + leftOffset = x + singleWidth; + topOffset = lineHeight * lineNum; + } + } + } + + let last = text.substring(currentIndex, length); + let lastWidth = this.measureWidth(last); + + if (last) { + x = this._resetTextPositionX(item, style, lastWidth, width); + y = this._resetTextPositionY(item, style, lineNum + 1); + this.ctx.fillText(last, x, y); + leftOffset = x + lastWidth; + topOffset = lineHeight * (lineNum + 1); + } + } else { + x = this._resetTextPositionX(item, style, textWidth, width); + y = this._resetTextPositionY(item, style); + this.ctx.fillText(item.text, x, y); + leftOffset = x + textWidth; + topOffset = lineHeight; + } + } else { + // block文本,如果文本长度超过宽度换行 + if (width && textWidth > width && whiteSpace !== "nowrap") { + let lineNum = Math.max(Math.floor(textWidth / width), 1); + let length = text.length; + let singleLength = Math.floor(length / lineNum); + let currentIndex = 0; + + // lineClamp参数限制最多行数 + if (style.lineClamp && lineNum + 1 > style.lineClamp) { + lineNum = style.lineClamp - 1; + } + + for (let i = 0; i < lineNum; i++) { + let { endIndex, single, singleWidth } = this._getTextSingleLine( + text, + width, + singleLength, + currentIndex, + ); + currentIndex = endIndex; + x = this._resetTextPositionX(item, style, singleWidth, width); + y = this._resetTextPositionY(item, style, i); + this.ctx.fillText(single, x, y); + } + + // 换行后剩余的文字,超过一行则截断增加省略号 + let last = text.substring(currentIndex, length); + let lastWidth = this.measureWidth(last); + if (lastWidth > width) { + let { single, singleWidth } = this._getTextSingleLine(last, width, singleLength); + lastWidth = singleWidth; + last = single.substring(0, single.length - 1) + "..."; + } + + x = this._resetTextPositionX(item, style, lastWidth, width); + y = this._resetTextPositionY(item, style, lineNum); + this.ctx.fillText(last, x, y); + } else { + x = this._resetTextPositionX(item, style, textWidth, width); + y = this._resetTextPositionY(item, style); + this.ctx.fillText(item.text, x, y); + } + } + + this.ctx.draw(true); + + this._updateProgress(item.progress); + + if (resolve) { + resolve(); + } else { + return { + leftOffset, + topOffset, + }; + } + } catch (e) { + reject && reject({ errcode: 1004, errmsg: "drawText error", e: e }); + } + } + + _drawTextBackgroud(item, style, textWidth, textHeight, isWxml) { + if (!style.width) return; + let zoom = isWxml ? 1 : this.zoom; + let width = style.width || textWidth; + let height = style.height || textHeight; + let rectStyle = { + fill: style.background, + border: style.border, + }; + style.padding = style.padding || [0, 0, 0, 0]; + width += (style.padding[1] || 0) + (style.padding[3] || 0); + height += (style.padding[0] || 0) + (style.padding[2] || 0); + width = width * zoom; + height = height * zoom; + this._drawRectToCanvas(item.x, item.y, width, height, rectStyle); + } + + _drawCircle(item, style, resolve, reject, isImage, isWxml) { + let zoom = this.zoom; + let r = style.r; + try { + item.x = this._resetPositionX(item, style); + item.y = this._resetPositionY(item, style); + + let url; + if (isImage) { + let index = this._findPicIndex(item.url); + if (index > -1) { + url = this.allPic[index].local; + } else { + url = item.url; + } + } + + if (!isWxml) { + r = r * zoom; + } + + this._drawCircleToCanvas(item.x, item.y, r, style, url); + + this._updateProgress(item.progress); + resolve && resolve(); + } catch (e) { + reject && + reject({ + errcode: isImage ? 1006 : 1005, + errmsg: isImage ? "drawCircleImage error" : "drawCircle error", + e, + }); + } + } + + _drawCircleToCanvas(x, y, r, style, url) { + let { fill, border, boxShadow } = style; + + this.ctx.save(); + + this._drawBoxShadow(boxShadow, (res) => { + // 真机上填充渐变色时,没有阴影,先画个相等大小的纯色矩形来实现阴影 + if ((fill && typeof fill !== "string") || (url && res.color)) { + this.ctx.setFillStyle(res.color || "#ffffff"); + this.ctx.beginPath(); + this.ctx.arc(x + r, y + r, r, 0, 2 * Math.PI); + this.ctx.closePath(); + this.ctx.fill(); + } + }); + + if (url) { + this.ctx.save(); + this.ctx.beginPath(); + this.ctx.arc(x + r, y + r, r, 0, 2 * Math.PI); + this.ctx.clip(); + this.ctx.drawImage(url, x, y, r * 2, r * 2); + this.ctx.closePath(); + this.ctx.restore(); + } else { + this._setFill(fill, () => { + this.ctx.beginPath(); + this.ctx.arc(x + r, y + r, r, 0, 2 * Math.PI); + this.ctx.closePath(); + this.ctx.fill(); + }); + } + + this._drawBorder(border, style, (border) => { + this.ctx.beginPath(); + this.ctx.arc(x + r, y + r, r + border.width / 2, 0, 2 * Math.PI); + this.ctx.stroke(); + this.ctx.closePath(); + }); + + this.ctx.draw(true); + this.ctx.restore(); + } + + _drawLine(item, style, resolve, reject, isWxml) { + let zoom = this.zoom; + try { + let x1 = item.x * zoom + this.translateX; + let y1 = item.y * zoom + this.translateY; + let x2 = item.x2 * zoom + this.translateX; + let y2 = item.y2 * zoom + this.translateY; + this._drawLineToCanvas(x1, y1, x2, y2, style); + + this._updateProgress(item.progress); + resolve && resolve(); + } catch (e) { + reject && reject({ errcode: 1007, errmsg: "drawLine error", e }); + } + } + + _drawLineToCanvas(x1, y1, x2, y2, style) { + let { stroke, dash, boxShadow } = style; + + this.ctx.save(); + if (stroke) { + this._setStroke(stroke); + } + + this._drawBoxShadow(boxShadow); + + if (dash) { + let dash = [style.dash[0] || 5, style.dash[1] || 5]; + let offset = style.dash[2] || 0; + this.ctx.setLineDash(dash, offset || 0); + } + + this.ctx.moveTo(x1, y1); + this.ctx.setLineWidth((style.width || 1) * this.zoom); + this.ctx.lineTo(x2, y2); + this.ctx.stroke(); + this.ctx.draw(true); + this.ctx.restore(); + } + + // 废弃,合并到_drawRect + _drawImage(item, style, resolve, reject, isWxml) { + let zoom = this.zoom; + try { + item.x = this._resetPositionX(item, style); + item.y = this._resetPositionY(item, style); + item.x = item.x + (style.padding[3] || 0); + item.y = item.y + (style.padding[0] || 0); + + let index = this._findPicIndex(item.url); + let url = index > -1 ? this.allPic[index].local : item.url; + this._drawImageToCanvas(url, item.x, item.y, style.width * zoom, style.height * zoom, style); + + this._updateProgress(item.progress); + resolve && resolve(); + } catch (e) { + reject && reject({ errcode: 1012, errmsg: "drawRect error", e }); + } + } + + // 废弃,合并到_drawRect + _drawImageToCanvas(url, x, y, width, height, style) { + let { fill, border, boxShadow } = style; + this.ctx.save(); + + this._drawBoxShadow(boxShadow); + this.ctx.drawImage(url, x, y, width, height); + + this._drawBorder(border, style, (border) => { + let fixBorder = border.width; + this.ctx.strokeRect(x - fixBorder / 2, y - fixBorder / 2, width + fixBorder, height + fixBorder); + }); + this.ctx.draw(true); + this.ctx.restore(); + } + + _drawWxml(item, style, resolve, reject) { + let self = this; + let all = []; + try { + this._getWxml(item, style).then((results) => { + // 上 -> 下 + let sorted = self._sortListByTop(results[0]); + let count = 0; + let progress = 0; + Object.keys(sorted).forEach((item) => { + count += sorted[item].length; + }); + progress = (this.distance * 3) / (count || 1); + + all = this._drawWxmlBlock(item, sorted, all, progress, results[1]); + all = this._drawWxmlInline(item, sorted, all, progress, results[1]); + + Promise.all(all) + .then((results) => { + resolve && resolve(); + }) + .catch((e) => { + reject && reject(e); + }); + }); + } catch (e) { + reject && reject({ errcode: 1008, errmsg: "drawWxml error" }); + } + } + + _drawWxmlBlock(item, sorted, all, progress, results) { + let self = this; + // 用来限定位置范围,取相对位置 + let limitLeft = results ? results.left : 0; + let limitTop = results ? results.top : 0; + Object.keys(sorted).forEach((top, topIndex) => { + // 左 -> 右 + let list = sorted[top].sort((a, b) => { + return a.left - b.left; + }); + + list = list.filter((sub) => sub.dataset.type && sub.dataset.type.indexOf("inline") === -1); + + list.forEach((sub, index) => { + all[index] = new Promise((resolve2, reject2) => { + sub = self._transferWxmlStyle(sub, item, limitLeft, limitTop); + sub.progress = progress; + let type = sub.dataset.type; + if (sub.dataset.delay) { + setTimeout(() => { + drawWxmlItem(); + }, sub.dataset.delay); + } else { + drawWxmlItem(); + } + function drawWxmlItem() { + if (type === "text") { + self._drawWxmlText(sub, resolve2, reject2); + } else if (type === "image") { + self._drawWxmlImage(sub, resolve2, reject2); + } else if (type === "radius-image") { + self._drawWxmlCircleImage(sub, resolve2, reject2); + } else if (type === "background-image") { + self._drawWxmlBackgroundImage(sub, resolve2, reject2); + } + } + }); + }); + }); + + return all; + } + + _drawWxmlInline(item, sorted, all, progress, results) { + let self = this; + let topOffset = 0; + let leftOffset = 0; + let lastTop = 0; + let limitLeft = results ? results.left : 0; + let limitTop = results ? results.top : 0; + let p = new Promise((resolve2, reject2) => { + let maxWidth = 0; + let minLeft = Infinity; + let maxRight = 0; + + // 找出同一top下的最小left和最大right,得到最大的宽度,用于换行 + Object.keys(sorted).forEach((top) => { + let inlineList = sorted[top].filter((sub) => sub.dataset.type && sub.dataset.type.indexOf("inline") > -1); + inlineList.forEach((sub) => { + if (sub.left < minLeft) { + minLeft = sub.left; + } + if (sub.right > maxRight) { + maxRight = sub.right; + } + }); + }); + maxWidth = Math.ceil(maxRight - minLeft || self.width); + + Object.keys(sorted).forEach((top, topIndex) => { + // 左 -> 右 + let list = sorted[top].sort((a, b) => { + return a.left - b.left; + }); + + // 换行的行内元素left放到后面,version2.0.6后无法获取高度,改用bottom值来判断是否换行了 + let position = -1; + for (let i = 0, len = list.length; i < len; i++) { + if (list[i] && list[i + 1]) { + if (list[i].bottom > list[i + 1].bottom) { + position = i; + break; + } + } + } + + if (position > -1) { + list.push(list.splice(position, 1)[0]); + } + + let inlineList = list.filter((sub) => sub.dataset.type && sub.dataset.type.indexOf("inline") > -1); + let originLeft = inlineList[0] ? inlineList[0].left : 0; + // 换行后和top不相等时,认为是换行了,要清除左边距;当左偏移量大于最大宽度时,也要清除左边距; 当左偏移小于左边距时,也要清除 + if ( + Math.abs(topOffset + lastTop - top) > 2 || + leftOffset - originLeft - limitLeft >= maxWidth || + leftOffset <= originLeft - limitLeft - 2 + ) { + leftOffset = 0; + } + + lastTop = +top; + topOffset = 0; + + inlineList.forEach((sub, index) => { + sub = self._transferWxmlStyle(sub, item, limitLeft, limitTop); + sub.progress = progress; + let type = sub.dataset.type; + if (type === "inline-text") { + let drawRes = self._drawWxmlInlineText(sub, leftOffset, maxWidth); + leftOffset = drawRes.leftOffset; + topOffset = drawRes.topOffset; + } else if (type === "inline-image") { + let drawRes = self._drawWxmlImage(sub) || {}; + leftOffset = drawRes.leftOffset || 0; + topOffset = drawRes.topOffset || 0; + } + }); + }); + resolve2(); + }); + + all.push(p); + return all; + } + + _drawWxmlInlineText(sub, leftOffset = 0, maxWidth) { + let text = sub.dataset.text || ""; + if (sub.dataset.maxlength && text.length > sub.dataset.maxlength) { + text = text.substring(0, sub.dataset.maxlength) + "..."; + } + + let textData = { + text, + originX: sub.left, + x: leftOffset ? leftOffset : sub.left, + y: sub.top, + progress: sub.progress, + leftOffset: leftOffset, + maxWidth: maxWidth, // 行内元素的最大宽度,取决于limit的宽度 + }; + + if (sub.backgroundColor !== "rgba(0, 0, 0, 0)") { + sub.background = sub.backgroundColor; + } else { + sub.background = "rgba(0, 0, 0, 0)"; + } + + if (sub.dataset.background) { + sub.background = sub.dataset.background; + } + + let res = this._drawText(textData, sub, null, null, "inline-text", "wxml"); + + return res; + } + + _drawWxmlText(sub, resolve, reject) { + let text = sub.dataset.text || ""; + if (sub.dataset.maxlength && text.length > sub.dataset.maxlength) { + text = text.substring(0, sub.dataset.maxlength) + "..."; + } + + let textData = { + text, + x: sub.left, + y: sub.top, + progress: sub.progress, + }; + if (sub.backgroundColor !== "rgba(0, 0, 0, 0)") { + sub.background = sub.backgroundColor; + } else { + sub.background = "rgba(0, 0, 0, 0)"; + } + + if (sub.dataset.background) { + sub.background = sub.dataset.background; + } + + this._drawText(textData, sub, resolve, reject, "text", "wxml"); + } + + _drawWxmlImage(sub, resolve, reject) { + let imageData = { + url: sub.dataset.url, + x: sub.left, + y: sub.top, + progress: sub.progress, + }; + + let res = this._drawRect(imageData, sub, resolve, reject, "image", "inline-wxml"); + + return res; + } + + _drawWxmlCircleImage(sub, resolve, reject) { + let imageData = { + url: sub.dataset.url, + x: sub.left, + y: sub.top, + progress: sub.progress, + }; + sub.r = sub.width / 2; + + this._drawCircle(imageData, sub, resolve, reject, true, "wxml"); + } + + _drawWxmlBackgroundImage(sub, resolve, reject) { + let url = sub.dataset.url; + let index = this._findPicIndex(url); + url = index > -1 ? this.allPic[index].local : url; + let size = sub.backgroundSize.replace(/px/g, "").split(" "); + + let imageData = { + url: url, + x: sub.left, + y: sub.top, + progress: sub.progress, + }; + + this._drawRect(imageData, sub, resolve, reject, "image", "wxml"); + } + + _getWxml(item, style) { + let self = this; + let query; + // if (this.obj) { + // query = wx.createSelectorQuery().in(this.obj); + // } else { + // query = wx.createSelectorQuery(); + // } + if (this.object) { + query = wx.createSelectorQuery().in(this.object); + } else { + query = wx.createSelectorQuery(); + } + + let p1 = new Promise((resolve, reject) => { + // 会触发两次,要限制 + let count = 0; + query + .selectAll(`${item.class}`) + .fields( + { + dataset: true, + size: true, + rect: true, + computedStyle: [ + "width", + "height", + "font", + "fontSize", + "fontFamily", + "fontWeight", + "fontStyle", + "textAlign", + "color", + "lineHeight", + "border", + "borderColor", + "borderStyle", + "borderWidth", + "verticalAlign", + "boxShadow", + "background", + "backgroundColor", + "backgroundImage", + "backgroundPosition", + "backgroundSize", + "paddingLeft", + "paddingTop", + "paddingRight", + "paddingBottom", + ], + }, + (res) => { + if (count++ === 0) { + let formated = self._formatImage(res); + let list = formated.list; + res = formated.res; + + self + ._preloadImage(list) + .then((result) => { + resolve(res); + }) + .catch((res) => { + reject && reject({ errcode: 1009, errmsg: "drawWxml preLoadImage error" }); + }); + } + }, + ) + .exec(); + }); + + let p2 = new Promise((resolve, reject) => { + if (!item.limit) { + resolve({ top: 0, width: self.width / self.zoom }); + } + + query + .select(`${item.limit}`) + .fields( + { + dataset: true, + size: true, + rect: true, + }, + (res) => { + resolve(res); + }, + ) + .exec(); + }); + + return Promise.all([p1, p2]); + } + + _getLineHeight(style) { + let zoom = this.zoom; + if (style.dataset && style.dataset.type) { + zoom = 1; + } + let lineHeight; + if (!isNaN(style.lineHeight) && style.lineHeight > style.fontSize) { + lineHeight = style.lineHeight; + } else { + style.lineHeight = (style.lineHeight || "") + ""; + lineHeight = +style.lineHeight.replace("px", ""); + lineHeight = lineHeight ? lineHeight : (style.fontSize || 14) * 1.2; + } + return lineHeight * zoom; + } + + _formatImage(res = []) { + let list = []; + res.forEach((item, index) => { + let dataset = item.dataset; + let uid = Util.getUid(); + let filename = `${wx.env.USER_DATA_PATH}/${uid}.png`; + if ((dataset.type === "image" || dataset.type === "radius-image") && dataset.url) { + let sub = { + url: dataset.base64 ? filename : dataset.url, + isBase64: dataset.base64 ? dataset.url : false, + }; + + res[index].dataset = Object.assign(res[index].dataset, sub); + list.push(sub); + } else if (dataset.type === "background-image" && item.backgroundImage.indexOf("url") > -1) { + let url = item.backgroundImage.replace(/url\((\"|\')?/, "").replace(/(\"|\')?\)$/, ""); + let sub = { + url: dataset.base64 ? filename : url, + isBase64: dataset.base64 ? url : false, + }; + res[index].dataset = Object.assign(res[index].dataset, sub); + list.push(sub); + } + }); + + return { list, res }; + } + + _updateProgress(distance) { + this.progressPercent += distance; + this.progress(this.progressPercent); + } + + _sortListByTop(list = []) { + let sorted = {}; + + // 粗略地认为2px相差的元素在同一行 + list.forEach((item, index) => { + let top = item.top; + if (!sorted[top]) { + if (sorted[top - 2]) { + top = top - 2; + } else if (sorted[top - 1]) { + top = top - 1; + } else if (sorted[top + 1]) { + top = top + 1; + } else if (sorted[top + 2]) { + top = top + 2; + } else { + sorted[top] = []; + } + } + sorted[top].push(item); + }); + + return sorted; + } + + _parseNumber(number) { + return isNaN(number) ? +(number || "").replace("px", "") : number; + } + + _transferWxmlStyle(sub, item, limitLeft, limitTop) { + let leftFix = +sub.dataset.left || 0; + let topFix = +sub.dataset.top || 0; + + sub.width = this._parseNumber(sub.width); + sub.height = this._parseNumber(sub.height); + sub.left = this._parseNumber(sub.left) - limitLeft + (leftFix + (item.x || 0)) * this.zoom; + sub.top = this._parseNumber(sub.top) - limitTop + (topFix + (item.y || 0)) * this.zoom; + + let padding = sub.dataset.padding || "0 0 0 0"; + if (typeof padding === "string") { + padding = Util.transferPadding(padding); + } + let paddingTop = Number(sub.paddingTop.replace("px", "")) + Number(padding[0]); + let paddingRight = Number(sub.paddingRight.replace("px", "")) + Number(padding[1]); + let paddingBottom = Number(sub.paddingBottom.replace("px", "")) + Number(padding[2]); + let paddingLeft = Number(sub.paddingLeft.replace("px", "")) + Number(padding[3]); + sub.padding = [paddingTop, paddingRight, paddingBottom, paddingLeft]; + + return sub; + } + + /** + * 支持负值绘制,从右边计算 + * @param {*} item + * @param {*} style + */ + _resetPositionX(item, style) { + let zoom = this.zoom; + let x = 0; + + if (style.dataset && style.dataset.type) { + zoom = 1; + } + + // 通过wxml获取的不需要重置坐标 + if (item.x < 0 && item.type) { + x = this.width + item.x * zoom - style.width * zoom; + } else { + x = item.x * zoom; + } + + if (parseInt(style.borderWidth)) { + x += parseInt(style.borderWidth); + } + + return x + this.translateX; + } + + /** + * 支持负值绘制,从底部计算 + * @param {*} item + * @param {*} style + */ + _resetPositionY(item, style, textHeight) { + let zoom = this.zoom; + let y = 0; + + if (style.dataset && style.dataset.type) { + zoom = 1; + } + + if (item.y < 0) { + y = this.height + item.y * zoom - (textHeight ? textHeight : style.height * zoom); + } else { + y = item.y * zoom; + } + + if (parseInt(style.borderWidth)) { + y += parseInt(style.borderWidth); + } + + return y + this.translateY; + } + + /** + * 文字的padding、text-align + * @param {*} item + * @param {*} style + * @param {*} textWidth + */ + _resetTextPositionX(item, style, textWidth, width) { + let textAlign = style.textAlign || "left"; + let x = item.x; + if (textAlign === "center") { + x = (width - textWidth) / 2 + item.x; + } else if (textAlign === "right") { + x = width - textWidth + item.x; + } + + let left = style.padding ? style.padding[3] || 0 : 0; + + return x + left + this.translateX; + } + + /** + * 文字的padding、text-align + * @param {*} item + * @param {*} style + * @param {*} textWidth + */ + _resetTextPositionY(item, style, lineNum = 0) { + let zoom = this.zoom; + if (style.dataset && style.dataset.type) { + zoom = 1; + } + + let lineHeight = this._getLineHeight(style); + let fontSize = Math.ceil((style.fontSize || 14) * zoom); + + let blockLineHeightFix = + ((style.dataset && style.dataset.type) || "").indexOf("inline") > -1 ? 0 : (lineHeight - fontSize) / 2; + + let top = style.padding ? style.padding[0] || 0 : 0; + + // y + lineheight偏移 + 行数 + paddingTop + 整体画布位移 + return item.y + blockLineHeightFix + lineNum * lineHeight + top + this.translateY; + } + + /** + * 当文本超过宽度时,计算每一行应该绘制的文本 + * @param {*} text + * @param {*} width + * @param {*} singleLength + * @param {*} currentIndex + * @param {*} widthOffset + */ + _getTextSingleLine(text, width, singleLength, currentIndex = 0, widthOffset = 0) { + let offset = 0; + let endIndex = currentIndex + singleLength + offset; + let single = text.substring(currentIndex, endIndex); + let singleWidth = this.measureWidth(single); + + while (Math.round(widthOffset + singleWidth) > width) { + offset--; + endIndex = currentIndex + singleLength + offset; + single = text.substring(currentIndex, endIndex); + singleWidth = this.measureWidth(single); + } + + return { + endIndex, + single, + singleWidth, + }; + } + + _drawBorder(border, style, callback) { + let zoom = this.zoom; + if (style.dataset && style.dataset.type) { + zoom = 1; + } + border = Util.transferBorder(border); + + if (border && border.width) { + // 空白阴影,清空掉边框的阴影 + this._drawBoxShadow(); + if (border) { + this.ctx.setLineWidth(border.width * zoom); + + if (border.style === "dashed") { + let dash = style.dash || [5, 5, 0]; + let offset = dash[2] || 0; + let array = [dash[0] || 5, dash[1] || 5]; + this.ctx.setLineDash(array, offset); + } + this.ctx.setStrokeStyle(border.color); + } + callback && callback(border); + } + } + + _drawBoxShadow(boxShadow, callback) { + boxShadow = Util.transferBoxShadow(boxShadow); + if (boxShadow) { + this.ctx.setShadow(boxShadow.offsetX, boxShadow.offsetY, boxShadow.blur, boxShadow.color); + } else { + this.ctx.setShadow(0, 0, 0, "#ffffff"); + } + + callback && callback(boxShadow || {}); + } + + _setFill(fill, callback) { + if (fill) { + if (typeof fill === "string") { + this.ctx.setFillStyle(fill); + } else { + let line = fill.line; + let color = fill.color; + let grd = this.ctx.createLinearGradient(line[0], line[1], line[2], line[3]); + grd.addColorStop(0, color[0]); + grd.addColorStop(1, color[1]); + this.ctx.setFillStyle(grd); + } + callback && callback(); + } + } + + _setStroke(stroke, callback) { + if (stroke) { + if (typeof stroke === "string") { + this.ctx.setStrokeStyle(stroke); + } else { + let line = stroke.line; + let color = stroke.color; + let grd = this.ctx.createLinearGradient(line[0], line[1], line[2], line[3]); + grd.addColorStop(0, color[0]); + grd.addColorStop(1, color[1]); + this.ctx.setStrokeStyle(grd); + } + + callback && callback(); + } + } + } + + if (!exports.__esModule) Object.defineProperty(exports, "__esModule", { value: true }); + exports.default = Wxml2Canvas; + }, + function (modId) { + var map = { "./util": 1685064731948 }; + return __REQUIRE__(map[modId], modId); + }, + ); + __DEFINE__( + 1685064731948, + function (require, module, exports) { + /** + * 获取字符的长度,full为true时,一个汉字算两个长度 + * @param {String} str + * @param {Boolean} full + */ + + function getTextLength(str, full) { + let len = 0; + for (let i = 0; i < str.length; i++) { + let c = str.charCodeAt(i); + //单字节加1 + if ((c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f)) { + len++; + } else { + len += full ? 2 : 1; + } + } + return len; + } + + /** + * rgba(255, 255, 255, 1) => #ffffff + * @param {String} color + */ + function transferColor(color = "") { + let res = "#"; + color = color.replace(/^rgba?\(/, "").replace(/\)$/, ""); + color = color.split(", "); + + color.length > 3 ? (color.length = 3) : ""; + for (let item of color) { + item = parseInt(item || 0); + if (item < 10) { + res += "0" + item; + } else { + res += item.toString(16); + } + } + + return res; + } + + function transferBorder(border = "") { + let res = border.match(/(\w+)px\s(\w+)\s(.*)/); + let obj = {}; + + if (res) { + obj = { + width: +res[1], + style: res[2], + color: res[3], + }; + } + + return res ? obj : null; + } + + /** + * 内边距,依次为上右下左 + * @param {*} padding + */ + function transferPadding(padding = "0 0 0 0") { + padding = padding.split(" "); + for (let i = 0, len = padding.length; i < len; i++) { + padding[i] = +padding[i].replace("px", ""); + } + + return padding; + } + /** + * type1: 0, 25, 17, rgba(0, 0, 0, 0.3) + * type2: rgba(0, 0, 0, 0.3) 0px 25px 17px 0px => (0, 25, 17, rgba(0, 0, 0, 0.3)) + * @param {*} shadow + */ + function transferBoxShadow(shadow = "", type) { + if (!shadow || shadow === "none") return; + let color; + let split; + + split = shadow.match(/(\w+)\s(\w+)\s(\w+)\s(rgb.*)/); + + if (split) { + split.shift(); + shadow = split; + color = split[3] || "#ffffff"; + } else { + split = shadow.split(") "); + color = split[0] + ")"; + shadow = split[1].split("px "); + } + + return { + offsetX: +shadow[0] || 0, + offsetY: +shadow[1] || 0, + blur: +shadow[2] || 0, + color, + }; + } + + function getUid(prefix) { + prefix = prefix || ""; + + return ( + prefix + + "xxyxxyxx".replace(/[xy]/g, (c) => { + let r = (Math.random() * 16) | 0; + let v = c === "x" ? r : (r & 0x3) | 0x8; + return v.toString(16); + }) + ); + } + + if (!exports.__esModule) Object.defineProperty(exports, "__esModule", { value: true }); + exports.default = { + getTextLength, + transferBorder, + transferColor, + transferPadding, + transferBoxShadow, + getUid, + }; + }, + function (modId) { + var map = {}; + return __REQUIRE__(map[modId], modId); + }, + ); + return __REQUIRE__(1685064731946); +})(); +//miniprogram-npm-outsideDeps=[] +//# sourceMappingURL=index.js.map diff --git a/src/components/customPoster/wxml2canvas/index.js.map b/src/components/customPoster/wxml2canvas/index.js.map new file mode 100644 index 0000000..68e773e --- /dev/null +++ b/src/components/customPoster/wxml2canvas/index.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["index.js","src/index.js","src/util.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;ACFA;AACA;AACA;AACA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["var __TEMP__ = require('./src/index');var Wxml2Canvas = __REQUIRE_DEFAULT__(__TEMP__);\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.default = Wxml2Canvas;","var __TEMP__ = require('./util');var Util = __REQUIRE_DEFAULT__(__TEMP__);\n\nconst imageMode = ['scaleToFill', 'aspectFit', 'aspectFill', 'widthFix', 'top', 'bottom', 'center', 'left', 'right', 'top left', 'top right', 'bottom left', 'bottom right']\n\nclass Wxml2Canvas {\n constructor (options = {}) {\n this.device = wx.getSystemInfoSync && wx.getSystemInfoSync() || {};\n \n if (!options.zoom) {\n this.zoom = this.device.windowWidth / 375;\n } else {\n this.zoom = options.zoom || 1;\n } \n \n this.element = options.element;\n this.object = options.obj;\n this.width = options.width * this.zoom || 0;\n this.height = options.height * this.zoom || 0;\n this.destZoom = options.destZoom || 3;\n this.destWidth = this.width * this.destZoom;\n this.destHeight = this.height * this.destZoom;\n this.translateX = options.translateX * this.zoom || 0;\n this.translateY = options.translateY * this.zoom || 0;\n this.gradientBackground = options.gradientBackground || null;\n this.background = options.background || '#ffffff';\n this.finishDraw = options.finish || function finish(params) {}\n this.errorHandler = options.error || function error(params) {}\n this.progress = options.progress || function progress(params) {}\n this.textAlign = options.textAlign || 'left';\n this.fullText = options.fullText || false;\n this.font = options.font || '14px PingFang SC';\n\n this._init();\n }\n\n draw (data = {}, that) {\n let self = this;\n this.data = data;\n this.fef = that;\n\n this.progress(10);\n this._preloadImage(data.list).then((result) => {\n this.progress(30);\n self._draw();\n }).catch((res) => {\n self.errorHandler(res);\n })\n }\n\n measureWidth (text, font) {\n if(font) {\n this.ctx.font = font;\n }\n let res = this.ctx.measureText(text) || {};\n return res.width || 0;\n }\n\n _init () {\n this.progressPercent = 0; // 绘制进度百分比\n this.data = null;\n this.ref = null;\n this.allPic = [];\n this.screenList = []; \n this.asyncList = [];\n this.imgUrl = '';\n this.progressPercent = 0;\n this.distance = 0;\n this.progress(0);\n\n this.ctx = wx.createCanvasContext(this.element, this.obj);\n this.ctx.font = this.font;\n this.ctx.setTextBaseline('top');\n this.ctx.setStrokeStyle('white');\n\n this.debug = this.device.platform === 'devtools' ? true : false;\n\n this._drawBakcground();\n }\n\n _drawBakcground () {\n if (this.gradientBackground) {\n let line = this.gradientBackground.line || [0, 0, 0, this.height];\n let color = this.gradientBackground.color || ['#fff', '#fff'];\n let style = { fill: { line, color } }\n this._drawRectToCanvas(0, 0, this.width, this.height, style);\n } else {\n let style = { fill: this.background }\n this._drawRectToCanvas(0, 0, this.width, this.height, style);\n }\n }\n\n _draw () {\n let self = this;\n let list = this.data.list || [];\n let index = 0;\n let all = [];\n let count = 0;\n\n list.forEach(item => {\n if(item.type === 'wxml') {\n count += 3;\n } else {\n count += 1;\n }\n })\n\n this.distance = 60 / (count || 1); // 进度条的间距\n this.progressPercent = 30;\n this.asyncList = list.filter( item => item.delay == true );\n list = list.filter( item => item.delay != true );\n drawList(list);\n\n Promise.all(all).then(results => {\n index = 0;\n drawList(self.asyncList, true);\n\n Promise.all(all).then(results => {\n self.progress(90);\n self._saveCanvasToImage();\n });\n }).catch (e => {\n console.log(e)\n self.errorHandler(e);\n });\n\n function drawList(list = [], noDelay) {\n list.forEach((item, i) => {\n all[index++] = new Promise((resolve, reject) => {\n let attr = item.style;\n item.progress = self.distance;\n if (noDelay) {\n item.delay = 0;\n }\n if (item.type === 'radius-image') {\n self._drawCircle(item, attr, resolve, reject, 'image');\n } else if (item.type === 'text') {\n self._drawText(item, attr, resolve, reject);\n } else if (item.type === 'line') {\n self._drawLine(item, attr, resolve, reject);\n } else if (item.type === 'circle') {\n self._drawCircle(item, attr, resolve, reject);\n } else if (item.type === 'rect') {\n self._drawRect(item, attr, resolve, reject);\n } else if (item.type === 'image') {\n self._drawRect(item, attr, resolve, reject, 'image');\n } else if (item.type === 'wxml') {\n self._drawWxml(item, attr, resolve, reject);\n }else {\n resolve();\n }\n });\n });\n }\n }\n\n _saveCanvasToImage () {\n let self = this; \n\n // 延时保存有两个原因,一个是等待绘制delay的元素,另一个是安卓上样式会错乱\n setTimeout(() => {\n self.progress(95);\n\n let obj = {\n x: 0,\n y: 0,\n width: self.width,\n height: self.height,\n canvasId: self.element,\n success: function (res) {\n\n self.progress(100);\n self.imgUrl = res.tempFilePath;\n self.finishDraw(self.imgUrl);\n },\n fail: function (res) {\n\n self.errorHandler({errcode: 1000, errmsg: 'save canvas error', e: res});\n }\n }\n\n if(self.destZoom !== 3) {\n obj.destWidth = self.destWidth;\n obj.destHeight = self.destHeight;\n }\n\n wx.canvasToTempFilePath(obj, self.object);\n }, self.device.system.indexOf('iOS') === -1 ? 300 : 100);\n }\n\n _preloadImage (list = []) {\n let self = this;\n let all = [];\n let count = 0;\n\n list.forEach((item, i) => {\n if (item.url && self._findPicIndex(item.url) === -1) {\n \n // 避免重复下载同一图片\n self.allPic.push({\n url: item.url,\n local: ''\n });\n all[count++] = new Promise((resolve, reject) => {\n // 非http(s)域名的就不下载了\n if (!/^http/.test(item.url) || /^http:\\/\\/(tmp)|(usr)\\//.test(item.url) || /^http:\\/\\/127\\.0\\.0\\.1/.test(item.url)) {\n if(item.isBase64) {\n let fileManager = wx.getFileSystemManager();\n\n fileManager.writeFile({\n filePath: item.url,\n data: item.isBase64.replace(/data:image\\/(.*);base64,/, ''),\n encoding: 'base64',\n success (res) {\n imageInfo(item.url);\n },\n fail (res) {\n reject(res);\n },\n })\n \n }else {\n imageInfo(item.url);\n }\n\n function imageInfo (url) {\n wx.getImageInfo({\n src: url,\n success (res) {\n let index = self._findPicIndex(url);\n if(index > -1) {\n self.allPic[index].local = url;\n self.allPic[index].width = res.width;\n self.allPic[index].height = res.height;\n }\n resolve({ tempFilePath: url });\n }, \n fail (res) {\n reject(res);\n }\n })\n }\n } else {\n wx.downloadFile({\n url: item.url.replace(/^https?/, 'https'),\n success: function (res) {\n wx.getImageInfo({\n src: res.tempFilePath,\n success (img) {\n let index = self._findPicIndex(item.url);\n if (index > -1) {\n self.allPic[index].local = res.tempFilePath;\n self.allPic[index].width = img.width;\n self.allPic[index].height = img.height;\n }\n resolve(res);\n },\n fail (res) {\n reject(res);\n }\n })\n },\n fail: (res) => {\n reject({errcode: 1001, errmsg: 'download pic error'});\n }\n })\n }\n }) \n }\n });\n\n return Promise.all(all).then(results => {\n return new Promise(resolve => { resolve() })\n }).catch((results) => {\n return new Promise((resolve, reject) => { reject(results) })\n })\n }\n\n _findPicIndex (url) {\n let index = this.allPic.findIndex(pic => pic.url === url);\n return index;\n }\n\n _drawRect (item, style, resolve, reject, isImage, isWxml) {\n let zoom = this.zoom;\n let leftOffset = 0;\n let topOffset = 0;\n let width = style.width;\n let height = style.height;\n let imgWidth = style.width;\n let imgHeight = style.height;\n let mode = null;\n\n try {\n item.x = this._resetPositionX(item, style);\n item.y = this._resetPositionY(item, style);\n \n let url;\n if(isImage) {\n let index = this._findPicIndex(item.url);\n if(index > -1) {\n url = this.allPic[index].local\n imgWidth = this.allPic[index].width\n imgHeight = this.allPic[index].height\n }else {\n url = item.url;\n }\n }\n\n style.padding = style.padding || [];\n if(isWxml === 'inline-wxml') {\n item.x = item.x + (style.padding[3] && style.padding[3] || 0)\n item.y = item.y + (style.padding[0] && style.padding[0] || 0)\n }\n\n leftOffset = item.x + style.width + (style.padding[1] && style.padding[1] || 0);\n\n if(!isWxml) {\n width = width * zoom;\n height = height * zoom;\n }\n\n if(style.dataset && style.dataset.mode && imageMode.indexOf(style.dataset.mode) > -1) {\n mode = {\n type: style.dataset.mode,\n width: imgWidth,\n height: imgHeight\n };\n }\n\n this._drawRectToCanvas(item.x, item.y, width, height, style, url, mode);\n this._updateProgress(item.progress);\n\n if(resolve) {\n resolve();\n }else {\n return {\n leftOffset,\n topOffset\n }\n }\n } catch (e) {\n reject && reject({ errcode: (isImage ? 1003 : 1002), errmsg: (isImage ? 'drawImage error' : 'drawRect error'), e });\n }\n }\n\n _drawRectToCanvas (x, y, width, height, style, url, mode) {\n let { fill, border, boxShadow } = style;\n this.ctx.save();\n this._drawBoxShadow(boxShadow, (res) => {\n // 真机上填充渐变色时,没有阴影,先画个相等大小的纯色矩形来实现阴影\n if(fill && typeof fill !== 'string' && !this.debug) {\n this.ctx.setFillStyle(res.color || '#ffffff');\n this.ctx.fillRect(x, y, width, height);\n }\n });\n \n if(url) {\n // 开发者工具有bug,先不裁剪\n if(mode) {\n this._resetImageByMode(url, x, y, width, height, mode);\n }else {\n this.ctx.drawImage(url, x, y, width, height)\n }\n }else {\n this._setFill(fill, () => {\n this.ctx.fillRect(x, y, width, height);\n });\n }\n\n this._drawBorder(border, style, (border) => {\n let fixBorder = border.width;\n this.ctx.strokeRect(x - fixBorder / 2, y - fixBorder / 2, width + fixBorder, height + fixBorder);\n });\n\n this.ctx.draw(true);\n this.ctx.restore();\n }\n\n _resetImageByMode (url, x, y, width, height, mode) {\n let self = this;\n let offsetX = 0;\n let offsetY = 0;\n let imgWidth = mode.width;\n let imgHeight = mode.height;\n\n switch (mode.type) {\n case 'scaleToFill': \n imgWidth = width;\n imgHeight = height;\n self.ctx.drawImage(url, x, y, width, height)\n break;\n case 'widthFix': \n height = width / ((imgWidth || 1) / (imgHeight || 1))\n self.ctx.drawImage(url, x, y, width, height)\n break; \n case 'aspectFit': \n if(imgWidth > imgHeight) {\n let realHeight = width / ((imgWidth || 1) / (imgHeight || 1))\n offsetY = -(height - realHeight) / 2\n imgWidth = width;\n imgHeight = realHeight;\n }else {\n let realWidth = height / ((imgHeight || 1) / (imgWidth || 1))\n offsetX = -(width - realWidth) / 2\n imgWidth = realWidth;\n imgHeight = height;\n }\n\n _clip();\n break;\n case 'aspectFill': \n if(imgWidth > imgHeight) {\n let realWidth = imgWidth / ((imgHeight || 1) / (height || 1))\n offsetX = (realWidth - width) / 2\n imgWidth = realWidth;\n imgHeight = height;\n }else {\n let realHeight = imgHeight / ((imgWidth || 1) / (width || 1))\n offsetY = (realHeight - height) / 2\n imgWidth = width;\n imgHeight = realHeight;\n }\n\n _clip();\n break;\n case 'top left': \n _clip();\n break;\n case 'top': \n offsetX = (mode.width - width) / 2;\n _clip();\n break;\n case 'top right': \n offsetX = (mode.width - width);\n _clip();\n break;\n case 'left': \n offsetY = (mode.height - height) / 2;\n _clip();\n break;\n case 'center': \n offsetX = (mode.width - width) / 2;\n offsetY = (mode.height - height) / 2;\n _clip();\n break;\n case 'right': \n offsetX = (mode.width - width);\n offsetY = (mode.height - height) / 2;\n _clip();\n break;\n case 'bottom left': \n offsetY = (mode.height - height)\n _clip();\n break;\n case 'bottom': \n offsetX = (mode.width - width) / 2;\n offsetY = (mode.height - height)\n _clip();\n break;\n case 'bottom right': \n offsetX = (mode.width - width);\n offsetY = (mode.height - height)\n _clip();\n break;\n default: \n imgWidth = width;\n imgHeight = height;\n break; \n }\n\n function _clip () {\n self.ctx.save();\n self.ctx.beginPath()\n self.ctx.rect(x, y, width, height)\n self.ctx.clip();\n self.ctx.drawImage(url, x - offsetX, y - offsetY, imgWidth, imgHeight)\n self.ctx.closePath();\n self.ctx.restore();\n }\n }\n\n _drawText (item, style, resolve, reject, type, isWxml) {\n let zoom = this.zoom;\n let leftOffset = 0;\n let topOffset = 0;\n\n try {\n style.fontSize = this._parseNumber(style.fontSize);\n let fontSize = Math.ceil((style.fontSize || 14) * zoom)\n this.ctx.setTextBaseline('top');\n this.ctx.font = (`${style.fontWeight ? (style.fontWeight) : 'normal'} ${ fontSize }px ${ style.fontFamily || 'PingFang SC' }`);\n this.ctx.setFillStyle(style.color || '#454545');\n\n let text = item.text || '';\n let textWidth = Math.floor(this.measureWidth(text, style.font || this.ctx.font));\n let lineHeight = this._getLineHeight(style);\n let textHeight = Math.ceil(textWidth / (style.width || textWidth)) * lineHeight;\n let width = Math.ceil((style.width || textWidth) * (!isWxml ? zoom : 1));\n let whiteSpace = style.whiteSpace || 'wrap';\n let x = 0;\n let y = 0; \n\n if(typeof style.padding === 'string') {\n style.padding = Util.transferPadding(style.padding);\n }\n item.x = this._resetPositionX(item, style);\n item.y = this._resetPositionY(item, style, textHeight);\n this._drawBoxShadow(style.boxShadow);\n\n if(style.background || style.border) {\n this._drawTextBackgroud(item, style, textWidth, textHeight, isWxml);\n }\n \n // 行内文本\n if(type === 'inline-text') {\n width = item.maxWidth;\n if(item.leftOffset + textWidth > width) {\n // 如果上一个行内元素换行了,这个元素要继续在后面补足一行\n let lineNum = Math.max(Math.floor(textWidth / width), 1);\n let length = text.length;\n let singleLength = Math.floor(length / lineNum);\n let widthOffset = item.leftOffset ? item.leftOffset - item.originX : 0;\n let { endIndex: currentIndex, single, singleWidth } = this._getTextSingleLine(text, width, singleLength, 0, widthOffset)\n x = this._resetTextPositionX(item, style, singleWidth);\n y = this._resetTextPositionY(item, style);\n this.ctx.fillText(single, x, y);\n leftOffset = x + singleWidth;\n topOffset = y;\n\n // 去除第一行补的内容,然后重置\n text = text.substring(currentIndex, text.length);\n currentIndex = 0;\n lineNum = Math.max(Math.floor(textWidth / width), 1);\n textWidth = Math.floor(this.measureWidth(text, style.font || this.ctx.font));\n item.x = item.originX; // 还原换行后的x\n for (let i = 0; i < lineNum; i++) {\n let { endIndex, single, singleWidth } = this._getTextSingleLine(text, width, singleLength, currentIndex);\n currentIndex = endIndex;\n if(single) {\n x = this._resetTextPositionX(item, style, singleWidth, width);\n y = this._resetTextPositionY(item, style, i + 1);\n this.ctx.fillText(single, x, y);\n if(i === lineNum - 1) {\n leftOffset = x + singleWidth;\n topOffset = lineHeight * lineNum;\n }\n }\n }\n\n let last = text.substring(currentIndex, length);\n let lastWidth = this.measureWidth(last);\n\n if(last) {\n x = this._resetTextPositionX(item, style, lastWidth, width);\n y = this._resetTextPositionY(item, style, lineNum + 1);\n this.ctx.fillText(last, x, y);\n leftOffset = x + lastWidth;\n topOffset = lineHeight * (lineNum + 1);\n }\n }else {\n x = this._resetTextPositionX(item, style, textWidth, width);\n y = this._resetTextPositionY(item, style);\n this.ctx.fillText(item.text, x, y);\n leftOffset = x + textWidth;\n topOffset = lineHeight;\n }\n }else {\n // block文本,如果文本长度超过宽度换行\n if (width && textWidth > width && whiteSpace !== 'nowrap') {\n let lineNum = Math.max(Math.floor(textWidth / width), 1);\n let length = text.length;\n let singleLength = Math.floor(length / lineNum);\n let currentIndex = 0;\n\n // lineClamp参数限制最多行数\n if (style.lineClamp && lineNum + 1 > style.lineClamp) {\n lineNum = style.lineClamp - 1;\n }\n\n for (let i = 0; i < lineNum; i++) {\n let { endIndex, single, singleWidth } = this._getTextSingleLine(text, width, singleLength, currentIndex);\n currentIndex = endIndex;\n x = this._resetTextPositionX(item, style, singleWidth, width);\n y = this._resetTextPositionY(item, style, i);\n this.ctx.fillText(single, x, y);\n\n }\n\n // 换行后剩余的文字,超过一行则截断增加省略号\n let last = text.substring(currentIndex, length);\n let lastWidth = this.measureWidth(last);\n if(lastWidth > width) {\n let { single, singleWidth } = this._getTextSingleLine(last, width, singleLength);\n lastWidth = singleWidth;\n last = single.substring(0, single.length - 1) + '...';\n }\n\n x = this._resetTextPositionX(item, style, lastWidth, width);\n y = this._resetTextPositionY(item, style, lineNum);\n this.ctx.fillText(last, x, y);\n\n }else {\n x = this._resetTextPositionX(item, style, textWidth, width);\n y = this._resetTextPositionY(item, style);\n this.ctx.fillText(item.text, x, y);\n }\n }\n \n this.ctx.draw(true);\n \n this._updateProgress(item.progress);\n\n if(resolve) {\n resolve();\n }else {\n return {\n leftOffset,\n topOffset\n }\n }\n } catch(e) {\n reject && reject({ errcode: 1004, errmsg: 'drawText error', e: e });\n }\n }\n\n _drawTextBackgroud (item, style, textWidth, textHeight, isWxml) {\n if(!style.width) return;\n let zoom = isWxml ? 1 : this.zoom;\n let width = style.width || textWidth;\n let height = style.height || textHeight;\n let rectStyle = {\n fill: style.background,\n border: style.border\n }\n style.padding = style.padding || [0, 0, 0, 0];\n width += (style.padding[1] || 0) + (style.padding[3] || 0);\n height += (style.padding[0] || 0) + (style.padding[2] || 0);\n width = width * zoom\n height = height * zoom\n this._drawRectToCanvas(item.x, item.y, width, height, rectStyle);\n }\n\n _drawCircle (item, style, resolve, reject, isImage, isWxml) {\n let zoom = this.zoom;\n let r = style.r;\n try {\n \n item.x = this._resetPositionX(item, style);\n item.y = this._resetPositionY(item, style);\n\n let url;\n if(isImage) {\n let index = this._findPicIndex(item.url);\n if (index > -1) {\n url = this.allPic[index].local;\n } else {\n url = item.url;\n }\n }\n\n if(!isWxml) {\n r = r * zoom;\n }\n\n this._drawCircleToCanvas(item.x, item.y, r, style, url);\n \n this._updateProgress(item.progress);\n resolve && resolve();\n } catch (e) {\n reject && reject({ errcode: (isImage ? 1006 : 1005), errmsg: (isImage ? 'drawCircleImage error' : 'drawCircle error'), e });\n }\n }\n\n _drawCircleToCanvas (x, y, r, style, url) {\n let { fill, border, boxShadow } = style;\n\n this.ctx.save();\n\n this._drawBoxShadow(boxShadow, (res) => {\n // 真机上填充渐变色时,没有阴影,先画个相等大小的纯色矩形来实现阴影\n if((fill && typeof fill !== 'string') || (url && res.color)) {\n this.ctx.setFillStyle(res.color || '#ffffff');\n this.ctx.beginPath();\n this.ctx.arc(x + r, y + r, r, 0, 2 * Math.PI);\n this.ctx.closePath();\n this.ctx.fill();\n }\n });\n\n if(url) {\n this.ctx.save();\n this.ctx.beginPath();\n this.ctx.arc(x + r, y + r, r, 0, 2 * Math.PI);\n this.ctx.clip();\n this.ctx.drawImage(url, x, y, r * 2, r * 2);\n this.ctx.closePath();\n this.ctx.restore();\n }else {\n this._setFill(fill, () => {\n this.ctx.beginPath();\n this.ctx.arc(x + r, y + r, r, 0, 2 * Math.PI);\n this.ctx.closePath();\n this.ctx.fill();\n });\n }\n\n this._drawBorder(border, style, (border) => {\n this.ctx.beginPath()\n this.ctx.arc(x + r, y + r, r + border.width / 2, 0, 2 * Math.PI)\n this.ctx.stroke()\n this.ctx.closePath();\n });\n\n this.ctx.draw(true);\n this.ctx.restore();\n }\n\n _drawLine (item, style, resolve, reject, isWxml) {\n let zoom = this.zoom;\n try {\n let x1 = item.x * zoom + this.translateX;\n let y1 = item.y * zoom + this.translateY;\n let x2 = item.x2 * zoom + this.translateX;\n let y2 = item.y2 * zoom + this.translateY;\n this._drawLineToCanvas(x1, y1, x2, y2, style);\n\n this._updateProgress(item.progress);\n resolve && resolve();\n } catch (e) {\n reject && reject({ errcode: 1007, errmsg: 'drawLine error', e });\n }\n }\n\n _drawLineToCanvas (x1, y1, x2, y2, style) {\n let { stroke, dash, boxShadow } = style;\n\n this.ctx.save();\n if(stroke) {\n this._setStroke(stroke);\n }\n\n this._drawBoxShadow(boxShadow);\n\n if(dash) {\n let dash = [style.dash[0] || 5, style.dash[1] || 5];\n let offset = style.dash[2] || 0;\n this.ctx.setLineDash(dash, offset || 0);\n }\n\n this.ctx.moveTo(x1, y1);\n this.ctx.setLineWidth((style.width || 1) * this.zoom);\n this.ctx.lineTo(x2, y2);\n this.ctx.stroke();\n this.ctx.draw(true);\n this.ctx.restore();\n }\n\n // 废弃,合并到_drawRect\n _drawImage (item, style, resolve, reject, isWxml) {\n let zoom = this.zoom;\n try {\n \n item.x = this._resetPositionX(item, style);\n item.y = this._resetPositionY(item, style);\n item.x = item.x + (style.padding[3] || 0);\n item.y = item.y + (style.padding[0] || 0);\n\n let index = this._findPicIndex(item.url);\n let url = index > -1 ? this.allPic[index].local : item.url;\n this._drawImageToCanvas(url, item.x, item.y, style.width * zoom, style.height * zoom, style);\n\n this._updateProgress(item.progress);\n resolve && resolve();\n } catch (e) {\n reject && reject({ errcode: 1012, errmsg: 'drawRect error', e });\n }\n }\n\n // 废弃,合并到_drawRect\n _drawImageToCanvas (url, x, y, width, height, style) {\n let { fill, border, boxShadow } = style;\n this.ctx.save();\n\n this._drawBoxShadow(boxShadow);\n this.ctx.drawImage(url, x, y, width, height);\n\n this._drawBorder(border, style, (border) => {\n let fixBorder = border.width;\n this.ctx.strokeRect(x - fixBorder / 2, y - fixBorder / 2, width + fixBorder, height + fixBorder);\n });\n this.ctx.draw(true);\n this.ctx.restore();\n }\n\n _drawWxml (item, style, resolve, reject) {\n let self = this;\n let all = [];\n try {\n this._getWxml(item, style).then((results) => {\n \n // 上 -> 下\n let sorted = self._sortListByTop(results[0]);\n let count = 0;\n let progress = 0;\n Object.keys(sorted).forEach(item => {\n count += sorted[item].length;\n })\n progress = this.distance * 3 / (count || 1);\n\n all = this._drawWxmlBlock(item, sorted, all, progress, results[1]);\n all = this._drawWxmlInline(item, sorted, all, progress, results[1]);\n\n Promise.all(all).then(results => {\n resolve && resolve();\n }).catch (e => {\n reject && reject(e);\n });\n });\n } catch (e) {\n reject && reject({ errcode: 1008, errmsg: 'drawWxml error' });\n }\n }\n\n _drawWxmlBlock (item, sorted, all, progress, results) {\n let self = this;\n // 用来限定位置范围,取相对位置\n let limitLeft = (results ? results.left : 0);\n let limitTop = (results ? results.top : 0);\n Object.keys(sorted).forEach((top, topIndex) => {\n // 左 -> 右\n let list = sorted[top].sort((a, b) => {\n return (a.left - b.left);\n });\n\n list = list.filter(sub => sub.dataset.type && sub.dataset.type.indexOf('inline') === -1);\n\n list.forEach((sub, index) => {\n all[index] = new Promise((resolve2, reject2) => {\n sub = self._transferWxmlStyle(sub, item, limitLeft, limitTop);\n sub.progress = progress;\n let type = sub.dataset.type;\n if(sub.dataset.delay) {\n setTimeout(() => {\n drawWxmlItem();\n }, sub.dataset.delay)\n } else {\n drawWxmlItem();\n }\n function drawWxmlItem () {\n if (type === 'text') {\n self._drawWxmlText(sub, resolve2, reject2);\n } else if (type === 'image') {\n self._drawWxmlImage(sub, resolve2, reject2);\n } else if (type === 'radius-image') {\n self._drawWxmlCircleImage(sub, resolve2, reject2);\n } else if (type === 'background-image') {\n self._drawWxmlBackgroundImage(sub, resolve2, reject2);\n }\n }\n });\n });\n });\n\n return all;\n\n }\n\n _drawWxmlInline (item, sorted, all, progress, results) {\n let self = this;\n let topOffset = 0;\n let leftOffset = 0;\n let lastTop = 0;\n let limitLeft = (results ? results.left : 0);\n let limitTop = (results ? results.top : 0);\n let p = new Promise((resolve2, reject2) => {\n let maxWidth = 0;\n let minLeft = Infinity;\n let maxRight = 0;\n\n // 找出同一top下的最小left和最大right,得到最大的宽度,用于换行\n Object.keys(sorted).forEach(top => {\n let inlineList = sorted[top].filter(sub => sub.dataset.type && sub.dataset.type.indexOf('inline') > -1);\n inlineList.forEach(sub => {\n if(sub.left < minLeft) {\n minLeft = sub.left\n }\n if(sub.right > maxRight) {\n maxRight = sub.right;\n }\n })\n });\n maxWidth = Math.ceil((maxRight - minLeft) || self.width);\n\n Object.keys(sorted).forEach((top, topIndex) => {\n // 左 -> 右\n let list = sorted[top].sort((a, b) => {\n return (a.left - b.left);\n });\n\n // 换行的行内元素left放到后面,version2.0.6后无法获取高度,改用bottom值来判断是否换行了\n let position = -1;\n for(let i = 0, len = list.length; i < len; i++) {\n if(list[i] && list[i + 1]) {\n if(list[i].bottom > list[i + 1].bottom) {\n position = i;\n break;\n }\n }\n }\n\n if(position > -1) {\n list.push(list.splice(position, 1)[0]);\n }\n\n let inlineList = list.filter(sub => sub.dataset.type && sub.dataset.type.indexOf('inline') > -1);\n let originLeft = (inlineList[0] ? inlineList[0].left : 0);\n // 换行后和top不相等时,认为是换行了,要清除左边距;当左偏移量大于最大宽度时,也要清除左边距; 当左偏移小于左边距时,也要清除\n if (Math.abs(topOffset + lastTop - top) > 2 || leftOffset - originLeft - limitLeft >= maxWidth || leftOffset <= originLeft - limitLeft - 2) {\n leftOffset = 0;\n }\n\n lastTop = +top;\n topOffset = 0;\n \n inlineList.forEach((sub, index) => {\n sub = self._transferWxmlStyle(sub, item, limitLeft, limitTop);\n sub.progress = progress;\n let type = sub.dataset.type;\n if (type === 'inline-text') {\n let drawRes = self._drawWxmlInlineText(sub, leftOffset, maxWidth);\n leftOffset = drawRes.leftOffset;\n topOffset = drawRes.topOffset;\n } else if (type === 'inline-image') {\n let drawRes = self._drawWxmlImage(sub) || {};\n leftOffset = drawRes.leftOffset || 0;\n topOffset = drawRes.topOffset || 0;\n }\n });\n });\n resolve2();\n })\n\n all.push(p); \n return all;\n }\n\n _drawWxmlInlineText (sub, leftOffset = 0, maxWidth) {\n let text = sub.dataset.text || '';\n if(sub.dataset.maxlength && text.length > sub.dataset.maxlength) {\n text = text.substring(0, sub.dataset.maxlength) + '...';\n }\n \n let textData = {\n text,\n originX: sub.left,\n x: leftOffset ? leftOffset : sub.left,\n y: sub.top,\n progress: sub.progress,\n leftOffset: leftOffset,\n maxWidth: maxWidth // 行内元素的最大宽度,取决于limit的宽度\n }\n\n if (sub.backgroundColor !== 'rgba(0, 0, 0, 0)') {\n sub.background = sub.backgroundColor;\n }else {\n sub.background = 'rgba(0, 0, 0, 0)';\n }\n\n if(sub.dataset.background) {\n sub.background = sub.dataset.background;\n }\n\n let res = this._drawText(textData, sub, null, null, 'inline-text', 'wxml');\n\n return res\n }\n\n _drawWxmlText (sub, resolve, reject) {\n let text = sub.dataset.text || '';\n if(sub.dataset.maxlength && text.length > sub.dataset.maxlength) {\n text = text.substring(0, sub.dataset.maxlength) + '...';\n }\n \n let textData = {\n text,\n x: sub.left,\n y: sub.top,\n progress: sub.progress\n }\n if (sub.backgroundColor !== 'rgba(0, 0, 0, 0)') {\n sub.background = sub.backgroundColor;\n }else {\n sub.background = 'rgba(0, 0, 0, 0)';\n }\n\n if(sub.dataset.background) {\n sub.background = sub.dataset.background;\n }\n\n this._drawText(textData, sub, resolve, reject, 'text', 'wxml');\n }\n\n _drawWxmlImage (sub, resolve, reject) {\n let imageData = {\n url: sub.dataset.url,\n x: sub.left,\n y: sub.top,\n progress: sub.progress\n }\n\n let res = this._drawRect(imageData, sub, resolve, reject, 'image', 'inline-wxml');\n\n return res\n }\n\n _drawWxmlCircleImage (sub, resolve, reject) {\n let imageData = {\n url: sub.dataset.url,\n x: sub.left,\n y: sub.top,\n progress: sub.progress\n }\n sub.r = sub.width / 2;\n\n this._drawCircle(imageData, sub, resolve, reject, true, 'wxml');\n }\n\n _drawWxmlBackgroundImage (sub, resolve, reject) {\n let url = sub.dataset.url;\n let index = this._findPicIndex(url);\n url = index > -1 ? this.allPic[index].local : url;\n let size = sub.backgroundSize.replace(/px/g, '').split(' ');\n\n let imageData = {\n url: url,\n x: sub.left,\n y: sub.top,\n progress: sub.progress\n }\n\n this._drawRect(imageData, sub, resolve, reject, 'image', 'wxml');\n }\n\n _getWxml (item, style) {\n let self = this;\n let query;\n if(this.obj) {\n query = wx.createSelectorQuery().in(this.obj);\n }else {\n query = wx.createSelectorQuery();\n }\n\n let p1 = new Promise((resolve, reject) => {\n // 会触发两次,要限制\n let count = 0;\n query.selectAll(`${item.class}`).fields({\n dataset: true,\n size: true,\n rect: true,\n computedStyle: ['width', 'height', 'font', 'fontSize', 'fontFamily', 'fontWeight', 'fontStyle', 'textAlign', \n 'color', 'lineHeight', 'border', 'borderColor', 'borderStyle', 'borderWidth', 'verticalAlign', 'boxShadow',\n 'background', 'backgroundColor', 'backgroundImage', 'backgroundPosition', 'backgroundSize', 'paddingLeft', 'paddingTop',\n 'paddingRight', 'paddingBottom'\n ]\n }, (res) => {\n if(count++ === 0) {\n let formated = self._formatImage(res);\n let list = formated.list;\n res = formated.res;\n\n self._preloadImage(list).then(result => {\n resolve(res);\n }).catch((res) => {\n reject && reject({ errcode: 1009, errmsg: 'drawWxml preLoadImage error' });\n });\n }\n }).exec();\n });\n\n let p2 = new Promise((resolve, reject) => {\n if (!item.limit) {\n resolve({ top: 0, width: self.width / self.zoom });\n }\n\n query.select(`${item.limit}`).fields({\n dataset: true,\n size: true,\n rect: true,\n }, (res) => {\n resolve(res);\n }).exec();\n });\n\n return Promise.all([p1, p2]);\n }\n\n _getLineHeight (style) {\n let zoom = this.zoom;\n if(style.dataset && style.dataset.type) {\n zoom = 1;\n }\n let lineHeight;\n if(!isNaN(style.lineHeight) && style.lineHeight > style.fontSize) {\n lineHeight = style.lineHeight;\n }else {\n style.lineHeight = (style.lineHeight || '') + '';\n lineHeight = +style.lineHeight.replace('px', '');\n lineHeight = lineHeight ? lineHeight : (style.fontSize || 14) * 1.2;\n }\n return lineHeight * zoom;\n }\n\n _formatImage (res = []) {\n let list = [];\n res.forEach((item, index) => {\n let dataset = item.dataset;\n let uid = Util.getUid();\n let filename = `${wx.env.USER_DATA_PATH}/${uid}.png`;\n if ((dataset.type === \"image\" || dataset.type === \"radius-image\") && dataset.url) {\n let sub = {\n url: dataset.base64 ? filename : dataset.url,\n isBase64: dataset.base64 ? dataset.url : false\n }\n\n res[index].dataset = Object.assign(res[index].dataset, sub);\n list.push(sub)\n } else if (dataset.type === 'background-image' && item.backgroundImage.indexOf('url') > -1) {\n let url = item.backgroundImage.replace(/url\\((\\\"|\\')?/, '').replace(/(\\\"|\\')?\\)$/, '');\n let sub = {\n url: dataset.base64 ? filename : url,\n isBase64: dataset.base64 ? url : false\n }\n res[index].dataset = Object.assign(res[index].dataset, sub);\n list.push(sub)\n }\n });\n\n return { list, res };\n }\n\n _updateProgress (distance) {\n this.progressPercent += distance;\n this.progress(this.progressPercent);\n }\n\n _sortListByTop (list = []) {\n let sorted = {};\n\n // 粗略地认为2px相差的元素在同一行\n list.forEach((item, index) => {\n let top = item.top;\n if (!sorted[top]) {\n if (sorted[top - 2]) {\n top = top - 2;\n }else if (sorted[top - 1]) {\n top = top - 1;\n } else if (sorted[top + 1]) {\n top = top + 1;\n } else if (sorted[top + 2]) {\n top = top + 2;\n } else {\n sorted[top] = [];\n }\n }\n sorted[top].push(item);\n });\n\n return sorted;\n }\n\n _parseNumber (number) {\n return isNaN(number) ? +(number || '').replace('px', '') : number;\n }\n\n _transferWxmlStyle (sub, item, limitLeft, limitTop) {\n let leftFix = (+sub.dataset.left || 0);\n let topFix = (+sub.dataset.top || 0);\n\n sub.width = this._parseNumber(sub.width);\n sub.height = this._parseNumber(sub.height);\n sub.left = this._parseNumber(sub.left) - limitLeft + (leftFix + (item.x || 0)) * this.zoom;\n sub.top = this._parseNumber(sub.top) - limitTop + (topFix + (item.y || 0)) * this.zoom;\n\n let padding = sub.dataset.padding || '0 0 0 0';\n if (typeof padding === 'string') {\n padding = Util.transferPadding(padding);\n }\n let paddingTop = Number(sub.paddingTop.replace('px', '')) + Number(padding[0]);\n let paddingRight = Number(sub.paddingRight.replace('px', '')) + Number(padding[1]);\n let paddingBottom = Number(sub.paddingBottom.replace('px', '')) + Number(padding[2]);\n let paddingLeft = Number(sub.paddingLeft.replace('px', '')) + Number(padding[3]);\n sub.padding = [paddingTop, paddingRight, paddingBottom, paddingLeft];\n \n return sub;\n }\n\n /**\n * 支持负值绘制,从右边计算\n * @param {*} item \n * @param {*} style \n */\n _resetPositionX (item, style) {\n let zoom = this.zoom;\n let x = 0;\n\n if(style.dataset && style.dataset.type) {\n zoom = 1;\n }\n\n // 通过wxml获取的不需要重置坐标\n if (item.x < 0 && item.type) {\n x = this.width + item.x * zoom - style.width * zoom;\n } else {\n x = item.x * zoom;\n }\n\n if (parseInt(style.borderWidth)) {\n x += parseInt(style.borderWidth)\n }\n\n return x + this.translateX;\n }\n\n /**\n * 支持负值绘制,从底部计算\n * @param {*} item \n * @param {*} style \n */\n _resetPositionY (item, style, textHeight) {\n let zoom = this.zoom;\n let y = 0;\n\n if(style.dataset && style.dataset.type) {\n zoom = 1;\n }\n\n if (item.y < 0) {\n y = this.height + item.y * zoom - (textHeight ? textHeight : style.height * zoom)\n } else {\n y = item.y * zoom;\n }\n\n if (parseInt(style.borderWidth)) {\n y += parseInt(style.borderWidth)\n }\n \n return y + this.translateY;\n }\n\n /**\n * 文字的padding、text-align\n * @param {*} item \n * @param {*} style \n * @param {*} textWidth\n */\n _resetTextPositionX (item, style, textWidth, width) {\n let textAlign = style.textAlign || 'left';\n let x = item.x;\n if (textAlign === 'center') {\n x = (width - textWidth) / 2 + item.x;\n } else if (textAlign === 'right') {\n x = width - textWidth + item.x;\n }\n\n let left = style.padding ? (style.padding[3] || 0) : 0;\n\n return x + left + this.translateX;\n }\n\n /**\n * 文字的padding、text-align\n * @param {*} item \n * @param {*} style \n * @param {*} textWidth\n */\n _resetTextPositionY (item, style, lineNum = 0) {\n let zoom = this.zoom;\n if(style.dataset && style.dataset.type) {\n zoom = 1;\n }\n\n let lineHeight = this._getLineHeight(style);\n let fontSize = Math.ceil((style.fontSize || 14) * zoom)\n\n let blockLineHeightFix = (style.dataset && style.dataset.type || '').indexOf('inline') > -1 ? 0 : (lineHeight - fontSize) / 2\n\n let top = style.padding ? (style.padding[0] || 0) : 0;\n\n // y + lineheight偏移 + 行数 + paddingTop + 整体画布位移\n return item.y + blockLineHeightFix + lineNum * lineHeight + top + this.translateY;\n }\n\n /**\n * 当文本超过宽度时,计算每一行应该绘制的文本\n * @param {*} text \n * @param {*} width \n * @param {*} singleLength \n * @param {*} currentIndex \n * @param {*} widthOffset\n */\n _getTextSingleLine(text, width, singleLength, currentIndex = 0, widthOffset = 0) {\n let offset = 0;\n let endIndex = currentIndex + singleLength + offset;\n let single = text.substring(currentIndex, endIndex);\n let singleWidth = this.measureWidth(single);\n\n while (Math.round(widthOffset + singleWidth) > width) {\n offset--;\n endIndex = currentIndex + singleLength + offset;\n single = text.substring(currentIndex, endIndex);\n singleWidth = this.measureWidth(single);\n }\n\n return {\n endIndex, \n single, \n singleWidth\n }\n }\n\n _drawBorder (border, style, callback) {\n let zoom = this.zoom;\n if(style.dataset && style.dataset.type) {\n zoom = 1;\n }\n border = Util.transferBorder(border);\n\n if (border && border.width) {\n // 空白阴影,清空掉边框的阴影\n this._drawBoxShadow();\n if (border) {\n \n this.ctx.setLineWidth(border.width * zoom);\n\n if (border.style === 'dashed') {\n let dash = style.dash || [5, 5, 0];\n let offset = dash[2] || 0;\n let array = [dash[0] || 5, dash[1] || 5];\n this.ctx.setLineDash(array, offset);\n }\n this.ctx.setStrokeStyle(border.color);\n }\n callback && callback(border);\n }\n } \n\n _drawBoxShadow (boxShadow, callback) {\n boxShadow = Util.transferBoxShadow(boxShadow);\n if (boxShadow) {\n this.ctx.setShadow(boxShadow.offsetX, boxShadow.offsetY, boxShadow.blur, boxShadow.color);\n }else {\n this.ctx.setShadow(0, 0, 0, '#ffffff');\n }\n\n callback && callback(boxShadow || {});\n }\n\n _setFill (fill, callback) {\n if(fill) {\n if (typeof fill === 'string') {\n this.ctx.setFillStyle(fill);\n } else {\n let line = fill.line;\n let color = fill.color;\n let grd = this.ctx.createLinearGradient(line[0], line[1], line[2], line[3]);\n grd.addColorStop(0, color[0]);\n grd.addColorStop(1, color[1]);\n this.ctx.setFillStyle(grd);\n }\n callback && callback();\n }\n }\n\n _setStroke (stroke, callback) {\n if(stroke) {\n if (typeof stroke === 'string') {\n this.ctx.setStrokeStyle(stroke);\n } else {\n let line = stroke.line;\n let color = stroke.color;\n let grd = this.ctx.createLinearGradient(line[0], line[1], line[2], line[3]);\n grd.addColorStop(0, color[0]);\n grd.addColorStop(1, color[1]);\n this.ctx.setStrokeStyle(grd);\n }\n\n callback && callback();\n }\n }\n}\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.default = Wxml2Canvas;","/**\n * 获取字符的长度,full为true时,一个汉字算两个长度\n * @param {String} str \n * @param {Boolean} full \n */\n\nfunction getTextLength (str, full) {\n let len = 0;\n for (let i = 0; i < str.length; i++) {\n let c = str.charCodeAt(i);\n //单字节加1 \n if ((c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f)) {\n len++;\n }\n else {\n len += (full ? 2 : 1);\n }\n }\n return len;\n}\n\n/**\n * rgba(255, 255, 255, 1) => #ffffff\n * @param {String} color \n */\nfunction transferColor (color = '') {\n let res = '#';\n color = color.replace(/^rgba?\\(/, '').replace(/\\)$/, '');\n color = color.split(', ');\n \n color.length > 3 ? color.length = 3 : '';\n for(let item of color) {\n item = parseInt(item || 0);\n if(item < 10) {\n res += ('0' + item)\n }else {\n res += (item.toString(16))\n }\n }\n\n return res;\n}\n\nfunction transferBorder (border = '') {\n let res = border.match(/(\\w+)px\\s(\\w+)\\s(.*)/);\n let obj = {};\n\n if(res) {\n obj = {\n width: +res[1],\n style: res[2],\n color: res[3]\n }\n }\n \n return res ? obj : null;\n}\n\n\n/**\n * 内边距,依次为上右下左\n * @param {*} padding \n */\nfunction transferPadding (padding = '0 0 0 0') {\n padding = padding.split(' ');\n for(let i = 0, len = padding.length; i < len; i++) {\n padding[i] = +padding[i].replace('px', '');\n }\n\n return padding;\n}\n/**\n * type1: 0, 25, 17, rgba(0, 0, 0, 0.3)\n * type2: rgba(0, 0, 0, 0.3) 0px 25px 17px 0px => (0, 25, 17, rgba(0, 0, 0, 0.3))\n * @param {*} shadow \n */\nfunction transferBoxShadow(shadow = '', type) {\n if(!shadow || shadow === 'none') return;\n let color;\n let split;\n\n split = shadow.match(/(\\w+)\\s(\\w+)\\s(\\w+)\\s(rgb.*)/);\n\n if (split) {\n split.shift();\n shadow = split;\n color = split[3] || '#ffffff';\n } else {\n split = shadow.split(') ');\n color = split[0] + ')'\n shadow = split[1].split('px ');\n }\n\n return {\n offsetX: +shadow[0] || 0,\n offsetY: +shadow[1] || 0,\n blur: +shadow[2] || 0,\n color\n }\n}\n\nfunction getUid(prefix) {\n prefix = prefix || '';\n\n return (\n prefix +\n 'xxyxxyxx'.replace(/[xy]/g, c => {\n let r = (Math.random() * 16) | 0;\n let v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n })\n );\n}\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.default = {\n getTextLength,\n transferBorder,\n transferColor,\n transferPadding,\n transferBoxShadow,\n getUid\n};"]} diff --git a/src/components/popup/index.ts b/src/components/popup/index.ts index 5407c88..bb8e5cd 100644 --- a/src/components/popup/index.ts +++ b/src/components/popup/index.ts @@ -13,6 +13,10 @@ Component({ return {} }, }, + top: { + type: Number, + value: 50, + }, }, observers: { show(val) { @@ -22,6 +26,14 @@ Component({ TCenterId: this.data.params.hospitalId, }) } + if (this.data.type === 'inhibitors') { + console.log(this.data.params) + this.setData({ + inhibitorsId: this.data.params.drugId || '', + inhibitorsName: this.data.params.drugName || '', + inhibitorsContent: this.data.params.content || '', + }) + } } }, }, diff --git a/src/components/popup/index.wxml b/src/components/popup/index.wxml index 3ba840f..67e1d21 100644 --- a/src/components/popup/index.wxml +++ b/src/components/popup/index.wxml @@ -4,7 +4,7 @@ round z-index="{{100000}}" show="{{ true }}" - custom-style="background:transparent;top:40%" + custom-style="background:transparent;{{!params.position ? 'top:'+top+'%' : ''}}" position="{{params.position || 'center'}}" safe-area-inset-bottom="{{false}}" root-portal diff --git a/src/doctor/pages/calculator/index.json b/src/doctor/pages/calculator/index.json new file mode 100644 index 0000000..fb7d291 --- /dev/null +++ b/src/doctor/pages/calculator/index.json @@ -0,0 +1,4 @@ +{ + "navigationBarTitleText": "用药计算器", + "usingComponents": {} +} diff --git a/src/doctor/pages/calculator/index.scss b/src/doctor/pages/calculator/index.scss new file mode 100644 index 0000000..3e48525 --- /dev/null +++ b/src/doctor/pages/calculator/index.scss @@ -0,0 +1,98 @@ +page { + background-color: rgba(246, 246, 246, 1); +} +.page-tip { + font-size: 28rpx; + color: rgba(255, 163, 112, 1); + padding: 18rpx; + text-align: center; + line-height: 44rpx; + background-color: rgba(255, 247, 232, 1); +} + +.page { + padding: 32rpx 40rpx 80rpx; + .banner { + background-color: rgba(28, 107, 255, 1); + border: 1px solid rgba(255, 255, 255, 1); + border-radius: 24rpx; + .b-header { + padding: 24rpx 32rpx; + display: flex; + align-items: center; + gap: 36rpx; + .left { + flex-shrink: 0; + .name { + font-size: 32rpx; + color: rgba(255, 255, 255, 1); + line-height: 44rpx; + } + .content { + margin-top: 8rpx; + font-size: 24rpx; + color: rgba(255, 255, 255, 0.5); + } + } + .wrap { + flex: 1; + flex-shrink: 0; + padding: 20rpx 32rpx; + background-color: #fff; + border-radius: 24rpx; + display: flex; + align-items: center; + .input { + font-size: 32rpx; + color: rgba(1, 1, 5, 1); + } + .input-place { + color: rgba(207, 209, 213, 1); + } + .sub { + flex-shrink: 0; + font-size: 32rpx; + color: rgba(1, 1, 5, 1); + } + } + } + .b-body { + padding: 32rpx 0; + display: flex; + background-color: #fff; + border-radius: 24rpx; + .item { + flex: 1; + padding: 0 48rpx; + .name { + font-size: 32rpx; + color: rgba(161, 164, 172, 1); + } + .content { + margin-top: 16rpx; + display: flex; + justify-content: space-between; + align-items: baseline; + .value { + font-size: 44rpx; + color: rgba(1, 1, 5, 1); + font-weight: bold; + } + .sub { + font-size: 32rpx; + color: rgba(1, 1, 5, 1); + } + } + } + .line { + flex-shrink: 0; + border-right: 1px dashed rgba(247, 247, 250, 1); + } + } + } + .illustrate { + margin-top: 32rpx; + width: 670rpx; + height: 964rpx; + } +} diff --git a/src/doctor/pages/calculator/index.ts b/src/doctor/pages/calculator/index.ts new file mode 100644 index 0000000..46dc720 --- /dev/null +++ b/src/doctor/pages/calculator/index.ts @@ -0,0 +1,27 @@ +const app = getApp() +import { calc } from 'a-calc' + +Page({ + data: { + injectionWeight: '', + + injectionDrugs: '', + injectionNum: '', + }, + onLoad() { + app.waitLogin({ type: 2 }).then(() => {}) + }, + handleWeight(e) { + const value = e.detail.value + if (value) { + const injectionDrugs = calc(`${value} * 1.25 | =0 ~+`) + const injectionNum = calc(`${value} * 1.25 / 2 | =0 ~+`) + this.setData({ + injectionDrugs, + injectionNum, + }) + } + }, +}) + +export {} diff --git a/src/doctor/pages/calculator/index.wxml b/src/doctor/pages/calculator/index.wxml new file mode 100644 index 0000000..86d4246 --- /dev/null +++ b/src/doctor/pages/calculator/index.wxml @@ -0,0 +1,40 @@ +此方案仅供参考,请以最新说明书计算方式为准 + + + + diff --git a/src/doctor/pages/index/index.scss b/src/doctor/pages/index/index.scss index 51a9d5a..ca2098e 100644 --- a/src/doctor/pages/index/index.scss +++ b/src/doctor/pages/index/index.scss @@ -2,14 +2,16 @@ page { background-color: rgba(246, 246, 246, 1); } .page-title { - width: 400rpx; - height: 52rpx; + width: 399rpx; + height: 30rpx; } .page { padding-bottom: 240rpx; - .header { - padding: 46rpx 0 50rpx; + min-height: 100vh; + padding: 0 40rpx; + .page-header { + padding: 54rpx 0 0; .user { padding: 0 32rpx; display: flex; @@ -18,8 +20,8 @@ page { gap: 24rpx; .avatar { flex-shrink: 0; - width: 120rpx; - height: 120rpx; + width: 116rpx; + height: 116rpx; border-radius: 50%; border: 2px solid #fff; } @@ -27,12 +29,13 @@ page { flex: 1; .name { font-size: 40rpx; - color: rgba(255, 255, 255, 1); + color: rgba(1, 1, 5, 1); + font-weight: bold; } .content { - margin-top: 22rpx; + margin-top: 12rpx; font-size: 28rpx; - color: rgba(255, 255, 255, 1); + color: rgba(137, 141, 151, 1); } } .code { @@ -41,361 +44,131 @@ page { height: 72rpx; } } - .patient { - margin-top: 26rpx; - .p-title { - padding-left: 32rpx; - font-size: 32rpx; - color: rgba(255, 255, 255, 1); - .num { - font-size: 48rpx; - font-weight: bold; - } - } - .swiper { - margin-top: 20rpx; - height: 116rpx; - .swiper-item { - padding: 0 12rpx 0; - box-sizing: border-box; - line-height: 1; - .swiper-item-container { - background-color: rgba(255, 255, 255, 0.9); - height: 100%; - border-radius: 24rpx; - padding: 20rpx; - display: flex; - align-items: center; - gap: 12rpx; - box-sizing: border-box; - .avatar { - width: 76rpx; - height: 76rpx; - border-radius: 50%; - } - .wrap { - .name { - font-size: 32rpx; - color: rgba(176, 178, 181, 1); - } - .date { - margin-top: 16rpx; - font-size: 24rpx; - color: rgba(105, 108, 114, 0.6); - } - } - } - &.active { - .swiper-item-container { - background-color: #fff; - box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1); - .wrap { - .name { - color: rgba(28, 107, 255, 1); - } - .date { - color: rgba(28, 107, 255, 0.6); - } - } - } - } - } - } + } + .banner { + margin-top: 46rpx; + height: 130rpx; + .b-img { + display: block; + width: 100%; + height: 100%; } } - .body { - position: relative; - padding: 42rpx 40rpx 0; - background-color: rgba(246, 246, 246, 1); - border-radius: 32rpx 32rpx 0 0; - &::before { - position: absolute; - top: -25rpx; - left: 80rpx; - content: ''; - width: 0; - height: 0; - border-style: solid; - border-width: 0 26rpx 26rpx 26rpx; - border-color: transparent transparent rgba(246, 246, 246, 1) transparent; + .info { + margin-top: 50rpx; + .i-header { + display: flex; + align-items: center; + justify-content: space-between; + .title { + width: 175rpx; + height: 34rpx; + } + .more { + font-size: 28rpx; + color: rgba(1, 1, 5, 0.4); + } } - .doctor { - padding: 32rpx; - border-radius: 32rpx; - background-color: #fff; - .d-header { - display: flex; - align-items: center; - justify-content: space-between; - .title { - font-size: 32rpx; - color: rgba(34, 34, 34, 1); - font-weight: bold; - } - .message { - position: relative; - font-size: 28rpx; - color: rgba(0, 0, 0, 0.6); - .dot { - position: absolute; - right: -7rpx; - top: -7rpx; - width: 16rpx; - height: 16rpx; - border-radius: 50%; - background-color: rgba(232, 70, 60, 1); - } - } + .info-list { + padding-left: 40rpx; + margin: 30rpx -40rpx 0; + overflow-x: auto; + overflow-y: hidden; + display: flex; + gap: 24rpx; + &::-webkit-scrollbar { + display: none; } - .d-container { - margin-top: 32rpx; - display: flex; - align-items: center; - justify-content: space-between; - gap: 16rpx; - .avatar { - flex-shrink: 0; - width: calc(92rpx * 1.5); - display: flex; - .a-img { - flex-shrink: 0; - width: 92rpx; - height: 92rpx; - border-radius: 50%; - &:last-of-type { - transform: translateX(-50%); - } - } - } - .wrap { - flex: 1; - .name { - font-size: 32rpx; - color: rgba(34, 34, 34, 1); - font-weight: bold; - } - .content { - margin-top: 12rpx; - font-size: 28rpx; - color: rgba(0, 0, 0, 0.3); - } + .card { + flex-shrink: 0; + .photo { + width: 424rpx; + height: 246rpx; + border-radius: 24rpx 24rpx 0 0; } - .btn { - flex-shrink: 0; - width: 162rpx; - height: 56rpx; - font-size: 32rpx; - color: rgba(255, 255, 255, 1); - text-align: center; - line-height: 56rpx; - background: #1c6bff; - border-radius: 68rpx 68rpx 68rpx 68rpx; + .content { + padding: 20rpx; + font-size: 28rpx; + color: rgba(1, 1, 5, 1); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + background-color: #fff; + line-height: 32rpx; + border-radius: 0 0 24rpx 24rpx; } } } - .progress { - margin-top: 38rpx; - .p-header { - padding-bottom: 32rpx; - font-size: 32rpx; - color: rgba(34, 34, 34, 1); - font-weight: bold; + } + .patient { + margin-top: 50rpx; + .p-header { + display: flex; + align-items: center; + .title { + width: 236rpx; + height: 34rpx; } + font-size: 36rpx; + color: rgba(28, 107, 255, 1); + } + .patient-list { .card { + margin-top: 24rpx; + padding: 32rpx; + background: linear-gradient(358deg, #ffffff 0%, #f7faff 100%); + border-radius: 24rpx 24rpx 24rpx 24rpx; + border: 2rpx solid #ffffff; display: flex; - gap: 24rpx; - .c-aside { + align-items: center; + justify-content: space-between; + gap: 20rpx; + .photo { flex-shrink: 0; - display: flex; - flex-direction: column; - align-items: center; - .line-top { - flex-shrink: 0; - height: 48rpx; - border-right: 1px dashed rgba(225, 225, 226, 1); - } - .num { - flex-shrink: 0; - width: 56rpx; - height: 56rpx; - border-radius: 50%; - font-size: 40rpx; - text-align: center; - line-height: 56rpx; - color: rgba(255, 255, 255, 1); - background-color: rgba(225, 225, 226, 1); - } - .line-bottom { - flex: 1; - border-right: 1px dashed rgba(225, 225, 226, 1); - } - .hide { - opacity: 0; - } + width: 112rpx; + height: 112rpx; + border-radius: 50%; } - .c-container { - padding-bottom: 24rpx; + .wrap { flex: 1; - .module { - position: relative; - padding: 32rpx; - background-color: #fff; - border-radius: 32rpx; - box-shadow: 0rpx 4rpx 24rpx 0rpx rgba(0, 0, 0, 0.05); - .m-header { - display: flex; - justify-content: space-between; - align-items: center; - .title { - font-size: 32rpx; - color: rgba(171, 174, 180, 1); - display: flex; - align-items: center; - gap: 8rpx; - .tag { - font-size: 28rpx; - color: rgba(171, 174, 180, 1); - line-height: 34rpx; - padding: 0 18rpx; - border-radius: 40rpx 40rpx 40rpx 40rpx; - border: 1rpx solid rgba(171, 174, 180, 0.52); - } - } - .date { - font-size: 28rpx; - color: rgba(171, 174, 180, 1); - } - } - .m-body { - margin-top: 16rpx; - display: flex; - align-items: center; - justify-content: space-between; - .status { - font-size: 28rpx; - color: rgba(171, 174, 180, 1); - display: flex; - align-items: center; - gap: 10rpx; - .icon { - opacity: 0; - width: 36rpx; - height: 36rpx; - } - } - .detail { - font-size: 28rpx; - color: rgba(28, 107, 255, 1); - } - } - .btn { - position: absolute; - top: 48rpx; - right: 32rpx; - width: 162rpx; - height: 56rpx; - display: flex; - align-items: center; - justify-content: center; + .title { + display: flex; + align-items: center; + gap: 24rpx; + .name { font-size: 32rpx; - color: rgba(181, 184, 189, 0.6); - border-radius: 68rpx 68rpx 68rpx 68rpx; - border: 2rpx solid rgba(181, 184, 189, 0.6); - } - } - } - &.finish { - .c-aside { - .line-top { - border-color: rgba(28, 107, 255, 1); - } - .num { - background-color: rgba(14, 209, 167, 1); + color: rgba(1, 1, 5, 1); } - .line-bottom { - border-color: rgba(28, 107, 255, 1); + .date { + font-size: 28rpx; + color: rgba(171, 174, 180, 1); } } - .c-container { - .module { - .m-header { - .title { - color: rgba(34, 34, 34, 1); - } - } - .m-body { - .status { - color: rgba(14, 209, 167, 1); - .icon { - opacity: 1; - } - } - .detail { - color: rgba(28, 107, 255, 1); - } - } - } - } - } - &.current { - .c-aside { - .line-top { - border-color: rgba(28, 107, 255, 1); - } - .num { - background-color: rgba(28, 107, 255, 1); + .content { + margin-top: 20rpx; + padding: 1px; + background-color: rgba(28, 107, 255, 1); + border-radius: 8rpx; + display: inline-flex; + align-items: center; + font-size: 24rpx; + line-height: 32rpx; + .step { + padding: 0 12rpx; + color: #fff; } - } - .c-container { - .module { - .m-header { - .title { - color: rgba(34, 34, 34, 1); - } - } - .m-body { - .status { - color: rgba(34, 34, 34, 1); - } - .detail { - color: rgba(28, 107, 255, 1); - } - } + .s-name { + padding: 0 12rpx; + border-radius: 0 8rpx 8rpx 8rpx; + color: rgba(28, 107, 255, 1); + background-color: #fff; } } } - } - } - .kkd { - margin-top: 32rpx; - display: flex; - align-items: center; - justify-content: space-between; - gap: 22rpx; - .k-item { - padding: 28rpx 24rpx; - flex: 1; - background: linear-gradient(52deg, #ffffff 0%, #eff4ff 100%); - border-radius: 32rpx 32rpx 32rpx 32rpx; - border: 2rpx solid #ffffff; - display: flex; - align-items: center; .icon { - width: 88rpx; - height: 88rpx; - } - .wrap { - padding-left: 14rpx; - .title { - font-size: 28rpx; - color: rgba(34, 34, 34, 1); - line-height: 44rpx; - } - .content { - font-size: 32rpx; - color: rgba(34, 34, 34, 1); - font-weight: bold; - } + flex-shrink: 0; + width: 60rpx; + height: 60rpx; } } } diff --git a/src/doctor/pages/index/index.ts b/src/doctor/pages/index/index.ts index 067b6bb..24b27ee 100644 --- a/src/doctor/pages/index/index.ts +++ b/src/doctor/pages/index/index.ts @@ -1,8 +1,23 @@ -const _app = getApp(); +const _app = getApp() Page({ data: {}, onLoad() {}, -}); + handleCalculator() { + wx.navigateTo({ + url: '/doctor/pages/calculator/index', + }) + }, + handlePatientDetail() { + wx.navigateTo({ + url: '/doctor/pages/patientDetail/index', + }) + }, + handlePoster() { + wx.navigateTo({ + url: '/doctor/pages/poster/index', + }) + }, +}) export {} diff --git a/src/doctor/pages/index/index.wxml b/src/doctor/pages/index/index.wxml index 32afce4..ba07e74 100644 --- a/src/doctor/pages/index/index.wxml +++ b/src/doctor/pages/index/index.wxml @@ -1,11 +1,8 @@ - + - - + + 王青 北京协和医院 - - - - - 您当前跟进 - 4 - 个患者 - - - - - - - 刘可 - 2024.3.12 - - - - + - - - - 共同照护团队 - - 最新消息12条 - - - - - - - - - - 刘湖飞 - 天津血液科诊断中心 - - 去看看> + + + + + + 查看更多 + - - 刘可的检查进展 - - - - 1 - - - - - - 诊前筛查 - 2024-08-24 - - - - 已完成 - - - 查看详情 - - - - - - - - 2 - - - - - - 注射前准备 - - - - 进行中 - - - - - - - - - - 3 - - - - - - - 诊前筛查 - 还未确定注射日 - - - - - 待启动 - - - - - - - - - - 4 - - - - - - 诊前筛查 - - - - 已完成 - - - - 待启动 - - + + + + 什么是基因疗法? 治疗流程了解 - - - - - 他的全部 - 病历档案 - - - - + + + + + (12) + + + + - 查看他的 - 健康变化 + + 刘可 + 入组:2024.3.12 + + + 第一阶段 + 诊前筛选 + + diff --git a/src/doctor/pages/patientDetail/index.json b/src/doctor/pages/patientDetail/index.json new file mode 100644 index 0000000..cf28c8e --- /dev/null +++ b/src/doctor/pages/patientDetail/index.json @@ -0,0 +1,7 @@ +{ + "navigationStyle": "custom", + "usingComponents": { + "navbar": "/components/navbar/index", + "doctor-tab-bar": "/doctor/components/doctor-tab-bar/index" + } +} diff --git a/src/doctor/pages/patientDetail/index.scss b/src/doctor/pages/patientDetail/index.scss new file mode 100644 index 0000000..cf0525e --- /dev/null +++ b/src/doctor/pages/patientDetail/index.scss @@ -0,0 +1,362 @@ +page { + background-color: rgba(246, 246, 246, 1); +} +.nav-title { + color: rgba(255, 255, 255, 1); + &.nav-title_dark { + color: #000; + } +} + +.page { + padding-bottom: 160rpx; + .header { + padding: 46rpx 0 0; + .user { + padding: 0 40rpx; + display: flex; + align-items: center; + justify-content: space-between; + gap: 24rpx; + .avatar { + flex-shrink: 0; + width: 120rpx; + height: 120rpx; + border-radius: 50%; + border: 2px solid #fff; + } + .wrap { + flex: 1; + .w-header { + display: flex; + align-items: center; + gap: 16rpx; + .name { + font-size: 36rpx; + color: rgba(255, 255, 255, 1); + } + .age { + padding: 0 14rpx; + border-radius: 20rpx; + display: flex; + align-items: center; + gap: 6rpx; + font-size: 24rpx; + color: rgba(255, 255, 255, 1); + line-height: 32rpx; + border: 1px solid #fff; + .icon { + width: 28rpx; + height: 28rpx; + } + } + .site { + padding: 0 14rpx; + border-radius: 20rpx; + background-color: rgba(32, 196, 255, 1); + display: flex; + align-items: center; + gap: 6rpx; + font-size: 24rpx; + color: rgba(255, 255, 255, 1); + line-height: 32rpx; + .icon { + width: 28rpx; + height: 28rpx; + } + } + } + .w-footer { + margin-top: 22rpx; + display: flex; + justify-content: space-between; + align-items: center; + .content { + font-size: 28rpx; + color: rgba(255, 255, 255, 1); + } + .doc { + font-size: 28rpx; + color: rgba(255, 255, 255, 1); + } + } + } + } + } + .body { + position: relative; + padding: 42rpx 40rpx 0; + .doctor { + padding: 32rpx; + border-radius: 32rpx; + background-color: #fff; + .d-header { + display: flex; + align-items: center; + justify-content: space-between; + .title { + font-size: 32rpx; + color: rgba(34, 34, 34, 1); + font-weight: bold; + } + .message { + position: relative; + font-size: 28rpx; + color: rgba(0, 0, 0, 0.6); + .dot { + position: absolute; + right: -7rpx; + top: -7rpx; + width: 16rpx; + height: 16rpx; + border-radius: 50%; + background-color: rgba(232, 70, 60, 1); + } + } + } + .d-container { + margin-top: 32rpx; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16rpx; + .avatar { + flex-shrink: 0; + width: calc(92rpx * 1.5); + display: flex; + .a-img { + flex-shrink: 0; + width: 92rpx; + height: 92rpx; + border-radius: 50%; + &:last-of-type { + transform: translateX(-50%); + } + } + } + .wrap { + flex: 1; + .name { + font-size: 32rpx; + color: rgba(34, 34, 34, 1); + font-weight: bold; + } + .content { + margin-top: 12rpx; + font-size: 28rpx; + color: rgba(0, 0, 0, 0.3); + } + } + .btn { + flex-shrink: 0; + width: 162rpx; + height: 56rpx; + font-size: 32rpx; + color: rgba(255, 255, 255, 1); + text-align: center; + line-height: 56rpx; + background: #1c6bff; + border-radius: 68rpx 68rpx 68rpx 68rpx; + } + } + } + .progress { + margin-top: 38rpx; + .p-header { + padding-bottom: 32rpx; + font-size: 36rpx; + color: rgba(34, 34, 34, 1); + font-weight: bold; + } + .card { + display: flex; + gap: 24rpx; + .c-aside { + flex-shrink: 0; + display: flex; + flex-direction: column; + align-items: center; + .line-top { + flex-shrink: 0; + height: 48rpx; + border-right: 1px dashed rgba(225, 225, 226, 1); + } + .num { + flex-shrink: 0; + width: 56rpx; + height: 56rpx; + border-radius: 50%; + font-size: 40rpx; + text-align: center; + line-height: 56rpx; + color: rgba(255, 255, 255, 1); + background-color: rgba(225, 225, 226, 1); + } + .line-bottom { + flex: 1; + border-right: 1px dashed rgba(225, 225, 226, 1); + } + .hide { + opacity: 0; + } + } + .c-container { + padding-bottom: 24rpx; + flex: 1; + .module { + position: relative; + padding: 32rpx; + background-color: #fff; + border-radius: 32rpx; + box-shadow: 0rpx 4rpx 24rpx 0rpx rgba(0, 0, 0, 0.05); + .m-header { + display: flex; + justify-content: space-between; + align-items: center; + .title { + font-size: 32rpx; + color: rgba(171, 174, 180, 1); + display: flex; + align-items: center; + gap: 8rpx; + .tag { + font-size: 28rpx; + color: rgba(171, 174, 180, 1); + line-height: 34rpx; + padding: 0 18rpx; + border-radius: 40rpx 40rpx 40rpx 40rpx; + border: 1rpx solid rgba(171, 174, 180, 0.52); + } + } + .date { + font-size: 28rpx; + color: rgba(171, 174, 180, 1); + } + } + .m-body { + margin-top: 16rpx; + display: flex; + align-items: center; + justify-content: space-between; + .status { + width: 154rpx; + height: 40rpx; + } + .detail { + font-size: 28rpx; + color: rgba(28, 107, 255, 1); + } + } + .btn { + position: absolute; + top: 48rpx; + right: 32rpx; + width: 162rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + color: rgba(181, 184, 189, 0.6); + border-radius: 68rpx 68rpx 68rpx 68rpx; + border: 2rpx solid rgba(181, 184, 189, 0.6); + } + } + } + &.finish { + .c-aside { + .line-top { + border-color: rgba(28, 107, 255, 1); + } + .num { + background-color: rgba(14, 209, 167, 1); + } + .line-bottom { + border-color: rgba(28, 107, 255, 1); + } + } + .c-container { + .module { + .m-header { + .title { + color: rgba(34, 34, 34, 1); + } + } + .m-body { + .status { + color: rgba(14, 209, 167, 1); + .icon { + opacity: 1; + } + } + .detail { + color: rgba(28, 107, 255, 1); + } + } + } + } + } + &.current { + .c-aside { + .line-top { + border-color: rgba(28, 107, 255, 1); + } + .num { + background-color: rgba(28, 107, 255, 1); + } + } + .c-container { + .module { + .m-header { + .title { + color: rgba(34, 34, 34, 1); + } + } + .m-body { + .status { + color: rgba(34, 34, 34, 1); + } + .detail { + color: rgba(28, 107, 255, 1); + } + } + } + } + } + } + } + .kkd { + margin-top: 32rpx; + display: flex; + align-items: center; + justify-content: space-between; + gap: 22rpx; + .k-item { + padding: 28rpx 24rpx; + flex: 1; + background: linear-gradient(52deg, #ffffff 0%, #eff4ff 100%); + border-radius: 32rpx 32rpx 32rpx 32rpx; + border: 2rpx solid #ffffff; + display: flex; + align-items: center; + .icon { + width: 88rpx; + height: 88rpx; + } + .wrap { + padding-left: 14rpx; + .title { + font-size: 28rpx; + color: rgba(34, 34, 34, 1); + line-height: 44rpx; + } + .content { + font-size: 32rpx; + color: rgba(34, 34, 34, 1); + font-weight: bold; + } + } + } + } + } +} diff --git a/src/doctor/pages/patientDetail/index.ts b/src/doctor/pages/patientDetail/index.ts new file mode 100644 index 0000000..c563ebc --- /dev/null +++ b/src/doctor/pages/patientDetail/index.ts @@ -0,0 +1,11 @@ +const _app = getApp() + +Page({ + data: {}, + onLoad() {}, + handleBack() { + wx.navigateBack() + }, +}) + +export {} diff --git a/src/doctor/pages/patientDetail/index.wxml b/src/doctor/pages/patientDetail/index.wxml new file mode 100644 index 0000000..6840707 --- /dev/null +++ b/src/doctor/pages/patientDetail/index.wxml @@ -0,0 +1,164 @@ + + + 患者详情 + + + + + + + + 王青 + + + 32岁 + + + + 北京 + + + + 入组时间:2025年6月12日 + + 查看档案 + + + + + + + + + + 共同照护团队 + + 最新消息12条 + + + + + + + + + + 刘湖飞 + 天津血液科诊断中心 + + 去看看> + + + + 刘可的检查进展 + + + + 1 + + + + + + 诊前筛查 + 2024-08-24 + + + + 查看详情 + + + + + + + + 2 + + + + + + 注射前准备 + + + + + + + + + + + 3 + + + + + + + 诊前筛查 + 还未确定注射日 + + + + + + + + + + + + 4 + + + + + + 诊前筛查 + + + + + 待启动 + + + + + + + + + 他的全部 + 病历档案 + + + + + + 查看他的 + 健康变化 + + + + + diff --git a/src/doctor/pages/poster/index.json b/src/doctor/pages/poster/index.json new file mode 100644 index 0000000..faad7bf --- /dev/null +++ b/src/doctor/pages/poster/index.json @@ -0,0 +1,6 @@ +{ + "navigationBarTitleText": "海报", + "usingComponents": { + "customPoster": "/components/customPoster/index" + } +} diff --git a/src/doctor/pages/poster/index.scss b/src/doctor/pages/poster/index.scss new file mode 100644 index 0000000..db1a6cf --- /dev/null +++ b/src/doctor/pages/poster/index.scss @@ -0,0 +1,29 @@ +page { + background-color: #c65174; +} +.page { + position: relative; + height: 1448rpx; + .poster { + width: 100%; + height: 100%; + } + .btn { + position: absolute; + bottom: 88rpx; + left: 50%; + transform: translateX(-50%); + margin: 36rpx auto; + padding: 18rpx 44rpx; + font-size: 36rpx; + line-height: 48rpx; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; + color: rgba(255, 255, 255, 1); + border-radius: 64rpx 64rpx 64rpx 64rpx; + border: 1px solid rgba(255, 255, 255, 1); + box-sizing: border-box; + } +} diff --git a/src/doctor/pages/poster/index.ts b/src/doctor/pages/poster/index.ts new file mode 100644 index 0000000..9b87de7 --- /dev/null +++ b/src/doctor/pages/poster/index.ts @@ -0,0 +1,114 @@ +const app = getApp() + +Page({ + data: { + doctor: {}, + hospital: {}, + qrCode: '', + + posterUrl: '', + params: {}, + }, + onLoad() { + const SystemInfo = app.globalSystemInfo + if (SystemInfo) { + const { bottom } = SystemInfo.capsulePosition + this.setData({ + bottom, + }) + } + app.waitLogin().then((_res) => { + wx.showLoading({ + title: '加载中', + }) + app.mpBehavior({ doctor: true, PageName: 'PG_DOCTORINVITEPATIENT' }) + this.getDetail() + }) + }, + getDetail() { + wx.ajax({ + method: 'GET', + url: '?r=zd/doctor/account/info', + data: {}, + }).then((res) => { + this.setData({ + doctor: res.doctor, + hospital: res.hospital, + }) + this.getPrepare(res.doctor.Id) + }) + }, + getPrepare(id) { + wx.ajax({ + method: 'POST', + url: '?r=poster/prepare', + data: { + posterId: 'doctor_mini', + doctorId: id, + }, + }).then((res) => { + this.setData({ + params: res, + }) + }) + }, + getQrCode() { + this.setData({ + qrCode: `${app.globalData.url}?r=zd/doctor/account/mp-info&loginState=${app.globalData.loginState}`, + }) + }, + handleDownload() { + wx.showToast({ + title: '请长按海报图片进行保存', + icon: 'none', + }) + // wx.showLoading({ + // title: '加载中', + // }) + // wx.downloadFile({ + // url: this.data.posterUrl, + // success: (res) => { + // if (res.statusCode === 200) { + // wx.saveImageToPhotosAlbum({ + // filePath: res.tempFilePath, + // success: () => { + // wx.hideLoading() + // wx.showToast({ + // title: '保存成功,请到相册中查看', + // }) + // }, + // fail: () => { + // wx.hideLoading() + // wx.showToast({ + // icon: 'none', + // title: '保存失败,请稍后重试', + // }) + // }, + // }) + // } else { + // wx.hideLoading() + // wx.showToast({ + // icon: 'none', + // title: '下载失败,请稍后重试', + // }) + // } + // }, + // fail: () => { + // wx.hideLoading() + // wx.showToast({ + // icon: 'none', + // title: '下载失败,请稍后重试', + // }) + // }, + // }) + }, + handlePosterFinish(e: any) { + this.setData({ + posterUrl: e.detail, + }) + wx.hideLoading() + }, + handleBack() { + wx.navigateBack() + }, +}) diff --git a/src/doctor/pages/poster/index.wxml b/src/doctor/pages/poster/index.wxml new file mode 100644 index 0000000..1fd0ca2 --- /dev/null +++ b/src/doctor/pages/poster/index.wxml @@ -0,0 +1,6 @@ + + + 长按图片保存 + + + diff --git a/src/images/banner2.png b/src/images/banner2.png new file mode 100644 index 0000000..8263615 Binary files /dev/null and b/src/images/banner2.png differ diff --git a/src/images/bg22.png b/src/images/bg22.png new file mode 100644 index 0000000..898c318 Binary files /dev/null and b/src/images/bg22.png differ diff --git a/src/images/icon100.png b/src/images/icon100.png new file mode 100644 index 0000000..a5f5504 Binary files /dev/null and b/src/images/icon100.png differ diff --git a/src/images/icon101.png b/src/images/icon101.png new file mode 100644 index 0000000..6f4e258 Binary files /dev/null and b/src/images/icon101.png differ diff --git a/src/images/icon102.png b/src/images/icon102.png new file mode 100644 index 0000000..c589676 Binary files /dev/null and b/src/images/icon102.png differ diff --git a/src/images/icon103.png b/src/images/icon103.png new file mode 100644 index 0000000..ce73d2a Binary files /dev/null and b/src/images/icon103.png differ diff --git a/src/images/illustrate.png b/src/images/illustrate.png new file mode 100644 index 0000000..3ff6ca5 Binary files /dev/null and b/src/images/illustrate.png differ diff --git a/src/images/title13.png b/src/images/title13.png new file mode 100644 index 0000000..42bdf1c Binary files /dev/null and b/src/images/title13.png differ diff --git a/src/images/title14.png b/src/images/title14.png new file mode 100644 index 0000000..1d98f58 Binary files /dev/null and b/src/images/title14.png differ diff --git a/src/images/title15.png b/src/images/title15.png new file mode 100644 index 0000000..1fd00c8 Binary files /dev/null and b/src/images/title15.png differ diff --git a/src/patient/pages/bbmReport/index.ts b/src/patient/pages/bbmReport/index.ts index 962488f..dff37b9 100644 --- a/src/patient/pages/bbmReport/index.ts +++ b/src/patient/pages/bbmReport/index.ts @@ -1,5 +1,5 @@ const app = getApp() -import { calc, fmt } from 'a-calc' +import { calc } from 'a-calc' Page({ data: { diff --git a/src/patient/pages/bbmReport/index.wxml b/src/patient/pages/bbmReport/index.wxml index bd6bdc7..0af3401 100644 --- a/src/patient/pages/bbmReport/index.wxml +++ b/src/patient/pages/bbmReport/index.wxml @@ -21,7 +21,7 @@ - + {{beginDate}} @@ -14,6 +14,10 @@ + + + 重选 + @@ -33,7 +37,7 @@ - + 其他情况备注 {{item.remark}} diff --git a/src/patient/pages/bloodAdd/index.wxml b/src/patient/pages/bloodAdd/index.wxml index 7e3279a..ea78a07 100644 --- a/src/patient/pages/bloodAdd/index.wxml +++ b/src/patient/pages/bloodAdd/index.wxml @@ -36,7 +36,7 @@ - 您近6个月的出血情况 + 其他情况备注 {{remark.length}} /200 diff --git a/src/patient/pages/bloodDetail/index.wxml b/src/patient/pages/bloodDetail/index.wxml index 377a47c..3eef931 100644 --- a/src/patient/pages/bloodDetail/index.wxml +++ b/src/patient/pages/bloodDetail/index.wxml @@ -13,7 +13,7 @@ - + 其他情况备注 {{detail.remark}} diff --git a/src/patient/pages/casesAdd/index.ts b/src/patient/pages/casesAdd/index.ts index a1e0603..ebb0138 100644 --- a/src/patient/pages/casesAdd/index.ts +++ b/src/patient/pages/casesAdd/index.ts @@ -1,3 +1,4 @@ +const licia = require('miniprogram-licia') import dayjs from 'dayjs' const app = getApp() @@ -249,11 +250,7 @@ Page({ bleedingParts, }) }, - updateSave(e = false) { - let back = false - if (e) { - back = (e as any).currentTarget.dataset.back - } + updateSave(back = false) { const { formKeys, ...reset } = this.data const form: any = formKeys.reduce((pre, cur) => { pre[cur] = reset[cur] @@ -280,12 +277,29 @@ Page({ }) this.time = setTimeout(() => { wx.navigateBack() - }) + }, 2000) } else { this.handlePrevUpdate() } }) }, + handleSubmit() { + const { formKeys, ...reset } = this.data + const empty = formKeys.every((key) => { + if (licia.isObj(reset[key])) { + return licia.isEmpty(1) + } + return !reset[key] + }) + if (empty) { + wx.showToast({ + icon: 'none', + title: '至少选择或填写一项信息', + }) + return + } + this.updateSave(true) + }, handlePrevUpdate() { const pages = getCurrentPages() const currentPage = pages[pages.length - 1] diff --git a/src/patient/pages/casesAdd/index.wxml b/src/patient/pages/casesAdd/index.wxml index 96d453e..7f7c47b 100644 --- a/src/patient/pages/casesAdd/index.wxml +++ b/src/patient/pages/casesAdd/index.wxml @@ -305,7 +305,7 @@ 上一步 - 完成 + 完成 @@ -314,7 +314,7 @@ - + @@ -362,7 +358,7 @@ 当前是注射后 diff --git a/src/patient/pages/file/index.scss b/src/patient/pages/file/index.scss index 80c5cf6..defd649 100644 --- a/src/patient/pages/file/index.scss +++ b/src/patient/pages/file/index.scss @@ -103,6 +103,11 @@ page { .container0 { padding: 8rpx 0 0; + .none { + display: block; + margin: 30rpx auto; + width: 80%; + } .card1 { margin-top: 24rpx; border-radius: 24rpx 24rpx 24rpx 24rpx; @@ -418,6 +423,7 @@ page { display: flex; gap: 32rpx; .label { + flex-shrink: 0; width: 9em; font-size: 32rpx; color: rgba(161, 164, 172, 1); @@ -447,6 +453,7 @@ page { align-items: center; gap: 32rpx; .label { + flex-shrink: 0; width: 11em; font-size: 32rpx; color: rgba(161, 164, 172, 1); @@ -538,6 +545,7 @@ page { position: relative; z-index: 1; padding: 40rpx 32rpx 0; + min-height: 400rpx; background-color: #fff; border-radius: 24rpx; .lengend { diff --git a/src/patient/pages/file/index.ts b/src/patient/pages/file/index.ts index df4b61b..d7ba0ec 100644 --- a/src/patient/pages/file/index.ts +++ b/src/patient/pages/file/index.ts @@ -14,10 +14,11 @@ Page({ fold3: false, fold4: false, - periodOne: {}, - periodTwo: {}, + periodOne: [], + periodTwo: [], periodThree: {}, - periodFour: {}, + periodFour: [], + empty0: false, medical: { lastBleedingInfo: { @@ -36,7 +37,7 @@ Page({ autoChoosedWhenJump: true, firstDayOfWeek: 'Mon', theme: 'geneb', - takeoverTap: true, + takeoverTap: false, highlightToday: true, onlyShowCurrentMonth: true, disableMode: { @@ -81,6 +82,14 @@ Page({ }).then((res) => { this.setData({ ...res, + empty0: + !res.periodOne.length && + !res.periodTwo.length && + !res.periodFour.length && + !res.periodThree.injectionDate && + !res.periodThree.injectionWeight && + !res.periodThree.injectionDrugs && + !res.periodThree.injectionNum, }) }) } @@ -219,6 +228,11 @@ Page({ url: `/patient/pages/preDiagnosisReport/index?examid=${id}&period=${period}`, }) }, + handleInjectionDate() { + wx.navigateTo({ + url: '/patient/pages/injectDate/index', + }) + }, }) export {} diff --git a/src/patient/pages/file/index.wxml b/src/patient/pages/file/index.wxml index 40cc781..4a1be3b 100644 --- a/src/patient/pages/file/index.wxml +++ b/src/patient/pages/file/index.wxml @@ -29,7 +29,7 @@ 随访日历 - + 随访期 @@ -56,7 +56,7 @@ 诊疗医院 - {{item.hospitalName || '--'}} + {{item.hospitalName || '未填写'}} 检查项目 @@ -69,7 +69,10 @@ - + 注射日 @@ -85,9 +88,12 @@ BBM-H901 - 体重 {{periodThree.injectionWeight}}kg - 用药 {{periodThree.injectionDrugs}}ml - 预期用药 {{periodThree.injectionNum}} 瓶 + 体重 {{periodThree.injectionWeight}}kg + 体重 未填写 + 用药 {{periodThree.injectionDrugs}}ml + 用药 未填写 + 预期用药 {{periodThree.injectionNum}} 瓶 + 预期用药 未填写 @@ -103,7 +109,7 @@ - + 基因治疗中心检查 @@ -115,11 +121,11 @@ 日期 {{item.beginDate}}~{{item.endDate}} - -- + 未填写 诊疗医院 - {{item.hospitalName || '--'}} + {{item.hospitalName || '未填写'}} 检查项目 @@ -143,7 +149,7 @@ - + 诊前筛查 @@ -155,11 +161,11 @@ 日期 {{item.beginDate}}~{{item.endDate}} - -- + 未填写 定点医院 - {{item.hospitalName || '--'}} + {{item.hospitalName || '未填写'}} 检查项目 @@ -183,6 +189,7 @@ + @@ -192,53 +199,59 @@ 确诊时间 - {{medical.diagnosisTime}}({{medical.diagnosisAge}}) + + {{medical.diagnosisTime || '未填写'}} + ({{medical.diagnosisAge}}) + 您的体重 - {{medical.weight}} kg + {{medical.weight}} kg +
未填写
凝血因子 - {{medical.clottingFactor}}% + {{medical.clottingFactor}}% + 未填写 治疗方案 - {{medical.treatmentPlanName}} + {{medical.treatmentPlanName || '未填写'}} FIX暴露日 - {{medical.fixExposureDay}} 个 + {{medical.fixExposureDay}}个 + 未填写 既往FIX抑制物病史 - {{medical.beforeFixHistoryName}} + {{medical.beforeFixHistoryName || '未填写'}} 既往肝脏疾病病史 - {{medical.beforeLiverHistoryName}} + {{medical.beforeLiverHistoryName || '未填写'}} - {{medical.liverRecord}} + {{medical.liverRecord}} 您是否有饮酒史 - {{medical.drinkingHistoryName}} + {{medical.drinkingHistoryName || '未填写'}} 您是否有过敏史 - {{medical.allergyHistoryName}} + {{medical.allergyHistoryName || '未填写'}} - {{medical.allergyRecord}} + {{medical.allergyRecord}} @@ -247,9 +260,9 @@ 使用糖皮质激素的情况? - {{medical.isNotGlucocorticoidName}} + {{medical.isNotGlucocorticoidName || '未填写'}} - {{medical.medicalHistoryInfo}} + {{medical.medicalHistoryInfo}}
@@ -257,9 +270,9 @@ 靶关节 - + {{item.bleedingPartName}} - +
@@ -267,16 +280,21 @@ 最近6个月出血 - {{medical.lastBleedingInfo.remark}} + {{medical.lastBleedingInfo.remark}}
- + - 注射日:{{followExam.injectionDateName}} + 注射日:{{followExam.injectionDateName}} + 请填写您的注射日生成随访计划 - + {{followExam.tipsInfo}} {{followExam.lastNoticeWeekInfo}} {{followExam.lastNoticeExamInfo}} diff --git a/src/patient/pages/followPlan/index.scss b/src/patient/pages/followPlan/index.scss index c8fbf60..c8ef32e 100644 --- a/src/patient/pages/followPlan/index.scss +++ b/src/patient/pages/followPlan/index.scss @@ -79,10 +79,9 @@ page { color: rgba(1, 1, 5, 1); line-height: 40rpx; white-space: pre-line; - &:last-of-type { - display: flex; - align-items: center; - } + display: flex; + justify-content: center; + flex-direction: column; } } .tr:nth-child(even) { diff --git a/src/patient/pages/followPlan/index.ts b/src/patient/pages/followPlan/index.ts index e61c30f..644f3ff 100644 --- a/src/patient/pages/followPlan/index.ts +++ b/src/patient/pages/followPlan/index.ts @@ -30,7 +30,7 @@ Page({ }).then((res) => { console.log('res: ', res) this.setData({ - injectionDate: dayjs(res.injectionDate).format('YYYY年MM月DD日 ddd'), + injectionDate: res.injectionDate ? dayjs(res.injectionDate).format('YYYY年MM月DD日 ddd') : '', stage: res.stage, }) }) diff --git a/src/patient/pages/followPlan/index.wxml b/src/patient/pages/followPlan/index.wxml index 89b0989..03b26c8 100644 --- a/src/patient/pages/followPlan/index.wxml +++ b/src/patient/pages/followPlan/index.wxml @@ -1,7 +1,7 @@ - 注射日:{{injectionDate}} + 注射日:{{injectionDate || '未录入'}} 基因治疗后时间监测频率 @@ -19,7 +19,10 @@ 1 周内 - 每周2次 输注后第3天,第6天 + + 每周2次 + 输注后第3天,第6天 + diff --git a/src/patient/pages/index/index.ts b/src/patient/pages/index/index.ts index 68a962f..6bc7d3d 100644 --- a/src/patient/pages/index/index.ts +++ b/src/patient/pages/index/index.ts @@ -8,13 +8,13 @@ Page({ // popupType: 'bindDoctor', // 绑定成功 // popupType: 'bindDoctorQuestion', // 为什么绑定医生 // popupType: 'conformBindDoctorQuestion', // 确认绑定曾经扫码医生 - popupType: 'conformBindDoctorConform', // 注册后绑定医生码 + ptrueopupType: 'conformBindDoctorConform', // 注册后绑定医生码 popupParams: {}, doctorList: [], progress: 1, - swiperHeight: 0, + swiperHeight: 220, treatment: { followUpWeekInfo: {}, examDiffDays: '', @@ -53,6 +53,7 @@ Page({ if (this.data.isLoad) { this.getBindDoctorList() this.getTeratmentInfo() + this.handleSwiperChange({ detail: { current: this.data.progress } }) } }, getBindDoctorList() { diff --git a/src/patient/pages/inhibitorsManage/index.ts b/src/patient/pages/inhibitorsManage/index.ts index 013dc6e..413320c 100644 --- a/src/patient/pages/inhibitorsManage/index.ts +++ b/src/patient/pages/inhibitorsManage/index.ts @@ -66,11 +66,14 @@ Page({ }) }, handleDrug() { - const { dict } = this.data + const { dict, drugId, drugName } = this.data this.setData({ popupShow: true, popupType: 'inhibitors', popupParams: { + drugId: drugId, + drugName: dict.drugList[drugId] == '其他' ? '其他' : drugName, + content: dict.drugList[drugId] == '其他' ? drugName : '', list: dict.drugList, }, }) diff --git a/src/patient/pages/inhibitorsManageDetail/index.ts b/src/patient/pages/inhibitorsManageDetail/index.ts index 5cb31f0..9fac190 100644 --- a/src/patient/pages/inhibitorsManageDetail/index.ts +++ b/src/patient/pages/inhibitorsManageDetail/index.ts @@ -18,13 +18,16 @@ Page({ dosageUnitIndex: 0, }, onLoad(options) { - console.log('DEBUGPRINT[71]: index.ts:29: options=', options) this.setData({ period: options.period, + recordId: options.id || '', }) + }, + onShow() { app.waitLogin({ type: 1 }).then(() => { - if (options.id) { - this.getDetail(options.id) + const recordId = this.data.recordId + if (recordId) { + this.getDetail(recordId) } }) }, diff --git a/src/patient/pages/preDiagnosisReport/index.ts b/src/patient/pages/preDiagnosisReport/index.ts index 35c50dc..f9b71d1 100644 --- a/src/patient/pages/preDiagnosisReport/index.ts +++ b/src/patient/pages/preDiagnosisReport/index.ts @@ -110,6 +110,11 @@ Page({ examId: options.examid || '', period: options.period || '1', }) + if (options.period === '2') { + wx.setNavigationBarTitle({ + title: '我的检查报告', + }) + } }, onShow() { const { formKeys, period } = this.data