Bläddra i källkod

新增合同签字模块

tanxue 19 timmar sedan
förälder
incheckning
4098401d4a
5 ändrade filer med 549 tillägg och 11 borttagningar
  1. 58 0
      common/styles/global/pages.scss
  2. 30 0
      pages.json
  3. 156 0
      pages/kehu/hetong/HetongInfo.vue
  4. 163 0
      pages/kehu/hetong/HetongList.vue
  5. 142 11
      pages/kehu/hetong/hetong.vue

+ 58 - 0
common/styles/global/pages.scss

@@ -634,4 +634,62 @@
 		height: calc(100vh - 250rpx);overflow-y: auto;
 		padding: 20rpx 32rpx; box-sizing: border-box;font-size: 28rpx;line-height: 1.6;color: #333;
 	}
+}
+
+/* 合同列表页 */
+.ht-list-page{
+	.uni-scroll-view-content{display: flex;flex-direction: column;}
+	.ht-list-box{background-color: #f8f9fd!important;padding: 12rpx 0;flex: 1;}
+	.ht-list-item-box{
+		margin:12rpx 24rpx;
+		.uni-list-item__container{padding: 0;border-radius: 12rpx;border: 1rpx solid #f2f2f4;}
+	}
+	.ht-card-row{width: 100%;padding: 36rpx 0;}
+	.ht-status-btn-row{
+		display: flex;justify-content: space-between;
+		.ht-status{width: 300rpx;height: 52rpx;line-height: 52rpx;font-size: 30rpx;
+		background-color: #42ca9c;box-sizing: border-box;padding: 0 24rpx;
+		color: #fff;border-radius: 0 26rpx 26rpx 0;}
+		.ht-biew-btn{
+			color: #666;font-size: 28rpx;padding: 0 16rpx;display: flex;align-items: center;color: #666;
+			.ht-jt{
+				width: 24rpx;height: 24rpx;
+				// background-image: url("@/static/images/common/nav-bar-jt-bottom.svg");
+				@include ezy-no-repeat-cover();transform: rotate(270deg);margin-left: 4rpx;
+			}
+		}
+	}
+	.ht-time{font-size: 28rpx;color: #666;display: flex;align-items: center;margin: 24rpx 24rpx 0;}
+	.phone-time-icon{width: 42rpx;height: 42rpx;@include ezy-no-repeat-cover();
+	margin-right: 8rpx;flex-shrink: 0;
+	// background-image: url("@/static/images/hetong/data-icon.png");
+	}
+	.status-0,.status-1,.status-2,.status-3{display: contents;}
+	.status-0 { color: #feff01; } 
+	.status-1 { color: #33ffff; } 
+	.status-2 { color: #fff; } 
+	.status-3 { color: #f56c6c; } 
+}
+
+/* 合同页 */
+.phone-hetong-page{
+	height: 100vh;display: flex;flex-direction: column;
+	.pdf-box{flex: 1;background-color: rgba(0, 0, 0, 0.5);margin: 24rpx;border-radius: 8rpx;
+	display: flex;flex-direction: column;align-items: center;overflow: auto;box-sizing: border-box;
+	padding: 16rpx 0;border:1rpx solid  rgba(0, 0, 0, 0.3);}
+	.pdf-img{width: 93%;margin: 16rpx auto;}
+	.pdf-tip-box{
+		position: absolute;top: calc(110rpx + var(--status-bar-height));left: 0;right:0;bottom: 0;
+		display: flex;align-items: center;justify-content: center;color: #666;background-color: #fff;
+		border: 24rpx solid #f8f9fd;}
+	.tip-img-box{display: flex;flex-direction: column;}
+	.tip-text{text-align: center;color: #666;}
+	.tip-text::before{content: '';width: 260rpx;height: 260rpx;display: block;margin-bottom: 16rpx;
+	// background-image: url("@/static/images/common/loading-icon.gif");
+	background-size: cover;}
+	.no-hetong-box{
+		height: 100vh;display: flex;flex-direction: column;
+		align-items: center;justify-content: center;color: #666;}
+	.ht-btn{width: 180px;margin-bottom: 24px;}
+	.hetong-tip-box{color: #3fd2a1;font-size: 24rpx;margin:0 24rpx 24rpx;line-height: 1.4;}
 }

+ 30 - 0
pages.json

@@ -10,6 +10,11 @@
 			"style": {
 				"navigationStyle": "custom"
 			}
+		}, {
+			"path": "pages/kehu/shouYe/shouye",
+			"style": {
+				"navigationStyle": "custom"
+			}
 		}
 	],
 	"subPackages": [{
@@ -141,6 +146,31 @@
 					"navigationStyle": "custom"
 				}
 			}]
+		},
+		{
+			"root": "pages/kehu/hetong",
+			"name": "hetong",
+			"pages": [{
+				"path": "hetong",
+				"style": {
+					"navigationStyle": "custom"
+				}
+			}]
+		},
+		{
+			"root": "pages/kehu/my",
+			"name": "my2",
+			"pages": [{
+				"path": "my",
+				"style": {
+					"navigationStyle": "custom"
+				}
+			},{
+				"path": "myInfo",
+				"style": {
+					"navigationStyle": "custom"
+				}
+			}]
 		}
 		// {
 		// 	"root": "components/zhuapaiConfirm",

+ 156 - 0
pages/kehu/hetong/HetongInfo.vue

@@ -0,0 +1,156 @@
+<template>
+	<view class="phone-hetong-page">
+		<view class="phone-navBar-box">
+			<view @click="goUpPage" class="nav-bar-icon"></view>
+			<text class="nav-bar-title">合同</text>
+			<view class="lsht-icon" @click="handleGoLishi"></view>
+		</view>
+		<template v-if="tId">
+			<view class="pdf-box">
+				<img v-for="item in imgList" mode="aspectFit" :src="`data:image/png;base64,${item}`"
+					@click="previewBase64Image(`data:image/png;base64,${item}`)" class="pdf-img" />
+			</view>
+			<!-- 加载提示 -->
+			<view class="pdf-tip-box" v-if="isLoading">
+				<view class="tip-img-box">
+					 <view class="tip-text">加载中...</view>
+				</view>
+			</view>
+		</template>
+
+		<view class="hetong-tip-box" v-if="tId && info.status == 0">
+			请务必仔细阅读上述内容,确认已完全理解所有条款后,点击【我已阅读】按钮完成签字确认。
+		</view>
+		<button @click="handleQianming" v-if="tId && info.status == 0" class="phone-green-btn ht-btn"
+			type="default">我已阅读</button>
+
+		<uni-popup ref="popupRef" type="bottom" background-color="#fff" :is-mask-click="false" :mask-click="false">
+			<view class="ht-qm-popup">
+				<view class="icon-title-navBar-box">
+					<view @click="goback2" class="nav-bar-icon"></view>
+					<text class="nav-bar-title">签名</text>
+				</view>
+				<writeSign @getBase64="getBase64"></writeSign>
+			</view>
+		</uni-popup>
+	</view>
+</template>
+
+<script setup>
+	import {
+		ref
+	} from "vue";
+	import * as httpApi from "@/api/hetong.js"
+	import {
+		onLoad
+	} from "@dcloudio/uni-app"
+	import writeSign from "@/components/writeSign/index.vue"
+	import {
+		throttleAdvanced
+	} from "@/utils/common.js"
+	import {
+		base64ToPath
+	} from "image-tools";
+
+	const tId = ref(null)
+
+	const info = ref({})
+	const show = ref(false)
+	const popupRef = ref(null)
+	const imgList = ref([])
+	const isLoading = ref(true)
+	const timer1 = ref(null)
+
+	onLoad((options) => {
+		tId.value = options.id;
+		init();
+	})
+
+	function handleQianming() {
+		popupRef.value.open()
+	}
+
+	function init() {
+		httpApi.getHetongInfo({
+			id: tId.value
+		}).then(res => {
+			info.value = res.data;
+			imgList.value = res.data.base64List;
+		}).finally(() => {
+			isLoading.value = false;
+		})
+	}
+
+	async function previewBase64Image(base64Data) {
+		try {
+			// 关键步骤:将 Base64 字符串转换为本地临时路径
+			// 此处以使用 image-tools 的 base64ToPath 为例
+			const localPath = await base64ToPath(base64Data);
+			// 调用 UniApp 的图片预览 API
+			uni.previewImage({
+				urls: [localPath], // 注意:urls 参数需要是数组,即使只预览一张图
+				current: 0, // 当前显示图片在 urls 数组中的索引
+			});
+		} catch (error) {
+			// 如果出现错误(如转换失败),隐藏加载提示并告知用户
+			console.error('预览失败:', error);
+			uni.showToast({
+				title: '预览失败',
+				icon: 'none'
+			});
+		}
+	}
+
+	function goUpPage() {
+		uni.navigateBack()
+		clearTimeout(timer1.value);
+		timer1.value = null;
+	}
+
+	const handleQM = throttleAdvanced((img) => {
+		uni.showToast({
+			title: "签名提交中...",
+			mask: true,
+		})
+
+		httpApi.getHetongQianming({
+			id: tId.value,
+			yifangBase64: img.replace(/^data:image\/\w+;base64,/, '')
+		}).then(res => {
+			if (res.data) {
+				uni.showToast({
+					title: "签名成功",
+					duration: 2000,
+					mask: true,
+					success() {
+						timer1.value = setTimeout(() => goUpPage(), 2000)
+					}
+				})
+			}
+		})
+	})
+
+	function getBase64(img) {
+		if (!img) {
+			uni.showToast({
+				title: '签名异常'
+			})
+			return;
+		}
+		handleQM(img)
+
+	}
+	function handleGoLishi() {
+		uni.redirectTo({
+			url: '/pages/admin/Hetong/HetongList'
+		})
+	}
+
+	function goback2() {
+		popupRef.value.close()
+	}
+</script>
+
+<style scoped>
+
+</style>

+ 163 - 0
pages/kehu/hetong/HetongList.vue

@@ -0,0 +1,163 @@
+<template>
+	<view class="phone-list-page ht-list-page">
+		<view class="icon-title-navBar-box">
+			<view @click="goUpPage" class="nav-bar-icon"></view>
+			<text class="nav-bar-title">历史合同</text>
+		</view>
+	<!-- 课程列表 -->
+	<scroll-view scroll-y="true" refresher-enabled="true" :refresher-triggered="data.loading"
+		:refresher-threshold="50" refresher-background="transparent" @refresherrefresh="onRefresh"  @scrolltolower="onScrolltolower"
+		class="admin-phone-tabbar-view">
+		<uni-list class="admin-list-box ht-list-box">
+			<uni-list-item v-for="item in data.list" class="ht-list-item-box">
+				<template v-slot:body>
+					<view class="ht-card-row">
+						<view class="ht-status-btn-row">
+							<view class="ht-status">合同状态:<view :class="'status-' + item.status">{{formatStatus(item.status)}}</view></view>
+							<view class="ht-biew-btn" @click="checkKecheng(item)">查看<icon class="ht-jt"></icon></view>
+						</view>
+						<view class="ht-time"><icon class="phone-time-icon" />合同开始时间:{{formatTime(item.startDate)}} </view>
+						<view class="ht-time"><icon class="phone-time-icon" />合同结束时间:{{formatTime(item.endDate)}} </view>
+					</view>
+				</template>
+			</uni-list-item>
+			<uni-load-more :status="data.state" @click="getMore(0)" :contentText="data.contentText"></uni-load-more>
+		</uni-list>
+	</scroll-view>
+	<!-- 页面底端 -->
+	<customTabbarClientVue></customTabbarClientVue>
+	</view>
+</template>
+
+<script setup>
+import {ref,reactive} from "vue";
+import {onLoad,onShow} from "@dcloudio/uni-app";
+import {getHetongList} from '@/api/hetong.js'
+import customTabbarClientVue from "@/components/custom-tabbar/custom-tabbar-admin.vue";
+const data = reactive({
+		list: [], // 考试列表
+		loading: false,
+		page: 0,
+		size: 10,
+		state: 'more',
+		contentText: {
+			contentdown: '查看更多',
+			contentrefresh: '加载中',
+			contentnomore: '没有更多'
+		},
+})	
+	
+onLoad((options) => {
+		data.from = options.from;
+		
+	})
+
+onShow(() => {
+  refreshData()
+})
+
+function onRefresh() {
+	data.page = 0;
+	data.list = [];
+	data.loading = true;
+	refreshData();
+}
+
+function refreshData() {
+	const opt = {
+		page: 1,
+		size: 10, // 固定查询10条
+	}
+	data.list = [];
+	// 数学
+	data.state = 'loading';
+	data.page++;
+	opt.page = data.page;
+	
+	    console.log('列表',opt)
+	
+	getHetongList(opt).then(res => {
+
+
+
+		data.list = data.list.concat(res.data.data);
+		data.loading = false;
+		if (res.data.total > data.list.length) {
+			data.state = 'more';
+			data.loading = false;
+		} else {
+			data.state = 'no-more';
+			data.loading = false;
+		}
+	}).catch(err => {
+		data.state = 'more';
+		data.loading = false;
+	})
+}
+
+function getMore() {
+	const opt = {
+		page: 1,
+		size: 10, // 固定查询10条
+	}
+	if (data.state == 'no-more') return;
+	data.state = 'loading';
+	data.page++;
+	opt.page = data.page;
+	getHetongList(opt).then(res => {
+    console.log('列表2',res.data)
+
+    data.list = data.list.concat(res.data.data);
+		data.loading = false;
+		if (res.data.total > data.list.length) {
+			data.state = 'more';
+			data.loading = false;
+		} else {
+			data.state = 'no-more';
+			data.loading = false;
+		}
+	}).catch(err => {
+		data.state = 'more';
+		data.loading = false;
+	})
+}
+
+function onScrolltolower() {
+	getMore()
+}
+
+function handleSearch() {
+	data.page = 0;
+	refreshData();
+}
+
+// 在现有代码底部添加此方法
+function formatStatus(status) {
+  const statusMap = {
+    0: '待签字',
+    1: '待审核',
+    2: '生效',
+	3: '失效',
+  };
+  return statusMap[status] || '未知状态'; // 处理未知状态
+}
+
+function formatTime(data){
+		return data.split(' ')[0];
+}
+
+function checkKecheng(item) {
+	uni.navigateTo({
+		url: `/pages/admin/Hetong/HetongInfo?id=${item.id}`
+	})
+}
+
+function goUpPage() {
+	uni.redirectTo({
+		url: '/pages/admin/ShouYe/shouye'
+	})	
+}
+</script>
+
+<style>
+</style>

+ 142 - 11
pages/kehu/hetong/hetong.vue

@@ -1,22 +1,153 @@
 <template>
-	<view>
+	<view class="phone-hetong-page">
+		<view class="phone-navBar-box">
+			<view @click="goUpPage" class="nav-bar-icon"></view>
+			<text class="nav-bar-title">合同</text>
+		</view>
+		<template v-if="tId">
+			<view class="pdf-box"> 
+				 <img v-for="item in imgList" mode="aspectFit" :src="`data:image/png;base64,${item}`" 
+				 @click="previewBase64Image(`data:image/png;base64,${item}`)" class="pdf-img"/>
+			</view>
+			<!-- 加载提示 -->
+			<view class="pdf-tip-box" v-if="isLoading">
+				<view class="tip-img-box">
+					 <view class="tip-text">加载中...</view>
+				</view>
+			</view>
+		</template>
+		<view class="hetong-tip-box" v-if="info.status == 0">
+				  请务必仔细阅读上述内容,确认已完全理解所有条款后,点击【我已阅读】按钮完成签字确认。
+		</view>
+		<button v-if="info.status == 0" type="default" class="phone-green-btn ht-btn"
+			@click="handleQianming">我已阅读</button>
+
+		<uni-popup ref="popupRef" type="bottom" background-color="#fff" :is-mask-click="false" :mask-click="false">
+			<view class="ht-qm-popup">
+				<view class="icon-title-navBar-box">
+					<view @click="goback2" class="nav-bar-icon"></view>
+					<text class="nav-bar-title">签名</text>
+				</view>
+				<writeSign @getBase64="getBase64"></writeSign>
+			</view>
+		</uni-popup>
 		
 	</view>
 </template>
 
-<script>
-	export default {
-		data() {
-			return {
-				
-			}
-		},
-		methods: {
-			
+<script setup>
+	import {
+		ref
+	} from "vue";
+	import * as httpApi from "@/api/hetong.js"
+	import {
+		onLoad
+	} from "@dcloudio/uni-app"
+	import writeSign from "@/components/writeSign/index.vue"
+  import {throttleAdvanced} from "@/utils/common.js"
+  import { base64ToPath } from 'image-tools';
+
+	const tId = ref(null)
+	const info = ref({})
+	const popupRef = ref(null)
+	const imgList = ref([])
+	const isLoading = ref(true)
+  const timer1 = ref(null)
+
+	function goUpPage() {
+		uni.navigateBack()
+    clearTimeout(timer1.value);
+    timer1.value = null;
+	}
+	onLoad(({id}) => {
+    tId.value = id;
+		init(id)
+	})
+
+	function init(id) {
+		if (!id) {
+			uni.showToast({
+				title: '数据异常,请确认路径是否完整'
+			})
+			return;
+		}
+
+		httpApi.getHetongInfo({id}).then(res => {
+			info.value = res.data;
+			imgList.value = res.data.base64List;
+		}).finally(() => {
+			isLoading.value = false;
+		})
+	}
+
+  async function previewBase64Image(base64Data) {
+      try {
+        // 关键步骤:将 Base64 字符串转换为本地临时路径
+        // 此处以使用 image-tools 的 base64ToPath 为例
+        const localPath = await base64ToPath(base64Data);
+        // 调用 UniApp 的图片预览 API
+        uni.previewImage({
+          urls: [localPath], // 注意:urls 参数需要是数组,即使只预览一张图
+          current: 0, // 当前显示图片在 urls 数组中的索引
+        });
+      } catch (error) {
+        // 如果出现错误(如转换失败),隐藏加载提示并告知用户
+        console.error('预览失败:', error);
+        uni.showToast({
+          title: '预览失败',
+          icon: 'none'
+        });
+      }
+  }
+
+	function handleGoLishi() {
+		uni.redirectTo({
+			url: '/pages/admin/Hetong/HetongList'
+		})
+	}
+
+	function handleQianming() {
+		popupRef.value.open()
+	}
+
+  const  handleQM = throttleAdvanced((img) => {
+    uni.showToast({
+      title: "签名提交中...",
+      mask: true,
+    })
+
+    httpApi.getHetongQianming({
+      id: tId.value,
+      yifangBase64: img.replace(/^data:image\/\w+;base64,/, ''), // 当前接口,只有唯一yifangBase64 + id
+    }).then(res => {
+      if (res.data) {
+        uni.showToast({
+          title: "签名成功",
+          duration: 2000,
+          mask: true,
+          success() {
+            timer1.value = setTimeout(() => uni.navigateBack(),2000)
+          }
+        })
+      }
+    })
+  })
+
+	function getBase64(img) {
+		if (!img) {
+			uni.showToast({
+				title: '签名异常'
+			})
+			return;
 		}
+    handleQM(img)
+	}
+
+	function goback2() {
+		popupRef.value.close()
 	}
 </script>
 
 <style>
 
-</style>
+</style>