lhl 1 viikko sitten
vanhempi
commit
bbc45e42f3

+ 73 - 0
api/signing.js

@@ -0,0 +1,73 @@
+import request from '@/utils/request'
+
+//创建签约
+export function createSigning(data) {
+	return request({
+		url: '/api/Education/form_save',
+		method: 'post',
+		data
+	});
+}
+
+//获取签约列表
+export function getContractList(data) {
+	return request({
+		url: '/api/Education/getContractList',
+		method: 'get',
+		data
+	});
+}
+
+//甲方签约
+export function Jcheck(data) {
+	return request({
+		url: '/api/Education/check',
+		method: 'post',
+		data
+	});
+}
+
+//乙方签约
+export function tjCheck(data) {
+	return request({
+		url: '/api/Education/to_check',
+		method: 'post',
+		data
+	});
+}
+
+//取消签约
+export function cancelSig(data) {
+	return request({
+		url: '/api/Education/lift_contract',
+		method: 'post',
+		data
+	});
+}
+
+//评价
+export function goPj(data) {
+	return request({
+		url: '/api/Education/comment',
+		method: 'post',
+		data
+	});
+}
+
+//打卡
+export function goDk(data) {
+	return request({
+		url: '/api/Education/clock_in',
+		method: 'post',
+		data
+	});
+}
+
+//打卡记录
+export function getClockList(data) {
+	return request({
+		url: '/api/Education/getClockList',
+		method: 'get',
+		data
+	});
+}

+ 83 - 0
components/am-sign-input/README.md

@@ -0,0 +1,83 @@
+### 使用方法
+* 注意
++ 同一个页面不同的输入框需要设置不同的canvasId和canvasIds,否则在同一个页面会出现冲突
+```
+<template>
+	<view class="content">
+		<signInput ref="sign" canvasId="twoDrowCanvas" canvasIds="twoRotateCanvas" :header="header" :action="action"
+			@signToUrl="signToUrl">
+		</signInput>
+	</view>
+</template>
+```
+```
+<script>
+	import signInput from "@/components/am-sign-input/am-sign-input.vue"
+	export default {
+		components: {
+			signInput
+		},
+		data() {
+			return {
+				action: "", //上传服务器的地址
+				header: {}, //图片上传携带头部信息
+			}
+		},
+		methods: {
+			/**
+			 * @param {Object} e
+			 * 签名完成回调
+			 */
+			signToUrl(e) {
+				if (e.error_code && e.error_code === '201') {
+					uni.showToast({
+						title: e.msg,
+						icon: 'none'
+					})
+					return
+				}
+			},
+		}
+	}
+</script>
+```
+```
+<style lang="scss">
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+	}
+</style>
+```
+
+#### 实际效果演示H5
+- 打开演示后,按F12调整到手机调试模式查看效果
+[实际效果演示](https://static-mp-2766f90e-0e50-4c71-87fb-0ab51aedcf85.next.bspapp.com/signInput/#/)
+
+### 参数说明Props
+
+参数|类型|说明|必传
+---|---|---|---
+action|String|生成图片后上传的接口地址|true
+canvasId|String|canvasId|true
+canvasIds|String|canvasIds与上一个id不可重复|true
+header|Object|文件上传携带的头部属性|true
+outSignWidth|Number|输出图片文件大小-宽度|false
+outSignHeight|Number|输出图片文件大小-高度|false
+minSpeed|Number|画笔最小速度|false
+minWidth|Number|线条最小粗度|false
+maxWidth|Number|线条最大粗度|false
+openSmooth|Boolean|开启平滑线条(笔锋)|false
+maxHistoryLength|Number|历史最大长度(用于撤销的步数)|false
+maxWidthDiffRate|Number|最大差异率|false
+undoScan|Number|撤销重新渲染偏移缩放校准|false
+bgColor|String|背景色如#ffffff 不传则为透明|false
+
+### 相关同源插件
+- 以页面形式展现
+- [电子签名组件](https://ext.dcloud.net.cn/plugin?id=5768)
+
+### 相关致谢
+- 插件参考 [大佬的npm库](https://github.com/linjc/smooth-signature)

+ 797 - 0
components/am-sign-input/am-sign-input.vue

@@ -0,0 +1,797 @@
+<template>
+	<view class="sign">
+		<view class="imgBox">
+			<template v-if="type == 1">
+				<view class="" @click="signModShow=true">
+					<slot></slot>
+				</view>
+			</template>
+			<template v-if="type == 0">
+				<view class="nom_img" v-if="!showImg" @click="signModShow=true">
+					<image v-if="!showImg" src="/static/other/signs.png" style="width: 34px;height: 34px;">
+					</image>
+				</view>
+				<view class="across_img" v-if="showImg">
+					<view v-if="showImg" class="delete_icon" @click.stop="deleteImg">
+						x
+					</view>
+					<image v-if="showImg" :src="showImg" style="width: 140px;height: 80px;"
+						@click="previewImg(showImg)">
+					</image>
+				</view>
+			</template>
+
+
+		</view>
+
+		<umask :show="signModShow" @click="signModShow=false" :duration="0">
+			<view class="warp">
+				<view class="signBox" @tap.stop>
+					<view class="wrapper">
+						<view class="handBtn">
+							<!-- #ifdef MP-WEIXIN -->
+							<image @click="selectColorEvent('black','#1A1A1A')"
+								:src="selectColor === 'black' ? '/static/other/color_black_selected.png' : '/static/other/color_black.png'"
+								class="black-select"></image>
+							<image @click="selectColorEvent('red','#ca262a')"
+								:src="selectColor === 'red' ? '/static/other/color_red_selected.png' : '/static/other/color_red.png'"
+								class="red-select"></image>
+							<!-- #endif -->
+							<!-- #ifndef MP-WEIXIN -->
+							<view class="color_pic" :style="{background:lineColor}" @click="showPickerColor=true">
+							</view>
+							<!-- #endif -->
+							<button @click="clear" class="delBtn">清空</button>
+							<button @click="saveCanvasAsImg" class="saveBtn">保存</button>
+							<button @click="previewCanvasImg" class="previewBtn">预览</button>
+							<button @click="subCanvas" class="subBtn">完成</button>
+							<button @click="undo" class="undoBtn">撤销</button>
+							<span class="emptyInfo" style="color: red;" v-if="emptyShow">你还没有绘制任何东西哦</span>
+						</view>
+						<view class="handCenter" :style="{left:canvasLeft+'px'}">
+							<canvas :disable-scroll="true" @touchstart="uploadScaleStart" @touchmove="uploadScaleMove"
+								@touchend="uploadScaleEnd" :style="{width:'100%',height:'calc(85vh - 8rpx)'}"
+								:canvas-id="canvasId"></canvas>
+						</view>
+
+						<view class="handCenters">
+							<canvas :canvas-id="canvasIds"
+								:style="{width:outSignWidth+'px',height:outSignHeight+'px'}"></canvas>
+						</view>
+						<view class="handRight">
+							<view class="handTitle">请签名
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</umask>
+		<pickerColor :isShow="showPickerColor" :bottom="0" @callback='getPickerColor' />
+	</view>
+</template>
+
+<script>
+	import signInput from "@/components/am-sign-input/am-sign-input.vue"
+	import umask from "./u-mask/u-mask.vue"
+	import pickerColor from "./pickerColor.vue"
+	export default {
+		components: {
+			umask,
+			pickerColor,
+			signInput
+		},
+		data() {
+			return {
+				canvasLeft: 10000,
+				emptyShow: false,
+				signModShow: false,
+				showImg: "",
+				showPickerColor: false,
+				ctx: '',
+				ctxs: '',
+				canvasWidth: 0,
+				canvasHeight: 0,
+				selectColor: 'black',
+				lineColor: '#1A1A1A',
+				points: [],
+				historyList: [],
+				canAddHistory: true,
+				getImagePath: () => {
+					let that = this
+					return new Promise((resolve) => {
+						uni.canvasToTempFilePath({
+							canvasId: that.canvasId,
+							fileType: 'png',
+							quality: 1, //图片质量
+							success: res => resolve(res.tempFilePath)
+						}, this)
+					})
+				},
+				requestAnimationFrame: void 0,
+			};
+		},
+		watch: {
+			signModShow(newValue, oldValue) {
+				newValue ? this.canvasLeft = 74 : this.canvasLeft = 10000
+			}
+		},
+		props: { //可用于修改的参数放在props里   也可单独放在外面做成组件调用  传值
+			type: {
+				type: Number,
+				default: 0
+			},
+			action: {
+				type: String,
+				default: ''
+			},
+			canvasId: {
+				type: String,
+				default: 'canvasDr'
+			},
+			canvasIds: {
+				type: String,
+				default: 'canvasRo'
+			},
+			header: {
+				type: Object,
+				default: {}
+			},
+			outSignWidth: {
+				type: Number,
+				default: 54 * 3
+			},
+			outSignHeight: {
+				type: Number,
+				default: 24 * 3
+			},
+			minSpeed: { //画笔最小速度
+				type: Number,
+				default: 1.5
+			},
+			minWidth: { //线条最小粗度
+				type: Number,
+				default: 3,
+			},
+			maxWidth: { //线条最大粗度
+				type: Number,
+				default: 10
+			},
+			openSmooth: { //开启平滑线条(笔锋)
+				type: Boolean,
+				default: true
+			},
+			maxHistoryLength: { //历史最大长度
+				type: Number,
+				default: 20
+			},
+			maxWidthDiffRate: { //最大差异率
+				type: Number,
+				default: 20
+			},
+			undoScan: { //撤销重新渲染偏移缩放校准
+				type: Number,
+				default: 0.83
+			},
+			bgColor: { //背景色
+				type: String,
+				default: ''
+			},
+		},
+		mounted() {
+			if (!this.ctx) {
+				this.ctx = uni.createCanvasContext(this.canvasId, this);
+			}
+			if (!this.ctxs) {
+				this.ctxs = uni.createCanvasContext(this.canvasIds, this);
+			}
+			let that = this
+			this.$nextTick(() => {
+				uni.createSelectorQuery().in(this).select('.handCenter').boundingClientRect(rect => {
+						that.canvasWidth = rect.width;
+						that.canvasHeight = rect.height;
+						that.drawBgColor()
+					})
+					.exec();
+			})
+		},
+		methods: {
+			getPickerColor(color) {
+				this.showPickerColor = false;
+				if (color) {
+					this.lineColor = color;
+				}
+			},
+			// 笔迹开始
+			uploadScaleStart(e) {
+				this.canAddHistory = true
+				this.ctx.setStrokeStyle(this.lineColor)
+				this.ctx.setLineCap("round") //'butt'、'round'、'square'
+			},
+			// 笔迹移动
+			uploadScaleMove(e) {
+				let temX = e.changedTouches[0].x
+				let temY = e.changedTouches[0].y
+				this.initPoint(temX, temY)
+				this.onDraw()
+			},
+			/**
+			 * 触摸结束
+			 */
+			uploadScaleEnd() {
+				this.canAddHistory = true;
+				this.points = [];
+			},
+			/**
+			 * 记录点属性
+			 */
+			initPoint(x, y) {
+				var point = {
+					x: x,
+					y: y,
+					t: Date.now()
+				};
+				var prePoint = this.points.slice(-1)[0];
+				if (prePoint && (prePoint.t === point.t || prePoint.x === x && prePoint.y === y)) {
+					return;
+				}
+				if (prePoint && this.openSmooth) {
+					var prePoint2 = this.points.slice(-2, -1)[0];
+					point.distance = Math.sqrt(Math.pow(point.x - prePoint.x, 2) + Math.pow(point.y - prePoint.y, 2));
+					point.speed = point.distance / (point.t - prePoint.t || 0.1);
+					point.lineWidth = this.getLineWidth(point.speed);
+					if (prePoint2 && prePoint2.lineWidth && prePoint.lineWidth) {
+						var rate = (point.lineWidth - prePoint.lineWidth) / prePoint.lineWidth;
+						var maxRate = this.maxWidthDiffRate / 100;
+						maxRate = maxRate > 1 ? 1 : maxRate < 0.01 ? 0.01 : maxRate;
+						if (Math.abs(rate) > maxRate) {
+							var per = rate > 0 ? maxRate : -maxRate;
+							point.lineWidth = prePoint.lineWidth * (1 + per);
+						}
+					}
+				}
+				this.points.push(point);
+				this.points = this.points.slice(-3);
+			},
+			/**
+			 * @param {Object} 
+			 * 线宽
+			 */
+			getLineWidth(speed) {
+				var minSpeed = this.minSpeed > 10 ? 10 : this.minSpeed < 1 ? 1 : this.minSpeed; //1.5
+				var addWidth = (this.maxWidth - this.minWidth) * speed / minSpeed;
+				var lineWidth = Math.max(this.maxWidth - addWidth, this.minWidth);
+				return Math.min(lineWidth, this.maxWidth);
+			},
+			/**
+			 * 绘画逻辑
+			 */
+			onDraw() {
+				if (this.points.length < 2) return;
+				this.addHistory();
+				var point = this.points.slice(-1)[0];
+				var prePoint = this.points.slice(-2, -1)[0];
+				let that = this
+				var onDraw = function onDraw() {
+					if (that.openSmooth) {
+						that.drawSmoothLine(prePoint, point);
+					} else {
+						that.drawNoSmoothLine(prePoint, point);
+					}
+				};
+				if (typeof this.requestAnimationFrame === 'function') {
+					this.requestAnimationFrame(function() {
+						return onDraw();
+					});
+				} else {
+					onDraw();
+				}
+			},
+			//添加历史图片地址
+			addHistory() {
+				if (!this.maxHistoryLength || !this.canAddHistory) return;
+				this.canAddHistory = false;
+				if (!this.getImagePath) {
+					this.historyList.length++;
+					return;
+				}
+				//历史地址 (暂时无用)
+				let that = this
+				that.getImagePath().then(function(url) {
+					if (url) {
+						that.historyList.push(url)
+						that.historyList = that.historyList.slice(-that.maxHistoryLength);
+					}
+				});
+			},
+			//画平滑线
+			drawSmoothLine(prePoint, point) {
+				var dis_x = point.x - prePoint.x;
+				var dis_y = point.y - prePoint.y;
+
+				if (Math.abs(dis_x) + Math.abs(dis_y) <= 2) {
+					point.lastX1 = point.lastX2 = prePoint.x + dis_x * 0.5;
+					point.lastY1 = point.lastY2 = prePoint.y + dis_y * 0.5;
+				} else {
+					point.lastX1 = prePoint.x + dis_x * 0.3;
+					point.lastY1 = prePoint.y + dis_y * 0.3;
+					point.lastX2 = prePoint.x + dis_x * 0.7;
+					point.lastY2 = prePoint.y + dis_y * 0.7;
+				}
+				point.perLineWidth = (prePoint.lineWidth + point.lineWidth) / 2;
+				if (typeof prePoint.lastX1 === 'number') {
+					this.drawCurveLine(prePoint.lastX2, prePoint.lastY2, prePoint.x, prePoint.y, point.lastX1, point
+						.lastY1, point.perLineWidth);
+					if (prePoint.isFirstPoint) return;
+					if (prePoint.lastX1 === prePoint.lastX2 && prePoint.lastY1 === prePoint.lastY2) return;
+					var data = this.getRadianData(prePoint.lastX1, prePoint.lastY1, prePoint.lastX2, prePoint.lastY2);
+					var points1 = this.getRadianPoints(data, prePoint.lastX1, prePoint.lastY1, prePoint.perLineWidth / 2);
+					var points2 = this.getRadianPoints(data, prePoint.lastX2, prePoint.lastY2, point.perLineWidth / 2);
+					this.drawTrapezoid(points1[0], points2[0], points2[1], points1[1]);
+				} else {
+					point.isFirstPoint = true;
+				}
+			},
+			//画不平滑线
+			drawNoSmoothLine(prePoint, point) {
+				point.lastX = prePoint.x + (point.x - prePoint.x) * 0.5;
+				point.lastY = prePoint.y + (point.y - prePoint.y) * 0.5;
+				if (typeof prePoint.lastX === 'number') {
+					this.drawCurveLine(prePoint.lastX, prePoint.lastY, prePoint.x, prePoint.y, point.lastX, point.lastY,
+						this.maxWidth);
+				}
+			},
+			//画线
+			drawCurveLine(x1, y1, x2, y2, x3, y3, lineWidth) {
+				lineWidth = Number(lineWidth.toFixed(1));
+				this.ctx.setLineWidth && this.ctx.setLineWidth(lineWidth);
+				this.ctx.lineWidth = lineWidth;
+				this.ctx.beginPath();
+				this.ctx.moveTo(Number(x1.toFixed(1)), Number(y1.toFixed(1)));
+				this.ctx.quadraticCurveTo(Number(x2.toFixed(1)), Number(y2.toFixed(1)), Number(x3.toFixed(1)), Number(y3
+					.toFixed(1)));
+				this.ctx.stroke();
+				this.ctx.draw && this.ctx.draw(true);
+			},
+			//画梯形
+			drawTrapezoid(point1, point2, point3, point4) {
+				this.ctx.beginPath();
+				this.ctx.moveTo(Number(point1.x.toFixed(1)), Number(point1.y.toFixed(1)));
+				this.ctx.lineTo(Number(point2.x.toFixed(1)), Number(point2.y.toFixed(1)));
+				this.ctx.lineTo(Number(point3.x.toFixed(1)), Number(point3.y.toFixed(1)));
+				this.ctx.lineTo(Number(point4.x.toFixed(1)), Number(point4.y.toFixed(1)));
+				this.ctx.setFillStyle && this.ctx.setFillStyle(this.lineColor);
+				this.ctx.fillStyle = this.lineColor;
+				this.ctx.fill();
+				this.ctx.draw && this.ctx.draw(true);
+			},
+			//获取弧度
+			getRadianData(x1, y1, x2, y2) {
+				var dis_x = x2 - x1;
+				var dis_y = y2 - y1;
+				if (dis_x === 0) {
+					return {
+						val: 0,
+						pos: -1
+					};
+				}
+				if (dis_y === 0) {
+					return {
+						val: 0,
+						pos: 1
+					};
+				}
+				var val = Math.abs(Math.atan(dis_y / dis_x));
+				if (x2 > x1 && y2 < y1 || x2 < x1 && y2 > y1) {
+					return {
+						val: val,
+						pos: 1
+					};
+				}
+				return {
+					val: val,
+					pos: -1
+				};
+			},
+			//获取弧度点
+			getRadianPoints(radianData, x, y, halfLineWidth) {
+				if (radianData.val === 0) {
+					if (radianData.pos === 1) {
+						return [{
+							x: x,
+							y: y + halfLineWidth
+						}, {
+							x: x,
+							y: y - halfLineWidth
+						}];
+					}
+					return [{
+						y: y,
+						x: x + halfLineWidth
+					}, {
+						y: y,
+						x: x - halfLineWidth
+					}];
+				}
+				var dis_x = Math.sin(radianData.val) * halfLineWidth;
+				var dis_y = Math.cos(radianData.val) * halfLineWidth;
+				if (radianData.pos === 1) {
+					return [{
+						x: x + dis_x,
+						y: y + dis_y
+					}, {
+						x: x - dis_x,
+						y: y - dis_y
+					}];
+				}
+				return [{
+					x: x + dis_x,
+					y: y - dis_y
+				}, {
+					x: x - dis_x,
+					y: y + dis_y
+				}];
+			},
+			/**
+			 * 背景色
+			 */
+			drawBgColor() {
+				if (!this.bgColor) return;
+				this.ctx.setFillStyle && this.ctx.setFillStyle(this.bgColor);
+				this.ctx.fillStyle = this.bgColor;
+				this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
+				this.ctx.draw && this.ctx.draw(true);
+			},
+			//图片绘制
+			drawByImage(url) {
+				this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
+				try {
+					this.ctx.drawImage(url, 0, 0, this.canvasWidth * this.undoScan, this.canvasHeight * this.undoScan);
+					this.ctx.draw && this.ctx.draw(true);
+				} catch (e) {
+					this.historyList.length = 0;
+				}
+			},
+			/**
+			 * 清空
+			 */
+			clear() {
+				this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
+				this.ctx.draw && this.ctx.draw();
+				this.drawBgColor();
+				this.historyList.length = 0;
+			},
+			//撤消
+			undo() {
+				if (!this.getImagePath || !this.historyList.length) return;
+				var pngURL = this.historyList.splice(-1)[0];
+				this.drawByImage(pngURL);
+				if (this.historyList.length === 0) {
+					this.clear();
+				}
+			},
+			//是否为空
+			isEmpty() {
+				return this.historyList.length === 0;
+			},
+			/**
+			 * @param {Object} str
+			 * @param {Object} color
+			 * 选择颜色
+			 */
+			selectColorEvent(str, color) {
+				this.selectColor = str;
+				this.lineColor = color;
+				this.ctx.setStrokeStyle(this.lineColor)
+			},
+
+			//完成
+			subCanvas() {
+				let that = this
+				if (that.isEmpty()) {
+					that.emptyShow = true
+					setTimeout(function() {
+						that.emptyShow = false
+					}, 1000)
+					return
+				}
+				uni.canvasToTempFilePath({
+					canvasId: that.canvasId,
+					fileType: 'png',
+					quality: 1, //图片质量
+					success(res) {
+						that.ctxs.translate(0, that.outSignHeight);
+						that.ctxs.rotate(-90 * Math.PI / 180)
+						that.ctxs.drawImage(res.tempFilePath, 0, 0, that.outSignHeight, that.outSignWidth)
+						that.ctxs.draw()
+						setTimeout(() => {
+							uni.canvasToTempFilePath({
+								canvasId: that.canvasIds,
+								fileType: 'png',
+								quality: 1, //图片质量
+								success: function(res1) {
+									if (that.action) {
+										uni.showLoading()
+										uni.uploadFile({
+											url: that.action, //图片上传post请求的地址
+											filePath: res1.tempFilePath,
+											name: "file",
+											header: that.header,
+											success: (uploadFileRes) => {
+												uni.hideLoading()
+												that.showImg = res1.tempFilePath
+												that.$emit('signToUrl',
+													uploadFileRes)
+												that.signModShow = false
+												that.clear()
+											},
+											fail: (error) => {
+												uni.hideLoading()
+											}
+										});
+									} else {
+										that.showImg = res1.tempFilePath
+										that.$emit('signToUrl', {
+											error_code: "201",
+											msg: "请配置上传文件接口参数action"
+										})
+										that.signModShow = false
+										that.clear()
+									}
+								},
+								fail: (err) => {}
+							}, that)
+						}, 200);
+					}
+				}, this);
+			},
+			//保存到相册
+			saveCanvasAsImg() {
+				uni.canvasToTempFilePath({
+					canvasId: this.canvasId,
+					fileType: 'png',
+					quality: 1, //图片质量
+					success(res) {
+						uni.saveImageToPhotosAlbum({
+							filePath: res.tempFilePath,
+							success(res) {
+								uni.showToast({
+									title: '已保存到相册',
+									duration: 2000
+								});
+							}
+						});
+					}
+				}, this);
+			},
+			//预览
+			previewCanvasImg() {
+				uni.canvasToTempFilePath({
+					canvasId: this.canvasId,
+					fileType: 'jpg',
+					quality: 1, //图片质量
+					success(res) {
+						uni.previewImage({
+							urls: [res.tempFilePath] //预览图片 数组
+						});
+					}
+				}, this);
+			},
+			deleteImg() {
+				this.showImg = ""
+			},
+
+			previewImg(img) {
+				uni.previewImage({
+					urls: [img] //预览图片 数组
+				});
+			},
+
+		}
+	};
+</script>
+
+<style lang="scss">
+	// page {
+	// 	background: #d9d9d9;
+	// 	height: auto;
+	// 	overflow: hidden;
+	// }
+
+	.imgBox {
+		// width: 140px;
+		// height: 80px;
+		// position: relative;
+
+		.nom_img {
+			border-radius: 8px;
+			border: 1px dashed;
+			border-color: #a3a3a3;
+			overflow: hidden;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			height: 80px;
+			width: 140px;
+		}
+
+		.nom_img:hover {
+			border-color: #008ef6 !important;
+		}
+
+		.across_img {
+			border: 1px dashed #a3a3a3;
+			border-radius: 8px;
+			height: 80px;
+			width: 140px;
+
+			.delete_icon {
+				position: absolute;
+				top: -12px;
+				right: -12px;
+				width: 24px;
+				height: 24px;
+				overflow: hidden;
+				color: #ffffff;
+				font-size: 24px;
+				text-align: center;
+				line-height: 20px;
+				background: #ff3c0c;
+				border-radius: 25px;
+				z-index: 1;
+			}
+		}
+	}
+
+	.warp {
+		width: 100%;
+		height: 100vh;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+
+		.signBox {
+			width: 85vw;
+			height: 85vh;
+			background: #ffffff;
+			border-radius: 8px;
+		}
+	}
+
+	.wrapper {
+		width: 85vw;
+		height: 85vh;
+		overflow: hidden;
+		display: flex;
+		align-content: center;
+		flex-direction: row;
+		justify-content: center;
+		font-size: 28rpx;
+	}
+
+	.handRight {
+		display: inline-flex;
+		align-items: center;
+	}
+
+	.handCenter {
+		position: fixed;
+		border: 4rpx dashed #e9e9e9;
+		flex: 5;
+		margin-top: 4rpx;
+		overflow: hidden;
+		box-sizing: border-box;
+		width: calc(100% - 84rpx - 200rpx);
+		height: calc(85vh - 8rpx)
+	}
+
+	.handCenters {
+		position: fixed;
+		top: 0;
+		left: 10000rpx;
+		flex: 5;
+		overflow: hidden;
+		box-sizing: border-box;
+	}
+
+
+	.handTitle {
+		transform: rotate(90deg);
+		flex: 1;
+		color: #666;
+	}
+
+	.handBtn button {
+		font-size: 28rpx;
+	}
+
+	.handBtn {
+		height: 85vh;
+		display: inline-flex;
+		flex-direction: column;
+		justify-content: space-between;
+		align-content: space-between;
+		flex: 1;
+	}
+
+	.delBtn {
+		position: absolute;
+		top: 380rpx;
+		left: 46rpx;
+		transform: rotate(90deg);
+		color: #666;
+	}
+
+	.subBtn {
+		position: absolute;
+		bottom: 158rpx;
+		left: 46rpx;
+		display: inline-flex;
+		transform: rotate(90deg);
+		background: #008ef6;
+		color: #fff;
+		text-align: center;
+		justify-content: center;
+	}
+
+	/*Peach - 新增 - 保存*/
+
+	.saveBtn {
+		position: absolute;
+		top: 650rpx;
+		left: 46rpx;
+		transform: rotate(90deg);
+		color: #666;
+	}
+
+	.previewBtn {
+		position: absolute;
+		top: 516rpx;
+		left: 46rpx;
+		transform: rotate(90deg);
+		color: #666;
+	}
+
+	.undoBtn {
+		position: absolute;
+		top: 780rpx;
+		left: 46rpx;
+		transform: rotate(90deg);
+		color: #666;
+	}
+
+	.emptyInfo {
+		position: absolute;
+		bottom: 418rpx;
+		left: -56rpx;
+		transform: rotate(90deg);
+		color: #666;
+	}
+
+	.color_pic {
+		width: 70rpx;
+		height: 70rpx;
+		border-radius: 25px;
+		position: absolute;
+		top: 200rpx;
+		left: 62rpx;
+		border: 1px solid #ddd;
+	}
+
+	/*Peach - 新增 - 保存*/
+
+	.black-select {
+		width: 60rpx;
+		height: 60rpx;
+		position: absolute;
+		top: 150rpx;
+		left: 70rpx;
+	}
+
+	.red-select {
+		width: 60rpx;
+		height: 60rpx;
+		position: absolute;
+		top: 260rpx;
+		left: 70rpx;
+	}
+</style>

+ 143 - 0
components/am-sign-input/pickerColor.vue

@@ -0,0 +1,143 @@
+<template>
+	<view v-show="isShow">
+		<view class="shade" @tap="hide">
+			<view class="pop">
+				<view class="list flex_col" v-for="(item,index) in colorArr" :key="index">
+					<view v-for="(v,i) in item" :key="i" :style="{'backgroundColor':v}" :data-color="v"
+						:data-index="index" :data-i="i" :class="{'active':(index==pickerArr[0] && i==pickerArr[1])}"
+						@tap.stop="picker"></view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'picker-color',
+		props: {
+			isShow: {
+				type: Boolean,
+				default: false,
+			},
+			bottom: {
+				type: Number,
+				default: 0,
+			}
+		},
+		data() {
+			return {
+				colorArr: [
+					['#000000', '#111111', '#222222', '#333333', '#444444', '#666666', '#999999', '#CCCCCC', '#EEEEEE',
+						'#FFFFFF'
+					],
+					['#ff0000', '#ff0033', '#ff3399', '#ff33cc', '#cc00ff', '#9900ff', '#cc00cc', '#cc0099', '#cc3399',
+						'#cc0066'
+					],
+					['#cc3300', '#cc6600', '#ff9933', '#ff9966', '#ff9999', '#ff99cc', '#ff99ff', '#cc66ff', '#9966ff',
+						'#cc33ff'
+					],
+					['#663300', '#996600', '#996633', '#cc9900', '#a58800', '#cccc00', '#ffff66', '#ffff99', '#ffffcc',
+						'#ffcccc'
+					],
+					['#336600', '#669900', '#009900', '#009933', '#00cc00', '#66ff66', '#339933', '#339966', '#009999',
+						'#33cccc'
+					],
+					['#003366', '#336699', '#3366cc', '#0099ff', '#000099', '#0000cc', '#660066', '#993366', '#993333',
+						'#800000'
+					]
+				],
+				pickerColor: '',
+				pickerArr: [-1, -1]
+			};
+		},
+		methods: {
+			picker(e) {
+				let data = e.currentTarget.dataset;
+				this.pickerColor = data.color;
+				this.pickerArr = [data.index, data.i];
+				this.$emit("callback", this.pickerColor);
+			},
+			hide() {
+				this.$emit("callback", '');
+			},
+		},
+	}
+</script>
+
+<style scoped>
+	.shade {
+		position: fixed;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		left: 0;
+		background-color: rgba(0, 0, 0, 0.5);
+		z-index: 10080;
+		display: flex;
+		justify-content: center;
+		align-items: center
+	}
+
+	.pop {
+		border-radius: 8px;
+		background-color: #fff;
+		z-index: 100;
+		padding: 12upx;
+		font-size: 32upx;
+		transform: rotate(90deg);
+	}
+
+	.flex_col {
+		display: flex;
+		flex-direction: row;
+		flex-wrap: nowrap;
+		justify-content: flex-start;
+		align-items: center;
+		align-content: center;
+	}
+
+	.list {
+		justify-content: space-between;
+	}
+
+	.list>view {
+		width: 60upx;
+		height: 60upx;
+		margin: 5upx;
+		box-sizing: border-box;
+		border-radius: 3px;
+		box-shadow: 0 0 2px #ccc;
+	}
+
+	.list .active {
+		box-shadow: 0 0 2px #09f;
+		transform: scale(1.05, 1.05);
+	}
+
+	.preview {
+		width: 180upx;
+		height: 60upx;
+	}
+
+	.value {
+		margin: 0 40upx;
+		flex-grow: 1;
+	}
+
+	.ok {
+		width: 160upx;
+		height: 60upx;
+		line-height: 60upx;
+		text-align: center;
+		background-color: #ff9933;
+		color: #fff;
+		border-radius: 4px;
+		letter-spacing: 3px;
+		font-size: 32upx;
+	}
+
+	.ok:active {
+		background-color: rgb(255, 107, 34);
+	}
+</style>

+ 124 - 0
components/am-sign-input/u-mask/u-mask.vue

@@ -0,0 +1,124 @@
+<template>
+	<view class="u-mask" hover-stop-propagation :style="[maskStyle, zoomStyle]" @tap="click"
+		@touchmove.stop.prevent="() => {}" :class="{
+		'u-mask-zoom': zoom,
+		'u-mask-show': show
+	}">
+		<slot />
+	</view>
+</template>
+
+<script>
+	/** 
+	 * mask 遮罩
+	 * @description 创建一个遮罩层,用于强调特定的页面元素,并阻止用户对遮罩下层的内容进行操作,一般用于弹窗场景
+	 * @tutorial https://www.uviewui.com/components/mask.html
+	 * @property {Boolean} show 是否显示遮罩(默认false)
+	 * @property {String Number} z-index z-index 层级(默认1070)
+	 * @property {Object} custom-style 自定义样式对象,见上方说明
+	 * @property {String Number} duration 动画时长,单位毫秒(默认300)
+	 * @property {Boolean} zoom 是否使用scale对遮罩进行缩放(默认true)
+	 * @property {Boolean} mask-click-able 遮罩是否可点击,为false时点击不会发送click事件(默认true)
+	 * @event {Function} click mask-click-able为true时,点击遮罩发送此事件
+	 * @example <u-mask :show="show" @click="show = false"></u-mask>
+	 */
+	export default {
+		name: "u-mask",
+		props: {
+			// 是否显示遮罩
+			show: {
+				type: Boolean,
+				default: false
+			},
+			// 层级z-index
+			zIndex: {
+				type: [Number, String],
+				default: '10070'
+			},
+			// 用户自定义样式
+			customStyle: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			// 遮罩的动画样式, 是否使用使用zoom进行scale进行缩放
+			zoom: {
+				type: Boolean,
+				default: true
+			},
+			// 遮罩的过渡时间,单位为ms
+			duration: {
+				type: [Number, String],
+				default: 300
+			},
+			// 是否可以通过点击遮罩进行关闭
+			maskClickAble: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				zoomStyle: {
+					transform: ''
+				},
+				scale: 'scale(1.2, 1.2)'
+			}
+		},
+		watch: {
+			show(n) {
+				if (n && this.zoom) {
+					// 当展示遮罩的时候,设置scale为1,达到缩小(原来为1.2)的效果
+					this.zoomStyle.transform = 'scale(1, 1)';
+				} else if (!n && this.zoom) {
+					// 当隐藏遮罩的时候,设置scale为1.2,达到放大(因为显示遮罩时已重置为1)的效果
+					this.zoomStyle.transform = this.scale;
+				}
+			}
+		},
+		computed: {
+			maskStyle() {
+				let style = {};
+				style.backgroundColor = "rgba(0, 0, 0, 0.6)";
+				if (this.show) style.zIndex = this.zIndex ? this.zIndex : this.$u.zIndex.mask;
+				else style.zIndex = -1;
+				style.transition = `all ${this.duration / 1000}s ease-in-out`;
+				// 判断用户传递的对象是否为空,不为空就进行合并
+				if (Object.keys(this.customStyle).length) style = {
+					...style,
+					...this.customStyle
+				};
+				return style;
+			}
+		},
+		methods: {
+			click() {
+				if (!this.maskClickAble) return;
+				this.$emit('click');
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	// @import "../../libs/css/style.components.scss";
+
+	.u-mask {
+		position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		opacity: 0;
+		transition: transform 0.3s;
+	}
+
+	.u-mask-show {
+		opacity: 1;
+	}
+
+	.u-mask-zoom {
+		transform: scale(1.2, 1.2);
+	}
+</style>

+ 1 - 1
components/empty/empty.vue

@@ -36,7 +36,7 @@
 		justify-content: center;
 		flex-direction: column;
 		
-		position: fixed;
+		position: absolute;
 		left: 0;
 		top: 0;
 		right: 0;

+ 8 - 3
manifest.json

@@ -1,6 +1,6 @@
 {
     "name" : "母婴界严选",
-    "appid" : "__UNI__FA8FAF8",
+    "appid" : "__UNI__DCA211F",
     "description" : "",
     "versionName" : "1.0.19",
     "versionCode" : 122,
@@ -164,9 +164,14 @@
             "urlCheck" : true,
             "minified" : true
         },
-        "permission" : {},
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "你的位置信息将用于查看与打卡点的距离"
+            }
+        },
         "lazyCodeLoading" : "requiredComponents",
-        "requiredBackgroundModes" : [ "location" ]
+        "requiredBackgroundModes" : [ "location" ],
+        "requiredPrivateInfos" : [ "getLocation", "chooseLocation" ]
     },
     "h5" : {
         "title" : "母婴界严选",

+ 24 - 0
pages.json

@@ -204,6 +204,30 @@
 				"style": {
 					"navigationBarTitleText": "排行榜"
 				}
+			},
+			{
+				"path": "signing/signing",
+				"style": {
+					"navigationBarTitleText": "签约"
+				}
+			},
+			{
+				"path": "signing/dk",
+				"style": {
+					"navigationBarTitleText": "打卡"
+				}
+			},
+			{
+				"path": "signing/dkhis",
+				"style": {
+					"navigationBarTitleText": "打卡历史"
+				}
+			},
+			{
+				"path": "signing/mySig",
+				"style": {
+					"navigationBarTitleText": "我的预约"
+				}
 			}
 			
 		]

+ 16 - 0
pages/user/model/model.vue

@@ -82,6 +82,7 @@
 				{{userTemplate.bookmark_count||0}}
 			</view>
 		</view>
+		<image src="/static/image/qy.png" mode="widthFix" class="qianyue" @click="goQy" v-if="userInfo.uid && userInfo.uid != uid"></image>
 	</view>
 </template>
 
@@ -273,6 +274,11 @@
 		},
 		methods: {
 			...mapMutations('user', ['setUserInfo']),
+			goQy() {
+				uni.navigateTo({
+					url:'/pages/user/signing/signing?id=' + this.uid
+				})
+			},
 			onDz(type) {
 				if (type == 1) {
 					if (this.userTemplate.is_like == 0) {
@@ -624,4 +630,14 @@
 			text-align: center;
 		}
 	}
+	.qianyue {
+		width: 98rpx;
+		height: 108rpx;
+		position: fixed;
+		right: 20rpx;
+		// top:300rpx;
+		bottom: 0;
+		top: 0;
+		margin: auto;
+	}
 </style>

+ 204 - 0
pages/user/signing/dk.vue

@@ -0,0 +1,204 @@
+<template>
+	<view class="content">
+		<view class="dk-title" @click="goDkHis">
+			打卡历史
+		</view>
+		<map name="" class="map" :latitude="latitude" :longitude="longitude" :markers="markers"></map>
+		<view class="info">
+			<text class="info-tit">打卡时段</text> <text class="info-val">{{today}} 00:00-23:59</text>
+		</view>
+		<view class="info">
+			<text class="info-tit">打卡地点</text> <text class="info-val">{{address}}</text>
+		</view>
+		<view class="info" v-for="(item, index) in list">
+			<text class="info-tit">打卡记录({{index + 1}})</text> <text class="info-val">{{item.create_time}}</text>
+		</view>
+		<view class="dk flex" @click="dk">
+			<view class="dk-main flex">
+				<view class="dk-main-tit">
+					立即打卡
+				</view>
+				<view class="dk-main-time">
+					{{time}}
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { goDk, getClockList } from '@/api/signing.js'
+	
+	const today = new Date();
+	const year = today.getFullYear();
+	const month = today.getMonth() + 1; // 月份从0开始,需要加1
+	const day = today.getDate();
+	const date = `${year}-${month}-${day}`
+	export default {
+		data() {
+			return {
+				loading: false,
+				id: 0,
+				today: date,
+				address: '',
+				timer: null,
+				time: '',
+				latitude: '',
+				longitude: '',
+				latitudes: '',
+				longitudes: '',
+				markers: [],
+				list: []
+			}
+
+		},
+		onLoad(opt) {
+			if (opt.latitude) {
+				this.id = opt.id
+				this.latitude = opt.latitude
+				this.longitude = opt.longitude
+				this.address = opt.address
+				this.markers.push({
+					id: 1,
+					latitude: this.latitude,
+					longitude: this.longitude,
+					title: '打卡点',
+					iconPath: '/static/icon/shopAddress.png',
+					width: 20,
+					height: 20
+				})
+			}
+			this.getLocation()
+			this.getDkHis()
+			if (this.timer) {
+				clearInterval(this.timer);
+			}
+			this.updateTime();
+			// 设置定时器每秒更新时间
+			this.timer = setInterval(() => {
+				this.updateTime();
+			}, 1000);
+		},
+		methods: {
+			goDkHis() {
+				uni.navigateTo({
+					url:'/pages/user/signing/dkhis?id=' + this.id
+				})
+			},
+			getLocation() {
+				let that = this
+				uni.getLocation({
+					type: 'gcj02',
+					success(res) {
+						that.latitudes = res.latitude
+						that.longitudes = res.longitude
+						console.log(that.latitudes,that.longitudes);
+					},
+					fail(err) {
+						console.log(err,'err');
+					}
+				})
+			},
+			updateTime() {
+				const now = new Date();
+				const hours = String(now.getHours()).padStart(2, '0');
+				const minutes = String(now.getMinutes()).padStart(2, '0');
+				const seconds = String(now.getSeconds()).padStart(2, '0');
+				this.time = `${hours}:${minutes}:${seconds}`;
+			},
+			dk() {
+				if(this.loading) return;
+				this.loading = true
+				goDk({
+					contract_id: this.id,
+					latitude: this.latitudes,
+					longitude: this.longitudes
+				}).then(res => {
+					this.getDkHis()
+					uni.showToast({
+						title: '打卡成功',
+						duration: 2000
+					});
+					setTimeout(()=> {
+						this.loading = false
+					})
+				}).catch(err => {
+					this.loading = false
+				})
+			},
+			getDkHis() {
+				getClockList({
+					contract_id: this.id,
+					page:1,
+					limit: 10,
+					time: this.today
+				}).then(res => {
+					// console.log(res);
+					this.list = res.data.list
+				})
+			}
+		},
+		onHide() {
+			if (this.timer) {
+				clearInterval(this.timer);
+			}
+		}
+
+	}
+</script>
+
+<style lang="scss" scoped>
+	.content {
+		background-color: #fff;
+		height: 100vh;
+	}
+	.dk-title {
+		padding: 20rpx;
+		text-align: right;
+		font-weight: bold;
+		font-size: 28rpx;
+		color: #0066FF;
+	}
+	.map {
+		width: 710rpx;
+		height: 500rpx;
+		margin:0 auto 40rpx;
+	}
+
+	.info {
+		font-size: 26rpx;
+		padding-left: 28rpx;
+		margin: 20rpx 0;
+		&-tit {
+			display: inline-block;
+			width: 160rpx;
+			color: #6E6E6E;
+		}
+
+		&-val {
+			color: #1B1B1B;
+		}
+	}
+
+	.dk {
+		width: 290rpx;
+		height: 290rpx;
+		margin: 50rpx auto;
+		border-radius: 50%;
+		border: 3rpx solid #0066FF;
+		justify-content: center;
+		align-items: center;
+
+		&-main {
+			flex-direction: column;
+			justify-content: center;
+			align-items: center;
+			width: 268rpx;
+			height: 268rpx;
+			background: #0066FF;
+			border-radius: 50%;
+			color: #FFFFFF;
+			font-size: 38rpx;
+		}
+	}
+</style>

+ 88 - 0
pages/user/signing/dkhis.vue

@@ -0,0 +1,88 @@
+<template>
+	<view class="content">
+		<view class="his" v-for="item in list">
+			<view class="his-tit">
+				日期:{{item.create_time.split(' ')[0]}}
+			</view>
+			<view class="his-info">
+				打卡状态:已打卡
+			</view>
+			<view class="his-info">
+				打卡时间:{{item.create_time.split(' ')[1]}}
+			</view>
+		</view>
+		<empty v-if="list.length == 0 && loaded"></empty>
+		<uni-load-more v-else :status="loading"></uni-load-more>
+	</view>
+</template>
+
+<script>
+	import empty from '@/components/empty/empty.vue'
+	import { getClockList } from '@/api/signing.js'
+	export default {
+		components: {
+			empty
+		},
+		data() {
+			return {
+				loaded: false,
+				list: [],
+				loading: 'more',
+				id: 0,
+				page: 1,
+				limit: 10
+			}
+		},
+		methods: {
+			getList() {
+				if(this.loading == 'loading' || this.loading == 'noMore') return;
+				this.loading = 'loading'
+				getClockList({
+					contract_id: this.id,
+					page: this.page,
+					limit: this.limit
+				}).then(res => {
+					// console.log(res);
+					this.list = this.list.concat(res.data.list)
+					if(res.data.list.length == this.limit) {
+						this.loading = 'more'
+					}else {
+						this.loading = 'noMore'
+					}
+					this.loaded = true
+				})
+			},
+		},
+		onLoad(opt) {
+			this.id = opt.id
+			this.getList()
+		},
+		onReachBottom() {
+			this.getList()
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.content {
+		height: 100vh;
+		background-color: #fff;
+	}
+	.his {
+		&-tit {
+			padding-left: 28rpx;
+			height: 68rpx;
+			line-height: 68rpx;
+			background: #EDF8FE;
+			font-size: 32rpx;
+			color: #000000;
+		}
+		&-info {
+			padding-left: 28rpx;
+			font-size: 28rpx;
+			color: #000000;
+			line-height: 55rpx;
+			margin: 20rpx 0;
+		}
+	}
+</style>

+ 697 - 0
pages/user/signing/mySig.vue

@@ -0,0 +1,697 @@
+<template>
+	<view class="content">
+		<view class="nav flex">
+			<view class="nav-item" v-for="(item,index) in navList" :key="index" :class="{'action': index == current}"
+				@click="tabChange(index)">
+				{{item.tit}}
+			</view>
+		</view>
+		<scroll-view scroll-y="true" class="good-content" :style="{'height': height}" @scrolltolower="getList">
+			<view>
+				
+				<view class="sig" v-for="item in navList[current].list" @click="check(item)">
+					<view class="sig-status" :style="{backgroundColor: item.status == 1 ? '#ff6e15': ( item.status == 2 ? '#31c98f': '#fd4664')}">
+						{{showStatus(item)}}
+					</view>
+					<view class="sig-tit">
+						服务预约
+					</view>
+					<view class="sig-info">
+						<text class="sig-info-tit">客户姓名</text><text class="sig-info-val">{{item.name}}</text>
+					</view>
+					<view class="sig-info">
+						<text class="sig-info-tit">联系方式</text><text class="sig-info-val">{{item.phone}}</text>
+					</view>
+					<view class="sig-info">
+						<text class="sig-info-tit">地址</text><text class="sig-info-val">{{item.address}}</text>
+					</view>
+					<view class="sig-info">
+						<text class="sig-info-tit">母婴师</text><text class="sig-info-val">{{item.to_name}}</text>
+					</view>
+					<view class="sig-info">
+						<text class="sig-info-tit">服务费</text><text class="sig-info-val">{{item.price}}</text>
+					</view>
+					<view class="sig-info">
+						<text class="sig-info-tit">定金</text><text class="sig-info-val">{{item.deposit}}</text>
+					</view>
+					<view class="sig-info">
+						<text class="sig-info-tit">开始时间</text><text class="sig-info-val">{{item.start_time | showTime}}</text>
+					</view>
+					<view class="sig-info">
+						<text class="sig-info-tit">结束时间</text><text class="sig-info-val">{{item.end_time | showTime}}</text>
+					</view>
+					<view class="sig-btn">
+						<signInput
+							v-if="!item.uid_img && userInfo.uid == item.uid && item.status == 0"
+							:ref="'sign' + index" :canvasId="'twoDrowCanvas' + index"
+							:type="1"
+							:canvasIds="'twoRotateCanvas' + index" :header="header" :action="action"
+							@signToUrl="signToUrl">
+							<view class="sig-btn-base">
+								签约
+							</view>
+						</signInput>
+						<view class="sig-btn-base" v-if="!item.to_uid_img && userInfo.uid == item.to_uid && item.status == 0" @click="openY(item)">
+							签约
+						</view>
+						<view class="sig-btn-base sig-btn-cancel"
+							v-if="item.status == 0" @click="cancelSig(item)">
+							取消
+						</view>
+						<!--  -->
+						
+						<view class="sig-btn-base" v-if="userInfo.uid == item.uid && item.status == 1 && !item.comment"  @click="pj(item)">
+							评价
+						</view>
+						<view class="sig-btn-base" v-if="userInfo.uid == item.to_uid && item.status == 1"  @click="dk(item)">
+							打卡
+						</view>
+						<view class="sig-btn-base sig-btn-cancel" v-if="item.comment && item.comment.id"  @click="lookPj(item)">
+							查看评价
+						</view>
+					</view>
+				</view>
+				<empty v-if="navList[current].list.length == 0 && navList[current].loaded"></empty>
+				<uni-load-more v-else :status="navList[current].loading"></uni-load-more>
+			</view>
+		</scroll-view>
+		<uni-popup ref="popup" type="center">
+			<view class="listBox">
+				<view class="list">
+					<view class="flex listItem">
+						<view class="flex titleBox">
+							<text class="font-color-red font-size-sm">✲</text> <text class="title">联系方式</text>
+						</view>
+						<view class="right flex">
+							<input class="input" v-model="partyB.phone" type="number" placeholder="请填写联系电话"
+								placeholder-class="placeholder" />
+						</view>
+					</view>
+				</view>
+				<view class="list">
+					<view class="flex listItem">
+						<view class="flex titleBox">
+							<text class="font-color-red font-size-sm">✲</text> <text class="title">地址</text>
+						</view>
+						<view class="right flex">
+							<input class="input" v-model="partyB.address" type="text" placeholder="请填写地址"
+								placeholder-class="placeholder" />
+						</view>
+					</view>
+				</view>
+				<view class="list">
+					<view class="flex listItem">
+						<view class="flex titleBox">
+							<text class="font-color-red font-size-sm">✲</text> <text class="title">身份证号</text>
+						</view>
+						<view class="right flex">
+							<input class="input" v-model="partyB.idcard" type="idcard" placeholder="请填写身份证号"
+								placeholder-class="placeholder" />
+						</view>
+					</view>
+				</view>
+				<view class="list">
+					<view class="flex listItem" style="border-bottom: none;">
+						<view class="flex titleBox">
+							<text class="font-color-red font-size-sm">✲</text> <text class="title">电子签名</text>
+						</view>
+					</view>
+				</view>
+				<view class=" qm-wrap">
+					<signInput
+						ref="sign" canvasId="twoDrowCanvas"
+						canvasIds="twoRotateCanvas" :header="header" :action="action"
+						@signToUrl="signToUrl">
+					</signInput>
+				</view>
+				<view class="btn" @click="goYqy"> 
+					提交
+				</view>
+			</view>
+		</uni-popup>
+		<uni-popup ref="pjPopup" type="center">
+			<view class="listBox" style="padding-top: 40rpx;">
+				<textarea name="" id="" cols="30" rows="10" placeholder="请输入您的评价" v-model="pjs" maxlength="200" class="pj-wrap"></textarea>
+				<view class="btn" @click="goPj">
+					提交
+				</view>
+			</view>
+		</uni-popup>
+		<uni-popup ref="lookPjPopup" type="center">
+			<view class="listBox" style="padding-top: 40rpx;">
+				<view class="pj-tit">
+					客户评价
+				</view>
+				<textarea name="" id="" cols="30" rows="10"  v-model="checkItem.comment.content" maxlength="200" class="pj-wrap" disabled></textarea>
+			</view>
+		</uni-popup>
+	</view>
+
+</template>
+
+<script>
+	import signInput from "@/components/am-sign-input/am-sign-input.vue"
+	import empty from '@/components/empty/empty.vue'
+	import {
+		mapState
+	} from "vuex"
+	import {
+		getContractList,
+		Jcheck,
+		tjCheck,
+		cancelSig,
+		goPj
+	} from '@/api/signing.js'
+	export default {
+		components: {
+			signInput,
+			empty
+		},
+		computed: {
+			// #ifdef H5
+			...mapState(['urlFile']),
+			// #endif
+			...mapState(['baseURL']),
+			...mapState('user', ['userInfo', 'hasLogin']),
+			action() {
+				return this.baseURL + '/api/user/qiniuUpload' //上传服务器的地址
+			}
+		},
+		data() {
+			return {
+				pjs: '',
+				partyB: {
+					address: '',
+					phone: '',
+					idcard: '',
+				},
+				checkItem: {},
+				header: {
+					"token": uni.getStorageSync('token')
+				}, //图片上传携带头部信息
+				qm: '',
+				height: '',
+				current: 0,
+				loading: false,
+				navList: [{
+						tit: '待签约',
+						status: 0,
+						list: [],
+						loading: 'more',
+						page: 1,
+						limit: 10,
+						loaded: false
+					},
+					{
+						tit: '进行中',
+						status: 1,
+						list: [],
+						loading: 'more',
+						page: 1,
+						limit: 10,
+						loaded: false
+					},
+					{
+						tit: '已结束',
+						status: 2,
+						list: [],
+						loading: 'more',
+						page: 1,
+						limit: 10,
+						loaded: false
+					}
+				]
+			}
+		},
+		onReady(res) {
+			var that = this;
+			uni.getSystemInfo({
+				success: resu => {
+					const query = uni.createSelectorQuery();
+					query.select('.good-content').boundingClientRect();
+					query.exec(function(res) {
+						that.height = resu.windowHeight - res[0].top + 'px';
+					});
+				},
+				fail: res => {}
+			});
+		},
+		filters: {
+			showTime(val) {
+				let date = new Date(val*1000)
+				const year = date.getFullYear();
+				const month = String(date.getMonth() + 1).padStart(2, '0');
+				const day = String(date.getDate()).padStart(2, '0');
+				return `${year}-${month}-${day}`;
+			}
+		},
+		methods: {
+			dk(item) {
+				uni.navigateTo({
+					url:'/pages/user/signing/dk?id=' + item.id + '&latitude=' + item.latitude + '&longitude=' + item.longitude + '&address=' + item.address
+				})
+			},
+			showStatus(item) {
+				if(item.status == 0) {
+					if(this.userInfo.uid == item.uid) {
+						if(!item.uid_img) {
+							return '待签约'
+						}else {
+							return '待乙方签约'
+						}
+					}else {
+						if(!item.to_uid_img) {
+							return '待签约'
+						}else {
+							return '待甲方签约'
+						}
+					}
+				}else if(item.status == 1){
+					return '进行中'
+				}else if(item.status == 2) {
+					return '已完成'
+				}
+			},
+			cancelSig(item) {
+				cancelSig({
+					contract_id: item.id
+				}).then(res => {
+					uni.showToast({
+						title: '取消成功',
+						duration: 2000
+					});
+					this.getList('re')
+				})
+			},
+			goYqy() {
+				let that = this
+				if (!that.partyB.phone) {
+					return uni.showModal({
+						title: '温馨提醒',
+						content: '请输入联系电话',
+						showCancel: false,
+					});
+					
+				}
+				if (!that.partyB.address) {
+					return uni.showModal({
+						title: '温馨提醒',
+						content: '请输入地址',
+						showCancel: false,
+					});
+					
+				}
+				if (!that.partyB.idcard) {
+					return uni.showModal({
+						title: '温馨提醒',
+						content: '请输入身份证号',
+						showCancel: false,
+					});
+					
+				}
+				if(!that.qm) {
+					return uni.showModal({
+						title: '温馨提醒',
+						content: '请完成电子签名',
+						showCancel: false,
+					});
+				}
+				this.goQy()
+			},
+			pj(item) {
+				this.checkItem = item
+				this.$refs.pjPopup.open()
+			},
+			lookPj(item) {
+				this.checkItem = item
+				this.$refs.lookPjPopup.open()
+			},
+			goPj() {
+				if(!this.pjs) {
+					return uni.showModal({
+						title: '温馨提醒',
+						content: '请填写评价',
+						showCancel: false,
+					});
+				}
+				if(this.loading) return;
+				this.loading = true
+				goPj({
+					contract_id: this.checkItem.id,
+					content: this.pjs
+				}).then(res => {
+					
+					this.$refs.pjPopup.close()
+					uni.showToast({
+						title: '评价成功',
+						duration: 2000
+					});
+					this.loading = false
+				}).catch(err => {
+					this.loading = false
+				})
+			},
+			openY(item) {
+				this.checkItem = item
+				this.$refs.popup.open()
+			},
+			check(item) {
+				this.checkItem = item
+			},
+			signToUrl(uploadFileRes) {
+				console.log(uploadFileRes, 'uploadFileResuploadFileResuploadFileRes')
+				let data = {}
+				if ("string" === typeof uploadFileRes.data) {
+					data = JSON.parse(uploadFileRes.data).data
+				} else {
+					data = uploadFileRes.data.data
+				}
+				this.qm = data.img
+				if (this.userInfo.uid == this.checkItem.uid) {
+					this.goQy()
+				}
+			},
+			goQy() {
+				let that = this
+				let date = ''
+				const today = new Date();
+				const year = today.getFullYear();
+				const month = String(today.getMonth() + 1).padStart(2, '0');
+				const day = String(today.getDate()).padStart(2, '0');
+				date = `${year}-${month}-${day}`;
+
+				if (that.loading) return;
+				that.loading = true
+				let qy = null;
+				let data = {}
+				if (that.userInfo.uid == that.checkItem.uid) {
+					qy = Jcheck
+					data = {
+						id: that.checkItem.id,
+						uid_img: that.qm,
+						check_time: date
+					}
+				} else {
+					qy = tjCheck
+					data = {
+						to_uid_img: that.qm,
+						to_check_time: date,
+						id: that.checkItem.id,
+						to_phone: that.partyB.phone,
+						to_address: that.partyB.phone,
+						to_card: that.partyB.idcard
+					}
+					
+				}
+				qy(data).then(res => {
+					that.qm = ''
+					if(that.checkItem.to_uid == that.userInfo.uid) {
+						this.$refs.popup.close()
+					}
+					that.checkItem = {}
+					uni.showToast({
+						title: '签约成功',
+						duration: 2000
+					});
+					that.getList('re')
+					that.loading = false
+					
+				}).catch(err => {
+					this.loading = false
+				})
+			},
+			tabChange(index) {
+				this.current = index
+				this.getList('tab')
+			},
+			getList(type) {
+				let that = this
+				let item = that.navList[that.current]
+				if (type == 're') {
+					item.page = 1
+					item.loading = 'more'
+					item.loaded = false
+					item.list = []
+				}
+				if (type == 'tab' && item.loaded) return;
+				if (item.loading == 'noMore' || item.loading == 'loading') return;
+				item.loading = 'loading'
+				getContractList({
+					page: item.page,
+					limit: item.limit,
+					status: item.status,
+					uid: that.userInfo.uid
+				}).then(res => {
+					let arr = res.data.list
+					item.list = item.list.concat(arr)
+					item.page++
+					if (arr.length == item.limit) {
+						item.loading = 'more'
+					} else {
+						item.loading = 'noMore'
+					}
+					item.loaded = true
+				})
+			}
+		},
+		onLoad() {
+			this.getList()
+		},
+		onShow() {
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.nav {
+		height: 80rpx;
+		background-color: #fff;
+
+		.nav-item {
+			line-height: 80rpx;
+			// width: 33.3%;
+			flex: 1;
+			text-align: center;
+			font-size: 30rpx;
+
+		}
+
+		.action {
+			font-weight: bold;
+			position: relative;
+
+			&::after {
+				position: absolute;
+				left: 0;
+				right: 0;
+				bottom: 0rpx;
+				margin: auto;
+				content: '';
+				width: 62rpx;
+				height: 4rpx;
+				background: #FC4564;
+				border-radius: 2rpx 2rpx 2rpx 2rpx;
+			}
+		}
+	}
+
+	.good-content {
+		padding-top: 1rpx;
+	}
+
+	.sig {
+		width: 688rpx;
+		background: #FFFFFF;
+		border-radius: 20rpx 20rpx 20rpx 20rpx;
+		margin: 20rpx auto;
+		padding: 35rpx;
+		padding-bottom: 20rpx;
+		position: relative;
+		&-status {
+			position: absolute;
+			right: 0;
+			top: 50rpx;
+			// width: 112rpx;
+			padding: 0 10rpx;
+			height: 40rpx;
+			background: #FC4564;
+			border-radius: 10rpx 0rpx 0rpx 10rpx;
+			text-align: center;
+			font-size: 24rpx;
+			line-height: 40rpx;
+			color: #fff;
+		}
+		&-tit {
+			font-weight: bold;
+			font-size: 32rpx;
+			color: #1E324D;
+			margin-bottom: 20rpx;
+		}
+
+		&-info {
+			margin: 10rpx 0;
+
+			&-tit {
+				display: inline-block;
+				width: 160rpx;
+				font-size: 26rpx;
+				color: #667177;
+			}
+
+			&-val {
+				font-size: 26rpx;
+				color: #1E3350;
+			}
+		}
+
+		&-line {
+			width: 616rpx;
+			height: 1rpx;
+			background: #eff0f1;
+			margin: auto;
+		}
+
+		&-btn {
+			padding-top: 20rpx;
+			display: flex;
+			justify-content: flex-end;
+
+			&-base {
+				width: 158rpx;
+				height: 58rpx;
+				line-height: 58rpx;
+				background: #FC4564;
+				border-radius: 28rpx 28rpx 28rpx 28rpx;
+				font-size: 26rpx;
+				color: #FFFFFF;
+				text-align: center;
+				margin-left: 20rpx;
+			}
+
+			&-cancel {
+				background: #fff;
+				border: 2rpx solid #E0E3E4;
+				font-size: 26rpx;
+				color: #999;
+			}
+		}
+	}
+	.listBox {
+		width: 690rpx;
+		// margin: $page-row-spacing;
+		margin-top: 30rpx;
+		border-radius: 20rpx;
+		overflow: hidden;
+		background-color: #FFFFFF;
+		padding-bottom: 40rpx;
+		.btn {
+			margin: 20rpx auto 0;
+			width: 400rpx;
+			background: #FC4564;
+			border-radius: 10rpx 10rpx 10rpx 10rpx;
+			font-size: 26rpx;
+			color: #FFFFFF;
+			text-align: center;
+			height: 58rpx;
+			line-height: 58rpx;
+		}
+	}
+	
+	.list {
+		
+		.input {
+			text-align: right;
+			font-size: $font-base;
+			color: $color-gray;
+			width: 100%;
+		}
+	
+		.listItem {
+			padding: 35rpx 40rpx;
+			border-bottom: 1px solid $page-color-light;
+	
+			.ql-editor.ql-blank:before {
+				font-style: normal;
+			}
+	
+			.textarea {
+				font-size: $font-base;
+				width: 100%;
+				word-wrap: break-word;
+				white-space: pre-line;
+				min-height: 9rem;
+			}
+		}
+	
+		.listIconImg {
+			width: 36rpx;
+		}
+	
+		.right {
+			color: $font-color-light;
+			font-size: $font-base;
+			flex-grow: 1;
+			justify-content: flex-end;
+	
+			.timetype {
+				width: 100%;
+				justify-content: flex-end;
+			}
+	
+			.citylist {
+				.del {
+					color: $color-red;
+					font-size: $font-sm;
+					border: 1px solid $color-red;
+					border-radius: 10rpx;
+					line-height: 1;
+					padding: 5rpx 15rpx;
+				}
+			}
+	
+			.img {
+				width: 26rpx;
+			}
+	
+			.buttom {
+				color: $base-color;
+				border: 1px solid $base-color;
+				border-radius: 10rpx;
+				line-height: 1;
+				padding: 10rpx 20rpx;
+			}
+		}
+	
+		.titleBox {
+			.title {
+				color: $font-color-base;
+				font-size: $font-base;
+			}
+		}
+	}
+	.qm-wrap {
+		position: relative;
+		// width: 100%;
+		width: 140px;
+		justify-content: center;
+		padding-bottom: 20rpx;
+		border-bottom: 1px solid $page-color-light;
+		margin: auto;
+	}
+	.pj-wrap {
+		width: 600rpx;
+		height: 300rpx;
+		border-radius: 20rpx;
+		border: 1px solid $page-color-light;
+		margin: auto;
+		padding: 20rpx;
+	}
+	.pj-tit {
+		text-align: center;
+		padding: 10rpx 0 30rpx;
+		font-size: 30rpx;
+		color: #000;
+		font-weight: bold;
+	}
+</style>

+ 718 - 0
pages/user/signing/signing.vue

@@ -0,0 +1,718 @@
+<template>
+	<view class="content ">
+<!-- 		<view class="errorIf" v-if="dataInitError">
+			初始化数据失败请重新填写数据
+		</view> -->
+		<!-- <view class="table flex" v-if="showModelType">
+			<text class="table-text" @click="updata.modelType=0" :class="{action:updata.modelType==0}">
+				图文模式
+			</text>
+			<text class="table-text" @click="updata.modelType=1" :class="{action:updata.modelType==1}">
+				纯图模式
+			</text>
+		</view> -->
+
+		<!-- <view class="item-name">
+				上传头像
+			</view>
+			<view class="con_box">
+				<view class="con_image">
+					<image class="img" v-if="modelid==7" @click="upImage('avatar',1)"
+						:src="updata.avatar||`../../../static/image/upImg.png`">
+					</image>
+					<image class="img" v-else @click="navCroper(400,400,'one')"
+						:src="updata.avatar||`../../../static/image/upImg.png`">
+					</image>
+				</view>
+			</view> -->
+		<view class="item-name">
+			甲方
+		</view>
+		<view class="listBox">
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">姓名</text>
+					</view>
+					<view class="right flex">
+						<input class="input" v-model="partyA.name" type="text" placeholder="请填写姓名"
+							placeholder-class="placeholder" />
+					</view>
+				</view>
+			</view>
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">联系电话</text>
+					</view>
+					<view class="right flex">
+						<input class="input" v-model="partyA.mobile" type="number" placeholder="请填写联系电话"
+							placeholder-class="placeholder" />
+					</view>
+				</view>
+			</view>
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">身份证号</text>
+					</view>
+					<view class="right flex">
+						<input class="input" v-model="partyA.idcard" type="idcard" placeholder="请填写身份证号"
+							placeholder-class="placeholder" />
+					</view>
+				</view>
+			</view>
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">地址</text>
+					</view>
+					<view class="right flex"  @click="chooseAddress">
+							<input class="input" v-model="partyA.address" type="textg" placeholder="请选择地址"
+								placeholder-class="placeholder" disabled/>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="item-name">
+			乙方
+		</view>
+		<view class="listBox">
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">姓名</text>
+					</view>
+					<view class="right flex">
+						<input class="input" v-model="partyB.name" :disabled="type == 0" type="text"
+							placeholder="请填写真实姓名" placeholder-class="placeholder" />
+					</view>
+				</view>
+			</view>
+			<template v-if="type == 1">
+				<view class="list">
+					<view class="flex listItem">
+						<view class="flex titleBox">
+							<text class="font-color-red font-size-sm">✲</text> <text class="title">联系电话</text>
+						</view>
+						<view class="right flex">
+							<input class="input" v-model="partyB.mobile" type="number" placeholder="请填写联系电话"
+								placeholder-class="placeholder" />
+						</view>
+					</view>
+				</view>
+				<view class="list">
+					<view class="flex listItem">
+						<view class="flex titleBox">
+							<text class="font-color-red font-size-sm">✲</text> <text class="title">身份证号</text>
+						</view>
+						<view class="right flex">
+							<input class="input" v-model="partyB.idcard" type="idcard" placeholder="请填写身份证号"
+								placeholder-class="placeholder" />
+						</view>
+					</view>
+				</view>
+				<view class="list">
+					<view class="flex listItem">
+						<view class="flex titleBox">
+							<text class="font-color-red font-size-sm">✲</text> <text class="title">地址</text>
+						</view>
+						<view class="right flex">
+							<input class="input" v-model="partyB.address" type="text" placeholder="请填写地址"
+								placeholder-class="placeholder" />
+						</view>
+					</view>
+				</view>
+			</template>
+
+		</view>
+		<view class="item-name">
+			合同明细
+		</view>
+		<view class="listBox">
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">服务周期</text>
+					</view>
+					<view class="right flex">
+						<input class="input" v-model="contract.day" type="text" placeholder="请填写服务周期"
+							placeholder-class="placeholder" />
+					</view>
+				</view>
+			</view>
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">开始日期</text>
+					</view>
+					<view class="right flex">
+						<picker mode="date" :value="contract.start_time"  @change="bindDateChange">
+							<input class="input" v-model="contract.start_time" disabled type="text" placeholder="请选择开始日期"
+								placeholder-class="placeholder" />
+						</picker>
+
+					</view>
+				</view>
+			</view>
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">结束日期</text>
+					</view>
+					<view class="right flex">
+						<picker mode="date" :value="contract.end_time"  @change="bindDateChanges">
+							<input class="input" v-model="contract.end_time" disabled type="text" placeholder="请选择结束日期"
+								placeholder-class="placeholder" />
+						</picker>
+						
+					</view>
+				</view>
+			</view>
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">服务费</text>
+					</view>
+					<view class="right flex">
+						<input class="input" v-model="contract.money" type="text" placeholder="请填写服务费"
+							placeholder-class="placeholder" />
+					</view>
+				</view>
+			</view>
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">定金</text>
+					</view>
+					<view class="right flex">
+						<input class="input" v-model="contract.deposit" type="text" placeholder="请填写定金"
+							placeholder-class="placeholder" />
+					</view>
+				</view>
+			</view>
+			<view class="list">
+				<view class="flex listItem">
+					<view class="flex titleBox">
+						<text class="font-color-red font-size-sm">✲</text> <text class="title">余款</text>
+					</view>
+					<view class="right flex">
+						<input class="input" v-model="contract.balance" type="text" placeholder="请填写余款"
+							placeholder-class="placeholder" />
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="item-name">
+			附加条款
+		</view>
+		<view class="listBox " style="padding: 20rpx;">
+			<textarea name="fujia" id="" cols="30" rows="30" maxlength="300" v-model="fujia" placeholder="请输入附加条款"
+				style="width: 100%;height: 200rpx;"></textarea>
+		</view>
+		<view class="item-name">
+			合同模板
+		</view>
+		<view class="ht-title" @click="lookMore">
+			{{htModel.title}}
+		</view>
+		<!-- <view class="item-name">
+			电子签名
+		</view>
+		<view class="listBox qm-content">
+			<signInput ref="sign" canvasId="twoDrowCanvas" canvasIds="twoRotateCanvas" :header="header" :action="action"
+				@signToUrl="signToUrl">
+			</signInput>
+		</view> -->
+		<view class="base-buttom flex">
+			<view class="updata flex flex-center" :class="{ 'bg-gray': loding }" @click="loding ? '' : confirm()">
+				提交
+			</view>
+		</view>
+		<uni-popup ref="popup" type="center">
+				<scroll-view scroll-y="true" class="ht-detail">
+					<view class="" v-html="htModel.content">
+					
+					</view>
+				</scroll-view>
+				
+		</uni-popup>
+	</view>
+</template>
+
+<script>
+	import signInput from "@/components/am-sign-input/am-sign-input.vue"
+
+	import {
+		getArtDetail
+	} from '@/api/index.js'
+	import AllAddress from '@/components/wangding-pickerAddress/data.js'
+	import {
+		getCommonUserCardInfo,
+
+		getServiceTimeTypeList,
+		getServiceTypeList,
+		subInfoAudit,
+		getUserWorkTypeList,
+		getUserCardInfo
+	} from '@/api/model.js';
+	import {
+		isCardNo
+	} from '@/utils/rocessor.js';
+	import {
+		getAstro,
+		getShengXiao,
+		IdCard
+	} from '@/utils/twelve.js';
+	import {
+		mapState
+	} from "vuex"
+
+	import {
+		createSigning
+	} from '@/api/signing.js'
+	import pickerAddress from '@/components/wangding-pickerAddress/wangding-pickerAddress.vue';
+
+	export default {
+		components: {
+			pickerAddress,
+			signInput
+		},
+		data() {
+			return {
+				loding: false,
+				fujia: '',
+				header: {
+					"token": uni.getStorageSync('token')
+				}, //图片上传携带头部信息
+				htModel: {
+					title: '',
+				},
+				contract: {
+					day: '',
+					start_time: '',
+					end_time: '',
+					money: '',
+					deposit: '',
+					balance: ''
+				},
+				partyA: {
+					phone: '',
+					address: '',
+					name: '',
+					idcard: ''
+				},
+				partyB: {},
+				uid: '', //乙方uid,
+				type: 0,
+			};
+		},
+		onLoad(options) {
+			this.getHtModel()
+			const that = this;
+			if (options.id) {
+				this.uid = options.id
+				this.getCommonUserCardInfo()
+				console.log(this.userInfo)
+				this.partyA.mobile = this.userInfo.mobile || ''
+			}
+			if (options.type) {
+				this.type == options.type
+			}
+		},
+		computed: {
+			// #ifdef H5
+			...mapState(['urlFile']),
+			// #endif
+			...mapState(['baseURL']),
+			...mapState('user', ['userInfo', 'hasLogin']),
+			action() {
+				return this.baseURL + '/api/user/qiniuUpload' //上传服务器的地址
+			}
+		},
+		methods: {
+			chooseAddress() {
+				console.log('zhe');
+				let that = this
+				uni.chooseLocation({
+					success(res) {
+						console.log(res)
+						if(res.errMsg == 'chooseLocation:ok') {
+						that.partyA.address = res.address
+						that.partyA.latitude = res.latitude
+						that.partyA.longitude = res.longitude	
+						}
+							
+							
+							console.log(that.partyA.address,res.address);
+						
+					},
+					fail(err) {
+						console.log(err);
+					}
+				})
+			},
+			bindDateChange(e) {
+				console.log(e);
+				this.contract.start_time = e.detail.value
+			},
+			bindDateChanges(e) {
+				console.log(e);
+				this.contract.end_time = e.detail.value
+			},
+			lookMore() {
+				this.$refs.popup.open()
+			},
+			getHtModel() {
+				try {
+					getArtDetail({
+						id: 11
+					}).then(res => {
+						this.htModel = res.data.list[0]
+						this.htModel.content = this.htModel.content.replace(/<img/g, '<img class="rich-img"')
+							.replace(/<p>\s*<img/g, '<p class="pHeight"><img').replace(/<div/g,
+								'<div style="max-width: 50% !important;"');
+						console.log(res)
+					})
+				} catch (err) {
+					console.log(err);
+				}
+
+			},
+			getCommonUserCardInfo() {
+				getCommonUserCardInfo({
+					uid: this.uid
+				}).then(res => {
+					this.partyB = res.data
+				})
+			},
+			confirm(e) {
+				const that = this;
+				if (!that.rendl()) {
+					return
+				}
+				that.loding = true;
+				createSigning({
+						id: 0,
+						to_name: that.partyB.name,
+						name: that.partyA.name,
+						uid: that.userInfo.uid,
+						phone: that.partyA.mobile,
+						address: that.partyA.address,
+						card: that.partyA.idcard,
+						to_uid: that.uid,
+						price: that.contract.money,
+						deposit: that.contract.deposit,
+						balance: that.contract.balance,
+						period: that.contract.day,
+						start_time: that.contract.start_time,
+						end_time: that.contract.end_time,
+						mark: that.fujia,
+						latitude: that.partyA.latitude,
+						longitude: that.partyA.longitude
+					})
+					.then((e) => {
+						uni.showModal({
+							title: '温馨提醒',
+							content: '成功创建合同,是否立即签约',
+							complete(res) {
+								if(res.confirm) {
+									uni.navigateTo({
+										url:'/pages/user/signing/mySig'
+									})
+								}
+							}
+						});
+						that.loding = false;
+					})
+					.catch(err => {
+						this.loding = false;
+						console.log(err);
+					});
+			},
+			// 认证
+			rendl() {
+				const that = this;
+				//
+				if (that.type == 0) {
+					if (!that.partyA.mobile) {
+						uni.showModal({
+							title: '温馨提醒',
+							content: '请输入联系电话',
+							showCancel: false,
+						});
+						return false
+					}
+					if (!that.partyA.address) {
+						uni.showModal({
+							title: '温馨提醒',
+							content: '请输入地址',
+							showCancel: false,
+						});
+						return false
+					}
+					if (!that.partyA.idcard) {
+						uni.showModal({
+							title: '温馨提醒',
+							content: '请输入身份证号',
+							showCancel: false,
+						});
+						return false
+					}
+					if (!that.contract.money) {
+						uni.showModal({
+							title: '温馨提醒',
+							content: '请输入服务费',
+							showCancel: false,
+						});
+						return false
+					}
+					if (!that.contract.deposit) {
+						uni.showModal({
+							title: '温馨提醒',
+							content: '请输入定金',
+							showCancel: false,
+						});
+						return false
+					}
+					if (!that.contract.balance) {
+						uni.showModal({
+							title: '温馨提醒',
+							content: '请输入余款',
+							showCancel: false,
+						});
+						return false
+					}
+					if (!that.contract.day) {
+						uni.showModal({
+							title: '温馨提醒',
+							content: '请输入周期天数',
+							showCancel: false,
+						});
+						return false
+					}
+					if (!that.contract.start_time) {
+						uni.showModal({
+							title: '温馨提醒',
+							content: '请选择开始日期',
+							showCancel: false,
+						});
+						return false
+					}
+					if (!that.contract.end_time) {
+						uni.showModal({
+							title: '温馨提醒',
+							content: '请选择结束日期',
+							showCancel: false,
+						});
+						return false
+					}
+
+				} else {
+
+				}
+				return true
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	.content,
+	page {
+		min-height: 100%;
+	}
+
+	.content {
+		padding-bottom: 150rpx;
+	}
+
+	.item-name {
+		margin: $page-row-spacing;
+		font-size: $font-lg;
+		font-weight: bold;
+		color: $font-color-dark;
+	}
+
+	.con_box {
+		margin: $page-row-spacing;
+
+		.con_image {
+			width: 150rpx;
+			height: 150rpx;
+			display: inline-block;
+			margin-right: 20rpx;
+			position: relative;
+
+			.img {
+				width: 100%;
+				height: 100%;
+			}
+
+			.tip {
+				position: absolute;
+				top: -10rpx;
+				right: -10rpx;
+				width: 30rpx;
+				height: 30rpx;
+				background-color: #FFF;
+				border-radius: 99rpx;
+			}
+		}
+	}
+
+	.listBox {
+		margin: $page-row-spacing;
+		margin-top: 30rpx;
+		border-radius: 20rpx;
+		overflow: hidden;
+		background-color: #FFFFFF;
+	}
+
+	.list {
+		.input {
+			text-align: right;
+			font-size: $font-base;
+			color: $color-gray;
+			width: 100%;
+		}
+
+		.listItem {
+			padding: 35rpx 40rpx;
+			border-bottom: 1px solid $page-color-light;
+
+			.ql-editor.ql-blank:before {
+				font-style: normal;
+			}
+
+			.textarea {
+				font-size: $font-base;
+				width: 100%;
+				word-wrap: break-word;
+				white-space: pre-line;
+				min-height: 9rem;
+			}
+		}
+
+		.listIconImg {
+			width: 36rpx;
+		}
+
+		.right {
+			color: $font-color-light;
+			font-size: $font-base;
+			flex-grow: 1;
+			justify-content: flex-end;
+
+			.timetype {
+				width: 100%;
+				justify-content: flex-end;
+			}
+
+			.citylist {
+				.del {
+					color: $color-red;
+					font-size: $font-sm;
+					border: 1px solid $color-red;
+					border-radius: 10rpx;
+					line-height: 1;
+					padding: 5rpx 15rpx;
+				}
+			}
+
+			.img {
+				width: 26rpx;
+			}
+
+			.buttom {
+				color: $base-color;
+				border: 1px solid $base-color;
+				border-radius: 10rpx;
+				line-height: 1;
+				padding: 10rpx 20rpx;
+			}
+		}
+
+		.titleBox {
+			.title {
+				color: $font-color-base;
+				font-size: $font-base;
+			}
+		}
+	}
+
+	.bg-gray {
+		background-color: $color-gray;
+	}
+
+	.base-buttom {
+		position: fixed;
+		bottom: 0rpx;
+		right: 0rpx;
+		left: 0rpx;
+		margin: 0;
+		padding: 0;
+		height: 100rpx;
+		border-radius: 0;
+
+		.updata,
+		.ylan {
+			// width: 50%;
+			flex: 1;
+			height: 100%;
+			justify-content: center;
+		}
+
+		.ylan {
+			background-color: #FFF;
+			color: $font-color-dark;
+		}
+	}
+
+	.errorIf {
+		font-size: $font-lg;
+		color: #F65486;
+		padding: 30rpx;
+	}
+
+	.table {
+		.table-text {
+			color: #F65486;
+			text-align: center;
+			flex-grow: 1;
+			padding: 30rpx;
+			border: 1px solid #F65486;
+
+			&.action {
+				background-color: #F65486;
+				color: #FFFFFF;
+			}
+		}
+	}
+
+	.ht-title {
+
+		padding: 20rpx;
+		background-color: #fff;
+		margin: 0 40rpx;
+		border-radius: 20rpx;
+	}
+
+	.ht-detail {
+		padding: 20rpx;
+		background-color: #fff;
+		border-radius: 20rpx;
+		width: 600rpx;
+		height: 80vh;
+	}
+
+	.rich-img {
+		width: 100%;
+	}
+
+	.qm-content {
+		padding: 40rpx;
+		display: flex;
+		justify-content: center;
+	}
+</style>

+ 20 - 1
pages/userhome/user.vue

@@ -86,7 +86,26 @@
 								<image class="img" src="../../static/icon/next1.png" mode="widthFix"></image>
 							</view>
 						</view> -->
-
+						<view class="flex listItem" @click="navTo('/pages/user/signing/mySig')" v-if="user.info_audit_status==-2">
+							<view class="flex titleBox">
+								<image class="listIconImg" src="../../static/icon/myyy.png" mode="widthFix"></image>
+								<text class="title">我的预约</text>
+							</view>
+							<view class="right flex">
+								<text></text>
+								<image class="img" src="../../static/icon/next1.png" mode="widthFix"></image>
+							</view>
+						</view>
+						<view v-else class="flex listItem" @click="navTo('/pages/user/signing/mySig')">
+							<view class="flex titleBox">
+								<image class="listIconImg" src="../../static/icon/myht.png" mode="widthFix"></image>
+								<text class="title">我的合同</text>
+							</view>
+							<view class="right flex">
+								<text></text>
+								<image class="img" src="../../static/icon/next1.png" mode="widthFix"></image>
+							</view>
+						</view>
 						<view class="flex listItem" @click="navTo('/pages/user/myteam')">
 							<view class="flex titleBox">
 								<image class="listIconImg" src="../../static/icon/myshare.png" mode="widthFix"></image>

BIN
static/icon/myht.png


BIN
static/icon/myyy.png


BIN
static/image/qy.png


BIN
static/other/color_black.png


BIN
static/other/color_black_selected.png


BIN
static/other/color_red.png


BIN
static/other/color_red_selected.png


BIN
static/other/signs.png