jwyu 3 yıl önce
ebeveyn
işleme
17c51876d0
100 değiştirilmiş dosya ile 7635 ekleme ve 2 silme
  1. 2 0
      .gitignore
  2. 71 0
      App.vue
  3. 3 2
      README.md
  4. 78 0
      api/approve/contract.js
  5. 52 0
      api/approve/custome.js
  6. 111 0
      api/approve/seal.js
  7. 21 0
      api/message.js
  8. 28 0
      api/user.js
  9. 204 0
      js_sdk/zww-md5/w_md5.js
  10. 22 0
      main.js
  11. 75 0
      manifest.json
  12. 117 0
      pages-approve/components/approveListItem.vue
  13. 172 0
      pages-approve/components/steps.vue
  14. 541 0
      pages-approve/contract/detail.vue
  15. 151 0
      pages-approve/contract/list.vue
  16. 102 0
      pages-approve/contract/reason.vue
  17. 157 0
      pages-approve/contract/search.vue
  18. 713 0
      pages-approve/custome/detail.vue
  19. 142 0
      pages-approve/custome/list.vue
  20. 112 0
      pages-approve/custome/reason.vue
  21. 568 0
      pages-approve/seal/addSeal.vue
  22. 548 0
      pages-approve/seal/detail.vue
  23. 558 0
      pages-approve/seal/edit.vue
  24. 172 0
      pages-approve/seal/list.vue
  25. 105 0
      pages-approve/seal/reason.vue
  26. 214 0
      pages-approve/search/index.vue
  27. 157 0
      pages-approve/search/result.vue
  28. BIN
      pages-approve/static/cancel-icon.png
  29. BIN
      pages-approve/static/click-icon.png
  30. BIN
      pages-approve/static/draw-back-icon.png
  31. BIN
      pages-approve/static/fail-icon.png
  32. BIN
      pages-approve/static/pass-icon.png
  33. BIN
      pages-approve/static/pdf.png
  34. BIN
      pages-approve/static/processing-icon.png
  35. BIN
      pages-approve/static/recall-icon.png
  36. BIN
      pages-approve/static/seal-icon.png
  37. BIN
      pages-approve/static/search-icon.png
  38. BIN
      pages-approve/static/upload-icon.png
  39. 31 0
      pages-approve/utils/util.js
  40. 219 0
      pages-todomessages/list/list.vue
  41. BIN
      pages-todomessages/static/icon-1.png
  42. BIN
      pages-todomessages/static/icon-2.png
  43. BIN
      pages-todomessages/static/icon-3.png
  44. BIN
      pages-todomessages/static/icon-4.png
  45. 175 0
      pages.json
  46. 76 0
      pages/index/index.vue
  47. 73 0
      pages/login/login.vue
  48. 113 0
      pages/mine/index.vue
  49. 68 0
      pages/workbench/index.vue
  50. BIN
      static/empty.png
  51. BIN
      static/icon-1.png
  52. BIN
      static/icon-2.png
  53. BIN
      static/icon-3.png
  54. BIN
      static/man.png
  55. BIN
      static/tabbar-icon-index-s.png
  56. BIN
      static/tabbar-icon-index.png
  57. BIN
      static/tabbar-icon-mine-s.png
  58. BIN
      static/tabbar-icon-mine.png
  59. BIN
      static/tabbar-icon-workbench-s.png
  60. BIN
      static/tabbar-icon-workbench.png
  61. 62 0
      store/index.js
  62. 83 0
      uni.scss
  63. 4 0
      utils/config.js
  64. 82 0
      utils/request.js
  65. 19 0
      utils/uni-async.js
  66. 91 0
      utils/uploadFile.js
  67. 11 0
      utils/vantRegister.js
  68. 1 0
      wxcomponents/vant/action-sheet/index.d.ts
  69. 70 0
      wxcomponents/vant/action-sheet/index.js
  70. 8 0
      wxcomponents/vant/action-sheet/index.json
  71. 69 0
      wxcomponents/vant/action-sheet/index.wxml
  72. 0 0
      wxcomponents/vant/action-sheet/index.wxss
  73. 1 0
      wxcomponents/vant/area/index.d.ts
  74. 235 0
      wxcomponents/vant/area/index.js
  75. 6 0
      wxcomponents/vant/area/index.json
  76. 20 0
      wxcomponents/vant/area/index.wxml
  77. 8 0
      wxcomponents/vant/area/index.wxs
  78. 1 0
      wxcomponents/vant/area/index.wxss
  79. 1 0
      wxcomponents/vant/button/index.d.ts
  80. 69 0
      wxcomponents/vant/button/index.js
  81. 7 0
      wxcomponents/vant/button/index.json
  82. 53 0
      wxcomponents/vant/button/index.wxml
  83. 39 0
      wxcomponents/vant/button/index.wxs
  84. 0 0
      wxcomponents/vant/button/index.wxss
  85. 67 0
      wxcomponents/vant/calendar/calendar.wxml
  86. 1 0
      wxcomponents/vant/calendar/components/header/index.d.ts
  87. 34 0
      wxcomponents/vant/calendar/components/header/index.js
  88. 3 0
      wxcomponents/vant/calendar/components/header/index.json
  89. 16 0
      wxcomponents/vant/calendar/components/header/index.wxml
  90. 1 0
      wxcomponents/vant/calendar/components/header/index.wxss
  91. 1 0
      wxcomponents/vant/calendar/components/month/index.d.ts
  92. 163 0
      wxcomponents/vant/calendar/components/month/index.js
  93. 3 0
      wxcomponents/vant/calendar/components/month/index.json
  94. 39 0
      wxcomponents/vant/calendar/components/month/index.wxml
  95. 71 0
      wxcomponents/vant/calendar/components/month/index.wxs
  96. 0 0
      wxcomponents/vant/calendar/components/month/index.wxss
  97. 1 0
      wxcomponents/vant/calendar/index.d.ts
  98. 309 0
      wxcomponents/vant/calendar/index.js
  99. 10 0
      wxcomponents/vant/calendar/index.json
  100. 25 0
      wxcomponents/vant/calendar/index.wxml

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+unpackage
+.hbuilderx

+ 71 - 0
App.vue

@@ -0,0 +1,71 @@
+<script>
+	export default {
+		onLaunch: function() {
+			console.log('App Launch')
+			// if(!this.$store.state.token){
+			// 	this.$store.dispatch('WXLogin')
+			// }
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		}
+	}
+</script>
+
+<style>
+	@import "/wxcomponents/vant/common/index.wxss";
+	/*每个页面公共css */
+	page{
+		font-size: 14px;
+		color: #333333;
+		width: 100%;
+		min-height: 100%;
+		background-color: #f5f5f5;
+	}
+	view{
+		box-sizing: border-box;
+	}
+	.flex{
+		display: flex;
+	}
+	.white-wrap{
+		background-color: #fff;
+	}
+	
+	/* 固定屏幕底部区域 */
+	.fix-bottom-wrap{
+		background: #FFFFFF;
+		border: 1px solid #F2F2F2;
+		box-shadow: 0px -2px 6px rgba(110, 119, 141, 0.07);
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		z-index: 99;
+		padding-top: 25rpx;
+		padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
+		padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+		padding-left: 32rpx;
+		padding-right: 32rpx;
+	}
+	
+	/* 审批列表页状态颜色 */
+	.approve-list-status-wait{
+		color: #3385FF;
+	}
+	.approve-list-status-success{
+		color: #63D594;
+	}
+	.approve-list-status-fail{
+		color:#FF4444;
+	}
+	.approve-list-status-back{
+		color: #999999;
+	}
+	.approve-list-status-cancel{
+		color: #999999;
+	}
+</style>

+ 3 - 2
README.md

@@ -1,3 +1,4 @@
-# crm-xcx
+## 弘则移动CRM
 
-crm小程序
+	1. ui使用vant 未全部引入 按需在page.json中配置
+	2. utils/config 中为项目配置

+ 78 - 0
api/approve/contract.js

@@ -0,0 +1,78 @@
+/**
+ * 审批模块(合同)
+ */
+import {httpGet,httpPost} from "@/utils/request.js"
+
+/**
+ * 合同审批列表
+ * @param {string} ContractType 合同类型,枚举值:’新签合同’,’续约合同’,’补充协议’
+ * @param {string} Status 合同状态,枚举值:’待审批’,’已审批’,’已驳回’,’已撤回’
+ * @param {int} ProductId 客户类型:传0或者不传为当前账号权限,1 代表是:ficc;2 代表是:权益
+ * @param {string} SellerId 选择的销售id
+ * @param {string} Keyword 搜索关键字
+ * @param {string} ModifyStartTime 服务更新时间的选择开始时间,格式:2021-05-23 00:00:00
+ * @param {string} ModifyEndTime 服务更新时间的选择结束时间,格式:2021-05-26 23:59:59
+ */
+export const apiContractApproveList=params=>{
+	return httpGet('/contract_approval/list',{...params,PageSize:20})
+}
+
+/**
+ * 合同审批详情 (在审批状态为 待审批、处理中 时 使用审批单详情 其余均使用合同详情)
+ * @param {int} ContractApprovalId 审批单id
+ * @param {int} ContractApprovalRecordId 审批流程单id  (从消息列表进入传此参数)
+ */
+export const apiContractApprovalDetail=params=>{
+	return httpGet('/contract_approval/detail',params)
+}
+
+/**
+ * 合同详情
+ * @param {int} ContractId 合同id 
+ */
+export const apiContractDetail=params=>{
+	return httpGet('/contract/detail',params) 
+}
+
+/**
+ * 合同审批 通过
+ * @param {int} ContractId 合同id
+ */
+export const apiContractApproved=params=>{
+	return httpPost('/contract_approval/approved',params)
+}
+
+/**
+ * 合同审批 拒绝
+ * @param {int} ContractId 审批单id
+ * @param {string} Remark 理由  
+ */
+export const apiContractReject=params=>{
+	return httpPost('/contract_approval/reject',params)
+}
+
+/**
+ * 合同搜索
+ * @param {string} CompanyName 搜索关键字
+ */
+export const apiContractSearch=params=>{
+	return httpGet('/contract/company_list',params)
+}
+
+/**
+ * 合同列表
+ * @param {int} CurrentIndex 页码 
+ * @param {string} Keyword 
+ */
+export const apiContractList=params=>{
+	return httpGet('/contract/list',params)
+}
+
+/**
+ * 合同回签附件
+ * @param {int}  ContractId 合同id
+ * @param {string}  FileUrl  文件地址 多个用#隔开
+ */
+export const apiContractSignBackFiles=params=>{
+	return httpPost('/contract/upload_check_back_file',params)
+}

+ 52 - 0
api/approve/custome.js

@@ -0,0 +1,52 @@
+/**
+ * 审批模块(客户)
+ */
+import {httpGet,httpPost} from "@/utils/request.js"
+
+/**
+ * 客户搜索
+ * @param {string} CompanyName 搜索值 
+ */
+export const apiCustomeSearch=params=>{
+	return httpGet('/approval/company_list',params)
+}
+
+/**
+ * 客户审批列表
+ * @param {int}  PageSize  每页数据条数
+ * @param {int}  CurrentIndex 当前页页码,从1开始
+ * @param {string}  Status  状态:’全部(传空串)’,’待审批’,’已审批’,’驳回’,’已撤回’
+ * @param {string}  KeyWord  搜索关键词    
+ */
+export const apiCustomeList=params=>{
+	return httpGet('/approval/list',{...params,PageSize:20})
+}
+
+/**
+ * 客户审批详情
+ * @param {int} ApprovalId 审批单id
+ */
+export const apiCustomeDetail=params=>{
+	return httpGet('/approval/detail',params)
+}
+
+/**
+ * 合同详情
+ * @param {int} CompanyId 客户ID
+ * @param {int} CompanyContractId 合同id
+ */
+export const apiCustomeContractDetail=params=>{
+	return httpGet('/company_contract/detail',params)
+}
+
+/**
+ * 同意/拒绝审批 
+ * @param {int} CompanyApprovalId 审批单id
+ * @param {int} CompanyContractId 合同id
+ * @param {int} CompanyId 客户id
+ * @param {string} Remark 审批理由
+ * @param {int} Status 审批状态,1:通过,2:拒绝
+ */
+export const apiCustomeApprove=params=>{
+	return httpPost('/approval/approve',params)
+}

+ 111 - 0
api/approve/seal.js

@@ -0,0 +1,111 @@
+/**
+ * 审批模块(用印)
+ */
+import {httpGet,httpPost} from "@/utils/request.js"
+
+/**
+ * 获取审批流详情(目前crm1.0 只在发起用印申请时需要调用此接口)
+ * @param {int} FlowId 审批流程id;1:ficc客户审批;2:权益客户审批;3:ficc合同审批,4:权益合同审批,5:用印审批
+ * 
+ */
+export const apiFlowDetail=params=>{
+	return httpGet('/approval/flowDetail',params)
+}
+
+/**
+ * 新增用印
+ * @param {string} CompanyName 客户名称,甲方名称
+ * @param {int}  ContractId	系统合同id
+ * @param {int}  CreditCode	社会统一信用代码
+ * @param {string}  FileUrl	文件附件地址
+ * @param {int}		FileNum	文件份数
+ * @param {string}  Remark 备注
+ * @param {string} SealType 加盖印章类型,枚举值:'合同章', '公章', '法人章'
+ * @param {string}  ServiceType	业务类型,枚举值:'新增业务', '续约业务', '补充协议'
+ * @param {string}  Use	用印用途,枚举值:'销售合同', '渠道合同', '付款通知函', '招投标', '战略合作协议'
+ * @param {string} UseCompanyName 实际使用方客户名称  
+ */
+export const apiSealAdd=params=>{
+	return httpPost('/seal/add',params)
+}
+
+/**
+ * 用印列表搜索
+ */
+export const apiSealSearch=params=>{
+	return httpGet('/seal/company_list',params)
+}
+
+/**
+ * 用印客户搜索出对应客户名称
+ * @param {string} CompanyName 客户名称 
+ */
+export const apiSearchCustome=params=>{
+	return httpGet('/contract/company_list',{...params,Status:'已审批'})
+}
+
+/**
+ * 根据客户搜索出对应客户下面合同数据
+ * @param {string} 	ContractStatus 合同状态(固定 已审批)
+ * @param {string}  Keyword	搜索关键字(对应客户名称)
+ */
+export const apiSearchContract=params=>{
+	return httpGet('/contract/list',{...params,ContractStatus:'已审批'})
+}
+
+/**
+ * 审批单列表
+ * @param {type} Status	状态:待审批、处理中、已审批、已作废 
+ */
+export const apiSealList=params=>{
+	return httpGet('/seal_approval/list',params)
+}
+
+/**
+ * 审批单详情
+ * @param {int}  ContractApprovalId 审批单id
+ * @param {int}  ContractApprovalRecordId 审批流程单id
+ */
+export const apiSealDetail=params=>{
+	return httpGet('/seal_approval/detail',params)
+}
+
+/**
+ * 用印审批通过
+ * @param {int} SealId
+ */
+export const apiApprovalPass=params=>{
+	return httpPost('/seal_approval/approved',params)
+}
+
+/**
+ * 修改用印审批单并通过审批
+ */
+export const apiApprovalPassModify=params=>{
+	return httpPost('/seal/check_edit',params)
+}
+
+/**
+ * 用印审批驳回
+ * @param {int} SealId
+ * @param {string} Remark  
+ */
+export const apiApprovalReject=params=>{
+	return httpPost('/seal_approval/reject',params)
+}
+
+/**
+ * 用印作废
+ * @param {boolean} IsInvalidContract 是否同步作废合同
+ * @param {int} SealId 用印id
+ */
+export const apiInvalidSeal=params=>{
+	return httpPost('/seal/invalid',params)
+}
+
+/**
+ * 用印编辑
+ */
+export const apiSealEdit=params=>{
+	return httpPost('/seal/edit',params)
+}

+ 21 - 0
api/message.js

@@ -0,0 +1,21 @@
+/**
+ * 消息模块
+ */
+import {httpGet,httpPost} from "@/utils/request.js"
+
+/**
+ * 消息统计(首页数据展示)
+ */
+export const apiMessageCount=()=>{
+	return httpGet('/message/summaryMessageList',{})
+}
+
+/**
+ * 消息列表
+ * @param {int} SourceType	消息类型(1:客户,2:合同,3:用印) 
+ * @param {int} CurrentIndex 当前页码
+ * @param {int} pageSize 每页数量
+ */
+export const apiMessageList=params=>{
+	return httpGet('/message/list',{...params,pageSize:20})
+}

+ 28 - 0
api/user.js

@@ -0,0 +1,28 @@
+/**
+ * 用户模块
+ */
+import {httpGet,httpPost} from "@/utils/request.js"
+import w_md5 from "@/js_sdk/zww-md5/w_md5.js"
+/**
+ * 微信登录 
+ * @param {string}  Code	微信唯一编码code
+ */
+export const apiWXLogin=params=>{
+	return httpGet('/wechat/wxapp/login',params)
+}
+
+/**
+ * 用户账号登录
+ * @param {string} Username 
+ * @param {string} Password  md5加密
+ */
+export const apiLogin=params=>{
+	return httpPost('/admin/login',{Username:params.Username,Password:w_md5.hex_md5_32(params.Password)})
+}
+
+/**
+ * 退出登录
+ */
+export const apiLoginOut=()=>{
+	return httpPost('/admin/logout',{})
+}

+ 204 - 0
js_sdk/zww-md5/w_md5.js

@@ -0,0 +1,204 @@
+let w_md5 = {}
+function hex_md5(string,bit) {
+    function md5_RotateLeft(lValue, iShiftBits) {
+        return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
+    }
+    function md5_AddUnsigned(lX, lY) {
+        var lX4, lY4, lX8, lY8, lResult;
+        lX8 = (lX & 0x80000000);
+        lY8 = (lY & 0x80000000);
+        lX4 = (lX & 0x40000000);
+        lY4 = (lY & 0x40000000);
+        lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
+        if (lX4 & lY4) {
+            return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
+        }
+        if (lX4 | lY4) {
+            if (lResult & 0x40000000) {
+                return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
+            } else {
+                return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
+            }
+        } else {
+            return (lResult ^ lX8 ^ lY8);
+        }
+    }
+    function md5_F(x, y, z) {
+        return (x & y) | ((~x) & z);
+    }
+    function md5_G(x, y, z) {
+        return (x & z) | (y & (~z));
+    }
+    function md5_H(x, y, z) {
+        return (x ^ y ^ z);
+    }
+    function md5_I(x, y, z) {
+        return (y ^ (x | (~z)));
+    }
+    function md5_FF(a, b, c, d, x, s, ac) {
+        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac));
+        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
+    };
+    function md5_GG(a, b, c, d, x, s, ac) {
+        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac));
+        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
+    };
+    function md5_HH(a, b, c, d, x, s, ac) {
+        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac));
+        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
+    };
+    function md5_II(a, b, c, d, x, s, ac) {
+        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac));
+        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
+    };
+    function md5_ConvertToWordArray(string) {
+        var lWordCount;
+        var lMessageLength = string.length;
+        var lNumberOfWords_temp1 = lMessageLength + 8;
+        var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
+        var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
+        var lWordArray = Array(lNumberOfWords - 1);
+        var lBytePosition = 0;
+        var lByteCount = 0;
+        while (lByteCount < lMessageLength) {
+            lWordCount = (lByteCount - (lByteCount % 4)) / 4;
+            lBytePosition = (lByteCount % 4) * 8;
+            lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
+            lByteCount++;
+        }
+        lWordCount = (lByteCount - (lByteCount % 4)) / 4;
+        lBytePosition = (lByteCount % 4) * 8;
+        lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
+        lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
+        lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
+        return lWordArray;
+    };
+    function md5_WordToHex(lValue) {
+        var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount;
+        for (lCount = 0; lCount <= 3; lCount++) {
+            lByte = (lValue >>> (lCount * 8)) & 255;
+            WordToHexValue_temp = "0" + lByte.toString(16);
+            WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
+        }
+        return WordToHexValue;
+    };
+    function md5_Utf8Encode(string) {
+        string = string.replace(/\r\n/g, "\n");
+        var utftext = "";
+        for (var n = 0; n < string.length; n++) {
+            var c = string.charCodeAt(n);
+            if (c < 128) {
+                utftext += String.fromCharCode(c);
+            } else if ((c > 127) && (c < 2048)) {
+                utftext += String.fromCharCode((c >> 6) | 192);
+                utftext += String.fromCharCode((c & 63) | 128);
+            } else {
+                utftext += String.fromCharCode((c >> 12) | 224);
+                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+                utftext += String.fromCharCode((c & 63) | 128);
+            }
+        }
+        return utftext;
+    };
+    var x = Array();
+    var k, AA, BB, CC, DD, a, b, c, d;
+    var S11 = 7, S12 = 12, S13 = 17, S14 = 22;
+    var S21 = 5, S22 = 9, S23 = 14, S24 = 20;
+    var S31 = 4, S32 = 11, S33 = 16, S34 = 23;
+    var S41 = 6, S42 = 10, S43 = 15, S44 = 21;
+    string = md5_Utf8Encode(string);
+    x = md5_ConvertToWordArray(string);
+    a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
+    for (k = 0; k < x.length; k += 16) {
+        AA = a; BB = b; CC = c; DD = d;
+        a = md5_FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
+        d = md5_FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
+        c = md5_FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
+        b = md5_FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
+        a = md5_FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
+        d = md5_FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
+        c = md5_FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
+        b = md5_FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
+        a = md5_FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
+        d = md5_FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
+        c = md5_FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
+        b = md5_FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
+        a = md5_FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
+        d = md5_FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
+        c = md5_FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
+        b = md5_FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
+        a = md5_GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
+        d = md5_GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
+        c = md5_GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
+        b = md5_GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
+        a = md5_GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
+        d = md5_GG(d, a, b, c, x[k + 10], S22, 0x2441453);
+        c = md5_GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
+        b = md5_GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
+        a = md5_GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
+        d = md5_GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
+        c = md5_GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
+        b = md5_GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
+        a = md5_GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
+        d = md5_GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
+        c = md5_GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
+        b = md5_GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
+        a = md5_HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
+        d = md5_HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
+        c = md5_HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
+        b = md5_HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
+        a = md5_HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
+        d = md5_HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
+        c = md5_HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
+        b = md5_HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
+        a = md5_HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
+        d = md5_HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
+        c = md5_HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
+        b = md5_HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
+        a = md5_HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
+        d = md5_HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
+        c = md5_HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
+        b = md5_HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
+        a = md5_II(a, b, c, d, x[k + 0], S41, 0xF4292244);
+        d = md5_II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
+        c = md5_II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
+        b = md5_II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
+        a = md5_II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
+        d = md5_II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
+        c = md5_II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
+        b = md5_II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
+        a = md5_II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
+        d = md5_II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
+        c = md5_II(c, d, a, b, x[k + 6], S43, 0xA3014314);
+        b = md5_II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
+        a = md5_II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
+        d = md5_II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
+        c = md5_II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
+        b = md5_II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
+        a = md5_AddUnsigned(a, AA);
+        b = md5_AddUnsigned(b, BB);
+        c = md5_AddUnsigned(c, CC);
+        d = md5_AddUnsigned(d, DD);
+    }
+    if(bit==32){
+        return (md5_WordToHex(a) + md5_WordToHex(b) + md5_WordToHex(c) + md5_WordToHex(d)).toLowerCase();
+    }
+    return (md5_WordToHex(b) + md5_WordToHex(c)).toLowerCase();
+}
+//16位小写
+w_md5.hex_md5_16 = function (string) { 
+	return hex_md5(string,16); 
+}
+//16位大写
+w_md5.hex_md5_16Upper = function (string) { 
+	return  hex_md5(string,16).toUpperCase(); 
+}
+//32位小写
+w_md5.hex_md5_32 = function (string) { 
+	return hex_md5(string,32); 
+}
+//32位大写
+w_md5.hex_md5_32Upper = function (string) { 
+	return hex_md5(string,32).toUpperCase(); 
+}
+export default w_md5

+ 22 - 0
main.js

@@ -0,0 +1,22 @@
+import Vue from 'vue'
+import App from './App'
+import store from './store'
+
+import uniAsync from '@/utils/uni-async.js'// uni api async 化
+Vue.prototype.$uniAsync = uniAsync
+
+import w_md5 from "./js_sdk/zww-md5/w_md5.js"
+Vue.prototype.$md5 = w_md5;//挂在MD5
+
+// vant 全局组件 
+import './utils/vantRegister';
+
+Vue.config.productionTip = false
+
+App.mpType = 'app'
+
+const app = new Vue({
+	store,
+    ...App
+})
+app.$mount()

+ 75 - 0
manifest.json

@@ -0,0 +1,75 @@
+{
+    "name" : "crm-xcx",
+    "appid" : "",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx67b68e39913e511e",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : true,
+            "minified" : true,
+            "postcss" : false
+        },
+        "usingComponents" : true,
+        "permission" : {}
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    }
+}

+ 117 - 0
pages-approve/components/approveListItem.vue

@@ -0,0 +1,117 @@
+<template>
+	<view class="message-item white-wrap" @click="handleGoDetail">
+		<view class="title flex">
+			<image src="../../static/man.png" mode="aspectFill" class="icon"></image>
+			<view>{{data.title}}</view>
+		</view>
+		<view class="message-content">
+			<view class="info">申请销售:{{data.saller}}</view>
+			<view class="info">申请类型:{{data.applyType}}</view>
+			<view class="info" v-if="data.status==='待审批'">提交时间:{{data.submitTime|formatTime}}</view>
+			<view class="info" v-if="data.status==='已审批'||data.status==='已驳回'">审批时间:{{data.approveTime|formatTime}}</view>
+			<view class="info" v-if="data.status==='已撤回'">撤回时间:{{data.backTime|formatTime}}</view>
+			<view class="info" v-if="data.status==='已作废'">作废时间:{{data.cancelTime|formatTime}}</view>
+			<view class="info" v-if="data.status==='已签回'">签回时间:{{data.checkBackTime|formatTime}}</view>
+		</view>
+		<view class="status approve-list-status-wait" v-if="data.status==='待审批'">待审批</view>
+		<view class="status approve-list-status-success" v-if="data.status==='已审批'">已审批</view>
+		<view class="status approve-list-status-fail" v-if="data.status==='已驳回'">已驳回</view>
+		<view class="status approve-list-status-cancel" v-if="data.status==='已作废'">已作废</view>
+		<view class="status approve-list-status-back" v-if="data.status==='已撤回'">已撤回</view>
+		<view class="status approve-list-status-success" v-if="data.status==='已签回'">已签回</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props:{
+			/**
+			 * {title:标题,
+			 * saller:申请销售,
+			 * applyType:申请类型,
+			 * submitTime:提交时间,
+			 * approveTime:审批时间,
+			 * backTime:撤回时间,
+			 * cancelTime:作废时间,
+			 * checkBackTime:签回时间
+			 * status:状态,
+			 * type:类型(客户custome、合同contract、用印seal)
+			 * id:审批单id
+			 * ContractApprovalId:审批单id
+			 * ContractApprovalRecordId:审批流程单id
+			 * ContractId:合同id}
+			 */
+			data:null
+		},
+		filters:{
+			formatTime(e){
+				if(e==='0001-01-01T00:00:00Z'){
+					return ''
+				}else{
+					return e.replace(/T/g,' ').replace(/\+08:00/g,' ').replace(/-/g,'.')
+				}
+			}
+		},
+		methods: {
+			handleGoDetail() {
+				if(this.data.type==='custome'){
+					uni.navigateTo({
+						url:'/pages-approve/custome/detail?id='+this.data.id
+					})
+				}
+				
+				if(this.data.type==='contract'){
+					uni.navigateTo({
+						url:`/pages-approve/contract/detail?ContractApprovalId=${this.data.ContractApprovalId}&ContractApprovalRecordId=${this.data.ContractApprovalRecordId}&ContractId=${this.data.ContractId}`
+					})
+				}
+				
+				if(this.data.type==='seal'){
+					uni.navigateTo({
+						url:`/pages-approve/seal/detail?ContractApprovalId=${this.data.ContractApprovalId}&ContractApprovalRecordId=${this.data.ContractApprovalRecordId}`
+					})
+				}
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.message-item {
+		padding: 30rpx;
+		box-shadow: 0px 3px 12px rgba(175, 175, 175, 0.16);
+		border-radius: 8px;
+		margin-bottom: 20rpx;
+
+		.title {
+			font-size: 16px;
+			font-weight: bold;
+			margin-bottom: 30rpx;
+
+			.icon {
+				width: 31rpx;
+				height: 34rpx;
+				flex-shrink: 0;
+				margin-right: 10rpx;
+				position: relative;
+				top: 4rpx;
+			}
+		}
+
+		.message-content {
+			font-size: 14px;
+			color: #666;
+			.info {
+				margin-bottom: 16rpx;
+			}
+
+			.info:last-child {
+				margin-bottom: 0;
+			}
+		}
+
+		.status {
+			text-align: right;
+		}
+	}
+</style>

+ 172 - 0
pages-approve/components/steps.vue

@@ -0,0 +1,172 @@
+<template>
+	<view class="steps-wrap">
+		<view class="step-box" v-for="(item,index) in stepArr" :key="index">
+			<view class="step-line" v-if="stepArr.length>1"></view>
+			<view class="step-content">
+				<view class="step-top-box">
+					<view style="width: 50%;">
+						<view class="title">{{item.name}}</view>
+						<view class="text">{{item.intro}}</view>
+					</view>
+					<view class="step-user">
+						<view v-for="(user,index2) in item.user" :key="user.ApproveUserName" style="display: flex;">
+							<view class="avatar">{{user.ApproveUserName}}</view>
+							<text class="symbol" v-if="index2!=item.user.length-1">{{item.auditType===1?'/':'+'}}</text>
+						</view>
+					</view>
+				</view>
+				
+				<view class="step-approve-box">
+					<view class="step-approve-item" v-for="approve in item.approve" :key="approve.name">
+						<view style="margin-bottom: 10rpx;">审批人:{{approve.name}}</view>
+						<view style="margin-bottom: 10rpx;">{{approve.time}}</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		
+	</view>
+</template>
+
+<script>
+	export default{
+		props:{
+			data:[]
+		},
+		computed:{
+			stepArr(){
+				let arr=[]
+				arr=this.data&&this.data.map(item=>{
+					let stepCon={
+						name:item[0].NodeType==='check'?'审批人':'抄送人',//审批节点标题
+						intro:'',//描述
+						user:item,//审批节点人列表
+						approve:[],//审批人信息{name:'',time:""}
+						auditType:1
+					}
+					if(item[0].NodeType==='check'){
+						if(item[0].AuditType===1){
+							stepCon.intro=item.length>1?item.length+'人或签':''
+							stepCon.auditType=1
+						}else if(item[0].AuditType===2){
+							stepCon.intro=item.length>1?item.length+'人会签':''
+							stepCon.auditType=2
+						}
+						item.forEach(item2=>{
+							if(item2.Status!=='待审批'&&item2.Status!=='已撤回'){
+								let time=''
+								if(item2.Status==='已驳回'){
+									time=`驳回时间:${this.timeFormat(item2.ApproveTime)}`
+								}else {
+									time=`审批时间:${this.timeFormat(item2.ApproveTime)}`
+								}
+								let obj={name:item2.ApproveUserName,time:time}
+								stepCon.approve.push(obj)
+							}
+						})
+					}
+					
+					if(item[0].NodeType==='cc'){
+						stepCon.intro='抄送'+item.length+'人'
+					}
+					
+					
+					return stepCon
+				})
+				
+				// console.log(arr);
+				return arr
+			}
+		},
+		methods: {
+			timeFormat(e) {
+				const time=new Date(e)
+				const year=time.getFullYear()
+				const month = time.getMonth() + 1 > 9 ? time.getMonth()+1 : '0'+(time.getMonth()+1);
+				const day = time.getDate() > 9 ? time.getDate() : "0" + time.getDate();
+				const hours=time.getHours() > 9 ? time.getHours():'0'+time.getHours()
+				const minutes=time.getMinutes()>9 ?time.getMinutes():'0'+time.getMinutes()
+				const seconds=time.getSeconds()>9?time.getSeconds():'0'+time.getSeconds()
+				return `${year}.${month}.${day} ${hours}:${minutes}:${seconds}`
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.step-box{
+		display: flex;
+		position: relative;
+	}
+	.step-line{
+		width: 2rpx;
+		background-color: #E1E1E1;
+		margin-right: 40rpx;
+		flex-shrink: 0;
+		position: relative;
+		top: 50rpx;
+		&::before{
+			position: absolute;
+			left: 50%;
+			transform: translateX(-50%);
+			content: '';
+			display: block;
+			width: 20rpx;
+			height: 20rpx;
+			border-radius: 50%;
+			background-color: #E1E1E1;
+		}
+	}
+	.step-content{
+		padding: 37rpx 0;
+		flex: 1;
+		border-bottom: 2rpx solid #e1e1e1;
+		font-size: 12px;
+		color: #666;
+		.title{
+			font-size: 16px;
+			font-weight: bold;
+			margin-bottom: 8rpx;
+			color: #333;
+		}
+	}
+	.step-box:last-child{
+		.step-content{
+			border: none;
+		}
+		
+	}
+	.step-box:last-child{
+		.step-line{
+			width: 0;
+		}
+	}
+	.step-approve-box{
+		margin-top: 40rpx;
+		.step-approve-item{
+			color: #F58D57;
+		}
+	}
+	.step-top-box{
+		display: flex;
+		justify-content: space-between;
+		.step-user{
+			display: flex;
+			flex-wrap: wrap;
+			.avatar{
+				padding: 0 16rpx;
+				color: #fff;
+				background-color: #3385FF;
+				border-radius: 8rpx;
+				height: 74rpx;
+				line-height: 74rpx;
+			}
+			.symbol{
+				font-size: 18px;
+				position: relative;
+				top: 18rpx;
+				margin: 0 10rpx;
+			}
+		}
+	}
+</style>

+ 541 - 0
pages-approve/contract/detail.vue

@@ -0,0 +1,541 @@
+<template>
+	<view class="detail-page white-wrap">
+		<view>
+		<!-- 基础信息 -->
+		<view class="section white-wrap base-info-wrap">
+			<image :src="statusImg" mode="aspectFill" class="status-img"></image>
+			<view class="title">
+				<text>{{detail.CompanyName}}</text>
+				<!-- van-tag 
+					color="#ECF5FF" 
+					text-color="#4A83F1"
+					style="margin-bottom:0;margin-left: 10rpx;"
+				>新签</van-tag> -->
+			</view>
+			<view class="base-info-list">
+				<view>
+					<text class="lable">合同编号:</text>
+					<text >{{detail.ContractCode}}</text>
+					<van-tag color="#ECF5FF" text-color="#4A83F1" style="margin-left:5px">{{detail.ContractType}}</van-tag>
+				</view>
+				<view>
+					<text class="lable">合同期限:</text>
+					<text >{{detail.StartDateStr+'~'+detail.EndDateStr | formatTime}}({{detail.StartDateStr|formateYear(detail.EndDateStr)}})</text>
+				</view>
+				<view>
+					<text class="lable">合同金额:</text>
+					<text style="color:#FE6B7C">{{detail.Price}}元</text>
+				</view>
+				<view>
+					<text class="lable">合同归属:</text>
+					<text>{{detail.ProductId===1?'FICC':'权益'}}</text>
+				</view>
+				<view>付款方式说明:{{detail.PayRemark}}</view>
+				<view v-if="detail && detail.Status === '已驳回'">
+					<text class="lable" style="color:#FE6B7C">驳回理由:</text>
+					<text style="color:#FE6B7C">{{ detail.ApprovalRemark }}</text>
+				</view>
+			</view>
+		</view>
+		<!-- 变更说明 -->
+		<view class="section white-wrap change-info-wrap" v-if="changeDesc.length>0">
+			<view class="title">变更说明</view>
+			<view style="margin-bottom: 20rpx;" v-for="(item,index) in changeDesc" :key="index">{{index+1}}、{{item}}</view>
+		</view>
+		<!-- 服务内容 -->
+		<view class="section white-wrap service-wrap">
+			<view class="title">服务内容</view>
+			<view style="border-bottom: 1px dashed rgba(112, 112, 112, .21);margin-bottom: 30rpx;" >
+				<view class="service-item" v-for="item in detail.Service" :key="item.Title" @click="handlePreviewImg(item.Value)">{{item.Title}}</view>
+			</view>
+			<view >补充内容:{{detail.Remark||'无'}}</view>
+		</view>
+		
+		<!-- 回签附件 -->
+		<view class="section white-wrap contract-wrap" v-if="detail.Status==='已签回'">
+			<view class="title">回签附件</view>
+			<view class="contract-list">
+				<view class="contract-item" v-for="item in checkBackFiles" :key="item.url"
+					@click="preViewContract(item)">
+					<image class="contract-img" mode="aspectFill" :src="item.img"></image>
+				</view>
+			</view>
+		</view>
+		
+		<!-- 合同模块 -->
+		<view class="section white-wrap contract-wrap" v-if="constractFiles.length !== 0&&detail.Status!='已签回'">
+			<view class="title">合同预览</view>
+			<view class="contract-list">
+				<view class="contract-item" v-for="item in constractFiles" :key="item.url"
+					@click="preViewContract(item)">
+					<image class="contract-img" mode="aspectFill" :src="item.img"></image>
+				</view>
+			</view>
+		</view>
+		
+		<!-- 客户信息模块 -->
+		<view class="section white-wrap customer-wrap">
+			<view class="title">客户信息</view>
+			<view class="customer-main-info">
+				<view class="customer-info-item">
+					<text>客户名称:</text>
+					<text>{{detail.CompanyName}}</text>
+				</view>
+				<view class="customer-info-item">
+					<text>社会信用码:</text>
+					<text>{{detail.CreditCode}}</text>
+				</view>
+				<view class="customer-info-item">
+					<text>地址:</text>
+					<text>{{detail.Province}}{{detail.City}}{{detail.Address}}</text>
+				</view>
+				<view class="customer-info-item">
+					<text>申请人:</text>
+					<text>{{detail.SellerName}}</text>
+				</view>
+			</view>
+		</view>
+		
+		<!-- 流程模块 -->
+		<view class="section white-wrap process-wrap">
+			<view class="title">审批流程</view>
+			<steps :data="flowNodeList"></steps>
+		</view>
+		</view>
+		
+		<!-- 审批按钮 -->
+		<view class="fix-bottom-wrap btns-wrap flex" v-if="opt.Approval">
+			<button class="pass-btn" @click="handlePass">通过</button>
+			<button class="refuse-btn" @click="handleRefuse">驳回</button>
+		</view>
+		<!-- 上传回签附件 -->
+		<view class="fix-bottom-wrap btns-wrap flex" v-if="opt.UploadFile">
+			<button class="pass-btn" style="width: 450rpx;" @click="handleUploadMethod('file')">{{detail.Status==='已审批'?'上传回签附件':'更新回签附件'}}</button>
+		</view>
+		<!-- 作废合同 -->
+		<!-- <view class="fix-bottom-wrap btns-wrap flex" v-if="opt.Invalid">
+			<button class="refuse-btn" style="width: 450rpx;" >作废合同</button>
+		</view> -->
+		
+		<!-- 上传回签附件选择上传文件类型弹窗 -->
+		<van-popup :show="show" @close="show=false" round>
+			<view style="width: 50vw;text-align: center;font-size: 16px;padding: 32rpx;line-height: 80rpx;">
+				<view @click="handleUploadMethod('img')">上传图片</view>
+				<view @click="handleUploadMethod('file')">上传文件</view>
+			</view>
+		</van-popup>
+		
+		<!-- 上传回签附件弹窗 -->
+		<van-popup :show="showUpload" @close="showUpload=false" position="bottom" round>
+			<view class="uploadfile-wrap">
+				<view style="text-align: center;font-size: 16px;font-weight: bold;">上传回签附件</view>
+				<view class="contract-list" style="height: 30vh;">
+					<view class="contract-item" v-for="item in files" :key="item.url"
+						@click="preViewContract(item)">
+						<image class="contract-img" mode="aspectFill" :src="item.img"></image>
+					</view>
+				</view>
+				<button style="width: 450rpx;" @click='handleConfirmSignBack'>确定</button>
+			</view>
+		</van-popup>
+		
+		<van-dialog id="van-dialog" />
+	</view>
+</template>
+
+<script>
+	
+	import {apiContractDetail,apiContractApproved,apiContractApprovalDetail,apiContractSignBackFiles} from '@/api/approve/contract.js'
+	import steps from '../components/steps.vue'
+	import {preViewFile} from '../utils/util.js'
+	import {uploadImg,uploadFiles} from '@/utils/uploadFile.js'
+	export default{
+		components:{
+			steps
+		},
+		filters:{
+			formatTime(e){
+				return e.replace(/-/g,'.')
+			},
+			formateYear(start,end){
+				let starttime = new Date(start);
+				let endtime = new Date(end);
+				let difftime = endtime - starttime;
+				let ret = parseFloat((difftime / (1000 * 60 * 60 * 24 * 365)).toFixed(1));
+				return `有效期为 ${ret} 年`;
+			}
+		},
+		computed:{
+			// 处理变更说明
+			changeDesc(){
+				return this.detail.ModifyContent&&this.detail.ModifyContent.split('<br/>').filter(item=> item)
+			},
+			//回签附件
+			checkBackFiles(){
+				const reg = /\.(pdf)$/;
+				return this.detail.CheckBackFileUrl&&this.detail.CheckBackFileUrl.split("#").map((item) => {
+					if (reg.test(item)) {
+						return {
+							type: "pdf",
+							url: item,
+							img: require("@/pages-approve/static/pdf.png"),
+						};
+					} else {
+						return {
+							type: "img",
+							url: item,
+							img: item,
+						};
+					}
+				});
+			},
+			// 合同附件
+			constractFiles(){
+				const reg = /\.(pdf)$/;
+				return this.detail.FileUrl&&this.detail.FileUrl.split("#").map((item) => {
+					if (reg.test(item)) {
+						return {
+							type: "pdf",
+							url: item,
+							img: require("@/pages-approve/static/pdf.png"),
+						};
+					} else {
+						return {
+							type: "img",
+							url: item,
+							img: item,
+						};
+					}
+				});
+			},
+			// 状态图标
+			statusImg(){
+				if(this.detail.Status==='已审批'){
+					return require('../static/pass-icon.png')
+				}else if(this.detail.Status==='已签回'){
+					return require('../static/draw-back-icon.png')
+				}else if(this.detail.Status==='已驳回'){
+					return require('../static/fail-icon.png')
+				}else if(this.detail.Status==='已作废'){
+					return require('../static/cancel-icon.png')
+				}else if(this.detail.Status==='已撤回'){
+					return require('../static/recall-icon.png')
+				}else if(this.detail.Status==='处理中'){
+					return require('../static/processing-icon.png')
+				}
+			}
+		},
+		data() {
+			return {
+				ContractApprovalId: null,//审批单id
+				ContractApprovalRecordId:null,//审批流程单id
+				ContractId:null,//合同id
+				detail:{},
+				flowNodeList:null,
+				opt:{},
+				show:false,
+				showUpload:false,
+				files:[],
+			}
+		},
+		onLoad(options) {
+			this.ContractApprovalId=options.ContractApprovalId
+			this.ContractApprovalRecordId=options.ContractApprovalRecordId 
+			this.ContractId=options.ContractId
+		},
+		onShow() {
+			// 当合同id 不存在时(或0) 使用审批详情
+			if(this.ContractId!=0){
+				this.getContractDetail()
+			}else{
+				this.getApprovalDetail()
+			}
+		},
+		onPullDownRefresh() {
+			setTimeout(()=>{
+				uni.stopPullDownRefresh()
+			},1000)
+		},
+		methods: {
+			//确认提交上传回签附件
+			async handleConfirmSignBack(){
+				let FileUrlarr=this.files.map(item=>item.url)
+				const res=await apiContractSignBackFiles({
+					ContractId:Number(this.detail.ContractId),
+					FileUrl:FileUrlarr.join('#')
+				})
+				if(res.code===200){
+					uni.showToast({
+						title:'操作成功',
+						icon:'none'
+					})
+					setTimeout(()=>{
+						this.showUpload=false
+						this.files=[]
+						if(this.ContractId!=0){
+							this.getContractDetail()
+						}else{
+							this.getApprovalDetail()
+						}
+					},1000)
+				}
+			},
+			
+			// 上传回签附件文件
+			async handleUploadMethod(type){
+				let res=[]
+				if(type==='img'){
+					res=await uploadImg()
+				}else if(type==='file'){
+					res=await uploadFiles({type:'file'})
+				}
+				const reg = /\.(pdf)$/;
+				if(!reg.test(res[0])){
+					uni.showToast({
+						title:'请上传pdf格式文件',
+						icon:"none"
+					})
+					return 
+				}
+				this.files=[{type:'pdf',url:res[0],img: require("@/pages-approve/static/pdf.png")}]
+				// this.files=res.map((item) => {
+				// 	if (reg.test(item)) {
+				// 		return {
+				// 			type: "pdf",
+				// 			url: item,
+				// 			img: require("@/pages-approve/static/pdf.png"),
+				// 		};
+				// 	} else {
+				// 		return {
+				// 			type: "img",
+				// 			url: item,
+				// 			img: item,
+				// 		};
+				// 	}
+				// });
+				this.show=false
+				this.showUpload=true
+			},
+			
+			// 审批单详情
+			async getApprovalDetail() {
+				const res=await apiContractApprovalDetail({
+					ContractApprovalId:Number(this.ContractApprovalId),
+					ContractApprovalRecordId:Number(this.ContractApprovalRecordId)
+				})
+				if(res.code===200){
+					this.detail=res.data.ContractDetail
+					this.opt=res.data.OpButton
+					this.flowNodeList=res.data.FlowNodeList
+				}
+			},
+			
+			//合同详情
+			async getContractDetail(){
+				const res=await apiContractDetail({ContractId:Number(this.ContractId)})
+				if(res.code===200){
+					this.detail=res.data.ContractDetail
+					this.opt=res.data.OpButton
+					this.flowNodeList=res.data.FlowNodeList
+				}
+			},
+			
+			handlePreviewImg(url){
+				uni.previewImage({
+					urls:[url]
+				})
+			},
+			
+			// 预览合同文件
+			preViewContract(e) {
+				if (e.type === "pdf") {
+					preViewFile(e.url)
+				} else {
+					this.handlePreviewImg(e.url)
+				}
+			},
+			
+			//审批通过
+			async handlePass(){
+				const res=await apiContractApproved({ContractId:Number(this.detail.ContractId)})
+				if(res.code===200){
+					this.$dialog.alert({
+						title: "处理成功",
+						confirmButtonColor: "#5890FB",
+					}).then(() => {
+						// on close
+ 						// 当合同id 不存在时(或0) 使用审批详情
+ 						if(this.ContractId!=0){
+ 							this.getContractDetail()
+ 						}else{
+ 							this.getApprovalDetail()
+ 						}
+					});
+				}
+			},
+			
+			//审批驳回
+			handleRefuse(){
+				uni.navigateTo({
+					url:"./reason?ContractId="+this.detail.ContractId
+				})
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.detail-page {
+		width: 100%;
+		min-height: 100%;
+		padding-bottom: calc(150rpx + constant(safe-area-inset-bottom));
+		padding-bottom: calc(150rpx + env(safe-area-inset-bottom));
+	}
+	
+	.section {
+		padding: 30rpx 34rpx;
+		border-bottom: 14rpx solid #f5f5f5;
+	
+		&:last-child {
+			margin-bottom: 0;
+			border: none;
+		}
+	
+		.title {
+			font-size: 16px;
+			font-weight: bold;
+			margin: 0 0 40rpx 0;
+			color: #333;
+		}
+	
+		.lable {
+			color: #666;
+			flex-shrink: 0;
+			font-size: 14px;
+		}
+	}
+	.section::last-child{
+		border: none;
+	}
+	.base-info-wrap {
+		position: relative;
+		.status-img{
+			position: absolute;
+			width: 222rpx;
+			height: 222rpx;
+			right: 0;
+			top: 150rpx;
+			z-index: 10;
+		}
+		.title {
+			margin: 0 0 40rpx 0;
+	
+			&::before {
+				content: "";
+				display: inline-block;
+				width: 15px;
+				height: 17px;
+				background-image: url("@/static/man.png");
+				background-size: cover;
+				background-repeat: no-repeat;
+				margin-right: 10rpx;
+				position: relative;
+				top: 4rpx;
+			}
+		}
+	
+		.base-info-list {
+			font-size: 14px;
+			color: #000;
+	
+			view {
+				margin-bottom: 10px;
+				display: flex;
+			}
+		}
+	}
+	
+	.contract-list {
+		display: flex;
+		flex-wrap: wrap;
+	
+		.contract-img {
+			width: 102rpx;
+			height: 120rpx;
+			margin-right: 20rpx;
+			margin-bottom: 20rpx;
+		}
+	}
+	
+	.service-wrap{
+		color: #666;
+		.service-item{
+			margin-bottom: 30rpx;
+			position: relative;
+			&::after{
+				content: '查看服务单';
+				display: inline-block;
+				color: #3385FF;
+				position: absolute;
+				left: 35%;
+			}
+		}
+	}
+	
+	.customer-main-info {
+		padding-bottom: 40rpx;
+	}
+	
+	.customer-info-item {
+		font-size: 14px;
+		color: #999;
+		margin-bottom: 20rpx;
+	}
+	
+	.customer-info-list {
+		flex-wrap: wrap;
+		padding-top: 20rpx;
+	}
+	
+	.btns-wrap {
+		justify-content: center;
+		button {
+			width: 220rpx;
+			height: 70rpx;
+			border-radius: 28px;
+			border: none;
+			margin: 0 10px;
+			font-size: 14px;
+			color: #fff;
+			line-height: 70rpx;
+		}
+		
+		.pass-btn {
+			background-color: #5890fb;
+		}
+		
+		.refuse-btn {
+			background-color: #f55768;
+		}
+	}
+	
+	.uploadfile-wrap{
+		width: 100%;
+		height: 50vh;
+		background-color: #FFFFFF;
+		padding: 32rpx;
+		button {
+			margin-left: auto;
+			margin-right: auto;
+			width: 220rpx;
+			height: 70rpx;
+			display: block;
+			border-radius: 28px;
+			border: none;
+			font-size: 14px;
+			color: #fff;
+			line-height: 70rpx;
+			background-color: #5890fb;
+		}
+	}
+</style>

+ 151 - 0
pages-approve/contract/list.vue

@@ -0,0 +1,151 @@
+<template>
+	<view>
+		<van-sticky>
+			<view class="top-wrap">
+				<view @click="handleGoSearch">
+					<van-search disabled use-left-icon-slot shape="round" placeholder="客户名称/社会信用码">
+						<view slot="left-icon">
+							<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+						</view>
+					</van-search>
+				</view>
+				<van-tabs id="tabs" :active="status" title-active-color="#3385FF" color="#3385FF" @change="typeChange">
+					<van-tab :title="item" :name="item" v-for="item in tabList" :key="item"></van-tab>
+				</van-tabs>
+			</view>
+		</van-sticky>
+		<van-empty description="暂无数据" :image="require('@/static/empty.png')" v-if="finished&&list.length===0"/>
+		<view class="list" v-else>
+			<approve-list-item v-for="item in list" :key="item.id" :data="item"></approve-list-item>
+		</view>
+		
+	</view>
+</template>
+
+<script>
+	import {apiContractApproveList} from '@/api/approve/contract.js'
+	import approveListItem from '../components/approveListItem.vue'
+	export default{
+		components:{
+			approveListItem
+		},
+		data() {
+			return {
+				tabList:[],
+				status: '待审批',
+				list:[],
+				page:1,
+				finished:false,
+			}
+		},
+		onLoad() {
+			this.initTabs()//初始化tab 栏
+		},
+		onShow() {
+			this.$nextTick(()=>{
+				this.selectComponent('#tabs').resize();// 解决初始渲染 vant tab 底部条
+			})
+			this.page=1
+			this.list=[]
+			this.getList()
+		},
+		onPullDownRefresh() {
+			this.page=1
+			this.finished=false
+			this.list=[]
+			this.getList()
+			setTimeout(()=>{
+				uni.stopPullDownRefresh()
+			},1500)
+		},
+		onReachBottom() {
+			if(this.finished) return
+			this.page++
+			this.getList()
+		},
+		methods: {
+			//初始化tab
+			initTabs(){
+				// ficc销售 权益销售
+				const RoleTypeCode=this.$store.state.userInfo.RoleTypeCode
+				if(RoleTypeCode==='ficc_seller'||RoleTypeCode==='rai_seller'){
+					this.tabList=['待审批','处理中','已审批','已签回','待提交','已撤回','已作废']
+					this.status='待审批'
+				}
+				// ficc管理员 权益管理员  合规compliance
+				if(RoleTypeCode==='ficc_admin'||RoleTypeCode==='rai_admin'||RoleTypeCode==='compliance'){
+					this.tabList=['待审批','处理中','已审批','已签回','已作废']
+					this.status='待审批'
+				}
+			},
+			
+			// 去搜索
+			handleGoSearch() {
+				uni.navigateTo({
+					url: '../search/index?type=contract'
+				})
+			},
+			
+			typeChange(e){
+				this.status=e.detail.name
+				this.page=1
+				this.finished=false
+				this.list=[]
+				this.getList()
+			},
+			
+			//获取列表数据
+			async getList(){
+				const res=await apiContractApproveList({
+					CurrentIndex:this.page,
+					Status:this.status,
+					KeyWord:''
+				})
+				if(res.code===200){
+					if(!res.data.List||res.data.List.length===0){
+						this.finished=true
+					}else{
+						let arr=res.data.List.map(item=>{
+							let backTime=''
+							if(item.Status==='已撤回'){
+								backTime=item.ModifyTime
+							}
+							return {
+								title:item.CompanyName,
+								saller:item.SellerName,
+								submitTime:item.CreateTime,
+								approveTime:item.ApproveTime,
+								checkBackTime:item.CheckBackFileTime,
+								backTime:backTime,
+								cancelTime:item.InvalidTime,
+								status:item.Status,
+								applyType:item.ApplyContent,
+								id:item.ContractApprovalId,
+								ContractApprovalId:item.ContractApprovalId,
+								ContractApprovalRecordId:item.ContractApprovalRecordId,
+								ContractId:item.ContractId,
+								type:'contract',
+							}
+						})
+						this.list=[...this.list,...arr]
+					}
+				}
+			},
+			
+		},
+	}
+</script>
+
+<style lang="scss">
+	.search-icon {
+		width: 40rpx;
+		height: 40rpx;
+		display: block;
+		position: relative;
+		top: 4rpx;
+		margin-right: 10rpx;
+	}
+	.list{
+		padding: 20rpx;
+	}
+</style>

+ 102 - 0
pages-approve/contract/reason.vue

@@ -0,0 +1,102 @@
+<template>
+	<view>
+		<view class="textarea-wrap white-wrap">
+			<textarea placeholder="请输入驳回理由" v-model="reason" maxlength="-1"></textarea>
+		</view>
+		<view class="btns-wrap flex">
+			<button class="confirm-btn" @click="handleSubmit">提交</button>
+			<button class="cancel-btn" @click="handleCancel">取消</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {apiContractReject} from '@/api/approve/contract.js'
+	export default {
+		data() {
+			return {
+				reason: '',
+				ContractId:null,
+			}
+		},
+		onLoad(options) {
+			this.ContractId=options.ContractId
+		},
+		methods: {
+			async handleSubmit() {
+				if(!this.reason){
+					uni.showToast({
+						title:'请填写理由',
+						icon:"none"
+					})
+					return
+				}
+				const res=await apiContractReject({
+					ContractId:Number(this.ContractId),
+					Remark:this.reason
+				})
+				if(res.code===200){
+					uni.showToast({
+						title:"审批成功",
+						icon:'success'
+					})
+					setTimeout(()=>{
+						this.handleCancel()
+					},1000)
+				}
+			},
+			
+			handleCancel(){
+				uni.navigateBack({
+					delta:1
+				})
+			}
+			
+		},
+	}
+</script>
+
+<style lang="scss">
+	.textarea-wrap {
+		padding: 17px;
+
+		textarea {
+			resize: none;
+			display: block;
+			box-sizing: border-box;
+			width: 100%;
+			height: 200px;
+			border: none;
+			font-size: 14px;
+			/* no */
+		}
+	}
+
+	.btns-wrap {
+		justify-content: center;
+		padding-top: 50px;
+		padding-bottom: 50px;
+
+		button {
+			width: 90px;
+			height: 28px;
+			border-radius: 28px;
+			border: none;
+			margin: 0 10px;
+			font-size: 14px;
+			/* no */
+			color: #fff;
+			line-height: 28px;
+		}
+
+		.confirm-btn {
+			background-color: #5890fb;
+		}
+
+		.cancel-btn {
+			border: 1px solid #5890fb;
+			color: #5890fb;
+			background-color: #fff;
+		}
+	}
+</style>

+ 157 - 0
pages-approve/contract/search.vue

@@ -0,0 +1,157 @@
+<template>
+	<view>
+		<van-sticky>
+			<view class="top-wrap">
+				<view @click="handleBack">
+					<van-search disabled use-left-icon-slot shape="round" :value="keyword" placeholder="客户名称/社会信用码">
+						<view slot="left-icon">
+							<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+						</view>
+					</van-search>
+				</view>
+			</view>
+		</van-sticky>
+		<van-empty description="暂无数据" :image="require('@/static/empty.png')" v-if="finished&&list.length===0"/>
+		<view class="list-wrap" v-else>
+			<view class="item white-wrap" v-for="item in list" :key="item.ContractCode" @click="handleGoDetail(item)">
+				<view class="title flex">
+					<image src="../../static/man.png" mode="aspectFill" class="icon"></image>
+					<view>{{item.CompanyName}}</view>
+				</view>
+				<view class="message-content">
+					<view class="info">合同期限:{{item.StartDateStr}}~{{item.EndDateStr}}</view>
+					<view class="info">合同金额:<text style="color:#FF4343">{{item.Price}}</text></view>
+					<view class="info">合同类型:{{item.ContractType}}</view>
+					<view class="info">销&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;售:{{item.SellerName}}</view>
+					<view class="info">更新时间:{{item.ModifyTimeStr}}</view>
+				</view>
+				<image :src="item.statusImg" mode="aspectFill" class="status-img"></image>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {apiContractList} from '@/api/approve/contract.js'
+	export default{
+		data() {
+			return {
+				keyword:'',
+				list:[],
+				page:1,
+				finished:false
+			}
+		},
+		onLoad(options) {
+			this.keyword=options.val 
+			this.getList()
+		},
+		methods: {
+			handleBack(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			
+			handleGoDetail(e){
+				uni.navigateTo({
+					url:`./detail?ContractId=${e.ContractId}&ContractApprovalId=0&ContractApprovalRecordId=0`
+				})
+			},
+			
+			async getList() {
+				const res=await apiContractList({
+					CurrentIndex:this.page,
+					KeyWord:this.keyword
+				})
+				if(res.code===200){
+					if(res.data.Paging.IsEnd){
+						this.finished=true
+					}
+					let arr=res.data.List.map(item=>{
+						let statusImg=''
+						switch(item.Status){
+							case '已作废':
+								statusImg=require('../static/cancel-icon.png')
+								break;
+							case '已驳回':
+								statusImg=require('../static/fail-icon.png')
+								break;
+							case '已审批':
+								statusImg=require('../static/pass-icon.png')
+								break;
+							case '待审批':
+								statusImg=require('@/pages-todomessages/static/icon-2.png')
+								break;
+						}
+						return{
+							...item,
+							statusImg
+						}
+					})
+					
+					this.list=[...this.list,...arr]
+				}
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.search-icon {
+		width: 40rpx;
+		height: 40rpx;
+		display: block;
+		position: relative;
+		top: 4rpx;
+		margin-right: 10rpx;
+	}
+	.list-wrap {
+		padding: 20rpx;
+		.item {
+			padding: 30rpx;
+			box-shadow: 0px 3px 12px rgba(175, 175, 175, 0.16);
+			border-radius: 8px;
+			margin-bottom: 20rpx;
+			position: relative;
+			.status-img{
+				width: 184rpx;
+				height: 184rpx;
+				position: absolute;
+				right: 0;
+				top: 80rpx;
+			}
+			.title {
+				font-size: 16px;
+				font-weight: bold;
+				margin-bottom: 30rpx;
+		
+				.icon {
+					width: 31rpx;
+					height: 34rpx;
+					flex-shrink: 0;
+					margin-right: 10rpx;
+					position: relative;
+					top: 4rpx;
+				}
+			}
+		
+			.message-content {
+				font-size: 14px;
+				color: #666;
+				.info {
+					margin-bottom: 16rpx;
+				}
+		
+				.info:last-child {
+					margin-bottom: 0;
+				}
+			}
+		
+			.status {
+				text-align: right;
+			}
+		}
+	}
+	
+</style>

+ 713 - 0
pages-approve/custome/detail.vue

@@ -0,0 +1,713 @@
+<template>
+	<view class="detail-page white-wrap">
+		<view>
+			<!-- 基础信息模块 -->
+			<view class="section white-wrap base-info-wrap" v-if="info">
+				<view class="title">
+					{{ info.CompanyApprovalDetail.CompanyName }}
+					<van-tag v-if="info.CompanyApprovalDetail.ApplyMethod === 3" color="#ECF5FF" text-color="#4A83F1"
+						style="margin-bottom:0;margin-left: 10rpx;">第{{ info.CompanyApprovalDetail.ApprovalCount }}次申请</van-tag>
+				</view>
+				<view class="base-info-list">
+					<view v-for="item in baseInfo" :key="item.key">
+						<text class="lable">{{ item.key }}:</text>
+						<text :style="{ color: item.color }">
+							{{ item.val }}
+
+						</text>
+						<van-tag v-if="item.tag" color="#ECF5FF" text-color="#4A83F1" style="margin-left:5px">
+							{{ item.tag }}
+						</van-tag>
+					</view>
+					<view v-if="info && info.CompanyApprovalDetail.ApproveStatus === '驳回'">
+						<text class="lable" style="color:#FE6B7C">驳回理由:</text>
+						<text style="color:#FE6B7C">{{ info.CompanyApprovalDetail.ApproveRemark }}</text>
+					</view>
+				</view>
+				<image :src="info.CompanyApprovalDetail.statusImg" mode="aspectFill" class="status-img"></image>
+				<!-- view class="status-img pass-img" v-if="info.CompanyApprovalDetail.ApproveStatus==='已审批'"></view>
+				<view class="status-img fail-img" v-if="info.CompanyApprovalDetail.ApproveStatus==='驳回'"></view> -->
+			</view>
+
+			<!-- 品种模块 -->
+			<view class="section white-wrap variety-wrap" v-if="varietyInfo">
+				<view class="title">{{ varietyInfo.title }}</view>
+				<view class="variety-list">
+					<view class="variety-item flex" v-for="item in varietyInfo.list" :key="item.ClassifyName">
+						<view class="lable">{{ item.ClassifyName }}:</view>
+						<view class="content">
+							<van-tag color="#ECF5FF" text-color="#4A83F1" v-for="tag in item.Items"
+								:key="tag.ChartPermissionId" custom-class="tag">{{ tag.PermissionName }}</van-tag>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<!-- 合同模块 -->
+			<view class="section white-wrap contract-wrap" v-if="constractFiles.length !== 0">
+				<view class="title">查看合同附件</view>
+				<view class="contract-list">
+					<view class="contract-item" v-for="item in constractFiles" :key="item.url"
+						@click="preViewContract(item)">
+						<image class="contract-img" mode="aspectFill" :src="item.img"></image>
+					</view>
+				</view>
+			</view>
+
+			<!-- 客户信息模块 -->
+			<view class="section white-wrap customer-wrap" v-if="showCustomerInfo && info">
+				<view class="title">客户信息</view>
+				<view class="customer-main-info">
+					<view class="customer-info-item">
+						<text>客户名称:</text>
+						<text>{{ info.CompanyApprovalDetail.CompanyName }}</text>
+					</view>
+					<view class="customer-info-item">
+						<text>社会信用码:</text>
+						<text>{{ info.CompanyApprovalDetail.CreditCode }}</text>
+					</view>
+					<view class="customer-info-item">
+						<text>地址:</text>
+						<text>{{ info.CompanyApprovalDetail.Address }}</text>
+					</view>
+				</view>
+				<view class="customer-info-list flex">
+					<view class="customer-info-item flex" style="width:50%">
+						<text style="flexShrink:0">客户类型:</text>
+						<text>{{ info.CompanyApprovalDetail.CompanyType }}</text>
+					</view>
+					<view class="customer-info-item flex" style="width:50%">
+						<text style="flexShrink:0">客户状态:</text>
+						<text>{{ info.CompanyApprovalDetail.Status }}</text>
+					</view>
+					<view class="customer-info-item flex" style="width:50%">
+						<text style="flexShrink:0">客户来源:</text>
+						<text>{{ info.CompanyApprovalDetail.Source }}</text>
+					</view>
+					<view class="customer-info-item flex" style="width:50%">
+						<text style="flexShrink:0">行业:</text>
+						<text>{{ info.CompanyApprovalDetail.IndustryName }}</text>
+					</view>
+					<view class="customer-info-item flex" style="width:50%">
+						<text style="flexShrink:0">申请销售:</text>
+						<text>{{ info.CompanyApprovalDetail.SellerName }}</text>
+					</view>
+				</view>
+			</view>
+			
+			<!-- 流程模块 -->
+			<view class="section process-wrap">
+				<view class="title" style="font-size: 16px;font-weight: bold;">审批流程</view>
+				<steps :data="info.FlowNodeList"></steps>
+			</view>
+		</view>
+		
+		
+		
+		<!-- 审批按钮 -->
+		<view class="fix-bottom-wrap btns-wrap flex" v-if="info &&info.CompanyApprovalDetail.OpButton&& info.CompanyApprovalDetail.ApproveStatus === '待审批'">
+			<button class="pass-btn" @click="handlePass">通过</button>
+			<button class="refuse-btn" @click="handleRefuse">驳回</button>
+		</view>
+
+		<van-dialog id="van-dialog" />
+	</view>
+</template>
+
+<script>
+	import {apiCustomeDetail,apiCustomeContractDetail,apiCustomeApprove} from '@/api/approve/custome.js'
+	import {preViewFile} from '../utils/util.js'
+	import steps from '../components/steps.vue'
+	export default {
+		components:{
+			steps
+		},
+		computed: {
+			//是否显示客户信息模块
+			showCustomerInfo() {
+				if (this.info && (this.info.CompanyApprovalDetail.ApplyMethod === 2 || this.info.CompanyApprovalDetail
+						.ApplyMethod === 4)) {
+					return false;
+				} else {
+					return true;
+				}
+			},
+		},
+		data() {
+			return {
+				id: null,
+				info: null,
+				baseInfo: null, //顶部基础信息
+				varietyInfo: null, //品种信息
+				constractFiles: [], //合同文件
+			};
+		},
+		onLoad(options) {
+			this.id = options.id
+		},
+		onShow() {
+			this.getDetail()
+		},
+		onPullDownRefresh() {
+			this.getDetail()
+			setTimeout(() => {
+				uni.stopPullDownRefresh()
+			}, 1000)
+		},
+		methods: {
+			// 同意
+			async handlePass() {
+				const res = await apiCustomeApprove({
+					CompanyApprovalId: Number(this.info.CompanyApprovalDetail.CompanyApprovalId),
+					CompanyContractId: Number(this.info.CompanyApprovalDetail.CompanyContractId),
+					CompanyId: Number(this.info.CompanyApprovalDetail.CompanyId),
+					Remark: "",
+					Status: 1,
+				})
+				if (res.code === 200) {
+					this.$dialog.alert({
+						title: "处理成功",
+						confirmButtonColor: "#5890FB",
+					}).then(() => {
+						// on close
+						this.getDetail()
+					});
+				}
+			},
+			
+			//拒绝
+			handleRefuse(){
+				const data=this.info.CompanyApprovalDetail
+				uni.navigateTo({
+					url:`./reason?CompanyApprovalId=${data.CompanyApprovalId}&CompanyContractId=${data.CompanyContractId}&CompanyId=${data.CompanyId}`
+				})
+			},
+			
+			// 获取审批单详情
+			async getDetail() {
+				const res = await apiCustomeDetail({
+					ApprovalId: this.id
+				})
+				if (res.code === 200) {
+					this.info = res.data
+					// 初始化状态
+					let statusImg=''
+					switch(res.data.CompanyApprovalDetail.ApproveStatus){
+						case '已审批':
+							statusImg=require('../static/pass-icon.png')
+							break;
+						case '驳回':
+							statusImg=require('../static/fail-icon.png')
+							break;
+					}
+					this.info.CompanyApprovalDetail.statusImg=statusImg
+					
+					let contractInfo = null
+					// 如果有合同id 则获取合同数据
+					if (res.data.CompanyApprovalDetail.CompanyContractId != 0) {
+						const contractRes = await apiCustomeContractDetail({
+							CompanyId: res.data.CompanyApprovalDetail.CompanyId,
+							CompanyContractId: res.data.CompanyApprovalDetail.CompanyContractId
+						})
+						if (contractRes.code === 200) {
+							contractInfo = contractRes.data
+						}
+					}
+
+					this.initTopInfo(res.data, contractInfo)
+					this.initVarietyInfo(res.data, contractInfo)
+					this.initContractFiles(res.data, contractInfo)
+				}
+			},
+
+			// 预览合同文件
+			preViewContract(e) {
+				if (e.type === "pdf") {
+					preViewFile(e.url)
+				} else {
+					uni.previewImage({
+						urls: [e.url]
+					})
+				}
+			},
+
+			// 初始化合同文件数据
+			initContractFiles(info, contractInfo) {
+				let arr = [];
+				if (info && contractInfo && (info.CompanyApprovalDetail.ApplyMethod === 1 || info
+						.CompanyApprovalDetail.ApplyMethod === 5)) {
+					const reg = /\.(pdf)$/;
+					arr = contractInfo.ImgUrl.split("#").map((item) => {
+						if (reg.test(item)) {
+							return {
+								type: "pdf",
+								url: item,
+								img: require("@/pages-approve/static/pdf.png"),
+							};
+						} else {
+							return {
+								type: "img",
+								url: item,
+								img: item,
+							};
+						}
+					});
+				}
+				this.constractFiles = arr
+			},
+
+			// 初始化顶部数据
+			initTopInfo(info, contractInfo) {
+				let arr = [];
+
+				//试用转正式
+				if (info && contractInfo && info.CompanyApprovalDetail.ApplyMethod === 1) {
+					uni.setNavigationBarTitle({
+						title:'试用转正式'
+					})
+					arr = [{
+							key: "合同编号",
+							val: contractInfo.ContractCode,
+							tag: contractInfo.ContractType,
+						},
+						{
+							key: "合同期限",
+							val: `${contractInfo.StartDate}~${contractInfo.EndDate}`.replace(/-/g, '.'),
+						},
+						{
+							key: "合同金额",
+							val: contractInfo.Money+'元',
+							color: "#FE6B7C",
+						},
+						{
+							key: "付款方式",
+							val: contractInfo.PayMethod,
+						},
+						{
+							key: "付款渠道",
+							val: contractInfo.PayChannel,
+						},
+						{
+							key: "申请时间",
+							val: info.CompanyApprovalDetail.ApprovalTime.replace(/-/g, '.'),
+						},
+						{
+							key: "申请理由",
+							val: info.CompanyApprovalDetail.ApplyReasons || "无",
+						},
+					];
+				}
+
+				//冻结转试用
+				if (info && info.CompanyApprovalDetail.ApplyMethod === 2) {
+					uni.setNavigationBarTitle({
+						title:'冻结转试用'
+					})
+					arr = [{
+							key: "客户状态",
+							val: info.CompanyApprovalDetail.Status,
+						},
+						{
+							key: "冻结期限",
+							val: `${info.CompanyApprovalDetail.FreezeStartDate}~${info.CompanyApprovalDetail.FreezeEndDate}`
+								.replace(/-/g, '.'),
+						},
+						{
+							key: "到期天数",
+							val: info.CompanyApprovalDetail.FreezeExpireDays,
+						},
+						{
+							key: "申请销售",
+							val: info.CompanyApprovalDetail.ApplyRealName,
+						},
+						{
+							key: "申请时间",
+							val: info.CompanyApprovalDetail.ApprovalTime.replace(/-/g, '.'),
+						},
+						{
+							key: "解冻理由",
+							val: info.CompanyApprovalDetail.ApplyReasons,
+						},
+					];
+				}
+
+				//试用延期
+				if (info && info.CompanyApprovalDetail.ApplyMethod === 3) {
+					uni.setNavigationBarTitle({
+						title:'试用延期'
+					})
+					arr = [{
+							key: "地址",
+							val: info.CompanyApprovalDetail.Address,
+						},
+						{
+							key: "申请时间",
+							val: info.CompanyApprovalDetail.ApprovalTime.replace(/-/g, '.'),
+						},
+						{
+							key: "申请理由",
+							val: info.CompanyApprovalDetail.ApplyReasons,
+						},
+					];
+				}
+
+				//原销售申请领取
+				if (info && info.CompanyApprovalDetail.ApplyMethod === 4) {
+					uni.setNavigationBarTitle({
+						title:'原销售申请领取'
+					})
+					arr = [{
+							key: "客户状态",
+							val: info.CompanyApprovalDetail.Status,
+						},
+						{
+							key: "原销售",
+							val: info.CompanyApprovalDetail.SellerName,
+						},
+						{
+							key: "申请销售",
+							val: info.CompanyApprovalDetail.ApplyRealName,
+						},
+						{
+							key: "申请时间",
+							val: info.CompanyApprovalDetail.ApprovalTime.replace(/-/g, '.'),
+						},
+						{
+							key: "申请理由",
+							val: info.CompanyApprovalDetail.ApplyReasons,
+						},
+					];
+				}
+
+				//服务更新
+				if (info && contractInfo && info.CompanyApprovalDetail.ApplyMethod === 5) {
+					uni.setNavigationBarTitle({
+						title:'服务更新'
+					})
+					arr = [{
+							key: "合同编号",
+							val: contractInfo.ContractCode,
+							tag: contractInfo.ContractType,
+						},
+						{
+							key: "合同期限",
+							val: `${contractInfo.StartDate}~${contractInfo.EndDate}`.replace(/-/g, '.'),
+						},
+						{
+							key: "合同金额",
+							val: contractInfo.Money+'元',
+							color: "#FE6B7C",
+						},
+						{
+							key: "付款方式",
+							val: contractInfo.PayMethod,
+						},
+						{
+							key: "付款渠道",
+							val: contractInfo.PayChannel,
+						},
+						{
+							key: "申请时间",
+							val: info.CompanyApprovalDetail.ApprovalTime.replace(/-/g, '.'),
+						},
+						{
+							key: "申请理由",
+							val: info.CompanyApprovalDetail.ApplyReasons || "无",
+						},
+					];
+				}
+
+				this.baseInfo = arr
+			},
+
+			// 初始化品种信息
+			initVarietyInfo(info, contractInfo) {
+				let usertype = this.$store.state.userInfo.ProductName; //用户身份类型admin,ficc,权益
+				let arr = [];
+				let obj = {
+					title: "",
+					list: [],
+				};
+
+				//试用转正式
+				if (info && contractInfo && info.CompanyApprovalDetail.ApplyMethod === 1) {
+					obj.title = "购买品种";
+					arr = contractInfo.PermissionList;
+					if (!arr) return obj;
+					arr.forEach((item) => {
+						let temarr = item.Items.filter((e) => {
+							let flag = item.CheckList.includes(e.ChartPermissionId);
+							if (flag) return e;
+						});
+						if (temarr.length !== 0) {
+							obj.list.push({
+								ClassifyName: item.ClassifyName,
+								Items: temarr,
+							});
+						}
+					});
+				}
+
+				//冻结转试用
+				if (info && info.CompanyApprovalDetail.ApplyMethod === 2) {
+					obj.title = "申请品种";
+					if (usertype === "ficc") {
+						arr = info.FiccPermissionList;
+					} else if (usertype === "权益") {
+						arr = info.PermissionList;
+					} else if (usertype === "admin") {
+						if (info.FiccPermissionList) {
+							arr = [...info.FiccPermissionList];
+						}
+						if (info.PermissionList) {
+							arr = [...info.PermissionList];
+						}
+					}
+
+					if (!arr) return obj;
+					arr.forEach((item) => {
+						let temarr = item.Items.filter((e) => {
+							let flag = item.CheckList.includes(e.ChartPermissionId);
+							if (flag) return e;
+						});
+						if (temarr.length !== 0) {
+							obj.list.push({
+								ClassifyName: item.ClassifyName,
+								Items: temarr,
+							});
+						}
+					});
+				}
+
+				//试用延期
+				if (info && info.CompanyApprovalDetail.ApplyMethod === 3) {
+					obj.title = "申请品种";
+					if (usertype === "ficc") {
+						arr = info.FiccPermissionList;
+					} else if (usertype === "权益") {
+						arr = info.PermissionList;
+					} else if (usertype === "admin") {
+						if (info.FiccPermissionList) {
+							arr = [...info.FiccPermissionList];
+						}
+						if (info.PermissionList) {
+							arr = [...info.PermissionList];
+						}
+					}
+					if (!arr) return obj;
+					arr.forEach((item) => {
+						let temarr = item.Items.filter((e) => {
+							let flag = item.CheckList.includes(e.ChartPermissionId);
+							if (flag) return e;
+						});
+						if (temarr.length !== 0) {
+							obj.list.push({
+								ClassifyName: item.ClassifyName,
+								Items: temarr,
+							});
+						}
+					});
+				}
+
+				//原销售申请领取
+				if (info && info.CompanyApprovalDetail.ApplyMethod === 4) {
+					obj.title = "申请品种";
+					if (usertype === "ficc") {
+						arr = info.FiccPermissionList;
+					} else if (usertype === "权益") {
+						arr = info.PermissionList;
+					} else if (usertype === "admin") {
+						if (info.FiccPermissionList) {
+							arr = [...info.FiccPermissionList];
+						}
+						if (info.PermissionList) {
+							arr = [...info.PermissionList];
+						}
+					}
+					if (!arr) return obj;
+					arr.forEach((item) => {
+						let temarr = item.Items.filter((e) => {
+							let flag = item.CheckList.includes(e.ChartPermissionId);
+							if (flag) return e;
+						});
+						if (temarr.length !== 0) {
+							obj.list.push({
+								ClassifyName: item.ClassifyName,
+								Items: temarr,
+							});
+						}
+					});
+				}
+
+				//服务更新
+				if (info && contractInfo && info.CompanyApprovalDetail.ApplyMethod === 5) {
+					obj.title = "购买品种";
+					arr = contractInfo.PermissionList;
+					if (!arr) return obj;
+					arr.forEach((item) => {
+						let temarr = item.Items.filter((e) => {
+							let flag = item.CheckList.includes(e.ChartPermissionId);
+							if (flag) return e;
+						});
+						if (temarr.length !== 0) {
+							obj.list.push({
+								ClassifyName: item.ClassifyName,
+								Items: temarr,
+							});
+						}
+					});
+				}
+
+				this.varietyInfo = obj
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.detail-page {
+		width: 100%;
+		min-height: 100vh;
+		padding-bottom: calc(150rpx + constant(safe-area-inset-bottom));
+		padding-bottom: calc(150rpx + env(safe-area-inset-bottom));
+	}
+
+	.section {
+		padding: 30rpx 34rpx;
+		border-bottom: 14rpx solid #f5f5f5;
+
+		&:last-child {
+			margin-bottom: 0;
+			border: none;
+		}
+
+		.title {
+			font-size: 16px;
+			font-weight: bold;
+			margin: 0 0 40rpx 0;
+		}
+
+		.lable {
+			color: #666;
+			flex-shrink: 0;
+			font-size: 14px;
+		}
+	}
+
+	.base-info-wrap {
+		position: relative;
+		.title {
+			margin: 0 0 40rpx 0;
+
+			&::before {
+				content: "";
+				display: inline-block;
+				width: 15px;
+				height: 17px;
+				background-image: url("@/static/man.png");
+				background-size: cover;
+				background-repeat: no-repeat;
+				margin-right: 10rpx;
+				position: relative;
+				top: 4rpx;
+			}
+		}
+
+		.base-info-list {
+			font-size: 14px;
+			color: #000;
+
+			view {
+				margin-bottom: 10px;
+				display: flex;
+			}
+		}
+		
+		.status-img{
+			position: absolute;
+			width: 220rpx;
+			height: 220rpx;
+			right: 0;
+			top: 100rpx;
+		}
+	}
+
+	.variety-wrap {
+		.variety-item {
+			padding-top: 24rpx;
+			padding-bottom: 24rpx;
+			border-bottom: 1px dashed rgba(112, 112, 112, 0.2);
+		}
+
+		.variety-item:first-child {
+			padding-top: 0;
+		}
+
+		.variety-item:last-child {
+			border-bottom: none;
+		}
+
+		.tag {
+			margin-right: 10rpx;
+			margin-bottom: 10rpx;
+		}
+	}
+
+	.contract-list {
+		display: flex;
+		flex-wrap: wrap;
+
+		.contract-img {
+			width: 180rpx;
+			height: 180rpx;
+		}
+	}
+
+	.customer-main-info {
+		padding-bottom: 40rpx;
+		border-bottom: 1px dashed rgba(112, 112, 112, 0.2);
+	}
+
+	.customer-info-item {
+		font-size: 14px;
+		color: #999;
+		margin-bottom: 20rpx;
+	}
+
+	.customer-info-list {
+		flex-wrap: wrap;
+		padding-top: 20rpx;
+	}
+	
+	.process-wrap{
+		// margin-left: auto;
+		// margin-right: auto;
+		// width: calc(100% - 68rpx);
+		// padding: 30rpx;
+		// border: 1px solid #E1E1E1;
+		// border-radius: 8rpx;
+	}
+
+	.btns-wrap {
+		justify-content: center;
+
+		button {
+			width: 220rpx;
+			height: 70rpx;
+			border-radius: 28px;
+			border: none;
+			margin: 0 10px;
+			font-size: 14px;
+			color: #fff;
+			line-height: 70rpx;
+		}
+
+		.pass-btn {
+			background-color: #5890fb;
+		}
+
+		.refuse-btn {
+			background-color: #f55768;
+		}
+	}
+</style>

+ 142 - 0
pages-approve/custome/list.vue

@@ -0,0 +1,142 @@
+<template>
+	<view>
+		<van-sticky>
+			<view class="top-wrap">
+				<view @click="handleGoSearch">
+					<van-search disabled use-left-icon-slot shape="round" placeholder="客户名称/社会信用码">
+						<view slot="left-icon">
+							<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+						</view>
+					</van-search>
+				</view>
+				<van-tabs swipeable id="tabs" :active="status" title-active-color="#3385FF" color="#3385FF" @change="typeChange">
+					<van-tab title="待审批" name="待审批"></van-tab>
+					<van-tab title="已审批" name="已审批"></van-tab>
+				</van-tabs>
+			</view>
+		</van-sticky>
+		<van-empty description="暂无数据" :image="require('@/static/empty.png')" v-if="finished&&list.length===0"/>
+		<view class="list-wrap" v-else>
+			<approve-list-item v-for="item in list" :key="item.CompanyApprovalId" :data="item"></approve-list-item>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {apiCustomeList} from '@/api/approve/custome.js'
+	import approveListItem from '../components/approveListItem.vue'
+	export default {
+		components: {
+			approveListItem
+		},
+		data() {
+			return {
+				status: '待审批',
+				list:[],
+				page:1,
+				finished:false,
+			}
+		},
+		onShow() {
+			this.getList()
+			this.selectComponent('#tabs').resize();// 解决初始渲染 vant tab 底部条
+		},
+		onPullDownRefresh() {
+			this.page=1
+			this.finished=false
+			this.list=[]
+			this.getList()
+			setTimeout(()=>{
+				uni.stopPullDownRefresh()
+			},1500)
+		},
+		onReachBottom() {
+			if(this.finished) return
+			this.page++
+			this.getList()
+		},
+		methods: {
+			// 去搜索
+			handleGoSearch() {
+				uni.navigateTo({
+					url: '../search/index?type=custome'
+				})
+			},
+			
+			
+			typeChange(e){
+				this.status=e.detail.name
+				this.page=1
+				this.finished=false
+				this.list=[]
+				this.getList()
+			},
+				
+			
+			//获取列表数据
+			async getList(){
+				const res=await apiCustomeList({
+					CurrentIndex:this.page,
+					Status:this.status,
+					KeyWord:''
+				})
+				if(res.code===200){
+					if(!res.data.List||res.data.List.length===0){
+						this.finished=true
+					}else{
+						let arr=res.data.List.map(item=>{
+							//申请类型:申请类型:1:试用->正式,2:冻结—>试用,3:试用延期,4:原销售申请领取流失客户,5:正式客户申请服务更新
+							let applyType=''
+							switch(item.ApplyMethod){
+								case 1:
+									applyType='试用转正式'
+									break;
+								case 2:
+									applyType='冻结转试用'
+									break;
+								case 3:
+									applyType='试用延期'
+									break;
+								case 4:
+									applyType='原销售申领'
+									break;
+								case 5:
+									applyType='服务更新'
+									break;	
+							}
+							return {
+								title:item.CompanyName,
+								saller:item.ApplyRealName,
+								submitTime:item.ApprovalTime,
+								approveTime:item.ApproveTime,
+								backTime:'',
+								cancelTime:'',
+								status:item.ApproveStatus,
+								applyType:applyType,
+								id:item.CompanyApprovalId,
+								type:'custome'
+							}
+						})
+						this.list=[...this.list,...arr]
+					}
+				}
+			},
+			
+		},
+	}
+</script>
+
+<style lang="scss">
+	.search-icon {
+		width: 40rpx;
+		height: 40rpx;
+		display: block;
+		position: relative;
+		top: 4rpx;
+		margin-right: 10rpx;
+	}
+
+	.list-wrap {
+		padding: 20rpx;
+	}
+</style>

+ 112 - 0
pages-approve/custome/reason.vue

@@ -0,0 +1,112 @@
+<template>
+	<view>
+		<view class="textarea-wrap white-wrap">
+			<textarea placeholder="请输入驳回理由" v-model="reason" maxlength="-1"></textarea>
+		</view>
+		<view class="btns-wrap flex">
+			<button class="confirm-btn" @click="handleSubmit">提交</button>
+			<button class="cancel-btn" @click="handleCancel">取消</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {apiCustomeApprove} from '@/api/approve/custome.js'
+	export default {
+		data() {
+			return {
+				reason: '',
+				CompanyApprovalId:'',
+				CompanyContractId:'',
+				CompanyId:"",
+			}
+		},
+		onLoad(options) {
+			this.CompanyApprovalId=options.CompanyApprovalId 
+			this.CompanyContractId=options.CompanyContractId 
+			this.CompanyId=options.CompanyId
+		},
+		methods: {
+			async handleSubmit() {
+				if(!this.reason){
+					uni.showToast({
+						title:"请填写理由",
+						icon:"none"
+					})
+					return 
+				}
+				let params={
+					CompanyApprovalId:Number(this.CompanyApprovalId),
+					CompanyContractId:Number(this.CompanyContractId),
+					CompanyId:Number(this.CompanyId),
+					Remark:this.reason,
+					Status:2
+				}
+				const res=await apiCustomeApprove(params)
+				if(res.code===200){
+					uni.showToast({
+						title:"审批成功",
+						icon:"success"
+					})
+					setTimeout(()=>{
+						uni.navigateBack({
+							delta:1
+						})
+					},1000)
+				}
+				
+			},
+			
+			handleCancel(){
+				uni.navigateBack({
+					delta:1
+				})
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.textarea-wrap {
+		padding: 17px;
+
+		textarea {
+			resize: none;
+			display: block;
+			box-sizing: border-box;
+			width: 100%;
+			height: 200px;
+			border: none;
+			font-size: 14px;
+			/* no */
+		}
+	}
+
+	.btns-wrap {
+		justify-content: center;
+		padding-top: 50px;
+		padding-bottom: 50px;
+
+		button {
+			width: 90px;
+			height: 28px;
+			border-radius: 28px;
+			border: none;
+			margin: 0 10px;
+			font-size: 14px;
+			/* no */
+			color: #fff;
+			line-height: 28px;
+		}
+
+		.confirm-btn {
+			background-color: #5890fb;
+		}
+
+		.cancel-btn {
+			border: 1px solid #5890fb;
+			color: #5890fb;
+			background-color: #fff;
+		}
+	}
+</style>

+ 568 - 0
pages-approve/seal/addSeal.vue

@@ -0,0 +1,568 @@
+<template>
+	<view class="add-page white-wrap">
+		<view class="section white-wrap">
+			<view class="section-title require">用印用途</view>
+			<view class="section-select-box" :style="{color:purpose?'#333':'#999'}" @click="showPurpose=true">{{purpose?purpose:'请选择'}}</view>
+		</view>
+		
+		<view class="section white-wrap">
+			<van-radio-group :value="radioVal" @change="radioChange" direction="horizontal">
+			  <van-radio name="系统合同" style="margin-right: 70px;">系统合同</van-radio>
+			  <van-radio name="上传附件">上传附件</van-radio>
+			</van-radio-group>
+		</view>
+		
+		<!-- 系统合同模块 -->
+		<view v-if="radioVal==='系统合同'">
+			<view class="section white-wrap">
+				<view class="section-title require">客户名称(全称)</view>
+				<view 
+					class="section-select-box" 
+					:style="{color:customeName?'#333':'#999'}" 
+					@click="showCustome=true"
+				>
+					{{customeName?customeName:'请输入客户名称'}}
+				</view>
+			</view>
+			<view class="section white-wrap" v-if="CreditCode">
+				<view class="section-title require">统一社会信用码</view>
+				<input type="text" v-model="CreditCode" placeholder="请填写统一社会信用码" disabled/>
+			</view>
+			<view class="section white-wrap" v-if="UseCompanyName">
+				<view class="section-title">实际使用方名称</view>
+				<input type="text" v-model="UseCompanyName" placeholder="请填写实际使用方名称" disabled/>
+			</view>
+			<view class="section white-wrap" v-if="ServiceType">
+				<view class="section-title require">业务类型</view>
+				<view :style="{color:ServiceType?'#333':'#999'}">{{ServiceType?ServiceType:'请选择业务类型'}}</view>
+			</view>
+		</view>
+		<!-- 上传附件模块 -->
+		<view v-if="radioVal==='上传附件'">
+			<view class="section white-wrap">
+				<view class="section-title require">客户名称(全称)</view>
+				<input type="text" v-model="customeName" placeholder="请输入客户名称"/>
+			</view>
+			<view class="section white-wrap">
+				<view class="section-title require">统一社会信用码</view>
+				<input type="text" v-model="CreditCode" placeholder="请填写统一社会信用码"/>
+			</view>
+			<view class="section white-wrap">
+				<view class="section-title">实际使用方名称</view>
+				<input type="text" v-model="UseCompanyName" placeholder="请填写实际使用方名称"/>
+			</view>
+			<view class="section white-wrap">
+				<view class="section-title require">业务类型</view>
+				<view class="section-select-box" :style="{color:ServiceType?'#333':'#999'}" @click="showServiceType=true">{{ServiceType?ServiceType:'请选择业务类型'}}</view>
+			</view>
+		</view>
+		
+		<!-- <view class="section white-wrap">
+			<view class="section-title require">客户名称(全称)</view>
+			<view class="section-select-box" :style="{color:customeName?'#333':'#999'}" @click="showCustome=true" v-if="radioVal==='系统合同'">{{customeName?customeName:'请输入客户名称'}}</view>
+			<input type="text" v-model="customeName" placeholder="请输入客户名称" v-else/>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title require">统一社会信用码</view>
+			<input type="text" v-model="CreditCode" placeholder="请填写统一社会信用码" :disabled="radioVal==='系统合同'"/>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title">实际使用方名称</view>
+			<input type="text" v-model="UseCompanyName" placeholder="请填写实际使用方名称" :disabled="radioVal==='系统合同'"/>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title require">业务类型</view>
+			<view class="section-select-box" :style="{color:ServiceType?'#333':'#999'}" @click="handleShowServiceType">{{ServiceType?ServiceType:'请选择业务类型'}}</view>
+		</view> -->
+		<view class="section white-wrap">
+			<view class="section-title require">文件份数</view>
+			<input type="number" v-model="fileNum" placeholder="请填写总共盖章文件份数"/>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title require">加盖何种印章</view>
+			<view class="section-select-box" :style="{color:type?'#333':'#999'}" @click="showType=true">{{type?type:'请选择'}}</view>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title">备注</view>
+			<textarea type="text" v-model="remark" placeholder="请填写备注"></textarea>
+		</view>
+		<view class="section white-wrap" v-if="radioVal==='系统合同'&&ContractfileUrl">
+			<view class="section-title require">合同附件</view>
+			<image src="../static/pdf.png" mode="aspectFill" style="width: 102rpx;height: 120rpx;"></image>
+		</view>
+		<view class="section white-wrap" v-if="radioVal==='上传附件'">
+			<view class="section-title require">附件上传</view>
+			<image :src="img" mode="aspectFill" style="width: 102rpx;height: 120rpx;margin:0 10rpx 10rpx 0" v-for="img in fileUrlArr" :key="img"></image>
+			<image @click="handleUpload" src="../static/upload-icon.png" mode="aspectFill" style="width: 102rpx;height: 120rpx;margin:0 10rpx 10rpx 0"></image>
+		</view>
+		
+		<!-- 流程模块 -->
+		<view class="section white-wrap process-wrap" v-if="processData">
+			<view class="title" style="font-size: 16px;font-weight: bold;">审批流程</view>
+			<steps :data="processData"></steps>
+		</view>
+		
+		<view class="fix-bottom-wrap" style="text-align: center;">
+			<van-button type="info" custom-class="btn" round @click="handleSubmit">提交</van-button>
+		</view>
+		
+		
+		<!-- 用印用途 -->
+		<van-popup :show="showPurpose" @close="showPurpose=false" position="bottom">
+			<van-picker 
+				show-toolbar 
+				title="选择用印用途" 
+				:columns="purposeArr" 
+				@confirm="handlePurposeConfirm"  
+				@cancel="showPurpose=false"
+			/>
+		</van-popup>
+		<!-- 何种印章 -->
+		<van-popup :show="showType" @close="showType=false" position="bottom">
+			<van-picker 
+				show-toolbar 
+				title="选择何种印章" 
+				:columns="typeArr" 
+				@confirm="handleTypeConfirm"  
+				@cancel="showType=false"
+			/>
+		</van-popup>
+		
+		<!-- 业务类型 -->
+		<van-popup :show="showServiceType" @close="showServiceType=false" position="bottom">
+			<van-picker 
+				show-toolbar 
+				title="选择业务类型" 
+				:columns="ServiceTypeArr" 
+				@confirm="handleServiceTypeConfirm"  
+				@cancel="showServiceType=false"
+			/>
+		</van-popup>
+		
+		<!-- 客户搜索 -->
+		<van-popup :show="showCustome" @close="showCustome=false" position="bottom" custom-style="height: 100vh">
+			<view class="custome-search-wrap">
+				<van-search use-left-icon-slot use-action-slot shape="round" :value="searchCustomeVal" placeholder="请输入客户名称" @change="onSearchValChange" @search="onSearch"
+					custom-class="search-box" field-class="search-con">
+					<view slot="left-icon">
+						<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+					</view>
+					<view slot="action" @click="showCustome=false" class="search-btn">取消</view>
+				</van-search>
+				<view class="search-result">
+					<view class="result-custome-box" v-if="searchContractList.length===0">
+						<van-empty description="暂无数据" :image="require('@/static/empty.png')" v-if="!searchCustomeStatus"/>
+						<view v-else>
+						<view class="result-item flex" v-for="item in searchCustomeList" :key="item" @click="getContract(item)">
+							<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+							<view class="con van-ellipsis">{{item}}</view>
+							<image src="../static/click-icon.png" mode="aspectFill" class="click-icon"></image>
+						</view>
+						</view>
+					</view>
+					<view class="result-contract-box" v-else>
+						<view class="result-contract-item" v-for="item in searchContractList" :key="item.ContractId" @click="handleChooseContract(item)">
+							<view class="name">{{item.CompanyName}}</view>
+							<view style="margin-top: 20rpx;">合同编号:{{item.ContractCode}}</view>
+							<view style="margin-top: 20rpx;">合同类型:{{item.ContractType}}</view>
+							<view style="margin-top: 20rpx;">合同金额:{{item.Price}}</view>
+						</view>
+					</view>
+					
+				</view>
+			</view>
+		</van-popup>
+	</view>
+</template>
+
+<script>
+	import steps from '../components/steps.vue'
+	import {apiFlowDetail,apiSealAdd,apiSearchCustome,apiSearchContract} from '@/api/approve/seal.js'
+	import {uploadImg} from '@/utils/uploadFile.js'
+	export default{ 
+		components:{
+			steps
+		},
+		data() {
+			return {
+				showPurpose:false,//显示用印用途选项
+				purposeArr:['销售合同','渠道合同','付款通知函','招投标','战略合作协议'],
+				purpose:"",
+				
+				showType:false,//显示用印用途选项
+				typeArr:['公章','合同章','法人章'],
+				type:"",
+				
+				showServiceType:false,//显示业务类型选项
+				ServiceTypeArr:['新签合同','续约合同','补充协议'],
+				ServiceType:'',//业务类型
+				
+				
+				showCustome:false,//显示搜索客户名称
+				customeName: '',//客户名称
+				fileNum:'',//文件数
+				remark:'',//备注
+				CreditCode:'',//社会统一信用代码
+				
+				UseCompanyName:'',//实际使用方客户名称
+				ContractId:0,//合同id
+				ContractfileUrl:'',//合同文件地址 pdf
+				
+				fileUrlArr:[],//上传附件 文件地址
+				
+				processData:null,//流程数据
+				
+				radioVal:'系统合同',
+				
+				searchCustomeVal:'',//搜索客户输入数据
+				searchCustomeStatus:true,//搜索客户 是否有结果
+				searchCustomeList:[],//搜索到的客户名称列表
+				searchContractList:[],//选择搜索中的客户后合同列表数据
+			}
+		},
+		methods: {
+			//上传附件
+			async handleUpload(){
+				const res=await uploadImg()
+				this.fileUrlArr=res
+			},
+			
+			// 客户搜索 
+			// 先搜索出客户 再通过客户去请求出客户下面存在的合同
+			onSearchValChange(e){
+				this.searchCustomeVal=e.detail
+			},
+			
+			//搜索客户
+			async onSearch(){
+				this.searchContractList=[]
+				this.searchCustomeList=[]
+				const res=await apiSearchCustome({CompanyName:this.searchCustomeVal})
+				if(res.code===200){
+					this.searchCustomeList=res.data
+					if(res.data.length===0){
+						this.searchCustomeStatus=false
+					}else{
+						this.searchCustomeStatus=true
+					}
+				}
+			},
+			
+			// 搜索客户对应的合同
+			async getContract(e){
+				const res=await apiSearchContract({Keyword:e})
+				if(res.code===200){
+					if(res.data.List){
+						this.searchContractList=res.data.List 
+					}else{
+						uni.showToast({
+							title:"此客户无合同,请重新选择",
+							icon:"none"
+						})
+					}
+				}
+			},
+			
+			// 选择合同 更新表单数据
+			handleChooseContract(e){
+				
+				this.customeName=e.CompanyName
+				this.CreditCode=e.CreditCode
+				this.ServiceType=e.ContractType
+				this.UseCompanyName=e.CompanyName
+				this.ContractId=e.ContractId
+				this.ContractfileUrl=e.FileUrl
+				// 关闭搜索弹窗
+				this.showCustome=false
+				this.searchCustomeVal=''
+				this.searchContractList=[]
+				this.searchCustomeList=[]
+			},
+			
+			// 系统合同 上传附件类型切换
+			radioChange(e){
+				this.radioVal=e.detail
+				this.customeName=''
+				this.CreditCode=''
+				this.ServiceType=''
+				this.UseCompanyName=''
+				this.ContractfileUrl=''
+				this.fileUrlArr=[]
+			},
+			
+			// 选择用印用途
+			handlePurposeConfirm(e) {
+				this.purpose=e.detail.value 
+				this.showPurpose=false
+			},
+			
+			//选择盖章类型
+			handleTypeConfirm(e){
+				this.type=e.detail.value
+				this.showType=false
+				this.getProcessData()
+			},
+			
+			//选择业务类型
+			handleServiceTypeConfirm(e){
+				this.ServiceType=e.detail.value
+				this.showServiceType=false
+			},
+			
+			//合同章5  公章、法人章 6
+			async getProcessData(){
+				let id=0
+				if(this.type==='合同章'){
+					id=5
+				}else{
+					id=6
+				}
+				
+				let res=await apiFlowDetail({FlowId:id})
+				if(res.code===200){
+					this.processData=res.data||[]
+				}
+			},
+			
+			// 提交申请
+			async handleSubmit(){
+				let fileUrl=''
+				if(this.radioVal==='系统合同'){
+					fileUrl=this.ContractfileUrl
+				}else{
+					fileUrl=this.fileUrlArr.join('#')
+				}
+				
+				let params={
+					CompanyName:this.customeName,
+					ContractId:this.ContractId,
+					CreditCode:this.CreditCode,
+					FileUrl:fileUrl,
+					FileNum:Number(this.fileNum),
+					Remark:this.remark,
+					SealType:this.type,
+					ServiceType:this.ServiceType,
+					Use:this.purpose,
+					UseCompanyName:this.UseCompanyName,
+				}
+				
+				if(!params.Use){
+					uni.showToast({
+						title:'请选择用印用途',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.CompanyName){
+					uni.showToast({
+						title:'客户名称不能为空',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.CreditCode){
+					uni.showToast({
+						title:'信用代码不能为空',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.ServiceType){
+					uni.showToast({
+						title:'业务类型不能为空',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.FileNum){
+					uni.showToast({
+						title:'请填写文件份数',
+						icon:"none"
+					})
+					return
+				}
+				if(params.FileNum<1){
+					uni.showToast({
+						title:'文件份数不合法',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.SealType){
+					uni.showToast({
+						title:'印章类型不能为空',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.FileUrl){
+					uni.showToast({
+						title:'请上传文件',
+						icon:"none"
+					})
+					return
+				}
+				console.log(params);
+				
+				const res=await apiSealAdd(params)
+				if(res.code===200){
+					uni.showToast({
+						title:"用印申请单已提交",
+						icon:"none"
+					})
+					setTimeout(()=>{
+						uni.navigateBack({
+							delta:1
+						})
+					},1000)
+				}
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.add-page{
+		width: 100%;
+		min-height: 100vh;
+		padding-bottom: calc(150rpx + constant(safe-area-inset-bottom));
+		padding-bottom: calc(150rpx + env(safe-area-inset-bottom));
+	}
+	.section{
+		padding: 30rpx 34rpx;
+		border-top: 10rpx solid #F5F5F5;
+		position: relative;
+		textarea{
+			width: 100%;
+			height: 200rpx;
+		}
+		.section-title{
+			font-size: 16px;
+			margin-bottom: 20rpx;
+		}
+		.require::before{
+			content: '*';
+			font-size: 16px;
+			color: #FF0000;
+			position: absolute;
+			left: 20rpx;
+		}
+		.section-select-box{
+			color: #999;
+			position: relative;
+			&::after{
+				position: absolute;
+				right: 0;
+				top: 50%;
+				content: '';
+				display: block;
+				width: 18rpx;
+				height: 18rpx;
+				border-top: 1px solid #999;
+				border-right: 1px solid #999;
+				transform: translateY(-50%) rotate(45deg);
+			}
+		}
+	}
+	
+	.btn{
+		width: 360rpx;
+		height: 60rpx;
+	}
+	
+	.radio-wrap{
+		.radio{
+			font-size: 14px;
+			&::before{
+				content: '';
+				display: inline-block;
+			}
+		}
+	}
+	
+	.custome-search-wrap{
+		padding: 34rpx;
+		height: 100%;
+		.search-box {
+			border: 1px solid #3385FF;
+			padding: 0 !important;
+			border-radius: 60rpx;
+			background-color: #fff !important;
+		}
+		
+		.search-con {
+			background-color: #fff !important;
+		}
+		
+		.van-search__content {
+			background-color: #fff !important;
+			padding-left: 30rpx !important;
+		}
+		
+		.search-btn {
+			position: relative;
+			color: #3385FF;
+			&::before {
+				content: '';
+				display: block;
+				width: 1px;
+				height: 60%;
+				background-color: #D1D1D1;
+				position: absolute;
+				left: -16rpx;
+				top: 20%;
+			}
+		}
+		.search-icon{
+			width: 40rpx;
+			height: 40rpx;
+			display: block;
+			position: relative;
+			top: 4rpx;
+			margin-right: 20rpx;
+		}
+
+		.click-icon{
+			width: 24rpx;
+			height: 24rpx;
+		}
+		.result-item{
+			align-items: center;
+			padding: 20rpx 0;
+			border-bottom: 1px solid #EBEBEB;
+			.con{
+				flex: 1;
+				margin-right: 20rpx;
+			}
+		}
+		.search-result{
+			overflow-y: auto;
+			height: 100%;
+		}
+	
+		.result-contract-box{
+			padding: 0 10rpx;
+			.result-contract-item{
+				margin-top: 30rpx;
+				box-shadow: 0px 0px 12rpx rgba(175, 175, 175, 0.38);
+				padding: 30rpx;
+				border-radius: 8px;
+				.name{
+					font-size: 16px;
+					font-weight: bold;
+					&::before{
+						content:'';
+						display:inline-block;
+						width: 31rpx;
+						height: 34rpx;
+						background-image: url(../../static/man.png);
+						background-size: cover;
+						position: relative;
+						top: 4rpx;
+						margin-right: 10rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 548 - 0
pages-approve/seal/detail.vue

@@ -0,0 +1,548 @@
+<template>
+	<view class="detail white-wrap">
+		<image :src="statusImg" mode="aspectFill" class="status-img" v-if="statusImg"></image>
+		<view class="section white-wrap">
+			<view class="section-title require">用印用途</view>
+			<view :class="opButton.CheckEdit?'section-select-box':null" :style="{color:oldUse?'#333':'#999'}" @click="handleOperation('showPurpose')">{{oldUse?oldUse:'请选择'}}</view>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title require">客户名称(全称)</view>
+			<view>{{detail.CompanyName}}</view>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title require">统一社会信用码</view>
+			<input type="text" v-model="detail.CreditCode" placeholder="请填写统一社会信用码" disabled/>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title">实际使用方名称</view>
+			<input type="text" v-model="detail.UseCompanyName" placeholder="请填写实际使用方名称" disabled/>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title require">业务类型</view>
+			<input type="text" v-model="detail.ServiceType" placeholder="请填写业务类型" disabled/>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title require">文件份数</view>
+			<input type="number" v-model="oldFileNum" placeholder="请填写总共盖章文件份数" :disabled="!opButton.CheckEdit"/>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title require">加盖何种印章</view>
+			<view :class="opButton.CheckEdit?'section-select-box':null" :style="{color:oldSealType?'#333':'#999'}" @click="handleOperation('showType')">{{oldSealType?oldSealType:'请选择'}}</view>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title require">合同附件</view>
+			<image 
+				:src="item.img" 
+				mode="aspectFill" 
+				style="width: 102rpx;height: 120rpx;margin:0 10rpx 10rpx 0" 
+				v-for="item in files" 
+				:key="item.url"
+				@click="handlepreViewFile(item)">
+			</image>
+		</view>
+		<view class="section white-wrap" v-if="detail.Status==='已驳回'">
+			<view class="section-title">驳回理由</view>
+			<view style="color: #F55768;">{{detail.ApprovalRemark}}</view>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title">备注</view>
+			<textarea type="text" v-model="oldRemark" placeholder="请填写备注" :disabled="!opButton.CheckEdit"></textarea>
+		</view>
+		<!-- 流程模块 -->
+		<view class="section white-wrap process-wrap">
+			<view class="title" style="font-size: 16px;font-weight: bold;">审批流程</view>
+			<steps :data="processData"></steps>
+		</view>
+		
+		<!-- 审批 -->
+		<view class="fix-bottom-wrap" style="text-align: center;" v-if="opButton.Approval">
+			<van-button type="info" color="#3385FF" custom-class="btn-small" round style="margin-right: 60rpx;" @click="handleClickPass">通过</van-button>
+			<van-button type="info" color="#F55768" custom-class="btn-small" round @click="handleApprovalReject">驳回</van-button>
+		</view>
+		<!-- 作废 -->
+		<view class="fix-bottom-wrap" style="text-align: center;" v-if="opButton.Invalid">
+			<view v-if="detail.ContractId>0" style="margin-bottom: 10rpx;" @click="checked=!checked"><radio value="同时作废合同" :checked="checked" color="#3385FF" style="transform:scale(0.8)"/>是否同时作废合同?</view>
+			<van-button type="info" color="#F55768" custom-class="btn-big" round @click="handleSealInvalid">用印作废</van-button>
+		</view>
+		<!-- 重新提交 -->
+		<view class="fix-bottom-wrap" style="text-align: center;" v-if="opButton.Edit">
+			<van-button type="info" color="#3385FF" custom-class="btn-big" round @click="handleEdit">重新申请</van-button>
+		</view>
+		
+		<!-- 用印用途 -->
+		<van-popup :show="showPurpose" @close="showPurpose=false" position="bottom">
+			<van-picker 
+				show-toolbar 
+				title="选择用印用途" 
+				:columns="purposeArr" 
+				@confirm="handlePurposeConfirm"  
+				@cancel="showPurpose=false"
+			/>
+		</van-popup>
+		<!-- 何种印章 -->
+		<van-popup :show="showType" @close="showType=false" position="bottom">
+			<van-picker 
+				show-toolbar 
+				title="选择何种印章" 
+				:columns="typeArr" 
+				@confirm="handleTypeConfirm"  
+				@cancel="showType=false"
+			/>
+		</van-popup>
+		
+		<!-- 客户搜索 -->
+		<van-popup :show="showCustome" @close="showCustome=false" position="bottom" custom-style="height: 100vh">
+			<view class="custome-search-wrap">
+				<van-search use-left-icon-slot use-action-slot shape="round" :value="searchCustomeVal" placeholder="请输入客户名称" @change="onSearchValChange" @search="onSearch"
+					custom-class="search-box" field-class="search-con">
+					<view slot="left-icon">
+						<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+					</view>
+					<view slot="action" @click="showCustome=false" class="search-btn">取消</view>
+				</van-search>
+				<view class="search-result">
+					<view class="result-custome-box" v-if="searchContractList.length===0">
+						<view class="result-item flex" v-for="item in searchCustomeList" :key="item" @click="getContract(item)">
+							<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+							<view class="con van-ellipsis">{{item}}</view>
+							<image src="../static/click-icon.png" mode="aspectFill" class="click-icon"></image>
+						</view>
+					</view>
+					<view class="result-contract-box" v-else>
+						<view class="result-contract-item" v-for="item in searchContractList" :key="item.ContractId" @click="handleChooseContract(item)">
+							<view class="name">{{item.CompanyName}}</view>
+							<view style="margin-top: 20rpx;">合同编号:{{item.ContractCode}}</view>
+							<view style="margin-top: 20rpx;">合同类型:{{item.ContractType}}</view>
+							<view style="margin-top: 20rpx;">合同金额:{{item.Price}}</view>
+						</view>
+					</view>
+					
+				</view>
+			</view>
+		</van-popup>
+		
+		<van-dialog id="van-dialog" />
+	</view>
+</template>
+
+<script>
+	import {apiSealDetail,apiSearchCustome,apiSearchContract,apiApprovalPass,apiInvalidSeal,apiApprovalPassModify} from '@/api/approve/seal.js'
+	import steps from '../components/steps.vue'
+	import {preViewFile} from '../utils/util.js'
+	export default{
+		components:{
+			steps
+		},
+		computed:{
+			statusImg(){
+				if(this.detail.Status==='已审批'){
+					return require('../static/pass-icon.png')
+				}else if(this.detail.Status==='已驳回'){
+					return require('../static/fail-icon.png')
+				}else if(this.detail.Status==='处理中'){
+					return require('../static/processing-icon.png')
+				}else if(this.detail.Status==='已撤回'){
+					return require('../static/recall-icon.png')
+				}else if(this.detail.Status==='已作废'){
+					return require('../static/cancel-icon.png')
+				}
+			}
+		},
+		data() {
+			return {
+				val:'',
+				ContractApprovalId:0,
+				ContractApprovalRecordId:0,
+				processData:null,
+				detail:{},
+				opButton:{},
+				files:[],
+				
+				showPurpose:false,//显示用印用途选项
+				purposeArr:['销售合同','渠道合同','付款通知涵','招投标','战略合作协议'],
+
+				showType:false,//显示用印用途选项
+				typeArr:['公章','合同章','法人章'],
+				showCustome:false,//显示搜索客户名称
+				searchCustomeVal:'',//搜索客户输入数据
+				searchCustomeList:[],//搜索到的客户名称列表
+				searchContractList:[],//选择搜索中的客户后合同列表数据
+				
+				checked:false,//是否同时作废合同
+				
+				oldUse:"",
+				oldFileNum:'',
+				oldSealType:'',
+				oldRemark:'',
+			}
+		},
+		onLoad(options) {
+			this.ContractApprovalId=options.ContractApprovalId
+			this.ContractApprovalRecordId=options.ContractApprovalRecordId
+		},
+		onShow() {
+			this.getDetail()
+		},
+		onPullDownRefresh() {
+			this.getDetail()
+			setTimeout(()=>{
+				uni.stopPullDownRefresh()
+			},1000)
+		},
+		methods: {
+			handlePurposeConfirm(e){
+				this.oldUse=e.detail.value
+				this.showPurpose=false
+			},
+			
+			handleTypeConfirm(e){
+				this.oldSealType=e.detail.value
+				this.showType=false
+			},
+			
+			//前去重审
+			handleEdit(){
+				uni.navigateTo({
+					url:`./edit?ContractApprovalId=${this.ContractApprovalId}&ContractApprovalRecordId=${this.ContractApprovalRecordId}`
+				})
+			},
+			
+			//用印作废
+			handleSealInvalid(){
+				this.$dialog.confirm({
+					title: '提示',
+					 message: '是否确认作废',
+				}).then(async ()=>{
+					const res=await apiInvalidSeal({
+						IsInvalidContract:this.checked,
+						SealId:this.detail.SealId
+					})
+					if(res.code===200){
+						uni.showToast({
+							title:'操作成功',
+							icon:'none'
+						})
+						this.getDetail()
+					}
+				}).catch(() => {
+					console.log('取消作废');
+				});
+				
+			},
+			
+			//点击通过
+			handleClickPass(){
+				//判断是否有内容修改
+				const flag1=this.oldUse===this.detail.Use 
+				const flag2=Number(this.oldFileNum)===Number(this.detail.FileNum)
+				const flag3=this.oldSealType===this.detail.SealType
+				const flag4=this.oldRemark===this.detail.Remark
+				if(flag1&&flag2&&flag3&&flag4){
+					this.handleApprovalPass()
+				}else{
+					this.handleApprovePassModify()
+				}
+			},
+			
+			//审批通过 (修改内容)
+			async handleApprovePassModify(){
+				const res=await apiApprovalPassModify({
+					FileNum:Number(this.oldFileNum),
+					Remark:this.oldRemark,
+					SealId:Number(this.detail.SealId),
+					SealType:this.oldSealType,
+					Use:this.oldUse
+				})
+				if(res.code===200){
+					this.$dialog.alert({
+						title: "处理成功",
+						confirmButtonColor: "#5890FB",
+					}).then(() => {
+						this.getDetail()
+					});
+				}
+			},
+			
+			// 审批通过 (未修改内容)
+			async handleApprovalPass(){
+				const res=await apiApprovalPass({
+					SealId:Number(this.detail.SealId),
+					Remark:""
+				})
+				if(res.code===200){
+					this.$dialog.alert({
+						title: "处理成功",
+						confirmButtonColor: "#5890FB",
+					}).then(() => {
+						this.getDetail()
+					});
+				}
+			},
+			
+			
+			
+			//驳回
+			handleApprovalReject(){
+				uni.navigateTo({
+					url:`./reason?SealId=${this.detail.SealId}`
+				})
+			},
+			
+			//预览文件
+			handlepreViewFile(e){
+				if (e.type === "pdf") {
+					preViewFile(e.url)
+				} else {
+					uni.previewImage({
+						urls: [e.url]
+					})
+				}
+			},
+			
+			// 只有当 checkEdit 为true  即合规才能修改
+			handleOperation(key){
+				if(this.opButton.CheckEdit){
+					this[key]=true
+				}
+			},
+			
+			//销售自己单独能修改的项
+			handleSelfOperation(key){
+				if(this.opButton.Edit){
+					this[key]=true
+				}
+			},
+			
+			// 客户搜索
+			// 先搜索出客户 再通过客户去请求出客户下面存在的合同
+			onSearchValChange(e){
+				this.searchCustomeVal=e.detail
+			},
+			
+			//搜索客户
+			async onSearch(){
+				this.searchContractList=[]
+				this.searchCustomeList=[]
+				const res=await apiSearchCustome({CompanyName:this.searchCustomeVal})
+				if(res.code===200){
+					this.searchCustomeList=res.data
+				}
+			},
+			
+			// 搜索客户对应的合同
+			async getContract(e){
+				const res=await apiSearchContract({Keyword:e})
+				if(res.code===200){
+					if(res.data.List){
+						this.searchContractList=res.data.List 
+					}else{
+						uni.showToast({
+							title:"此客户无合同,请重新选择",
+							icon:"none"
+						})
+					}
+				}
+			},
+			
+			// 选择合同 更新表单数据
+			handleChooseContract(e){
+				
+				this.detail.CompanyName=e.CompanyName
+				this.detail.ServiceType=e.ContractType
+				this.detail.CreditCode=e.CreditCode
+				this.detail.UseCompanyName=e.CompanyName
+				this.detail.ContractId=e.ContractId
+				this.detail.ContractfileUrl=e.FileUrl
+				// 关闭搜索弹窗
+				this.showCustome=false
+				this.searchCustomeVal=''
+				this.searchContractList=[]
+				this.searchCustomeList=[]
+			},
+			
+			//获取详情
+			async getDetail() {
+				const res=await apiSealDetail({
+					ContractApprovalId:Number(this.ContractApprovalId),
+					ContractApprovalRecordId:Number(this.ContractApprovalRecordId)
+				})
+				if(res.code===200){
+					this.detail=res.data.SealDetail
+					this.oldUse=res.data.SealDetail.Use
+					this.oldFileNum=res.data.SealDetail.FileNum
+					this.oldSealType=res.data.SealDetail.SealType
+					this.oldRemark=res.data.SealDetail.Remark
+					this.processData=res.data.FlowNodeList
+					this.opButton=res.data.OpButton
+					this.handleFile(res.data.SealDetail.FileUrl)
+				}
+			},
+			
+			//处理文件
+			handleFile(filesUrl){
+				const reg = /\.(pdf)$/;
+				let arr=filesUrl.split('#')
+				this.files=arr.map(item=>{
+					if (reg.test(item)) {
+						return {
+							type: "pdf",
+							url: item,
+							img: require("../static/pdf.png"),
+						};
+					} else {
+						return {
+							type: "img",
+							url: item,
+							img: item,
+						};
+					}
+				})
+			},
+		},
+	}
+</script>
+
+<style lang="scss">
+	.detail{
+		width: 100%;
+		min-height: 100vh;
+		padding-bottom: calc(150rpx + constant(safe-area-inset-bottom));
+		padding-bottom: calc(150rpx + env(safe-area-inset-bottom));
+		position: relative;
+	}
+	.status-img{
+		position: absolute;
+		right: 0;
+		top: 50rpx;
+		width: 220rpx;
+		height: 220rpx;
+		z-index: 10;
+	}
+	.section{
+		padding: 30rpx 34rpx;
+		border-top: 10rpx solid #F5F5F5;
+		position: relative;
+		.section-title{
+			font-size: 16px;
+			margin-bottom: 20rpx;
+		}
+		.require::before{
+			content: '*';
+			font-size: 16px;
+			color: #FF0000;
+			position: absolute;
+			left: 20rpx;
+		}
+		.section-select-box{
+			color: #999;
+			position: relative;
+			&::after{
+				position: absolute;
+				right: 0;
+				top: 50%;
+				content: '';
+				display: block;
+				width: 18rpx;
+				height: 18rpx;
+				border-top: 1px solid #999;
+				border-right: 1px solid #999;
+				transform: translateY(-50%) rotate(45deg);
+			}
+		}
+	}
+	
+	.btn-big{
+		width: 450rpx;
+		height: 60rpx;
+	}
+	.btn-small{
+		width: 220rpx;
+		height: 60rpx;
+	}
+	
+	.custome-search-wrap{
+		padding: 34rpx;
+		height: 100%;
+		.search-box {
+			border: 1px solid #3385FF;
+			padding: 0 !important;
+			border-radius: 60rpx;
+			background-color: #fff !important;
+		}
+		
+		.search-con {
+			background-color: #fff !important;
+		}
+		
+		.van-search__content {
+			background-color: #fff !important;
+			padding-left: 30rpx !important;
+		}
+		
+		.search-btn {
+			position: relative;
+			color: #3385FF;
+			&::before {
+				content: '';
+				display: block;
+				width: 1px;
+				height: 60%;
+				background-color: #D1D1D1;
+				position: absolute;
+				left: -16rpx;
+				top: 20%;
+			}
+		}
+		.search-icon{
+			width: 40rpx;
+			height: 40rpx;
+			display: block;
+			position: relative;
+			top: 4rpx;
+			margin-right: 20rpx;
+		}
+	
+		.click-icon{
+			width: 24rpx;
+			height: 24rpx;
+		}
+		.result-item{
+			align-items: center;
+			padding: 20rpx 0;
+			border-bottom: 1px solid #EBEBEB;
+			.con{
+				flex: 1;
+				margin-right: 20rpx;
+			}
+		}
+		.search-result{
+			overflow-y: auto;
+			height: 100%;
+		}
+	
+		.result-contract-box{
+			padding: 0 10rpx;
+			.result-contract-item{
+				margin-top: 30rpx;
+				box-shadow: 0px 0px 12rpx rgba(175, 175, 175, 0.38);
+				padding: 30rpx;
+				border-radius: 8px;
+				.name{
+					font-size: 16px;
+					font-weight: bold;
+					&::before{
+						content:'';
+						display:inline-block;
+						width: 31rpx;
+						height: 34rpx;
+						background-image: url(../../static/man.png);
+						background-size: cover;
+						position: relative;
+						top: 4rpx;
+						margin-right: 10rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 558 - 0
pages-approve/seal/edit.vue

@@ -0,0 +1,558 @@
+<template>
+	<view class="add-page white-wrap">
+		<view class="section white-wrap">
+			<view class="section-title require">用印用途</view>
+			<view class="section-select-box" :style="{color:purpose?'#333':'#999'}" @click="showPurpose=true">{{purpose?purpose:'请选择'}}</view>
+		</view>
+		
+		<!-- 系统合同模块 -->
+		<view v-if="radioVal==='系统合同'">
+			<view class="section white-wrap">
+				<view class="section-title require">客户名称(全称)</view>
+				<view 
+					class="section-select-box" 
+					:style="{color:customeName?'#333':'#999'}" 
+					@click="showCustome=true"
+				>
+					{{customeName?customeName:'请输入客户名称'}}
+				</view>
+			</view>
+			<view class="section white-wrap" v-if="CreditCode">
+				<view class="section-title require">统一社会信用码</view>
+				<input type="text" v-model="CreditCode" placeholder="请填写统一社会信用码" disabled/>
+			</view>
+			<view class="section white-wrap" v-if="UseCompanyName">
+				<view class="section-title">实际使用方名称</view>
+				<input type="text" v-model="UseCompanyName" placeholder="请填写实际使用方名称" disabled/>
+			</view>
+			<view class="section white-wrap" v-if="ServiceType">
+				<view class="section-title require">业务类型</view>
+				<view :style="{color:ServiceType?'#333':'#999'}">{{ServiceType?ServiceType:'请选择业务类型'}}</view>
+			</view>
+		</view>
+		<!-- 上传附件模块 -->
+		<view v-if="radioVal==='上传附件'">
+			<view class="section white-wrap">
+				<view class="section-title require">客户名称(全称)</view>
+				<input type="text" v-model="customeName" placeholder="请输入客户名称"/>
+			</view>
+			<view class="section white-wrap">
+				<view class="section-title require">统一社会信用码</view>
+				<input type="text" v-model="CreditCode" placeholder="请填写统一社会信用码"/>
+			</view>
+			<view class="section white-wrap">
+				<view class="section-title">实际使用方名称</view>
+				<input type="text" v-model="UseCompanyName" placeholder="请填写实际使用方名称"/>
+			</view>
+			<view class="section white-wrap">
+				<view class="section-title require">业务类型</view>
+				<view class="section-select-box" :style="{color:ServiceType?'#333':'#999'}" @click="showServiceType=true">{{ServiceType?ServiceType:'请选择业务类型'}}</view>
+			</view>
+		</view>
+		
+		<view class="section white-wrap">
+			<view class="section-title require">文件份数</view>
+			<input type="number" v-model="fileNum" placeholder="请填写总共盖章文件份数"/>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title require">加盖何种印章</view>
+			<view class="section-select-box" :style="{color:type?'#333':'#999'}" @click="showType=true">{{type?type:'请选择'}}</view>
+		</view>
+		<view class="section white-wrap">
+			<view class="section-title">备注</view>
+			<textarea type="text" v-model="remark" placeholder="请填写备注"></textarea>
+		</view>
+		<view class="section white-wrap" v-if="radioVal==='系统合同'&&ContractfileUrl">
+			<view class="section-title require">合同附件</view>
+			<image src="../static/pdf.png" mode="aspectFill" style="width: 102rpx;height: 120rpx;"></image>
+		</view>
+		<view class="section white-wrap" v-if="radioVal==='上传附件'">
+			<view class="section-title require">附件上传</view>
+			<image :src="img" mode="aspectFill" style="width: 102rpx;height: 120rpx;margin:0 10rpx 10rpx 0" v-for="img in fileUrlArr" :key="img"></image>
+			<image @click="handleUpload" src="../static/upload-icon.png" mode="aspectFill" style="width: 102rpx;height: 120rpx;margin:0 10rpx 10rpx 0"></image>
+		</view>
+		
+		<!-- 流程模块 -->
+		<view class="section white-wrap process-wrap" v-if="processData">
+			<view class="title" style="font-size: 16px;font-weight: bold;">审批流程</view>
+			<steps :data="processData"></steps>
+		</view>
+		
+		<view class="fix-bottom-wrap" style="text-align: center;">
+			<van-button type="info" custom-class="btn" round @click="handleSubmit">提交</van-button>
+		</view>
+		
+		
+		<!-- 用印用途 -->
+		<van-popup :show="showPurpose" @close="showPurpose=false" position="bottom">
+			<van-picker 
+				show-toolbar 
+				title="选择用印用途" 
+				:columns="purposeArr" 
+				@confirm="handlePurposeConfirm"  
+				@cancel="showPurpose=false"
+			/>
+		</van-popup>
+		<!-- 何种印章 -->
+		<van-popup :show="showType" @close="showType=false" position="bottom">
+			<van-picker 
+				show-toolbar 
+				title="选择何种印章" 
+				:columns="typeArr" 
+				@confirm="handleTypeConfirm"  
+				@cancel="showType=false"
+			/>
+		</van-popup>
+		
+		<!-- 业务类型 -->
+		<van-popup :show="showServiceType" @close="showServiceType=false" position="bottom">
+			<van-picker 
+				show-toolbar 
+				title="选择业务类型" 
+				:columns="ServiceTypeArr" 
+				@confirm="handleServiceTypeConfirm"  
+				@cancel="showServiceType=false"
+			/>
+		</van-popup>
+		
+		<!-- 客户搜索 -->
+		<van-popup :show="showCustome" @close="showCustome=false" position="bottom" custom-style="height: 100vh">
+			<view class="custome-search-wrap">
+				<van-search use-left-icon-slot use-action-slot shape="round" :value="searchCustomeVal" placeholder="请输入客户名称" @change="onSearchValChange" @search="onSearch"
+					custom-class="search-box" field-class="search-con">
+					<view slot="left-icon">
+						<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+					</view>
+					<view slot="action" @click="showCustome=false" class="search-btn">取消</view>
+				</van-search>
+				<view class="search-result">
+					<view class="result-custome-box" v-if="searchContractList.length===0">
+						<view class="result-item flex" v-for="item in searchCustomeList" :key="item" @click="getContract(item)">
+							<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+							<view class="con van-ellipsis">{{item}}</view>
+							<image src="../static/click-icon.png" mode="aspectFill" class="click-icon"></image>
+						</view>
+					</view>
+					<view class="result-contract-box" v-else>
+						<view class="result-contract-item" v-for="item in searchContractList" :key="item.ContractId" @click="handleChooseContract(item)">
+							<view class="name">{{item.CompanyName}}</view>
+							<view style="margin-top: 20rpx;">合同编号:{{item.ContractCode}}</view>
+							<view style="margin-top: 20rpx;">合同类型:{{item.ContractType}}</view>
+							<view style="margin-top: 20rpx;">合同金额:{{item.Price}}</view>
+						</view>
+					</view>
+					
+				</view>
+			</view>
+		</van-popup>
+	</view>
+</template>
+
+<script>
+	import steps from '../components/steps.vue'
+	import {apiFlowDetail,apiSealEdit,apiSearchCustome,apiSearchContract,apiSealDetail} from '@/api/approve/seal.js'
+	import {uploadImg} from '@/utils/uploadFile.js'
+	export default{ 
+		components:{
+			steps
+		},
+		data() {
+			return {
+				SealId:0,
+				showPurpose:false,//显示用印用途选项
+				purposeArr:['销售合同','渠道合同','付款通知函','招投标','战略合作协议'],
+				purpose:"",
+				
+				showType:false,//显示用印用途选项
+				typeArr:['公章','合同章','法人章'],
+				type:"",
+				
+				showServiceType:false,//显示业务类型选项
+				ServiceTypeArr:['新签合同','续约合同','补充协议'],
+				ServiceType:'',//业务类型
+				
+				
+				showCustome:false,//显示搜索客户名称
+				customeName: '',//客户名称
+				fileNum:'',//文件数
+				remark:'',//备注
+				CreditCode:'',//社会统一信用代码
+				
+				UseCompanyName:'',//实际使用方客户名称
+				ContractId:0,//合同id
+				ContractfileUrl:'',//合同文件地址 pdf
+				
+				fileUrlArr:[],//上传附件 文件地址
+				
+				processData:null,//流程数据
+				
+				radioVal:'系统合同',
+				
+				searchCustomeVal:'',//搜索客户输入数据
+				searchCustomeList:[],//搜索到的客户名称列表
+				searchContractList:[],//选择搜索中的客户后合同列表数据
+			}
+		},
+		onLoad(options) {
+			let ContractApprovalId=options.ContractApprovalId
+			let ContractApprovalRecordId=options.ContractApprovalRecordId
+			this.getDetail({ContractApprovalId,ContractApprovalRecordId})
+		},
+		methods: {
+			// 获取详情
+			async getDetail({ContractApprovalId,ContractApprovalRecordId}){
+				const res=await apiSealDetail({
+					ContractApprovalId:Number(ContractApprovalId),
+					ContractApprovalRecordId:Number(ContractApprovalRecordId)
+				})
+				if(res.code===200){
+					if(res.data.SealDetail.ContractId>0){
+						this.radioVal='系统合同'
+						
+					}else{
+						this.radioVal='上传附件'
+					}
+					this.SealId=res.data.SealDetail.SealId
+					this.purpose=res.data.SealDetail.Use
+					this.type=res.data.SealDetail.SealType
+					this.ServiceType=res.data.SealDetail.ServiceType
+					this.customeName=res.data.SealDetail.CompanyName
+					this.fileNum=res.data.SealDetail.FileNum
+					this.remark=res.data.SealDetail.Remark
+					this.CreditCode=res.data.SealDetail.CreditCode
+					this.UseCompanyName=res.data.SealDetail.UseCompanyName
+					this.ContractfileUrl=res.data.SealDetail.FileUrl
+					this.ContractId=res.data.SealDetail.ContractId
+					this.getProcessData()
+				}
+			},
+			
+			//上传附件
+			async handleUpload(){
+				const res=await uploadImg()
+				this.fileUrlArr=res
+			},
+			
+			// 客户搜索 
+			// 先搜索出客户 再通过客户去请求出客户下面存在的合同
+			onSearchValChange(e){
+				this.searchCustomeVal=e.detail
+			},
+			
+			//搜索客户
+			async onSearch(){
+				this.searchContractList=[]
+				this.searchCustomeList=[]
+				const res=await apiSearchCustome({CompanyName:this.searchCustomeVal})
+				if(res.code===200){
+					this.searchCustomeList=res.data
+				}
+			},
+			
+			// 搜索客户对应的合同
+			async getContract(e){
+				const res=await apiSearchContract({Keyword:e})
+				if(res.code===200){
+					if(res.data.List){
+						this.searchContractList=res.data.List 
+					}else{
+						uni.showToast({
+							title:"此客户无合同,请重新选择",
+							icon:"none"
+						})
+					}
+				}
+			},
+			
+			// 选择合同 更新表单数据
+			handleChooseContract(e){
+				
+				this.customeName=e.CompanyName
+				this.CreditCode=e.CreditCode
+				this.ServiceType=e.ContractType
+				this.UseCompanyName=e.CompanyName
+				this.ContractId=e.ContractId
+				this.ContractfileUrl=e.FileUrl
+				// 关闭搜索弹窗
+				this.showCustome=false
+				this.searchCustomeVal=''
+				this.searchContractList=[]
+				this.searchCustomeList=[]
+			},
+			
+			// 选择用印用途
+			handlePurposeConfirm(e) {
+				this.purpose=e.detail.value 
+				this.showPurpose=false
+			},
+			
+			//选择盖章类型
+			handleTypeConfirm(e){
+				this.type=e.detail.value
+				this.showType=false
+				this.getProcessData()
+			},
+			
+			//选择业务类型
+			handleServiceTypeConfirm(e){
+				this.ServiceType=e.detail.value
+				this.showServiceType=false
+			},
+			
+			//合同章5  公章、法人章 6
+			async getProcessData(){
+				let id=0
+				if(this.type==='合同章'){
+					id=5
+				}else{
+					id=6
+				}
+				
+				let res=await apiFlowDetail({FlowId:id})
+				if(res.code===200){
+					this.processData=res.data||[]
+				}
+			},
+			
+			// 提交申请
+			async handleSubmit(){
+				let fileUrl=''
+				if(this.radioVal==='系统合同'){
+					fileUrl=this.ContractfileUrl
+				}else{
+					fileUrl=this.fileUrlArr.join('#')
+				}
+				
+				let params={
+					CompanyName:this.customeName,
+					ContractId:this.ContractId,
+					CreditCode:this.CreditCode,
+					FileUrl:fileUrl,
+					FileNum:Number(this.fileNum),
+					Remark:this.remark,
+					SealType:this.type,
+					ServiceType:this.ServiceType,
+					Use:this.purpose,
+					UseCompanyName:this.UseCompanyName,
+					SealId:Number(this.SealId)
+				}
+				
+				if(!params.Use){
+					uni.showToast({
+						title:'请选择用印用途',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.CompanyName){
+					uni.showToast({
+						title:'客户名称不能为空',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.CreditCode){
+					uni.showToast({
+						title:'信用代码不能为空',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.ServiceType){
+					uni.showToast({
+						title:'业务类型不能为空',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.FileNum){
+					uni.showToast({
+						title:'请填写文件份数',
+						icon:"none"
+					})
+					return
+				}
+				if(params.FileNum<1){
+					uni.showToast({
+						title:'文件份数不合法',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.SealType){
+					uni.showToast({
+						title:'印章类型不能为空',
+						icon:"none"
+					})
+					return
+				}
+				if(!params.FileUrl){
+					uni.showToast({
+						title:'请上传文件',
+						icon:"none"
+					})
+					return
+				}
+				
+				const res=await apiSealEdit(params)
+				if(res.code===200){
+					uni.showToast({
+						title:"用印申请单已提交",
+						icon:"none"
+					})
+					setTimeout(()=>{
+						uni.navigateBack({
+							delta:2
+						})
+					},1000)
+				}
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.add-page{
+		width: 100%;
+		min-height: 100vh;
+		padding-bottom: calc(150rpx + constant(safe-area-inset-bottom));
+		padding-bottom: calc(150rpx + env(safe-area-inset-bottom));
+	}
+	.section{
+		padding: 30rpx 34rpx;
+		border-top: 10rpx solid #F5F5F5;
+		position: relative;
+		textarea{
+			width: 100%;
+			height: 200rpx;
+		}
+		.section-title{
+			font-size: 16px;
+			margin-bottom: 20rpx;
+		}
+		.require::before{
+			content: '*';
+			font-size: 16px;
+			color: #FF0000;
+			position: absolute;
+			left: 20rpx;
+		}
+		.section-select-box{
+			color: #999;
+			position: relative;
+			&::after{
+				position: absolute;
+				right: 0;
+				top: 50%;
+				content: '';
+				display: block;
+				width: 18rpx;
+				height: 18rpx;
+				border-top: 1px solid #999;
+				border-right: 1px solid #999;
+				transform: translateY(-50%) rotate(45deg);
+			}
+		}
+	}
+	
+	.btn{
+		width: 360rpx;
+		height: 60rpx;
+	}
+	
+	.radio-wrap{
+		.radio{
+			font-size: 14px;
+			&::before{
+				content: '';
+				display: inline-block;
+			}
+		}
+	}
+	
+	.custome-search-wrap{
+		padding: 34rpx;
+		height: 100%;
+		.search-box {
+			border: 1px solid #3385FF;
+			padding: 0 !important;
+			border-radius: 60rpx;
+			background-color: #fff !important;
+		}
+		
+		.search-con {
+			background-color: #fff !important;
+		}
+		
+		.van-search__content {
+			background-color: #fff !important;
+			padding-left: 30rpx !important;
+		}
+		
+		.search-btn {
+			position: relative;
+			color: #3385FF;
+			&::before {
+				content: '';
+				display: block;
+				width: 1px;
+				height: 60%;
+				background-color: #D1D1D1;
+				position: absolute;
+				left: -16rpx;
+				top: 20%;
+			}
+		}
+		.search-icon{
+			width: 40rpx;
+			height: 40rpx;
+			display: block;
+			position: relative;
+			top: 4rpx;
+			margin-right: 20rpx;
+		}
+
+		.click-icon{
+			width: 24rpx;
+			height: 24rpx;
+		}
+		.result-item{
+			align-items: center;
+			padding: 20rpx 0;
+			border-bottom: 1px solid #EBEBEB;
+			.con{
+				flex: 1;
+				margin-right: 20rpx;
+			}
+		}
+		.search-result{
+			overflow-y: auto;
+			height: 100%;
+		}
+	
+		.result-contract-box{
+			padding: 0 10rpx;
+			.result-contract-item{
+				margin-top: 30rpx;
+				box-shadow: 0px 0px 12rpx rgba(175, 175, 175, 0.38);
+				padding: 30rpx;
+				border-radius: 8px;
+				.name{
+					font-size: 16px;
+					font-weight: bold;
+					&::before{
+						content:'';
+						display:inline-block;
+						width: 31rpx;
+						height: 34rpx;
+						background-image: url(../../static/man.png);
+						background-size: cover;
+						position: relative;
+						top: 4rpx;
+						margin-right: 10rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 172 - 0
pages-approve/seal/list.vue

@@ -0,0 +1,172 @@
+<template>
+	<view>
+		<van-sticky>
+			<view class="top-wrap">
+				<view @click="handleGoSearch">
+					<van-search disabled use-left-icon-slot shape="round" placeholder="客户名称/社会信用码">
+						<view slot="left-icon">
+							<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+						</view>
+					</van-search>
+				</view>
+				<van-tabs id="tabs" :active="status" title-active-color="#3385FF" color="#3385FF" @change="typeChange">
+					<van-tab :title="item" :name="item" v-for="item in tabList" :key="item"></van-tab>
+				</van-tabs>
+			</view>
+		</van-sticky>
+		<van-empty description="暂无数据" :image="require('@/static/empty.png')" v-if="finished&&list.length===0" />
+		<view class="list" v-else>
+			<approve-list-item v-for="item in list" :key="item" :data="item"></approve-list-item>
+		</view>
+		
+		<image src="../static/seal-icon.png" mode="aspectFill" class="add-btn" @click="handleGoApplySeal" v-if="showAddSeal"></image>
+	</view>
+
+</template>
+
+<script>
+	import {apiSealList} from '@/api/approve/seal.js'
+	import approveListItem from '../components/approveListItem.vue'
+	export default {
+		components:{
+			approveListItem
+		},
+		computed:{
+			showAddSeal(){
+				// 只有销售和合规能有此按钮
+				let RoleTypeCode=this.$store.state.userInfo.RoleTypeCode
+				if(RoleTypeCode==='ficc_seller'||RoleTypeCode==='rai_seller'||RoleTypeCode==='compliance'){
+					return true
+				}else{
+					return false
+				}
+			}
+		},
+		data() {
+			return {
+				tabList:[],
+				status: '待审批',
+				list:[],
+				page:1,
+				finished:false,
+			}
+		},
+		onLoad() {
+			this.initTabs()
+		},
+		onShow() {
+			this.list=[]
+			this.page=1
+			this.getList()
+			this.$nextTick(()=>{
+				this.selectComponent('#tabs').resize();// 解决初始渲染 vant tab 底部条
+			})
+		},
+		onPullDownRefresh() {
+			this.page=1
+			this.finished=false
+			this.list=[]
+			this.getList()
+			setTimeout(()=>{
+				uni.stopPullDownRefresh()
+			},1500)
+		},
+		onReachBottom() {
+			if(this.finished) return
+			this.page++
+			this.getList()
+		},
+		methods: {
+			//初始化tab
+			initTabs(){
+				// ficc销售 权益销售
+				const RoleTypeCode=this.$store.state.userInfo.RoleTypeCode
+				if(RoleTypeCode==='ficc_seller'||RoleTypeCode==='rai_seller'){
+					this.tabList=['待审批','处理中','已审批','已撤回','已作废']
+					this.status='待审批'
+				}
+				// ficc管理员 权益管理员  合规compliance
+				if(RoleTypeCode==='ficc_admin'||RoleTypeCode==='rai_admin'||RoleTypeCode==='compliance'){
+					this.tabList=['待审批','处理中','已审批','已作废']
+					this.status='待审批'
+				}
+			},
+			
+			//去添加用印申请、
+			handleGoApplySeal(){
+				uni.navigateTo({
+					url:'/pages-approve/seal/addSeal'
+				})
+			},
+			
+			// 去搜索
+			handleGoSearch() {
+				uni.navigateTo({
+					url: '../search/index?type=seal'
+				})
+			},
+			
+			typeChange(e){
+				this.status=e.detail.name
+				this.page=1
+				this.finished=false
+				this.list=[]
+				this.getList()
+			},
+			
+			//获取列表数据
+			async getList(){
+				const res=await apiSealList({
+					CurrentIndex:this.page,
+					Status:this.status,
+					KeyWord:''
+				})
+				if(res.code===200){
+					if(!res.data.List||res.data.List.length===0){
+						this.finished=true
+					}else{
+						let arr=res.data.List.map(item=>{
+							return {
+								title:item.CompanyName,
+								saller:item.UserName,
+								submitTime:item.CreateTimeStr,
+								approveTime:item.ApproveTimeStr,
+								backTime:'',
+								cancelTime:item.InvalidTime,
+								status:item.Status,
+								applyType:item.SealType,
+								ContractApprovalId:item.ContractApprovalId,
+								ContractApprovalRecordId:item.ContractApprovalRecordId,
+								type:'seal'
+							}
+						})
+						this.list=[...this.list,...arr]
+					}
+				}
+			},
+			
+		},
+	}
+</script>
+
+<style lang="scss">
+	.search-icon {
+		width: 40rpx;
+		height: 40rpx;
+		display: block;
+		position: relative;
+		top: 4rpx;
+		margin-right: 10rpx;
+	}
+	.list{
+		padding: 20rpx;
+	}
+	.add-btn{
+		width: 120rpx;
+		height: 120rpx;
+		position: fixed;
+		bottom: 102rpx;
+		right: 34rpx;
+		z-index: 100;
+	}
+</style>

+ 105 - 0
pages-approve/seal/reason.vue

@@ -0,0 +1,105 @@
+<template>
+	<view>
+		<view class="textarea-wrap white-wrap">
+			<textarea placeholder="请输入驳回理由" v-model="reason" maxlength="-1"></textarea>
+		</view>
+		<view class="btns-wrap flex">
+			<button class="confirm-btn" @click="handleSubmit">提交</button>
+			<button class="cancel-btn" @click="handleCancel">取消</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {apiApprovalReject} from '@/api/approve/seal.js'
+	export default {
+		data() {
+			return {
+				reason: '',
+				SealId:0,
+			}
+		},
+		onLoad(options) {
+			this.SealId=options.SealId
+		},
+		methods: {
+			async handleSubmit() {
+				if(!this.reason){
+					uni.showToast({
+						title:"请填写理由",
+						icon:"none"
+					})
+					return 
+				}
+				let params={
+					SealId:Number(this.SealId),
+					Remark:this.reason,
+				}
+				const res=await apiApprovalReject(params)
+				if(res.code===200){
+					uni.showToast({
+						title:"审批成功",
+						icon:"success"
+					})
+					setTimeout(()=>{
+						uni.navigateBack({
+							delta:1
+						})
+					},1000)
+				}
+				
+			},
+			
+			handleCancel(){
+				uni.navigateBack({
+					delta:1
+				})
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.textarea-wrap {
+		padding: 17px;
+
+		textarea {
+			resize: none;
+			display: block;
+			box-sizing: border-box;
+			width: 100%;
+			height: 200px;
+			border: none;
+			font-size: 14px;
+			/* no */
+		}
+	}
+
+	.btns-wrap {
+		justify-content: center;
+		padding-top: 50px;
+		padding-bottom: 50px;
+
+		button {
+			width: 90px;
+			height: 28px;
+			border-radius: 28px;
+			border: none;
+			margin: 0 10px;
+			font-size: 14px;
+			/* no */
+			color: #fff;
+			line-height: 28px;
+		}
+
+		.confirm-btn {
+			background-color: #5890fb;
+		}
+
+		.cancel-btn {
+			border: 1px solid #5890fb;
+			color: #5890fb;
+			background-color: #fff;
+		}
+	}
+</style>

+ 214 - 0
pages-approve/search/index.vue

@@ -0,0 +1,214 @@
+<template>
+	<view class="search-page white-wrap">
+		<van-sticky>
+			<view class="white-wrap" style="padding: 20rpx 34rpx 10rpx 34rpx;">
+				<van-search use-left-icon-slot use-action-slot shape="round" focus :placeholder="placeholder" @change="onChange"
+					@search="onSearch" custom-class="search-box" field-class="search-con">
+					<view slot="left-icon">
+						<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+					</view>
+					<view slot="action" @click="onSearch" class="search-btn">搜索</view>
+				</van-search>
+			</view>
+		</van-sticky>
+		<van-empty description="未找到搜索结果" :image="require('@/static/empty.png')" v-if="empty" />
+		<view class="search-result" v-else>
+			<view class="result-item flex" v-for="item in list" :key="item" @click="handleGoDetail(item)">
+				<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+				<view class="con van-ellipsis">{{item}}</view>
+				<image src="../static/click-icon.png" mode="aspectFill" class="click-icon"></image>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	import {apiCustomeSearch} from '@/api/approve/custome.js'
+	import {apiContractSearch} from '@/api/approve/contract.js'
+	import {apiSealSearch} from '@/api/approve/seal.js'
+	export default {
+		data() {
+			return {
+				value: '',
+				placeholder: '',
+				type: '',
+				list: [],
+				empty:false,
+			}
+		},
+		onLoad(options) {
+			this.type = options.type
+			this.initPage(options.type)
+		},
+		methods: {
+			// 初始化界面
+			initPage(type) {
+				let navBarTitle = ''
+				switch (type) {
+					case 'custome':
+						navBarTitle = '客户审批'
+						this.placeholder = '客户名称/社会信用码'
+						break;
+					case 'contract':
+						navBarTitle = '合同审批'
+						this.placeholder = '客户名称/社会信用码'
+						break;
+					case 'seal':
+						navBarTitle = '用印审批'
+						this.placeholder = '客户名称/社会信用码'
+						break;
+				}
+				uni.setNavigationBarTitle({
+					title: navBarTitle
+				})
+			},
+
+			//跳转搜索结果页
+			handleGoDetail(e) {
+				// 如果是合同则跳转合同搜索结果页 否则跳转公共的搜索结果页
+				if(this.type==='contract'){
+					uni.navigateTo({
+						url:"/pages-approve/contract/search?val="+e
+					})
+				}else{
+					uni.navigateTo({
+						url:`./result?type=${this.type}&val=${e}`
+					})
+				}
+			},
+
+			// 客户审批搜索
+			async handleCustome() {
+				const res = await apiCustomeSearch({
+					CompanyName: this.value,
+				})
+				if (res.code === 200) {
+					
+					this.list = res.data
+					if(res.data.length===0){
+						this.empty=true
+					}else{
+						this.empty=false
+					}
+				}
+			},
+
+			// 合同审批搜索
+			async handleContract() {
+				const res = await apiContractSearch({
+					CompanyName: this.value,
+				})
+				if (res.code === 200) {
+					this.list = res.data
+					if(res.data.length===0){
+						this.empty=true
+					}else{
+						this.empty=false
+					}
+				}
+			},
+			
+			//用印搜索
+			async handleSeal(){
+				const res=await apiSealSearch({
+					CompanyName: this.value,
+				})
+				if (res.code === 200) {
+					this.list = res.data
+					if(res.data.length===0){
+						this.empty=true
+					}else{
+						this.empty=false
+					}
+				}
+			},
+
+			onChange(e) {
+				this.value = e.detail
+			},
+
+			onSearch() {
+				if (this.type === 'custome') {
+					this.handleCustome()
+				}
+
+				if (this.type === 'contract') {
+					this.handleContract()
+				}
+				
+				if(this.type==='seal'){
+					this.handleSeal()
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.search-page {
+		width: 100%;
+		min-height: 100vh;
+		// padding: 20rpx 34rpx;
+	}
+
+	.search-box {
+		border: 1px solid #3385FF;
+		padding: 0 !important;
+		border-radius: 60rpx;
+		background-color: #fff !important;
+	}
+
+	.search-con {
+		background-color: #fff !important;
+	}
+
+	.van-search__content {
+		background-color: #fff !important;
+		padding-left: 30rpx !important;
+	}
+
+	.search-btn {
+		position: relative;
+		color: #3385FF;
+
+		&::before {
+			content: '';
+			display: block;
+			width: 1px;
+			height: 60%;
+			background-color: #D1D1D1;
+			position: absolute;
+			left: -16rpx;
+			top: 20%;
+		}
+	}
+
+	.search-icon {
+		width: 40rpx;
+		height: 40rpx;
+		display: block;
+		position: relative;
+		top: 4rpx;
+		margin-right: 20rpx;
+	}
+
+	.click-icon {
+		width: 24rpx;
+		height: 24rpx;
+	}
+
+	.search-result{
+		padding: 20rpx 34rpx;
+	}
+	.result-item {
+		align-items: center;
+		padding: 20rpx 0;
+		border-bottom: 1px solid #EBEBEB;
+
+		.con {
+			flex: 1;
+			margin-right: 20rpx;
+		}
+	}
+</style>

+ 157 - 0
pages-approve/search/result.vue

@@ -0,0 +1,157 @@
+<template>
+	<view>
+		<van-sticky>
+			<view class="top-wrap">
+				<view @click="handleBack">
+					<van-search disabled use-left-icon-slot shape="round" :value="keyword" placeholder="客户名称/社会信用码">
+						<view slot="left-icon">
+							<image src="../static/search-icon.png" mode="aspectFill" class="search-icon"></image>
+						</view>
+					</van-search>
+				</view>
+			</view>
+		</van-sticky>
+		<van-empty description="暂无数据" :image="require('@/static/empty.png')" v-if="finished&&list.length===0"/>
+		<view class="list-wrap" v-else>
+			<approve-list-item v-for="item in list" :key="item.CompanyApprovalId" :data="item"></approve-list-item>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {apiCustomeList} from '@/api/approve/custome.js'
+	import {apiSealList} from '@/api/approve/seal.js'
+	import approveListItem from '../components/approveListItem.vue'
+	export default{
+		components: {
+			approveListItem
+		},
+		data() {
+			return {
+				type:'',
+				keyword:'',
+				list:[],
+				page:1,
+				finished:false
+			}
+		},
+		onLoad(options) {
+			this.type=options.type 
+			this.keyword=options.val 
+			if(options.type==='custome'){
+				this.getCustomeList()
+			}else if(options.type==='seal'){
+				this.getSealList()
+			}
+		},
+		onReachBottom() {
+			if(this.finished) return
+			this.page++
+			
+			if(this.type==='custome'){
+				this.getCustomeList()
+			}
+		},
+		methods: {
+			handleBack(){
+				uni.navigateBack({
+					delta:1
+				})
+			},
+			
+			// 获取客户搜索数据
+			async getCustomeList() {
+				const res=await apiCustomeList({
+					CurrentIndex:this.page,
+					Status:'',
+					KeyWord:this.keyword
+				})
+				if(res.code===200){
+					if(!res.data.List||res.data.List.length===0){
+						this.finished=true
+					}else{
+						let arr=res.data.List.map(item=>{
+							//申请类型:申请类型:1:试用->正式,2:冻结—>试用,3:试用延期,4:原销售申请领取流失客户,5:正式客户申请服务更新
+							let applyType=''
+							switch(item.ApplyMethod){
+								case 1:
+									applyType='试用转正式'
+									break;
+								case 2:
+									applyType='冻结转试用'
+									break;
+								case 3:
+									applyType='试用延期'
+									break;
+								case 4:
+									applyType='原销售申领'
+									break;
+								case 5:
+									applyType='服务更新'
+									break;	
+							}
+							return {
+								title:item.CompanyName,
+								saller:item.ApplyRealName,
+								submitTime:item.ApprovalTime,
+								approveTime:item.ApproveTime,
+								backTime:'',
+								cancelTime:'',
+								status:item.ApproveStatus,
+								applyType:applyType,
+								id:item.CompanyApprovalId,
+								type:'custome'
+							}
+						})
+						this.list=[...this.list,...arr]
+					}
+				}
+			},
+		
+			//获取用印数据
+			async getSealList(){
+				const res=await apiSealList({
+					CurrentIndex:this.page,
+					Status:'',
+					KeyWord:this.keyword
+				})
+				if(res.code===200){
+					if(!res.data.List||res.data.List.length===0){
+						this.finished=true
+					}else{
+						let arr=res.data.List.map(item=>{
+							return {
+								title:item.CompanyName,
+								saller:item.UserName,
+								submitTime:item.CreateTimeStr,
+								approveTime:item.ApproveTimeStr,
+								backTime:'',
+								cancelTime:'',
+								status:item.Status,
+								applyType:item.SealType,
+								ContractApprovalId:item.ContractApprovalId,
+								ContractApprovalRecordId:item.ContractApprovalRecordId,
+								type:'seal'
+							}
+						})
+						this.list=[...this.list,...arr]
+					}
+				}
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.search-icon {
+		width: 40rpx;
+		height: 40rpx;
+		display: block;
+		position: relative;
+		top: 4rpx;
+		margin-right: 10rpx;
+	}
+	.list-wrap {
+		padding: 20rpx;
+	}
+</style>

BIN
pages-approve/static/cancel-icon.png


BIN
pages-approve/static/click-icon.png


BIN
pages-approve/static/draw-back-icon.png


BIN
pages-approve/static/fail-icon.png


BIN
pages-approve/static/pass-icon.png


BIN
pages-approve/static/pdf.png


BIN
pages-approve/static/processing-icon.png


BIN
pages-approve/static/recall-icon.png


BIN
pages-approve/static/seal-icon.png


BIN
pages-approve/static/search-icon.png


BIN
pages-approve/static/upload-icon.png


+ 31 - 0
pages-approve/utils/util.js

@@ -0,0 +1,31 @@
+/**
+ * 审批模块 公共方法
+ */
+
+// 预览文件
+export const preViewFile=fileurl=>{
+	uni.showLoading({
+		title:'文件下载中...'
+	})
+	uni.downloadFile({
+		url:fileurl,
+		success(res){
+			const tempFilePath = res.tempFilePath;
+			uni.openDocument({
+				filePath:tempFilePath,
+				success() {
+					console.log('打开成功');
+				}
+			})
+		},
+		fail(){
+			uni.showToast({
+				title:'下载失败,请重试',
+				icon:"none"
+			})
+		},
+		complete(){
+			uni.hideLoading()
+		}
+	})
+}

+ 219 - 0
pages-todomessages/list/list.vue

@@ -0,0 +1,219 @@
+<template>
+	<view class="message-list-page">
+		<!-- <view class="top-time-box white-wrap">昨天 21:00</view> -->
+		<view class="list">
+			<view class="message-item flex" v-for="item in list" :key="item.Id">
+				<image :src="avatar" mode="aspectFill" style="width: 88rpx;height: 88rpx;"></image>
+				<view class="message-content">
+					<view style="color:#666;font-size: 12px;">审批小助手</view>
+					<view class="message-card white-wrap" @click="handleGoDetail(item)">
+						<view class="type">{{item.ApprovalInfo.Type}}</view>
+						<view class="title">{{item.CompanyName}}</view>
+						<view v-if="item.MessageType===3">
+							<view class="info">{{item.Content}}</view>
+							<view class="info">修改时间:{{item.CreateTime|formatTime}}</view>
+						</view>
+						<view v-else>
+						<view class="info">申请销售:{{item.ApprovalInfo.ApplyName}}</view>
+						<view class="info" v-if="item.statusName==='待审批'">提交时间:{{item.ApprovalInfo.ApplyTime|formatTime}}</view>
+						<view class="info" v-else>审批时间:{{item.ApprovalInfo.ApprovalTime|formatTime}}</view>
+						</view>
+						<image :src="item.statusImg" mode="aspectFill" class="status-img"></image>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {apiMessageList} from '@/api/message.js'
+	export default {
+		filters:{
+			formatTime(e){
+				return e.replace(/-/g,'.')
+			}
+		},
+		data() {
+			return {
+				type: null, //类型对应的数字: 1客户(custome)、2合同(contract)、3用印(seal)
+				typeName:null,//类型对应的英文 客户(custome)、合同(contract)、用印(seal)
+				page: 1, //页码
+				list: [],
+				finished: false, //是否列表为最后一页
+				
+				avatar:'',// 头像
+			}
+		},
+		onLoad(options) {
+			this.initPage(options.type)
+			this.getList()
+		},
+		onPullDownRefresh() {
+			this.page=1
+			this.finished=false
+			this.list=[]
+			this.getList()
+			setTimeout(()=>{
+				uni.stopPullDownRefresh()
+			},1500)
+		},
+		onReachBottom() {
+			if(this.finished) return
+			this.page++
+			this.getList()
+		},
+		methods: {
+			handleGoDetail(e){
+				if(this.typeName==='custome'){
+					uni.navigateTo({
+						url:'/pages-approve/custome/detail?id='+e.CompanyApprovalId
+					})
+				}
+				
+				if(this.typeName==='contract'){
+					uni.navigateTo({
+						url:`/pages-approve/contract/detail?ContractApprovalId=0&ContractId=0&ContractApprovalRecordId=${e.CompanyApprovalId}`
+					})
+				}
+				
+				if(this.typeName==='seal'){
+					uni.navigateTo({
+						url:`/pages-approve/seal/detail?ContractApprovalId=0&ContractApprovalRecordId=${e.CompanyApprovalId}`
+					})
+				}
+			},
+			
+			//获取列表数据
+			async getList() {
+				const res = await apiMessageList({
+					SourceType: this.type,
+					CurrentIndex: this.page
+				})
+				if (res.code === 200) {
+					if(!res.data.List){
+						this.finished=true
+					}else{
+						let arr=res.data.List.map(item=>{
+							let statusImg='',statusName='';
+							switch(item.ApprovalStatus){
+								case 1:
+									statusImg=require('../static/icon-2.png')
+									statusName='待审批'
+									break;
+								case 2:
+									statusImg=require('../static/icon-3.png')
+									statusName='已审批'
+									break;
+								case 3:
+									statusImg=require('../static/icon-4.png')
+									statusName='已驳回'
+									break
+							}
+							return {
+								statusImg:statusImg,
+								statusName:statusName,
+								...item
+							}
+						})
+						this.list = [...this.list, ...arr]
+					}
+					
+				}
+			},
+
+			// 初始化界面
+			initPage(type) {
+				this.typeName=type
+				let navBarTitle = ''
+				const _this = this
+				switch (type) {
+					case 'custome':
+						navBarTitle = '客户待审批'
+						_this.type = 1
+						_this.avatar=require('../../static/icon-1.png')
+						break;
+					case 'contract':
+						navBarTitle = '合同待审批'
+						_this.type = 2
+						_this.avatar=require('../../static/icon-2.png')
+						break;
+					case 'seal':
+						navBarTitle = '用印待审批'
+						_this.type = 3
+						_this.avatar=require('../../static/icon-3.png')
+						break;
+				}
+				uni.setNavigationBarTitle({
+					title: navBarTitle
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.message-list-page {
+		padding: 20rpx;
+	}
+	
+	.top-time-box{
+		margin-left: auto;
+		margin-right: auto;
+		width: 200rpx;
+		height: 50rpx;
+		line-height: 50rpx;
+		font-size: 12px;
+		text-align: center;
+		border-radius: 25rpx;
+	}
+	.list{
+		padding: 0 36rpx 0 19rpx;
+		.message-item{
+			margin-top: 60rpx;
+		}
+		.message-content{
+			margin-left: 20rpx;
+			flex: 1;
+		}
+		.message-card{
+			margin-top: 20rpx;
+			padding: 20rpx;
+			border: 1px solid #EFEFEF;
+			box-shadow: 0px 3rpx 12rpx rgba(175, 175, 175, 0.08);
+			border-radius: 6px;
+			position: relative;
+			.type{
+				color: #FFB005;
+				font-weight: bold;
+				&::before{
+					content: '';
+					display: inline-block;
+					width: 28rpx;
+					height: 28rpx;
+					background-image: url(../static/icon-1.png);
+					background-size: cover;
+					margin-right: 20rpx;
+					position: relative;
+					top: 4rpx;
+				}
+			}
+			.title{
+				font-size: 16px;
+				font-weight: bold;
+				margin-top: 20rpx;
+			}
+			.info{
+				margin-top: 20rpx;
+			}
+			.status-img{
+				position: absolute;
+				top: 0;
+				right: 0;
+				width: 145rpx;
+				height: 145rpx;
+			}
+		}
+	}
+	
+</style>

BIN
pages-todomessages/static/icon-1.png


BIN
pages-todomessages/static/icon-2.png


BIN
pages-todomessages/static/icon-3.png


BIN
pages-todomessages/static/icon-4.png


+ 175 - 0
pages.json

@@ -0,0 +1,175 @@
+{
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "待办",
+				"enablePullDownRefresh": true
+			}
+		},
+		{
+			"path": "pages/workbench/index",
+			"style": {
+				"navigationBarTitleText": "工作台"
+			}
+		},
+		{
+			"path": "pages/mine/index",
+			"style": {
+				"navigationBarTitleText": "我的"
+			}
+		}, {
+			"path": "pages/login/login"
+		}
+	],
+	"subPackages": [{
+			"root": "pages-todomessages", // 待办消息列表包
+			"pages": [{
+				"path": "list/list",// 参数:type类型: 客户(custome)、合同(contract)、用印(seal)
+				"style":{
+					"enablePullDownRefresh":true
+				}
+			}]
+		},
+		{
+			"root": "pages-approve", //审批模块分包
+			"pages": [
+				{
+					"path": "search/index" //审批模块搜索 参数 type类型: 客户(custome)、合同(contract)、用印(seal)
+				}, 
+				{
+					"path":"search/result", //搜索结果 参数 type类型: 客户(custome)、合同(contract)、用印(seal)  val 搜索关键字
+					"style":{
+						"navigationBarTitleText":"搜索结果"
+					}
+				},
+				{
+					"path": "custome/list",
+					"style": {
+						"navigationBarTitleText": "客户审批",
+						"enablePullDownRefresh":true
+					}
+				}, 
+				{
+					"path": "custome/detail",// 参数id:审批单id
+					"style": {
+						"enablePullDownRefresh": true,
+						"navigationBarTitleText": "客户审批"
+					}
+				},
+				{
+					"path":"custome/reason",// 参数CompanyApprovalId(审批单id)、CompanyContractId(合同id)、CompanyId(客户id)
+					"style":{
+						"navigationBarTitleText":"驳回理由"
+					}
+				},
+				{
+					"path": "contract/list",
+					"style": {
+						"navigationBarTitleText": "合同审批",
+						"enablePullDownRefresh":true
+					}
+				},
+				{
+					"path":"contract/detail",// 
+					"style":{
+						"navigationBarTitleText": "合同审批",
+						"enablePullDownRefresh":true
+					}
+				},
+				{
+					"path":"contract/reason",// 参数ApprovalId(审批单id)
+					"style":{
+						"navigationBarTitleText":"驳回理由"
+					}
+				},
+				{
+					"path":"contract/search",// 参数 val 搜索关键字
+					"style":{
+						"navigationBarTitleText":"搜索结果"
+					}
+				},
+				{
+                    "path" : "seal/list",
+                    "style" :{
+						"navigationBarTitleText": "用印审批",
+						"enablePullDownRefresh": true
+					}
+                },
+				{
+					"path":"seal/detail", 
+					"style":{
+						"navigationBarTitleText":"用印审批",
+						"enablePullDownRefresh":true
+					}
+				},
+				{
+					"path":"seal/edit",
+					"style":{
+						"navigationBarTitleText":"用印编辑",
+						"enablePullDownRefresh":true
+					}
+				},
+				{
+					"path":"seal/addSeal",
+					"style":{
+						"navigationBarTitleText":"用印申请"
+					}
+				},
+				{
+					"path":"seal/reason",
+					"style":{
+						"navigationBarTitleText":"驳回理由"
+					}
+				}
+            ]
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "弘则移动CRM",
+		"navigationBarBackgroundColor": "#FFFFFF",
+		"usingComponents": {
+			"van-button": "/wxcomponents/vant/button/index",
+			"van-toast": "/wxcomponents/vant/toast/index",
+			"van-popup": "/wxcomponents/vant/popup/index",
+			"van-tag": "/wxcomponents/vant/tag/index",
+			"van-search": "/wxcomponents/vant/search/index",
+			"van-tab": "/wxcomponents/vant/tab/index",
+			"van-tabs": "/wxcomponents/vant/tabs/index",
+			"van-sticky": "/wxcomponents/vant/sticky/index",
+			"van-field": "/wxcomponents/vant/field/index",
+			"van-dialog": "/wxcomponents/vant/dialog/index",
+			"van-empty": "/wxcomponents/vant/empty/index",
+			"van-picker": "/wxcomponents/vant/picker/index",
+			"van-radio": "/wxcomponents/vant/radio/index",
+			"van-radio-group": "/wxcomponents/vant/radio-group/index"
+		}
+	},
+	"tabBar": {
+		"color":"#333333",
+		"selectedColor":"#3385FF",
+		"list": [{
+				"pagePath": "pages/index/index",
+				"text": "消息",
+				"iconPath":"static/tabbar-icon-index.png",
+				"selectedIconPath":"static/tabbar-icon-index-s.png"
+			},
+			{
+				"pagePath": "pages/workbench/index",
+				"text": "工作台",
+				"iconPath":"static/tabbar-icon-workbench.png",
+				"selectedIconPath":"static/tabbar-icon-workbench-s.png"
+			},
+			{
+				"pagePath": "pages/mine/index",
+				"text": "我的",
+				"iconPath":"static/tabbar-icon-mine.png",
+				"selectedIconPath":"static/tabbar-icon-mine-s.png"
+			}
+		]
+	},
+	"easycom": {
+		"autoscan": true
+	}
+}

+ 76 - 0
pages/index/index.vue

@@ -0,0 +1,76 @@
+<template>
+	<view class="home-page">
+		<view class="message-box flex white-wrap" @click="handleGONext('custome')" v-if="custome.Total>0">
+			<image class="icon" src="../../static/icon-1.png" mode="aspectFill"></image>
+			<text style="flex: 1;margin-right: 5px;" class="van-ellipsis">{{custome.Message}}</text>
+			<van-tag round type="danger" color="#FF4444">{{custome.Total}}</van-tag>
+		</view>
+		<view class="message-box flex white-wrap" @click="handleGONext('contract')" v-if="contract.Total>0">
+			<image class="icon" src="../../static/icon-2.png" mode="aspectFill"></image>
+			<text style="flex: 1;margin-right: 5px;" class="van-ellipsis">{{contract.Message}}</text>
+			<van-tag round type="danger" color="#FF4444">{{contract.Total}}</van-tag>
+		</view>
+		<view class="message-box flex white-wrap" @click="handleGONext('seal')" v-if="seal.Total>0">
+			<image class="icon" src="../../static/icon-3.png" mode="aspectFill"></image>
+			<text style="flex: 1;margin-right: 5px;" class="van-ellipsis">{{seal.Message}}</text>
+			<van-tag round type="danger" color="#FF4444">{{seal.Total}}</van-tag>
+		</view>
+	</view>
+	
+</template>
+
+<script>
+	import {apiMessageCount} from '@/api/message.js'
+	export default {
+		data() {
+			return {
+				custome:null,
+				contract:null,
+				seal:null
+			}
+		},
+		onShow() {
+			this.getMessage()
+		},
+		onPullDownRefresh() {
+			this.getMessage()
+			setTimeout(()=>{
+				uni.stopPullDownRefresh()
+			}, 500);
+		},
+		
+		methods: {
+			// 跳转消息列表
+			handleGONext(e){
+				uni.navigateTo({
+					url:'../../pages-todomessages/list/list?type='+e
+				})
+			},
+			
+			//获取消息数据
+			async getMessage(){
+				const res=await apiMessageCount()
+				if(res.code===200){
+					this.custome=res.data.CompanyMessage
+					this.contract=res.data.ContractMessage
+					this.seal=res.data.SealMessage
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.message-box{
+		padding: 20rpx 34rpx;
+		align-items: center;
+		border-bottom: 1rpx solid #f5f5f5;
+		font-size: 16px;
+		.icon{
+			width: 102rpx;
+			height: 102rpx;
+			margin-right: 34rpx;
+			flex-shrink: 0;
+		}
+	}
+</style>

+ 73 - 0
pages/login/login.vue

@@ -0,0 +1,73 @@
+<template>
+	<view class="login-page white-wrap">
+		<view class="title">您好,<br />欢迎使用弘则移动CRM!</view>
+		<view class="input-wrap">
+			<van-field :value="username" @change="inputChange('username',$event)" type="text" placeholder="请输入管理后台账号" custom-style="margin-bottom: 20px;" />
+			<van-field :value="password" @change="inputChange('password',$event)" password placeholder="请输入密码" />
+		</view>
+		<van-button custom-class="login-btn" block color="#3385FF" round @click="handleLogin">登录</van-button>
+	</view>
+</template>
+
+<script>
+	import {apiLogin} from '@/api/user.js'
+	export default {
+		data() {
+			return {
+				username: "",
+				password: "hongze123"
+			}
+		},
+		onLoad() {
+			this.$store.dispatch('WXLogin')
+		},
+		methods: {
+			inputChange(type,event){
+				this[type]=event.detail
+			},
+			
+			// 登录
+			async handleLogin(){
+				if(!this.username){
+					uni.showToast({
+						title:'请输入账号',
+						icon:'none'
+					})
+					return
+				}
+				if(!this.password){
+					uni.showToast({
+						title:'请输入密码',
+						icon:'none'
+					})
+					return
+				}
+				const res=await apiLogin({Username:this.username,Password:this.password})
+				if(res.code===200){
+					this.$store.commit('addUserData',res.data)
+					uni.navigateBack({
+						delta:1 
+					})
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.login-page {
+		width: 100%;
+		height: 100%;
+		padding: 120rpx 32rpx 0 32rpx;
+
+		.title {
+			font-size: 26px;
+			font-weight: bold;
+			margin-bottom: 150rpx;
+		}
+
+		.login-btn {
+			margin-top: 90rpx;
+		}
+	}
+</style>

+ 113 - 0
pages/mine/index.vue

@@ -0,0 +1,113 @@
+<template>
+	<view class="mine-page white-wrap">
+		<view class="top-box">
+			<view class="avatar">
+				<open-data class="avatar" style="width: 100%;height: 100%;" type="userAvatarUrl"></open-data>
+			</view>
+			
+			<!-- <image class="avatar" src="../../static/icon-1.png" mode="aspectFill"></image> -->
+			<text>{{userInfo.RealName}}</text>
+		</view>
+		
+		<view class="base-info-wrap white-wrap">
+			<view style="font-size: 16px;font-weight: bold;">基础信息</view>
+			<view class="info-list">
+				<view class="info-item">
+					<text class="label">公司名称</text>
+					<text>弘则弥道(上海)投资咨询有限公司</text>
+				</view>
+				<view class="info-item">
+					<text class="label">职位</text>
+					<text>{{userInfo.RoleName}}</text>
+				</view>
+			</view>
+		</view>
+		<view class="loginout-btn">
+			<van-button type="info" custom-class="btn" round  @click="handleLoginOut">退出登录</van-button>
+		</view>
+		
+		<van-dialog id="van-dialog" />
+	</view>
+</template>
+
+<script>
+	export default{
+		onShow() {
+			if(!this.$store.state.token||!this.$store.state.userInfo.ProductName){
+				uni.navigateTo({
+					url:'/pages/login/login'
+				})
+			}
+		},
+		computed: {
+			userInfo() {
+				return this.$store.state.userInfo 
+			}
+		},
+		methods: {
+			handleLoginOut() {
+				this.$dialog.confirm({
+					title: '提示',
+					 message: '是否确认退出',
+				}).then(()=>{
+					this.$store.dispatch('loginOut')
+				}).catch(() => {
+					console.log('取消退出登录');
+				});
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.mine-page{
+		width: 100%;
+		min-height: 100vh;
+	}
+	.top-box{
+		background-color: #FDF9F8;
+		text-align: center;
+		height: 400rpx;
+		padding-top: 60rpx;
+		font-size: 16px;
+		font-weight: bold;
+		line-height: 2;
+		.avatar{
+			display: block;
+			margin-left: auto;
+			margin-right: auto;
+			width: 178rpx;
+			height: 178rpx;
+			border-radius: 50%;
+			overflow: hidden;
+		}
+	}
+	.base-info-wrap{
+		box-shadow: 0 -3rpx 0rpx rgba(212, 34, 34, 0.05);
+		border-radius: 32rpx 32rpx 0 0;
+		margin-top: -20rpx;
+		min-height: 200rpx;
+		padding: 60rpx 34rpx;
+	}
+	
+	.info-list{
+		margin-top: 40rpx;
+		.info-item{
+			margin-bottom: 30rpx;
+			color: #666;
+			.label{
+				width: 200rpx;
+				display: inline-block;
+			}
+		}
+	}
+	
+	.loginout-btn{
+		margin-top: 200rpx;
+		text-align: center;
+		.btn{
+			width: 300rpx;
+			height: 56rpx;
+		}
+	}
+</style>

+ 68 - 0
pages/workbench/index.vue

@@ -0,0 +1,68 @@
+<template>
+	<view class="workbench-page">
+		<view class="section white-wrap" @click="handleGoNext('custome')">
+			<image src="../../static/icon-1.png" mode="aspectFill" class="icon"></image>
+			<view class="label">客户审批</view>
+		</view>
+		<view class="section white-wrap" @click="handleGoNext('contract')">
+			<image src="../../static/icon-2.png" mode="aspectFill" class="icon"></image>
+			<view class="label">合同审批</view>
+		</view>
+		<view class="section white-wrap" @click="handleGoNext('seal')">
+			<image src="../../static/icon-3.png" mode="aspectFill" class="icon"></image>
+			<view class="label">用印审批</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default{
+		data() {
+			return {
+				
+			}
+		},
+		methods: {
+			handleGoNext(type) {
+				if(type==='custome'){
+					uni.navigateTo({
+						url:"/pages-approve/custome/list"
+					})
+				}
+				
+				if(type==='contract'){
+					uni.navigateTo({
+						url:"/pages-approve/contract/list"
+					})
+				}
+				
+				if(type==='seal'){
+					uni.navigateTo({
+						url:"/pages-approve/seal/list"
+					})
+				}
+			}
+		},
+	}
+</script>
+
+<style lang="scss">
+	.workbench-page{
+		padding: 20rpx;
+	}
+	.section{
+		padding: 50rpx 0;
+		text-align: center;
+		font-size: 16px;
+		font-weight: 400;
+		color: #666;
+		box-shadow: 0px 3px 12px rgba(175, 175, 175, 0.16);
+		border-radius: 8px;
+		margin-bottom: 20rpx;
+		.icon{
+			width: 128rpx;
+			height: 128rpx;
+			margin-bottom: 14rpx;
+		}
+	}
+</style>

BIN
static/empty.png


BIN
static/icon-1.png


BIN
static/icon-2.png


BIN
static/icon-3.png


BIN
static/man.png


BIN
static/tabbar-icon-index-s.png


BIN
static/tabbar-icon-index.png


BIN
static/tabbar-icon-mine-s.png


BIN
static/tabbar-icon-mine.png


BIN
static/tabbar-icon-workbench-s.png


BIN
static/tabbar-icon-workbench.png


+ 62 - 0
store/index.js

@@ -0,0 +1,62 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import {apiWXLogin,apiLoginOut} from '@/api/user.js'
+import uniAsync from '@/utils/uni-async.js'
+Vue.use(Vuex);//vue的插件机制
+const token=uni.getStorageSync('token')||''
+let userInfo={}
+if(uni.getStorageSync('userInfo')){
+	userInfo=JSON.parse(uni.getStorageSync('userInfo'))
+}
+//Vuex.Store 构造器选项
+const store = new Vuex.Store({
+    state:{//存放状态
+        token:token,
+		userInfo:userInfo,
+    },
+	mutations:{
+		// 添加token
+		addToken(state,data){
+			state.token=data
+			uni.setStorageSync('token',data)
+		},
+		// 添加用户信息
+		addUserData(state,data){
+			state.userInfo=data
+			uni.setStorageSync('userInfo',JSON.stringify(data))
+		},
+		// 清空用户信息和token 
+		removeUser(state){
+			state.token='',
+			state.userInfo={}
+			uni.navigateTo({
+				url:'/pages/login/login'
+			})
+		}
+	},
+	actions:{
+		// 微信登录
+		async WXLogin(context){
+			
+			let wxLoginRes=await uniAsync.login({
+				provider:'weixin'
+			})
+			
+			const res=await apiWXLogin({Code:wxLoginRes.code})
+			if(res.code===200){
+				context.commit('addToken',res.data.Authorization)
+				context.commit('addUserData',res.data.UserInfo)
+			}
+		},
+		
+		// 退出登录
+		async loginOut(context){
+			const res=await apiLoginOut()
+			if(res.code===200){
+				uni.clearStorageSync()
+				context.commit('removeUser')
+			}
+		}
+	}
+})
+export default store

+ 83 - 0
uni.scss

@@ -0,0 +1,83 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:24rpx;
+$uni-font-size-base:28rpx;
+$uni-font-size-lg:32rpx;
+
+/* 图片尺寸 */
+$uni-img-size-sm:40rpx;
+$uni-img-size-base:52rpx;
+$uni-img-size-lg:80rpx;
+
+/* Border Radius */
+$uni-border-radius-sm: 4rpx;
+$uni-border-radius-base: 6rpx;
+$uni-border-radius-lg: 12rpx;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 10px;
+$uni-spacing-row-base: 20rpx;
+$uni-spacing-row-lg: 30rpx;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 8rpx;
+$uni-spacing-col-base: 16rpx;
+$uni-spacing-col-lg: 24rpx;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:40rpx;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:36rpx;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:30rpx;
+
+/* 审批状态文字颜色 */
+$approve-status-wait:#3385FF;//待审批颜色
+$approve-status-success:#63D594;//审批通过颜色
+$approve-status-fail:#FF4444;//审批驳回颜色
+$approve-status-back:#999999;//审批驳回颜色
+$approve-status-cancel:#999999;//审批驳回颜色

+ 4 - 0
utils/config.js

@@ -0,0 +1,4 @@
+// 配置文件
+
+// 请求根路径
+export const baseUrl='http://8.136.199.33:8607/h5adminapi'

+ 82 - 0
utils/request.js

@@ -0,0 +1,82 @@
+import {baseUrl} from './config.js'
+import store from '@/store/index.js'
+// 请求错误消息提示
+const showError=error=>{
+	let errMsg=''
+	switch(error.code){
+		case 400:
+			errMsg=error.msg;
+			break;
+		default:
+			errMsg=error.msg;
+			break;
+	}
+	uni.showToast({
+		title:errMsg,
+		icon:'none',
+		duration:1000
+	})
+}
+
+// 请求数
+let LOADINGCOUNT = 0;
+const http=(url,params,method)=>{
+	// 设置loading
+	if (LOADINGCOUNT === 0) {
+	  uni.showLoading({
+	  	title:'加载中...'
+	  })
+	}
+	LOADINGCOUNT++;
+	
+	
+	return new Promise((resolve,reject)=>{
+		uni.request({
+			url:baseUrl+url,
+			data:params,
+			method:method,
+			header:{
+				Authorization:store.state.token,
+			},
+			success(res) {
+				if(res.data.code!==200){
+					setTimeout(()=>{showError(res.data)},0)//解决 hideloading 冲突问题
+				}
+				//401 代表token异常,用户需要重新静默授权,获取最新的token
+				//403 用户需要进行绑定操作,需要跳转到输入账号密码绑定页面用户需要进行绑定操作
+				if(res.data.code===401||res.data.code===403){
+					uni.navigateTo({
+						url:"/pages/login/login"
+					})
+				}
+				
+				
+				resolve(res.data)
+			},
+			fail(error) {
+				console.log(error);
+				uni.showToast({
+					title:'服务器错误',
+					icon:'none'
+				})
+			},
+			complete() {
+				// 关闭loading
+				LOADINGCOUNT--;
+				if (LOADINGCOUNT === 0) {
+				  uni.hideLoading()
+				}
+			}
+		})
+	})
+}
+
+// get 请求
+export const httpGet=(url,params)=>{
+	return http(url,params,'GET')
+}
+
+// post 请求
+export const httpPost=(url,params)=>{
+	return http(url,params,'POST')
+}

+ 19 - 0
utils/uni-async.js

@@ -0,0 +1,19 @@
+
+// 使用proxy转换为异步化的uni方法
+const uniAsync = new Proxy({}, {
+	get(target, name) {
+		return (obj) => new Promise((resolve, reject) => {
+			uni[name]({
+				...obj,
+				success: ret => {
+					resolve(ret)
+				},
+				fail: err => {
+					reject(err)
+				}
+			})
+		})
+	}
+})
+
+export default uniAsync

+ 91 - 0
utils/uploadFile.js

@@ -0,0 +1,91 @@
+/**
+ * 上传文件公共方法
+ */
+import uniAsync from '@/utils/uni-async.js'// uni api async 化
+import {baseUrl} from './config.js'
+import store from '@/store/index.js'
+/**
+ * 上传图片
+ * count 同时上传张数
+ */
+export const uploadImg=async (count)=>{
+	const {tempFilePaths}=await uniAsync.chooseImage({count})
+	uni.showLoading({
+		title:'上传中...'
+	})
+	const uploadResArr=tempFilePaths.map(item=>{
+		return uploadToServer(item)
+	})
+	return new Promise((resolve,reject)=>{
+		Promise.all(uploadResArr).then(res=>{
+			uni.hideLoading()
+			const arr=res.map(item=>{
+				return item
+			})
+			resolve(arr)
+		}).catch(res=>{
+			uni.hideLoading()
+		})
+	})
+}
+
+/**
+ * 上传文件
+ * count 同时上传文件数
+ * type 能选择文件类型
+ */
+export const uploadFiles=async ({count=1,type='all'})=>{
+	const tempFilePaths=await asyncChooseFiles({count,type})
+	uni.showLoading({
+		title:'上传中...'
+	})
+	const uploadResArr=tempFilePaths.map(item=>{
+		return uploadToServer(item.path)
+	})
+	return new Promise((resolve,reject)=>{
+		Promise.all(uploadResArr).then(res=>{
+			uni.hideLoading()
+			const arr=res.map(item=>{
+				return item
+			})
+			resolve(arr)
+		}).catch(res=>{
+			uni.hideLoading()
+		})
+	})
+}
+
+/**
+ * 选择文件
+ */
+const asyncChooseFiles=({count,type})=>{
+	return new Promise((resolve,reject)=>{
+		wx.chooseMessageFile({
+			count:count,
+			type:type,
+			success:(res)=>{
+				resolve(res.tempFiles)
+			}
+		})
+	})
+}
+
+
+
+/**
+ * 上传文件到服务器
+ */
+const uploadToServer=async (path)=>{
+	const temres=await uniAsync.uploadFile({
+		url:baseUrl+'/resource/upload',
+		filePath:path,
+		name:'file',
+		header:{
+			Authorization:store.state.token
+		}
+	})
+	const res=JSON.parse(temres.data)
+	if(res.code===200){
+		return res.data.ResourceUrl
+	}
+}

+ 11 - 0
utils/vantRegister.js

@@ -0,0 +1,11 @@
+/* —————————————— Vant 组件注册 (需使用this方法的特殊组件): uniapp版 ———————————————— */
+
+import Vue from 'vue'
+
+import Dialog from '../wxcomponents/vant/dialog/dialog';
+import Toast from '../wxcomponents/vant/toast/toast';
+import Notify from '../wxcomponents/vant/notify/notify';
+
+Vue.prototype.$dialog = Dialog;
+Vue.prototype.$toast = Toast;
+Vue.prototype.$notify = Notify;

+ 1 - 0
wxcomponents/vant/action-sheet/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 70 - 0
wxcomponents/vant/action-sheet/index.js

@@ -0,0 +1,70 @@
+import { VantComponent } from '../common/component';
+import { button } from '../mixins/button';
+VantComponent({
+  mixins: [button],
+  props: {
+    show: Boolean,
+    title: String,
+    cancelText: String,
+    description: String,
+    round: {
+      type: Boolean,
+      value: true,
+    },
+    zIndex: {
+      type: Number,
+      value: 100,
+    },
+    actions: {
+      type: Array,
+      value: [],
+    },
+    overlay: {
+      type: Boolean,
+      value: true,
+    },
+    closeOnClickOverlay: {
+      type: Boolean,
+      value: true,
+    },
+    closeOnClickAction: {
+      type: Boolean,
+      value: true,
+    },
+    safeAreaInsetBottom: {
+      type: Boolean,
+      value: true,
+    },
+  },
+  methods: {
+    onSelect(event) {
+      const { index } = event.currentTarget.dataset;
+      const { actions, closeOnClickAction, canIUseGetUserProfile } = this.data;
+      const item = actions[index];
+      if (item) {
+        this.$emit('select', item);
+        if (closeOnClickAction) {
+          this.onClose();
+        }
+        if (item.openType === 'getUserInfo' && canIUseGetUserProfile) {
+          wx.getUserProfile({
+            desc: item.getUserProfileDesc || '  ',
+            complete: (userProfile) => {
+              this.$emit('getuserinfo', userProfile);
+            },
+          });
+        }
+      }
+    },
+    onCancel() {
+      this.$emit('cancel');
+    },
+    onClose() {
+      this.$emit('close');
+    },
+    onClickOverlay() {
+      this.$emit('click-overlay');
+      this.onClose();
+    },
+  },
+});

+ 8 - 0
wxcomponents/vant/action-sheet/index.json

@@ -0,0 +1,8 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-icon": "../icon/index",
+    "van-popup": "../popup/index",
+    "van-loading": "../loading/index"
+  }
+}

+ 69 - 0
wxcomponents/vant/action-sheet/index.wxml

@@ -0,0 +1,69 @@
+<wxs src="../wxs/utils.wxs" module="utils" />
+
+<van-popup
+  show="{{ show }}"
+  position="bottom"
+  round="{{ round }}"
+  z-index="{{ zIndex }}"
+  overlay="{{ overlay }}"
+  custom-class="van-action-sheet"
+  safe-area-inset-bottom="{{ safeAreaInsetBottom }}"
+  close-on-click-overlay="{{ closeOnClickOverlay }}"
+  bind:close="onClickOverlay"
+>
+  <view wx:if="{{ title }}" class="van-action-sheet__header">
+    {{ title }}
+    <van-icon
+      name="cross"
+      custom-class="van-action-sheet__close"
+      bind:click="onClose"
+    />
+  </view>
+  <view wx:if="{{ description }}" class="van-action-sheet__description van-hairline--bottom">
+    {{ description }}
+  </view>
+  <view wx:if="{{ actions && actions.length }}">
+    <!-- button外包一层view,防止actions动态变化,导致渲染时button被打散 -->
+    <button
+      wx:for="{{ actions }}"
+      wx:key="index"
+      open-type="{{ item.disabled || item.loading || (canIUseGetUserProfile && item.openType === 'getUserInfo') ? '' : item.openType }}"
+      style="{{ item.color ? 'color: ' + item.color : '' }}"
+      class="{{ utils.bem('action-sheet__item', { disabled: item.disabled || item.loading }) }} {{ item.className || '' }}"
+      hover-class="van-action-sheet__item--hover"
+      data-index="{{ index }}"
+      bindtap="{{ item.disabled || item.loading ? '' : 'onSelect' }}"
+      bindgetuserinfo="onGetUserInfo"
+      bindcontact="onContact"
+      bindgetphonenumber="onGetPhoneNumber"
+      binderror="onError"
+      bindlaunchapp="onLaunchApp"
+      bindopensetting="onOpenSetting"
+      lang="{{ lang }}"
+      session-from="{{ sessionFrom }}"
+      send-message-title="{{ sendMessageTitle }}"
+      send-message-path="{{ sendMessagePath }}"
+      send-message-img="{{ sendMessageImg }}"
+      show-message-card="{{ showMessageCard }}"
+      app-parameter="{{ appParameter }}"
+    >
+      <block wx:if="{{ !item.loading }}">
+        {{ item.name }}
+        <view wx:if="{{ item.subname }}" class="van-action-sheet__subname" >{{ item.subname }}</view>
+      </block>
+      <van-loading wx:else custom-class="van-action-sheet__loading" size="22px" />
+    </button>
+  </view>
+  <slot />
+  <block wx:if="{{ cancelText }}">
+    <view class="van-action-sheet__gap" />
+    <view
+      class="van-action-sheet__cancel"
+      hover-class="van-action-sheet__cancel--hover"
+      hover-stay-time="70"
+      bind:tap="onCancel"
+    >
+      {{ cancelText }}
+    </view>
+  </block>
+</van-popup>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
wxcomponents/vant/action-sheet/index.wxss


+ 1 - 0
wxcomponents/vant/area/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 235 - 0
wxcomponents/vant/area/index.js

@@ -0,0 +1,235 @@
+import { VantComponent } from '../common/component';
+import { pickerProps } from '../picker/shared';
+import { requestAnimationFrame } from '../common/utils';
+const EMPTY_CODE = '000000';
+VantComponent({
+  classes: ['active-class', 'toolbar-class', 'column-class'],
+  props: Object.assign(Object.assign({}, pickerProps), {
+    value: {
+      type: String,
+      observer(value) {
+        this.code = value;
+        this.setValues();
+      },
+    },
+    areaList: {
+      type: Object,
+      value: {},
+      observer: 'setValues',
+    },
+    columnsNum: {
+      type: null,
+      value: 3,
+    },
+    columnsPlaceholder: {
+      type: Array,
+      observer(val) {
+        this.setData({
+          typeToColumnsPlaceholder: {
+            province: val[0] || '',
+            city: val[1] || '',
+            county: val[2] || '',
+          },
+        });
+      },
+    },
+  }),
+  data: {
+    columns: [{ values: [] }, { values: [] }, { values: [] }],
+    typeToColumnsPlaceholder: {},
+  },
+  mounted() {
+    requestAnimationFrame(() => {
+      this.setValues();
+    });
+  },
+  methods: {
+    getPicker() {
+      if (this.picker == null) {
+        this.picker = this.selectComponent('.van-area__picker');
+      }
+      return this.picker;
+    },
+    onCancel(event) {
+      this.emit('cancel', event.detail);
+    },
+    onConfirm(event) {
+      const { index } = event.detail;
+      let { value } = event.detail;
+      value = this.parseValues(value);
+      this.emit('confirm', { value, index });
+    },
+    emit(type, detail) {
+      detail.values = detail.value;
+      delete detail.value;
+      this.$emit(type, detail);
+    },
+    parseValues(values) {
+      const { columnsPlaceholder } = this.data;
+      return values.map((value, index) => {
+        if (
+          value &&
+          (!value.code || value.name === columnsPlaceholder[index])
+        ) {
+          return Object.assign(Object.assign({}, value), {
+            code: '',
+            name: '',
+          });
+        }
+        return value;
+      });
+    },
+    onChange(event) {
+      var _a;
+      const { index, picker, value } = event.detail;
+      this.code = value[index].code;
+      (_a = this.setValues()) === null || _a === void 0
+        ? void 0
+        : _a.then(() => {
+            this.$emit('change', {
+              picker,
+              values: this.parseValues(picker.getValues()),
+              index,
+            });
+          });
+    },
+    getConfig(type) {
+      const { areaList } = this.data;
+      return (areaList && areaList[`${type}_list`]) || {};
+    },
+    getList(type, code) {
+      if (type !== 'province' && !code) {
+        return [];
+      }
+      const { typeToColumnsPlaceholder } = this.data;
+      const list = this.getConfig(type);
+      let result = Object.keys(list).map((code) => ({
+        code,
+        name: list[code],
+      }));
+      if (code != null) {
+        // oversea code
+        if (code[0] === '9' && type === 'city') {
+          code = '9';
+        }
+        result = result.filter((item) => item.code.indexOf(code) === 0);
+      }
+      if (typeToColumnsPlaceholder[type] && result.length) {
+        // set columns placeholder
+        const codeFill =
+          type === 'province'
+            ? ''
+            : type === 'city'
+            ? EMPTY_CODE.slice(2, 4)
+            : EMPTY_CODE.slice(4, 6);
+        result.unshift({
+          code: `${code}${codeFill}`,
+          name: typeToColumnsPlaceholder[type],
+        });
+      }
+      return result;
+    },
+    getIndex(type, code) {
+      let compareNum = type === 'province' ? 2 : type === 'city' ? 4 : 6;
+      const list = this.getList(type, code.slice(0, compareNum - 2));
+      // oversea code
+      if (code[0] === '9' && type === 'province') {
+        compareNum = 1;
+      }
+      code = code.slice(0, compareNum);
+      for (let i = 0; i < list.length; i++) {
+        if (list[i].code.slice(0, compareNum) === code) {
+          return i;
+        }
+      }
+      return 0;
+    },
+    setValues() {
+      const picker = this.getPicker();
+      if (!picker) {
+        return;
+      }
+      let code = this.code || this.getDefaultCode();
+      const provinceList = this.getList('province');
+      const cityList = this.getList('city', code.slice(0, 2));
+      const stack = [];
+      const indexes = [];
+      const { columnsNum } = this.data;
+      if (columnsNum >= 1) {
+        stack.push(picker.setColumnValues(0, provinceList, false));
+        indexes.push(this.getIndex('province', code));
+      }
+      if (columnsNum >= 2) {
+        stack.push(picker.setColumnValues(1, cityList, false));
+        indexes.push(this.getIndex('city', code));
+        if (cityList.length && code.slice(2, 4) === '00') {
+          [{ code }] = cityList;
+        }
+      }
+      if (columnsNum === 3) {
+        stack.push(
+          picker.setColumnValues(
+            2,
+            this.getList('county', code.slice(0, 4)),
+            false
+          )
+        );
+        indexes.push(this.getIndex('county', code));
+      }
+      return Promise.all(stack)
+        .catch(() => {})
+        .then(() => picker.setIndexes(indexes))
+        .catch(() => {});
+    },
+    getDefaultCode() {
+      const { columnsPlaceholder } = this.data;
+      if (columnsPlaceholder.length) {
+        return EMPTY_CODE;
+      }
+      const countyCodes = Object.keys(this.getConfig('county'));
+      if (countyCodes[0]) {
+        return countyCodes[0];
+      }
+      const cityCodes = Object.keys(this.getConfig('city'));
+      if (cityCodes[0]) {
+        return cityCodes[0];
+      }
+      return '';
+    },
+    getValues() {
+      const picker = this.getPicker();
+      if (!picker) {
+        return [];
+      }
+      return this.parseValues(picker.getValues().filter((value) => !!value));
+    },
+    getDetail() {
+      const values = this.getValues();
+      const area = {
+        code: '',
+        country: '',
+        province: '',
+        city: '',
+        county: '',
+      };
+      if (!values.length) {
+        return area;
+      }
+      const names = values.map((item) => item.name);
+      area.code = values[values.length - 1].code;
+      if (area.code[0] === '9') {
+        area.country = names[1] || '';
+        area.province = names[2] || '';
+      } else {
+        area.province = names[0] || '';
+        area.city = names[1] || '';
+        area.county = names[2] || '';
+      }
+      return area;
+    },
+    reset(code) {
+      this.code = code || '';
+      return this.setValues();
+    },
+  },
+});

+ 6 - 0
wxcomponents/vant/area/index.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-picker": "../picker/index"
+  }
+}

+ 20 - 0
wxcomponents/vant/area/index.wxml

@@ -0,0 +1,20 @@
+<wxs src="./index.wxs" module="computed" />
+
+<van-picker
+  class="van-area__picker"
+  active-class="active-class"
+  toolbar-class="toolbar-class"
+  column-class="column-class"
+  show-toolbar
+  value-key="name"
+  title="{{ title }}"
+  loading="{{ loading }}"
+  columns="{{ computed.displayColumns(columns, columnsNum) }}"
+  item-height="{{ itemHeight }}"
+  visible-item-count="{{ visibleItemCount }}"
+  cancel-button-text="{{ cancelButtonText }}"
+  confirm-button-text="{{ confirmButtonText }}"
+  bind:change="onChange"
+  bind:confirm="onConfirm"
+  bind:cancel="onCancel"
+/>

+ 8 - 0
wxcomponents/vant/area/index.wxs

@@ -0,0 +1,8 @@
+/* eslint-disable */
+function displayColumns(columns, columnsNum) {
+  return columns.slice(0, +columnsNum);
+}
+
+module.exports = {
+  displayColumns: displayColumns,
+};

+ 1 - 0
wxcomponents/vant/area/index.wxss

@@ -0,0 +1 @@
+@import '../common/index.wxss';

+ 1 - 0
wxcomponents/vant/button/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 69 - 0
wxcomponents/vant/button/index.js

@@ -0,0 +1,69 @@
+import { VantComponent } from '../common/component';
+import { button } from '../mixins/button';
+import { canIUseFormFieldButton } from '../common/version';
+const mixins = [button];
+if (canIUseFormFieldButton()) {
+  mixins.push('wx://form-field-button');
+}
+VantComponent({
+  mixins,
+  classes: ['hover-class', 'loading-class'],
+  data: {
+    baseStyle: '',
+  },
+  props: {
+    formType: String,
+    icon: String,
+    classPrefix: {
+      type: String,
+      value: 'van-icon',
+    },
+    plain: Boolean,
+    block: Boolean,
+    round: Boolean,
+    square: Boolean,
+    loading: Boolean,
+    hairline: Boolean,
+    disabled: Boolean,
+    loadingText: String,
+    customStyle: String,
+    loadingType: {
+      type: String,
+      value: 'circular',
+    },
+    type: {
+      type: String,
+      value: 'default',
+    },
+    dataset: null,
+    size: {
+      type: String,
+      value: 'normal',
+    },
+    loadingSize: {
+      type: String,
+      value: '20px',
+    },
+    color: String,
+  },
+  methods: {
+    onClick(event) {
+      this.$emit('click', event);
+      const {
+        canIUseGetUserProfile,
+        openType,
+        getUserProfileDesc,
+        lang,
+      } = this.data;
+      if (openType === 'getUserInfo' && canIUseGetUserProfile) {
+        wx.getUserProfile({
+          desc: getUserProfileDesc || '  ',
+          lang: lang || 'en',
+          complete: (userProfile) => {
+            this.$emit('getuserinfo', userProfile);
+          },
+        });
+      }
+    },
+  },
+});

+ 7 - 0
wxcomponents/vant/button/index.json

@@ -0,0 +1,7 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-icon": "../icon/index",
+    "van-loading": "../loading/index"
+  }
+}

+ 53 - 0
wxcomponents/vant/button/index.wxml

@@ -0,0 +1,53 @@
+<wxs src="../wxs/utils.wxs" module="utils" />
+<wxs src="./index.wxs" module="computed" />
+
+<button
+  id="{{ id }}"
+  data-detail="{{ dataset }}"
+  class="custom-class {{ utils.bem('button', [type, size, { block, round, plain, square, loading, disabled, hairline, unclickable: disabled || loading }]) }} {{ hairline ? 'van-hairline--surround' : '' }}"
+  hover-class="van-button--active hover-class"
+  lang="{{ lang }}"
+  form-type="{{ formType }}"
+  style="{{ computed.rootStyle({ plain, color, customStyle }) }}"
+  open-type="{{ disabled || loading || (canIUseGetUserProfile && openType === 'getUserInfo') ? '' : openType }}"
+  business-id="{{ businessId }}"
+  session-from="{{ sessionFrom }}"
+  send-message-title="{{ sendMessageTitle }}"
+  send-message-path="{{ sendMessagePath }}"
+  send-message-img="{{ sendMessageImg }}"
+  show-message-card="{{ showMessageCard }}"
+  app-parameter="{{ appParameter }}"
+  aria-label="{{ ariaLabel }}"
+  bindtap="{{ disabled || loading ? '' : 'onClick' }}"
+  bindgetuserinfo="onGetUserInfo"
+  bindcontact="onContact"
+  bindgetphonenumber="onGetPhoneNumber"
+  binderror="onError"
+  bindlaunchapp="onLaunchApp"
+  bindopensetting="onOpenSetting"
+>
+  <block wx:if="{{ loading }}">
+    <van-loading
+      custom-class="loading-class"
+      size="{{ loadingSize }}"
+      type="{{ loadingType }}"
+      color="{{ computed.loadingColor({ type, color, plain }) }}"
+    />
+    <view wx:if="{{ loadingText }}" class="van-button__loading-text">
+      {{ loadingText }}
+    </view>
+  </block>
+  <block wx:else>
+    <van-icon
+      wx:if="{{ icon }}"
+      size="1.2em"
+      name="{{ icon }}"
+      class-prefix="{{ classPrefix }}"
+      class="van-button__icon"
+      custom-style="line-height: inherit;"
+    />
+    <view class="van-button__text">
+      <slot />
+    </view>
+  </block>
+</button>

+ 39 - 0
wxcomponents/vant/button/index.wxs

@@ -0,0 +1,39 @@
+/* eslint-disable */
+var style = require('../wxs/style.wxs');
+
+function rootStyle(data) {
+  if (!data.color) {
+    return data.customStyle;
+  }
+
+  var properties = {
+    color: data.plain ? data.color : '#fff',
+    background: data.plain ? null : data.color,
+  };
+
+  // hide border when color is linear-gradient
+  if (data.color.indexOf('gradient') !== -1) {
+    properties.border = 0;
+  } else {
+    properties['border-color'] = data.color;
+  }
+
+  return style([properties, data.customStyle]);
+}
+
+function loadingColor(data) {
+  if (data.plain) {
+    return data.color ? data.color : '#c9c9c9';
+  }
+
+  if (data.type === 'default') {
+    return '#c9c9c9';
+  }
+
+  return '#fff';
+}
+
+module.exports = {
+  rootStyle: rootStyle,
+  loadingColor: loadingColor,
+};

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
wxcomponents/vant/button/index.wxss


+ 67 - 0
wxcomponents/vant/calendar/calendar.wxml

@@ -0,0 +1,67 @@
+<view class="van-calendar">
+  <header
+    title="{{ title }}"
+    showTitle="{{ showTitle }}"
+    subtitle="{{ subtitle }}"
+    showSubtitle="{{ showSubtitle }}"
+    firstDayOfWeek="{{ firstDayOfWeek }}"
+  >
+    <slot name="title" slot="title"></slot>
+  </header>
+
+  <scroll-view
+    class="van-calendar__body"
+    scroll-y
+    scroll-into-view="{{ scrollIntoView }}"
+  >
+    <month
+      wx:for="{{ computed.getMonths(minDate, maxDate) }}"
+      wx:key="index"
+      id="month{{ index }}"
+      class="month"
+      data-date="{{ item }}"
+      date="{{ item }}"
+      type="{{ type }}"
+      color="{{ color }}"
+      minDate="{{ minDate }}"
+      maxDate="{{ maxDate }}"
+      showMark="{{ showMark }}"
+      formatter="{{ formatter }}"
+      rowHeight="{{ rowHeight }}"
+      currentDate="{{ currentDate }}"
+      showSubtitle="{{ showSubtitle }}"
+      allowSameDay="{{ allowSameDay }}"
+      showMonthTitle="{{ index !== 0 || !showSubtitle }}"
+      firstDayOfWeek="{{ firstDayOfWeek }}"
+      bind:click="onClickDay"
+    />
+  </scroll-view>
+
+  <view
+    class="{{ utils.bem('calendar__footer', { safeAreaInsetBottom }) }}"
+  >
+    <slot name="footer"></slot>
+  </view>
+
+  <view
+    class="{{ utils.bem('calendar__footer', { safeAreaInsetBottom }) }}"
+  >
+    <van-button
+      wx:if="{{ showConfirm }}"
+      round
+      block
+      type="danger"
+      color="{{ color }}"
+      custom-class="van-calendar__confirm"
+      disabled="{{ computed.getButtonDisabled(type, currentDate) }}"
+      nativeType="text"
+      bind:click="onConfirm"
+    >
+      {{
+        computed.getButtonDisabled(type, currentDate)
+          ? confirmDisabledText
+          : confirmText
+      }}
+    </van-button>
+  </view>
+</view>

+ 1 - 0
wxcomponents/vant/calendar/components/header/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 34 - 0
wxcomponents/vant/calendar/components/header/index.js

@@ -0,0 +1,34 @@
+import { VantComponent } from '../../../common/component';
+VantComponent({
+  props: {
+    title: {
+      type: String,
+      value: '日期选择',
+    },
+    subtitle: String,
+    showTitle: Boolean,
+    showSubtitle: Boolean,
+    firstDayOfWeek: {
+      type: Number,
+      observer: 'initWeekDay',
+    },
+  },
+  data: {
+    weekdays: [],
+  },
+  created() {
+    this.initWeekDay();
+  },
+  methods: {
+    initWeekDay() {
+      const defaultWeeks = ['日', '一', '二', '三', '四', '五', '六'];
+      const firstDayOfWeek = this.data.firstDayOfWeek || 0;
+      this.setData({
+        weekdays: [
+          ...defaultWeeks.slice(firstDayOfWeek, 7),
+          ...defaultWeeks.slice(0, firstDayOfWeek),
+        ],
+      });
+    },
+  },
+});

+ 3 - 0
wxcomponents/vant/calendar/components/header/index.json

@@ -0,0 +1,3 @@
+{
+  "component": true
+}

+ 16 - 0
wxcomponents/vant/calendar/components/header/index.wxml

@@ -0,0 +1,16 @@
+<view class="van-calendar__header">
+  <block wx:if="{{ showTitle }}">
+    <view class="van-calendar__header-title"><slot name="title"></slot></view>
+    <view class="van-calendar__header-title">{{ title }}</view>
+  </block>
+
+  <view wx:if="{{ showSubtitle }}" class="van-calendar__header-subtitle">
+    {{ subtitle }}
+  </view>
+
+  <view class="van-calendar__weekdays">
+    <view wx:for="{{ weekdays }}" wx:key="index" class="van-calendar__weekday">
+      {{ item }}
+    </view>
+  </view>
+</view>

+ 1 - 0
wxcomponents/vant/calendar/components/header/index.wxss

@@ -0,0 +1 @@
+@import '../../../common/index.wxss';.van-calendar__header{-webkit-flex-shrink:0;flex-shrink:0;box-shadow:0 2px 10px rgba(125,126,128,.16);box-shadow:var(--calendar-header-box-shadow,0 2px 10px rgba(125,126,128,.16))}.van-calendar__header-subtitle,.van-calendar__header-title{text-align:center;height:44px;height:var(--calendar-header-title-height,44px);font-weight:500;font-weight:var(--font-weight-bold,500);line-height:44px;line-height:var(--calendar-header-title-height,44px)}.van-calendar__header-title+.van-calendar__header-title,.van-calendar__header-title:empty{display:none}.van-calendar__header-title:empty+.van-calendar__header-title{display:block!important}.van-calendar__weekdays{display:-webkit-flex;display:flex}.van-calendar__weekday{-webkit-flex:1;flex:1;text-align:center;font-size:12px;font-size:var(--calendar-weekdays-font-size,12px);line-height:30px;line-height:var(--calendar-weekdays-height,30px)}

+ 1 - 0
wxcomponents/vant/calendar/components/month/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 163 - 0
wxcomponents/vant/calendar/components/month/index.js

@@ -0,0 +1,163 @@
+import { VantComponent } from '../../../common/component';
+import {
+  getMonthEndDay,
+  compareDay,
+  getPrevDay,
+  getNextDay,
+} from '../../utils';
+VantComponent({
+  props: {
+    date: {
+      type: null,
+      observer: 'setDays',
+    },
+    type: {
+      type: String,
+      observer: 'setDays',
+    },
+    color: String,
+    minDate: {
+      type: null,
+      observer: 'setDays',
+    },
+    maxDate: {
+      type: null,
+      observer: 'setDays',
+    },
+    showMark: Boolean,
+    rowHeight: null,
+    formatter: {
+      type: null,
+      observer: 'setDays',
+    },
+    currentDate: {
+      type: null,
+      observer: 'setDays',
+    },
+    firstDayOfWeek: {
+      type: Number,
+      observer: 'setDays',
+    },
+    allowSameDay: Boolean,
+    showSubtitle: Boolean,
+    showMonthTitle: Boolean,
+  },
+  data: {
+    visible: true,
+    days: [],
+  },
+  methods: {
+    onClick(event) {
+      const { index } = event.currentTarget.dataset;
+      const item = this.data.days[index];
+      if (item.type !== 'disabled') {
+        this.$emit('click', item);
+      }
+    },
+    setDays() {
+      const days = [];
+      const startDate = new Date(this.data.date);
+      const year = startDate.getFullYear();
+      const month = startDate.getMonth();
+      const totalDay = getMonthEndDay(
+        startDate.getFullYear(),
+        startDate.getMonth() + 1
+      );
+      for (let day = 1; day <= totalDay; day++) {
+        const date = new Date(year, month, day);
+        const type = this.getDayType(date);
+        let config = {
+          date,
+          type,
+          text: day,
+          bottomInfo: this.getBottomInfo(type),
+        };
+        if (this.data.formatter) {
+          config = this.data.formatter(config);
+        }
+        days.push(config);
+      }
+      this.setData({ days });
+    },
+    getMultipleDayType(day) {
+      const { currentDate } = this.data;
+      if (!Array.isArray(currentDate)) {
+        return '';
+      }
+      const isSelected = (date) =>
+        currentDate.some((item) => compareDay(item, date) === 0);
+      if (isSelected(day)) {
+        const prevDay = getPrevDay(day);
+        const nextDay = getNextDay(day);
+        const prevSelected = isSelected(prevDay);
+        const nextSelected = isSelected(nextDay);
+        if (prevSelected && nextSelected) {
+          return 'multiple-middle';
+        }
+        if (prevSelected) {
+          return 'end';
+        }
+        return nextSelected ? 'start' : 'multiple-selected';
+      }
+      return '';
+    },
+    getRangeDayType(day) {
+      const { currentDate, allowSameDay } = this.data;
+      if (!Array.isArray(currentDate)) {
+        return '';
+      }
+      const [startDay, endDay] = currentDate;
+      if (!startDay) {
+        return '';
+      }
+      const compareToStart = compareDay(day, startDay);
+      if (!endDay) {
+        return compareToStart === 0 ? 'start' : '';
+      }
+      const compareToEnd = compareDay(day, endDay);
+      if (compareToStart === 0 && compareToEnd === 0 && allowSameDay) {
+        return 'start-end';
+      }
+      if (compareToStart === 0) {
+        return 'start';
+      }
+      if (compareToEnd === 0) {
+        return 'end';
+      }
+      if (compareToStart > 0 && compareToEnd < 0) {
+        return 'middle';
+      }
+      return '';
+    },
+    getDayType(day) {
+      const { type, minDate, maxDate, currentDate } = this.data;
+      if (compareDay(day, minDate) < 0 || compareDay(day, maxDate) > 0) {
+        return 'disabled';
+      }
+      if (type === 'single') {
+        return compareDay(day, currentDate) === 0 ? 'selected' : '';
+      }
+      if (type === 'multiple') {
+        return this.getMultipleDayType(day);
+      }
+      /* istanbul ignore else */
+      if (type === 'range') {
+        return this.getRangeDayType(day);
+      }
+      return '';
+    },
+    getBottomInfo(type) {
+      if (this.data.type === 'range') {
+        if (type === 'start') {
+          return '开始';
+        }
+        if (type === 'end') {
+          return '结束';
+        }
+        if (type === 'start-end') {
+          return '开始/结束';
+        }
+      }
+    },
+  },
+});

+ 3 - 0
wxcomponents/vant/calendar/components/month/index.json

@@ -0,0 +1,3 @@
+{
+  "component": true
+}

+ 39 - 0
wxcomponents/vant/calendar/components/month/index.wxml

@@ -0,0 +1,39 @@
+<wxs src="./index.wxs" module="computed"></wxs>
+<wxs src="../../../wxs/utils.wxs" module="utils" />
+
+<view class="van-calendar__month" style="{{ computed.getMonthStyle(visible, date, rowHeight) }}">
+  <view wx:if="{{ showMonthTitle }}" class="van-calendar__month-title">
+    {{ computed.formatMonthTitle(date) }}
+  </view>
+
+  <view wx:if="{{ visible }}" class="van-calendar__days">
+    <view wx:if="{{ showMark }}" class="van-calendar__month-mark">
+      {{ computed.getMark(date) }}
+    </view>
+
+    <view
+      wx:for="{{ days }}"
+      wx:key="index"
+      style="{{ computed.getDayStyle(item.type, index, date, rowHeight, color, firstDayOfWeek) }}"
+      class="{{ utils.bem('calendar__day', [item.type]) }} {{ item.className }}"
+      data-index="{{ index }}"
+      bindtap="onClick"
+    >
+      <view wx:if="{{ item.type === 'selected' }}" class="van-calendar__selected-day" style="background: {{ color }}">
+        <view wx:if="{{ item.topInfo }}" class="van-calendar__top-info">{{ item.topInfo }}</view>
+        {{ item.text }}
+        <view wx:if="{{ item.bottomInfo }}" class="van-calendar__bottom-info">
+          {{ item.bottomInfo }}
+        </view>
+      </view>
+
+      <view wx:else>
+        <view wx:if="{{ item.topInfo }}" class="van-calendar__top-info">{{ item.topInfo }}</view>
+        {{ item.text }}
+        <view wx:if="{{ item.bottomInfo }}" class="van-calendar__bottom-info">
+          {{ item.bottomInfo }}
+        </view>
+      </view>
+    </view>
+  </view>
+</view>

+ 71 - 0
wxcomponents/vant/calendar/components/month/index.wxs

@@ -0,0 +1,71 @@
+/* eslint-disable */
+var utils = require('../../utils.wxs');
+
+function getMark(date) {
+  return getDate(date).getMonth() + 1;
+}
+
+var ROW_HEIGHT = 64;
+
+function getDayStyle(type, index, date, rowHeight, color, firstDayOfWeek) {
+  var style = [];
+  var current = getDate(date).getDay() || 7;
+  var offset = current < firstDayOfWeek ? (7 - firstDayOfWeek + current) :
+               current === 7 && firstDayOfWeek === 0 ? 0 :
+               (current - firstDayOfWeek);
+
+  if (index === 0) {
+    style.push(['margin-left', (100 * offset) / 7 + '%']);
+  }
+
+  if (rowHeight !== ROW_HEIGHT) {
+    style.push(['height', rowHeight + 'px']);
+  }
+
+  if (color) {
+    if (
+      type === 'start' ||
+      type === 'end' ||
+      type === 'start-end' ||
+      type === 'multiple-selected' ||
+      type === 'multiple-middle'
+    ) {
+      style.push(['background', color]);
+    } else if (type === 'middle') {
+      style.push(['color', color]);
+    }
+  }
+
+  return style
+    .map(function(item) {
+      return item.join(':');
+    })
+    .join(';');
+}
+
+function formatMonthTitle(date) {
+  date = getDate(date);
+  return date.getFullYear() + '年' + (date.getMonth() + 1) + '月';
+}
+
+function getMonthStyle(visible, date, rowHeight) {
+  if (!visible) {
+    date = getDate(date);
+
+    var totalDay = utils.getMonthEndDay(
+      date.getFullYear(),
+      date.getMonth() + 1
+    );
+    var offset = getDate(date).getDay();
+    var padding = Math.ceil((totalDay + offset) / 7) * rowHeight;
+
+    return 'padding-bottom:' + padding + 'px';
+  }
+}
+
+module.exports = {
+  getMark: getMark,
+  getDayStyle: getDayStyle,
+  formatMonthTitle: formatMonthTitle,
+  getMonthStyle: getMonthStyle
+};

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
wxcomponents/vant/calendar/components/month/index.wxss


+ 1 - 0
wxcomponents/vant/calendar/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 309 - 0
wxcomponents/vant/calendar/index.js

@@ -0,0 +1,309 @@
+import { VantComponent } from '../common/component';
+import {
+  ROW_HEIGHT,
+  getNextDay,
+  compareDay,
+  copyDates,
+  calcDateNum,
+  formatMonthTitle,
+  compareMonth,
+  getMonths,
+  getDayByOffset,
+} from './utils';
+import Toast from '../toast/toast';
+import { requestAnimationFrame } from '../common/utils';
+VantComponent({
+  props: {
+    title: {
+      type: String,
+      value: '日期选择',
+    },
+    color: String,
+    show: {
+      type: Boolean,
+      observer(val) {
+        if (val) {
+          this.initRect();
+          this.scrollIntoView();
+        }
+      },
+    },
+    formatter: null,
+    confirmText: {
+      type: String,
+      value: '确定',
+    },
+    rangePrompt: String,
+    showRangePrompt: {
+      type: Boolean,
+      value: true,
+    },
+    defaultDate: {
+      type: null,
+      observer(val) {
+        this.setData({ currentDate: val });
+        this.scrollIntoView();
+      },
+    },
+    allowSameDay: Boolean,
+    confirmDisabledText: String,
+    type: {
+      type: String,
+      value: 'single',
+      observer: 'reset',
+    },
+    minDate: {
+      type: null,
+      value: Date.now(),
+    },
+    maxDate: {
+      type: null,
+      value: new Date(
+        new Date().getFullYear(),
+        new Date().getMonth() + 6,
+        new Date().getDate()
+      ).getTime(),
+    },
+    position: {
+      type: String,
+      value: 'bottom',
+    },
+    rowHeight: {
+      type: null,
+      value: ROW_HEIGHT,
+    },
+    round: {
+      type: Boolean,
+      value: true,
+    },
+    poppable: {
+      type: Boolean,
+      value: true,
+    },
+    showMark: {
+      type: Boolean,
+      value: true,
+    },
+    showTitle: {
+      type: Boolean,
+      value: true,
+    },
+    showConfirm: {
+      type: Boolean,
+      value: true,
+    },
+    showSubtitle: {
+      type: Boolean,
+      value: true,
+    },
+    safeAreaInsetBottom: {
+      type: Boolean,
+      value: true,
+    },
+    closeOnClickOverlay: {
+      type: Boolean,
+      value: true,
+    },
+    maxRange: {
+      type: null,
+      value: null,
+    },
+    firstDayOfWeek: {
+      type: Number,
+      value: 0,
+    },
+  },
+  data: {
+    subtitle: '',
+    currentDate: null,
+    scrollIntoView: '',
+  },
+  created() {
+    this.setData({
+      currentDate: this.getInitialDate(),
+    });
+  },
+  mounted() {
+    if (this.data.show || !this.data.poppable) {
+      this.initRect();
+      this.scrollIntoView();
+    }
+  },
+  methods: {
+    reset() {
+      this.setData({ currentDate: this.getInitialDate() });
+      this.scrollIntoView();
+    },
+    initRect() {
+      if (this.contentObserver != null) {
+        this.contentObserver.disconnect();
+      }
+      const contentObserver = this.createIntersectionObserver({
+        thresholds: [0, 0.1, 0.9, 1],
+        observeAll: true,
+      });
+      this.contentObserver = contentObserver;
+      contentObserver.relativeTo('.van-calendar__body');
+      contentObserver.observe('.month', (res) => {
+        if (res.boundingClientRect.top <= res.relativeRect.top) {
+          // @ts-ignore
+          this.setData({ subtitle: formatMonthTitle(res.dataset.date) });
+        }
+      });
+    },
+    getInitialDate() {
+      const { type, defaultDate, minDate } = this.data;
+      if (type === 'range') {
+        const [startDay, endDay] = defaultDate || [];
+        return [
+          startDay || minDate,
+          endDay || getNextDay(new Date(minDate)).getTime(),
+        ];
+      }
+      if (type === 'multiple') {
+        return defaultDate || [minDate];
+      }
+      return defaultDate || minDate;
+    },
+    scrollIntoView() {
+      requestAnimationFrame(() => {
+        const {
+          currentDate,
+          type,
+          show,
+          poppable,
+          minDate,
+          maxDate,
+        } = this.data;
+        // @ts-ignore
+        const targetDate = type === 'single' ? currentDate : currentDate[0];
+        const displayed = show || !poppable;
+        if (!targetDate || !displayed) {
+          return;
+        }
+        const months = getMonths(minDate, maxDate);
+        months.some((month, index) => {
+          if (compareMonth(month, targetDate) === 0) {
+            this.setData({ scrollIntoView: `month${index}` });
+            return true;
+          }
+          return false;
+        });
+      });
+    },
+    onOpen() {
+      this.$emit('open');
+    },
+    onOpened() {
+      this.$emit('opened');
+    },
+    onClose() {
+      this.$emit('close');
+    },
+    onClosed() {
+      this.$emit('closed');
+    },
+    onClickDay(event) {
+      const { date } = event.detail;
+      const { type, currentDate, allowSameDay } = this.data;
+      if (type === 'range') {
+        // @ts-ignore
+        const [startDay, endDay] = currentDate;
+        if (startDay && !endDay) {
+          const compareToStart = compareDay(date, startDay);
+          if (compareToStart === 1) {
+            this.select([startDay, date], true);
+          } else if (compareToStart === -1) {
+            this.select([date, null]);
+          } else if (allowSameDay) {
+            this.select([date, date]);
+          }
+        } else {
+          this.select([date, null]);
+        }
+      } else if (type === 'multiple') {
+        let selectedIndex;
+        // @ts-ignore
+        const selected = currentDate.some((dateItem, index) => {
+          const equal = compareDay(dateItem, date) === 0;
+          if (equal) {
+            selectedIndex = index;
+          }
+          return equal;
+        });
+        if (selected) {
+          // @ts-ignore
+          const cancelDate = currentDate.splice(selectedIndex, 1);
+          this.setData({ currentDate });
+          this.unselect(cancelDate);
+        } else {
+          // @ts-ignore
+          this.select([...currentDate, date]);
+        }
+      } else {
+        this.select(date, true);
+      }
+    },
+    unselect(dateArray) {
+      const date = dateArray[0];
+      if (date) {
+        this.$emit('unselect', copyDates(date));
+      }
+    },
+    select(date, complete) {
+      if (complete && this.data.type === 'range') {
+        const valid = this.checkRange(date);
+        if (!valid) {
+          // auto selected to max range if showConfirm
+          if (this.data.showConfirm) {
+            this.emit([
+              date[0],
+              getDayByOffset(date[0], this.data.maxRange - 1),
+            ]);
+          } else {
+            this.emit(date);
+          }
+          return;
+        }
+      }
+      this.emit(date);
+      if (complete && !this.data.showConfirm) {
+        this.onConfirm();
+      }
+    },
+    emit(date) {
+      const getTime = (date) => (date instanceof Date ? date.getTime() : date);
+      this.setData({
+        currentDate: Array.isArray(date) ? date.map(getTime) : getTime(date),
+      });
+      this.$emit('select', copyDates(date));
+    },
+    checkRange(date) {
+      const { maxRange, rangePrompt, showRangePrompt } = this.data;
+      if (maxRange && calcDateNum(date) > maxRange) {
+        if (showRangePrompt) {
+          Toast({
+            duration: 0,
+            context: this,
+            message: rangePrompt || `选择天数不能超过 ${maxRange} 天`,
+          });
+        }
+        this.$emit('over-range');
+        return false;
+      }
+      return true;
+    },
+    onConfirm() {
+      if (
+        this.data.type === 'range' &&
+        !this.checkRange(this.data.currentDate)
+      ) {
+        return;
+      }
+      wx.nextTick(() => {
+        // @ts-ignore
+        this.$emit('confirm', copyDates(this.data.currentDate));
+      });
+    },
+  },
+});

+ 10 - 0
wxcomponents/vant/calendar/index.json

@@ -0,0 +1,10 @@
+{
+  "component": true,
+  "usingComponents": {
+    "header": "./components/header/index",
+    "month": "./components/month/index",
+    "van-button": "../button/index",
+    "van-popup": "../popup/index",
+    "van-toast": "../toast/index"
+  }
+}

+ 25 - 0
wxcomponents/vant/calendar/index.wxml

@@ -0,0 +1,25 @@
+<wxs src="./index.wxs" module="computed" />
+<wxs src="../wxs/utils.wxs" module="utils" />
+
+<import src="./calendar.wxml" />
+
+<van-popup
+  wx:if="{{ poppable }}"
+  custom-class="van-calendar__popup--{{ position }}"
+  close-icon-class="van-calendar__close-icon"
+  show="{{ show }}"
+  round="{{ round }}"
+  position="{{ position }}"
+  closeable="{{ showTitle || showSubtitle }}"
+  close-on-click-overlay="{{ closeOnClickOverlay }}"
+  bind:enter="onOpen"
+  bind:close="onClose"
+  bind:after-enter="onOpened"
+  bind:after-leave="onClosed"
+>
+  <include src="calendar.wxml" />
+</van-popup>
+
+<include wx:else src="calendar.wxml" />
+
+<van-toast id="van-toast" />

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor