浏览代码

线索详情页新增手动上传录音,点击上传录音,打开手机录音列表,需用户选择一条录音进行上传

chenyidong 4 月之前
父节点
当前提交
57de53d261

+ 0 - 1
components/ba-tree-picker/ba-tree-picker.vue

@@ -319,7 +319,6 @@
319 319
 			},
320 320
 			// 节点选中、取消选中
321 321
 			_onItemSelect(item, index) {
322
-				console.log(item);
323 322
 				// if(item.Pchildren !== void 0 && item.Pchildren.length > 0) return;
324 323
 				//console.log('_onItemSelect')
325 324
 				//console.log(item)

+ 2 - 0
components/ld-select/ld-select.vue

@@ -371,6 +371,8 @@
371 371
 
372 372
 					.title {
373 373
 						flex: 1;
374
+						overflow-wrap: break-word;
375
+						word-break: break-all;
374 376
 					}
375 377
 
376 378
 					.checkboxMark {

+ 2 - 2
manifest.json

@@ -2,8 +2,8 @@
2 2
     "name" : "时与",
3 3
     "appid" : "__UNI__0C0EF72",
4 4
     "description" : "",
5
-    "versionName" : "1.1.8",
6
-    "versionCode" : 118,
5
+    "versionName" : "1.1.9",
6
+    "versionCode" : 119,
7 7
     "transformPx" : false,
8 8
     /* 5+App特有相关 */
9 9
     "app-plus" : {

+ 7 - 0
pages.json

@@ -184,6 +184,13 @@
184 184
 			{
185 185
 				"navigationStyle": "custom"
186 186
 			}
187
+		},
188
+		{
189
+			"path": "pages/uploadRecord/index",
190
+			"style": {
191
+				"navigationBarTitleText": "上传录音",
192
+				"navigationStyle": "custom"
193
+			}
187 194
 		}
188 195
 	],
189 196
 	"globalStyle": {

+ 402 - 404
pages/circumstances/index.vue

@@ -1,434 +1,432 @@
1 1
 <template>
2
-  <view class="cirumstances_wrap">
3
-    <u-navbar placeholder :autoBack="true" title="环境监测" v-hideNav>
4
-      <view class="u-nav-slot right_btn" slot="right">
5
-        <view class="reset" @click="handleReset">
6
-          <u-icon name="reload" color="#108CFF"></u-icon>刷新
7
-        </view>
8
-      </view>
9
-    </u-navbar>
10
-    <view class="option_list">
11
-      <view class="option_item">
12
-        <view class="item_left">
13
-          <text class="label">登录用户</text>
14
-        </view>
15
-        <view class="item-right">
16
-          <text>{{ nickName }}</text>
17
-        </view>
18
-      </view>
19
-      <template v-if="isCallOff === '1'">
20
-        <view class="option_item">
21
-          <view class="item_left">
22
-            <text class="label">外呼分机号</text>
23
-          </view>
24
-          <view class="item-right">
25
-            <text>{{
2
+	<view class="cirumstances_wrap">
3
+		<u-navbar placeholder :autoBack="true" title="环境监测" v-hideNav>
4
+			<view class="u-nav-slot right_btn" slot="right">
5
+				<view class="reset" @click="handleReset">
6
+					<u-icon name="reload" color="#108CFF"></u-icon>刷新
7
+				</view>
8
+			</view>
9
+		</u-navbar>
10
+		<view class="option_list">
11
+			<view class="option_item">
12
+				<view class="item_left">
13
+					<text class="label">登录用户</text>
14
+				</view>
15
+				<view class="item-right">
16
+					<text>{{ nickName }}</text>
17
+				</view>
18
+			</view>
19
+			<template v-if="isCallOff === '1'">
20
+				<view class="option_item">
21
+					<view class="item_left">
22
+						<text class="label">外呼分机号</text>
23
+					</view>
24
+					<view class="item-right">
25
+						<text>{{
26 26
               callAccount ? callAccount.extenNum : "未配置分机号"
27 27
             }}</text>
28
-          </view>
29
-        </view>
30
-        <view class="option_item">
31
-          <view class="item_left">
32
-            <text class="label">外呼注册状态</text>
33
-          </view>
34
-          <view class="item-right" @longpress="handleRegisterFun">
35
-            <text v-if="callAccount != null && callSystem != null">
36
-              {{ ipAddress }}
37
-            </text>
38
-            <text class="btn" v-else @click="handleRegisterFun">点击注册</text>
39
-            <icon-img
40
-              :value="callAccount != null && callSystem != null"
41
-            ></icon-img>
42
-          </view>
43
-        </view>
44
-        <view class="option_item">
45
-          <view class="item_left">
46
-            <text class="label">外呼签入状态</text>
47
-          </view>
48
-          <view class="item-right">
49
-            <text v-if="signInState">正常</text>
50
-            <text class="btn" v-else @click="handleRegisterFun">点击签入</text>
51
-            <icon-img :value="signInState"></icon-img>
52
-            <icon-img :value="redisStatus"></icon-img>
53
-          </view>
54
-        </view>
55
-      </template>
56
-      <template v-else>
57
-        <view class="option_item">
58
-          <view class="item_left">
59
-            <text class="label">呼叫中心</text>
60
-          </view>
61
-          <view class="item-right" @longpress="handleRegisterFun">
62
-            <text>不启用</text>
63
-            <icon-img :value="isCallOff === '1'"></icon-img>
64
-          </view>
65
-        </view>
66
-      </template>
67
-      <view class="option_item">
68
-        <view class="item_left">
69
-          <text class="label">麦克风权限</text>
70
-        </view>
71
-        <view class="item-right">
72
-          <text v-if="isRecord">已授权</text>
73
-          <text class="btn" v-else @click="handleOpenSet">点击授权</text>
74
-          <icon-img :value="isRecord"></icon-img>
75
-        </view>
76
-      </view>
77
-      <!-- 通话自动录音状态 -->
78
-      <view class="option_item">
79
-        <view class="item_left">
80
-          <text class="label">通话自动录音</text>
81
-        </view>
82
-        <view class="item-right">
83
-          <text v-if="isAutoRecord">已开启</text>
84
-          <text class="btn" v-else @click="toCallRecorderSettings"
85
-            >点击开启</text
86
-          >
87
-          <icon-img :value="isAutoRecord"></icon-img>
88
-        </view>
89
-      </view>
90
-      <!-- 文件访问权限状态 -->
91
-      <view class="option_item">
92
-        <view class="item_left">
93
-          <text class="label">文件访问权限</text>
94
-        </view>
95
-        <view class="item-right">
96
-          <text v-if="hasStoragePermission">已授权</text>
97
-          <text class="btn" v-else @click="requestStoragePermission"
98
-            >点击授权</text
99
-          >
100
-          <icon-img :value="hasStoragePermission"></icon-img>
101
-        </view>
102
-      </view>
103
-      <view class="option_item">
104
-        <view class="item_left">
105
-          <text class="label">定位服务</text>
106
-        </view>
107
-        <view class="item-right">
108
-          <text v-if="systemLocation">已开启</text>
109
-          <text v-else>未开启</text>
110
-          <icon-img :value="systemLocation"></icon-img>
111
-        </view>
112
-      </view>
113
-      <view class="option_item">
114
-        <view class="item_left">
115
-          <text class="label">定位权限</text>
116
-        </view>
117
-        <view class="item-right">
118
-          <text v-if="isLocation">已授权</text>
119
-          <text class="btn" v-else @click="handleOpenSet">点击授权</text>
120
-          <icon-img :value="isLocation"></icon-img>
121
-        </view>
122
-      </view>
123
-      <view class="option_item">
124
-        <view class="item_left">
125
-          <text class="label">X5引擎</text>
126
-        </view>
127
-        <view class="item-right">
128
-          <text v-if="isX5">启用</text>
129
-          <text v-else>不启用</text>
130
-          <icon-img :value="isX5"></icon-img>
131
-        </view>
132
-      </view>
133
-      <view class="option_item">
134
-        <view class="item_left">
135
-          <text class="label">品牌型号</text>
136
-        </view>
137
-        <view class="item-right">
138
-          <text>{{ deviceBrand }}</text>
139
-        </view>
140
-      </view>
141
-      <view class="option_item">
142
-        <view class="item_left">
143
-          <text class="label">所用系统</text>
144
-        </view>
145
-        <view class="item-right">
146
-          <text>{{ system }}</text>
147
-        </view>
148
-      </view>
149
-    </view>
150
-    <view class="tips_wrap" @longpress="handleOpenSet">
151
-      <view class="left">
152
-        <image
153
-          src="../../static/circumstances/success.png"
154
-          mode=""
155
-          class="icon_img"
156
-        >
157
-        </image>
158
-        <text>正常</text>
159
-      </view>
160
-      <view class="right">
161
-        <image
162
-          src="../../static/circumstances/error.png"
163
-          mode=""
164
-          class="icon_img"
165
-        ></image>
166
-        <text>不正常</text>
167
-      </view>
168
-    </view>
169
-  </view>
28
+					</view>
29
+				</view>
30
+				<view class="option_item">
31
+					<view class="item_left">
32
+						<text class="label">外呼注册状态</text>
33
+					</view>
34
+					<view class="item-right" @longpress="handleRegisterFun">
35
+						<text v-if="callAccount != null && callSystem != null">
36
+							{{ ipAddress }}
37
+						</text>
38
+						<text class="btn" v-else @click="handleRegisterFun">点击注册</text>
39
+						<icon-img :value="callAccount != null && callSystem != null"></icon-img>
40
+					</view>
41
+				</view>
42
+				<view class="option_item">
43
+					<view class="item_left">
44
+						<text class="label">外呼签入状态</text>
45
+					</view>
46
+					<view class="item-right">
47
+						<text v-if="signInState">正常</text>
48
+						<text class="btn" v-else @click="handleRegisterFun">点击签入</text>
49
+						<icon-img :value="signInState"></icon-img>
50
+						<icon-img :value="redisStatus"></icon-img>
51
+					</view>
52
+				</view>
53
+			</template>
54
+			<template v-else>
55
+				<view class="option_item">
56
+					<view class="item_left">
57
+						<text class="label">呼叫中心</text>
58
+					</view>
59
+					<view class="item-right" @longpress="handleRegisterFun">
60
+						<text>不启用</text>
61
+						<icon-img :value="isCallOff === '1'"></icon-img>
62
+					</view>
63
+				</view>
64
+			</template>
65
+			<view class="option_item">
66
+				<view class="item_left">
67
+					<text class="label">麦克风权限</text>
68
+				</view>
69
+				<view class="item-right">
70
+					<text v-if="isRecord">已授权</text>
71
+					<text class="btn" v-else @click="handleOpenSet">点击授权</text>
72
+					<icon-img :value="isRecord"></icon-img>
73
+				</view>
74
+			</view>
75
+			<!-- 通话自动录音状态 -->
76
+			<view class="option_item">
77
+				<view class="item_left">
78
+					<text class="label">通话自动录音</text>
79
+				</view>
80
+				<view class="item-right">
81
+					<text v-if="isAutoRecord">已开启</text>
82
+					<text class="btn" v-else @click="toCallRecorderSettings">点击开启</text>
83
+					<icon-img :value="isAutoRecord"></icon-img>
84
+				</view>
85
+			</view>
86
+			<!-- 文件访问权限状态 -->
87
+			<view class="option_item">
88
+				<view class="item_left">
89
+					<text class="label">文件访问权限</text>
90
+				</view>
91
+				<view class="item-right">
92
+					<text v-if="hasStoragePermission">已授权</text>
93
+					<text class="btn" v-else @click="requestStoragePermission">点击授权</text>
94
+					<icon-img :value="hasStoragePermission"></icon-img>
95
+				</view>
96
+			</view>
97
+			<!-- 读取手机状态权限 -->
98
+			<view class="option_item">
99
+				<view class="item_left">
100
+					<text class="label">读取手机状态</text>
101
+				</view>
102
+				<view class="item-right">
103
+					<text v-if="hasReadPhoneStatePermission">已授权</text>
104
+					<text class="btn" v-else @click="handleOpenSet">点击授权</text>
105
+					<icon-img :value="hasReadPhoneStatePermission"></icon-img>
106
+				</view>
107
+			</view>
108
+			<!-- 拨打电话权限 -->
109
+			<view class="option_item">
110
+				<view class="item_left">
111
+					<text class="label">拨打电话</text>
112
+				</view>
113
+				<view class="item-right">
114
+					<text v-if="hasCallPhonePermission">已授权</text>
115
+					<text class="btn" v-else @click="handleOpenSet">点击授权</text>
116
+					<icon-img :value="hasCallPhonePermission"></icon-img>
117
+				</view>
118
+			</view>
119
+			<!-- 读取通话记录权限 -->
120
+			<view class="option_item">
121
+				<view class="item_left">
122
+					<text class="label">读取通话记录</text>
123
+				</view>
124
+				<view class="item-right">
125
+					<text v-if="hasReadCallLogPermission">已授权</text>
126
+					<text class="btn" v-else @click="handleOpenSet">点击授权</text>
127
+					<icon-img :value="hasReadCallLogPermission"></icon-img>
128
+				</view>
129
+			</view>
130
+			<!-- 读取电话号码权限 -->
131
+			<view class="option_item">
132
+				<view class="item_left">
133
+					<text class="label">读取电话号码</text>
134
+				</view>
135
+				<view class="item-right">
136
+					<text v-if="hasReadPhoneNumbersPermission">已授权</text>
137
+					<text class="btn" v-else @click="handleOpenSet">点击授权</text>
138
+					<icon-img :value="hasReadPhoneNumbersPermission"></icon-img>
139
+				</view>
140
+			</view>
141
+			<view class="option_item">
142
+				<view class="item_left">
143
+					<text class="label">定位服务</text>
144
+				</view>
145
+				<view class="item-right">
146
+					<text v-if="systemLocation">已开启</text>
147
+					<text v-else>未开启</text>
148
+					<icon-img :value="systemLocation"></icon-img>
149
+				</view>
150
+			</view>
151
+			<view class="option_item">
152
+				<view class="item_left">
153
+					<text class="label">定位权限</text>
154
+				</view>
155
+				<view class="item-right">
156
+					<text v-if="isLocation">已授权</text>
157
+					<text class="btn" v-else @click="handleOpenSet">点击授权</text>
158
+					<icon-img :value="isLocation"></icon-img>
159
+				</view>
160
+			</view>
161
+			<view class="option_item">
162
+				<view class="item_left">
163
+					<text class="label">X5引擎</text>
164
+				</view>
165
+				<view class="item-right">
166
+					<text v-if="isX5">启用</text>
167
+					<text v-else>不启用</text>
168
+					<icon-img :value="isX5"></icon-img>
169
+				</view>
170
+			</view>
171
+			<view class="option_item">
172
+				<view class="item_left">
173
+					<text class="label">品牌型号</text>
174
+				</view>
175
+				<view class="item-right">
176
+					<text>{{ deviceBrand }}</text>
177
+				</view>
178
+			</view>
179
+			<view class="option_item">
180
+				<view class="item_left">
181
+					<text class="label">所用系统</text>
182
+				</view>
183
+				<view class="item-right">
184
+					<text>{{ system }}</text>
185
+				</view>
186
+			</view>
187
+		</view>
188
+		<view class="tips_wrap" @longpress="handleOpenSet">
189
+			<view class="left">
190
+				<image src="../../static/circumstances/success.png" mode="" class="icon_img">
191
+				</image>
192
+				<text>正常</text>
193
+			</view>
194
+			<view class="right">
195
+				<image src="../../static/circumstances/error.png" mode="" class="icon_img"></image>
196
+				<text>不正常</text>
197
+			</view>
198
+		</view>
199
+	</view>
170 200
 </template>
171 201
 
172 202
 <script>
173
-import { mapState } from "vuex";
174
-import iconImg from "./components/iconImg.vue";
175
-import permision from "@/js_sdk/wa-permission/permission.js";
203
+	import {
204
+		mapState
205
+	} from "vuex";
206
+	import iconImg from "./components/iconImg.vue";
207
+	import permision from "@/js_sdk/wa-permission/permission.js";
176 208
 
177
-import {
178
-  checkIsAutoRecord,
179
-  toCallAutoRecorderPage,
180
-  navigateToCallRecordingSettings,
181
-  jumpToPermissionPage,
182
-} from "@/uni_modules/yao-lister";
209
+	export default {
210
+		components: {
211
+			iconImg,
212
+		},
213
+		computed: {
214
+			...mapState({
215
+				nickName: (state) => state.user.userInfo.nickName,
216
+				callAccount: (state) => state.app.callAccount,
217
+				signInState: (state) => state.app.signInState,
218
+				ipAddress: (state) =>
219
+					state.app.registerInfo && state.app.registerInfo.register ?
220
+					state.app.registerInfo.register["IPv4"].slice(-3) : "异常",
221
+				redisStatus: (state) => state.app.redisStatus,
222
+				callSystem: (state) => state.app.callSystem,
223
+				uuid: (state) => state.user.uuid,
224
+				isCallOff: (state) => state.user.netConfig.isCallOff,
225
+				// 从call store获取权限状态
226
+				isAutoRecord: (state) => state.call.isAutoRecord,
227
+				hasStoragePermission: (state) => state.call.hasStoragePermission,
228
+				hasReadPhoneStatePermission: (state) => state.call.hasReadPhoneStatePermission,
229
+				hasCallPhonePermission: (state) => state.call.hasCallPhonePermission,
230
+				hasReadCallLogPermission: (state) => state.call.hasReadCallLogPermission,
231
+				hasReadPhoneNumbersPermission: (state) => state.call.hasReadPhoneNumbersPermission,
232
+			}),
233
+		},
234
+		data() {
235
+			return {
236
+				isX5: false,
237
+				isRecord: false,
238
+				isLocation: false,
239
+				system: "",
240
+				deviceBrand: "",
241
+				systemLocation: false,
242
+			};
243
+		},
244
+		methods: {
245
+			// 刷新
246
+			handleReset() {
247
+				this.getAllStatus();
248
+				uni.$u.toast("刷新成功");
249
+			},
250
+			// 打开设置
251
+			handleOpenSet() {
252
+				this.$store.dispatch('call/handleOpenSet');
253
+			},
254
+			// 请求文件访问权限
255
+			requestStoragePermission() {
256
+				this.$store.dispatch('call/requestStoragePermission');
257
+			},
258
+			// 跳转到通话录音设置页面
259
+			toCallRecorderSettings() {
260
+				this.$store.dispatch('call/toCallRecorderSettings');
261
+			},
262
+			// 跳转到系统通话录音界面
263
+			toSystemRecorderSettings() {
264
+				this.$store.dispatch('call/toSystemRecorderSettings');
265
+			},
266
+			// 注册
267
+			handleRegisterFun() {
268
+				this.$store
269
+					.dispatch("app/getExtensionByUserId", {
270
+						userId: this.$store.state.user.userInfo.userId,
271
+					})
272
+					.then((res) => {
273
+						Promise.all([
274
+							permision.requestAndroidPermission(
275
+								"android.permission.RECORD_AUDIO"
276
+							),
277
+							permision.requestAndroidPermission(
278
+								"android.permission.MODIFY_AUDIO_SETTINGS"
279
+							),
280
+						]).then((result) => {
281
+							const flag = result.every((v) => v == 1);
282
+							if (flag) {
283
+								this.$store.dispatch("app/createWv");
284
+							}
285
+						});
286
+					});
287
+			},
288
+			// 检测定位服务是否开启
289
+			checkSystemEnableLocation() {
290
+				let context = plus.android.importClass("android.content.Context");
291
+				let locationManager = plus.android.importClass(
292
+					"android.location.LocationManager"
293
+				);
294
+				let main = plus.android.runtimeMainActivity();
295
+				let mainSvr = main.getSystemService(context.LOCATION_SERVICE);
296
+				let result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER);
297
+				return result;
298
+			},
299
+			getAllStatus() {
300
+				uni.getSystemInfo({
301
+					success: (res) => {
302
+						this.deviceBrand = res.deviceBrand + " " + res.model;
303
+						this.system = res.system ? res.system : "获取失败";
304
+						this.isX5 = res.browserName.includes("x5");
305
+					},
306
+				});
183 307
 
184
-export default {
185
-  components: {
186
-    iconImg,
187
-  },
188
-  computed: {
189
-    ...mapState({
190
-      nickName: (state) => state.user.userInfo.nickName,
191
-      callAccount: (state) => state.app.callAccount,
192
-      signInState: (state) => state.app.signInState,
193
-      ipAddress: (state) =>
194
-        state.app.registerInfo && state.app.registerInfo.register
195
-          ? state.app.registerInfo.register["IPv4"].slice(-3)
196
-          : "异常",
197
-      redisStatus: (state) => state.app.redisStatus,
198
-      callSystem: (state) => state.app.callSystem,
199
-      uuid: (state) => state.user.uuid,
200
-      isCallOff: (state) => state.user.netConfig.isCallOff,
201
-    }),
202
-  },
203
-  data() {
204
-    return {
205
-      isX5: false,
206
-      isRecord: false,
207
-      isLocation: false,
208
-      system: "",
209
-      deviceBrand: "",
210
-      systemLocation: false,
211
-      isAutoRecord: false, // 通话自动录音状态
212
-      hasStoragePermission: false, // 文件访问权限状态
213
-    };
214
-  },
215
-  methods: {
216
-    // 刷新
217
-    handleReset() {
218
-      this.getAllStatus();
219
-      uni.$u.toast("刷新成功");
220
-    },
221
-    // 打开设置
222
-    handleOpenSet() {
223
-      permision.gotoAppPermissionSetting("android.permission.RECORD_AUDIO");
224
-    },
225
-    // 请求文件访问权限
226
-    requestStoragePermission() {
227
-      jumpToPermissionPage();
228
-    },
229
-    // 跳转到通话录音设置页面
230
-    toCallRecorderSettings() {
231
-      toCallAutoRecorderPage();
232
-    },
233
-    // 跳转到系统通话录音界面
234
-    toSystemRecorderSettings() {
235
-      navigateToCallRecordingSettings();
236
-    },
237
-    // 注册
238
-    handleRegisterFun() {
239
-      this.$store
240
-        .dispatch("app/getExtensionByUserId", {
241
-          userId: this.$store.state.user.userInfo.userId,
242
-        })
243
-        .then((res) => {
244
-          Promise.all([
245
-            permision.requestAndroidPermission(
246
-              "android.permission.RECORD_AUDIO"
247
-            ),
248
-            permision.requestAndroidPermission(
249
-              "android.permission.MODIFY_AUDIO_SETTINGS"
250
-            ),
251
-          ]).then((result) => {
252
-            const flag = result.every((v) => v == 1);
253
-            if (flag) {
254
-              this.$store.dispatch("app/createWv");
255
-            }
256
-          });
257
-        });
258
-    },
259
-    // 检测定位服务是否开启
260
-    checkSystemEnableLocation() {
261
-      let context = plus.android.importClass("android.content.Context");
262
-      let locationManager = plus.android.importClass(
263
-        "android.location.LocationManager"
264
-      );
265
-      let main = plus.android.runtimeMainActivity();
266
-      let mainSvr = main.getSystemService(context.LOCATION_SERVICE);
267
-      let result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER);
268
-      return result;
269
-    },
270
-    getAllStatus() {
271
-      uni.getSystemInfo({
272
-        success: (res) => {
273
-          this.deviceBrand = res.deviceBrand + " " + res.model;
274
-          this.system = res.system ? res.system : "获取失败";
275
-          this.isX5 = res.browserName.includes("x5");
276
-          // 获取Android版本号
277
-          let androidVersion = 0;
278
-          if (this.system && this.system.includes("Android")) {
279
-            const versionMatch = this.system.match(/Android (\d+\.?\d*)/);
280
-            if (versionMatch && versionMatch[1]) {
281
-              androidVersion = parseFloat(versionMatch[1]);
282
-            }
283
-          }
308
+				// 检查麦克风权限
309
+				permision
310
+					.requestAndroidPermission("android.permission.RECORD_AUDIO")
311
+					.then((result) => {
312
+						this.isRecord = result == "1";
313
+					});
284 314
 
285
-          // 检查文件访问权限
286
-          if (androidVersion >= 11) {
287
-            // Android 11及以上版本,使用MANAGE_EXTERNAL_STORAGE权限(特殊权限)
288
-            try {
289
-              // 使用Android原生API检查特殊权限
290
-              const Environment = plus.android.importClass("android.os.Environment");
291
-              const Context = plus.android.importClass("android.content.Context");
292
-              const mainActivity = plus.android.runtimeMainActivity();
293
-              // 检查是否有管理外部存储的权限
294
-              this.hasStoragePermission = Environment.isExternalStorageManager();
295
-              console.log('MANAGE_EXTERNAL_STORAGE权限状态:', this.hasStoragePermission);
296
-            } catch (e) {
297
-              console.error('检查存储权限失败:', e);
298
-              this.hasStoragePermission = false;
299
-            }
300
-          } else {
301
-            // Android 11以下版本,使用READ_EXTERNAL_STORAGE权限
302
-            permision
303
-              .requestAndroidPermission(
304
-                "android.permission.READ_EXTERNAL_STORAGE"
305
-              )
306
-              .then((result) => {
307
-                this.hasStoragePermission = result === 1;
308
-              });
309
-          }
310
-        },
311
-      });
315
+				// 检查定位服务状态
316
+				this.systemLocation = this.checkSystemEnableLocation();
312 317
 
313
-      permision
314
-        .requestAndroidPermission("android.permission.RECORD_AUDIO")
315
-        .then((result) => {
316
-          this.isRecord = result == "1";
317
-        });
318
+				// 检查定位权限
319
+				permision
320
+					.requestAndroidPermission("android.permission.ACCESS_FINE_LOCATION")
321
+					.then((result) => {
322
+						this.isLocation = result == "1";
323
+					});
318 324
 
319
-      this.systemLocation = this.checkSystemEnableLocation();
320
-
321
-      permision
322
-        .requestAndroidPermission("android.permission.ACCESS_FINE_LOCATION")
323
-        .then((result) => {
324
-          this.isLocation = result == "1";
325
-        });
326
-
327
-      // 检查通话自动录音状态
328
-      this.isAutoRecord = checkIsAutoRecord();
329
-    },
330
-  },
331
-  mounted() {
332
-    this.getAllStatus();
333
-  },
334
-};
325
+				// 从store检查所有其他权限状态
326
+				this.$store.dispatch('call/checkAllPermissions');
327
+			},
328
+		},
329
+		mounted() {
330
+			this.getAllStatus();
331
+		},
332
+	};
335 333
 </script>
336 334
 
337 335
 <style lang="scss" scoped>
338
-.right_btn {
339
-  display: flex;
340
-  align-items: center;
341
-  font-size: 14px;
336
+	.right_btn {
337
+		display: flex;
338
+		align-items: center;
339
+		font-size: 14px;
342 340
 
343
-  .copy_btn {
344
-    display: flex;
345
-    align-items: center;
346
-    margin-right: 20rpx;
347
-  }
341
+		.copy_btn {
342
+			display: flex;
343
+			align-items: center;
344
+			margin-right: 20rpx;
345
+		}
348 346
 
349
-  .reset {
350
-    display: flex;
351
-    align-items: center;
352
-  }
353
-}
347
+		.reset {
348
+			display: flex;
349
+			align-items: center;
350
+		}
351
+	}
354 352
 
355
-.tips_wrap {
356
-  display: flex;
357
-  align-items: center;
358
-  justify-content: space-evenly;
359
-  height: 40px;
360
-  background: #ececec;
361
-  border-radius: 4px;
362
-  margin-top: 15px;
363
-  margin-left: 15px;
364
-  margin-right: 15px;
365
-  margin-bottom: 50px;
353
+	.tips_wrap {
354
+		display: flex;
355
+		align-items: center;
356
+		justify-content: space-evenly;
357
+		height: 40px;
358
+		background: #ececec;
359
+		border-radius: 4px;
360
+		margin-top: 15px;
361
+		margin-left: 15px;
362
+		margin-right: 15px;
363
+		margin-bottom: 50px;
366 364
 
367
-  .right {
368
-    display: flex;
369
-    align-items: center;
370
-  }
365
+		.right {
366
+			display: flex;
367
+			align-items: center;
368
+		}
371 369
 
372
-  .left {
373
-    display: flex;
374
-    align-items: center;
375
-  }
370
+		.left {
371
+			display: flex;
372
+			align-items: center;
373
+		}
376 374
 
377
-  .icon_img {
378
-    width: 40rpx;
379
-    height: 40rpx;
380
-    margin-right: 20rpx;
381
-  }
382
-}
375
+		.icon_img {
376
+			width: 40rpx;
377
+			height: 40rpx;
378
+			margin-right: 20rpx;
379
+		}
380
+	}
383 381
 
384
-.option_list {
385
-  margin-top: 30rpx;
386
-  padding: 0 30rpx;
382
+	.option_list {
383
+		margin-top: 30rpx;
384
+		padding: 0 30rpx;
387 385
 
388
-  .option_item {
389
-    display: flex;
390
-    justify-content: space-between;
391
-    align-items: center;
392
-    padding-bottom: 36rpx;
393
-    padding-top: 36rpx;
394
-    border-bottom: 2rpx solid #dfdfdf;
386
+		.option_item {
387
+			display: flex;
388
+			justify-content: space-between;
389
+			align-items: center;
390
+			padding-bottom: 36rpx;
391
+			padding-top: 36rpx;
392
+			border-bottom: 2rpx solid #dfdfdf;
395 393
 
396
-    .item_left {
397
-      display: flex;
398
-      align-items: center;
394
+			.item_left {
395
+				display: flex;
396
+				align-items: center;
399 397
 
400
-      image {
401
-        width: 32rpx;
402
-        height: 32rpx;
403
-        margin-right: 20rpx;
404
-      }
398
+				image {
399
+					width: 32rpx;
400
+					height: 32rpx;
401
+					margin-right: 20rpx;
402
+				}
405 403
 
406
-      .label {
407
-        font-size: 28rpx;
408
-        color: #202020;
409
-      }
410
-    }
404
+				.label {
405
+					font-size: 28rpx;
406
+					color: #202020;
407
+				}
408
+			}
411 409
 
412
-    .item-right {
413
-      display: flex;
414
-      justify-content: space-between;
415
-      align-items: center;
410
+			.item-right {
411
+				display: flex;
412
+				justify-content: space-between;
413
+				align-items: center;
416 414
 
417
-      text {
418
-        font-size: 24rpx;
419
-        color: #999999;
420
-        margin-right: 10rpx;
421
-      }
415
+				text {
416
+					font-size: 24rpx;
417
+					color: #999999;
418
+					margin-right: 10rpx;
419
+				}
422 420
 
423
-      .btn {
424
-        color: #108cff;
425
-      }
421
+				.btn {
422
+					color: #108cff;
423
+				}
426 424
 
427
-      .icon_img {
428
-        width: 40rpx;
429
-        height: 40rpx;
430
-      }
431
-    }
432
-  }
433
-}
425
+				.icon_img {
426
+					width: 40rpx;
427
+					height: 40rpx;
428
+				}
429
+			}
430
+		}
431
+	}
434 432
 </style>

+ 106 - 63
pages/clueDetail/page/clueDetail.vue

@@ -3,7 +3,8 @@
3 3
 		<view class="telPhone">
4 4
 			<view class="left">
5 5
 				<view class="phone">电话:
6
-					<show-real-text :real="clueDetail.telephone" :type='params.type' v-if="clueDetail.telephone"></show-real-text>
6
+					<show-real-text :real="clueDetail.telephone" :type='params.type'
7
+						v-if="clueDetail.telephone"></show-real-text>
7 8
 					<template v-if="clueDetail.telAddr">
8 9
 						({{clueDetail.telAddr}})
9 10
 					</template>
@@ -86,25 +87,27 @@
86 87
 			<view class="clue_tag_add_btn" @click="handleAddClueTag">
87 88
 				+ 添加标签
88 89
 			</view>
89
-			<u-tag :text="tag.name" plain plainFill :closable="true" @close="hanldeTagClose(tag)"  borderColor="#fff" v-for="(tag) in clueDetail.clueTags"
90
-				:key="tag.id" style="margin-left: 10px;margin-bottom: 10px;" :bgColor="tag.color" color="#fff"></u-tag>
90
+			<u-tag :text="tag.name" plain plainFill :closable="true" @close="hanldeTagClose(tag)" borderColor="#fff"
91
+				v-for="(tag) in clueDetail.clueTags" :key="tag.id" style="margin-left: 10px;margin-bottom: 10px;"
92
+				:bgColor="tag.color" color="#fff"></u-tag>
91 93
 		</view>
92 94
 
93 95
 
94 96
 		<yui-tabs :tabs="tabs" :swipeThreshold="10" v-model="activeIndex" :lineWidth="'120rpx'" :isLazyRender="false"
95
-			color="#108cff" titleActiveColor="#108cff" @click="tabClick" @change="tabChange" :swipeable="false">
97
+			color="#108cff" titleActiveColor="#108cff" :swipeable="false">
96 98
 			<template #clueInfo>
97
-				<clueInfo :clueId="clueDetail.id" v-if="clueDetail.id" :params="params"></clueInfo>
99
+				<clueInfo :clueId="clueDetail.id" v-if="clueDetail.id" :params="params" ref="clueInfoRef"></clueInfo>
98 100
 			</template>
99 101
 			<template #advertising>
100
-				<advertising :clueId="clueDetail.id" v-if="clueDetail.id"></advertising>
102
+				<advertising :clueId="clueDetail.id" v-if="clueDetail.id" ref="advertisingRef"></advertising>
101 103
 			</template>
102 104
 			<template #followRecord>
103
-				<followRecord :clueId="clueDetail.id" v-if="clueDetail.id"></followRecord>
105
+				<followRecord :clueId="clueDetail.id" v-if="clueDetail.id" ref="followRecordRef"></followRecord>
106
+			</template>
107
+			<template #callRecord>
108
+				<callRecord :clueId="clueDetail.id" :clueDetail="clueDetail" v-if="clueDetail.id" ref="callRecordRef">
109
+				</callRecord>
104 110
 			</template>
105
-		<!-- 	<template #callRecord>
106
-				<callRecord :clueId="clueDetail.id" v-if="clueDetail.id"></callRecord>
107
-			</template> -->
108 111
 		</yui-tabs>
109 112
 		<u-tabbar class="clueDetail_tabber" :fixed="true" inactiveColor="#ffffff" :placeholder="true"
110 113
 			:safeAreaInsetBottom="true">
@@ -114,17 +117,24 @@
114 117
 				@click="handleDistribution"></u-tabbar-item>
115 118
 			<u-tabbar-item text="写新跟进" icon="../../static/caseDetail/icon-follow.png"
116 119
 				@click="handleAddFollow"></u-tabbar-item>
120
+			<u-tabbar-item text="上传录音" icon="../../static/caseDetail/icon-record.png"
121
+				@click="handleUploadRecord"></u-tabbar-item>
117 122
 		</u-tabbar>
118
-		
119
-		<distribution-modal :clueDetail="clueDetail" ref="distribution" @handleSuccess="distributionSuccess"></distribution-modal>
120 123
 
121
-		<group-select class="clueTagsSelect" :list="clueTagGroupVoList" scrollHeight="720rpx" groupName="groupName" groupChild="clueTagDataList" label-key="name" value-key="id" placeholder="请选择线索标签" v-model="checkTags" multiple clearable ref="clueTag" @confirm="handleClueTagConfirm"></group-select>
124
+		<distribution-modal :clueDetail="clueDetail" ref="distribution"
125
+			@handleSuccess="distributionSuccess"></distribution-modal>
126
+
127
+		<group-select class="clueTagsSelect" :list="clueTagGroupVoList" scrollHeight="720rpx" groupName="groupName"
128
+			groupChild="clueTagDataList" label-key="name" value-key="id" placeholder="请选择线索标签" v-model="checkTags"
129
+			multiple clearable ref="clueTag" @confirm="handleClueTagConfirm"></group-select>
122 130
 	</view>
123 131
 </template>
124 132
 
125 133
 <script>
126
-	import { cloneDeep } from "lodash";
127
-import {
134
+	import {
135
+		cloneDeep
136
+	} from "lodash";
137
+	import {
128 138
 		selectDictLabel
129 139
 	} from "@/utils/util";
130 140
 	import clueInfo from "../tabs/clueInfo/index";
@@ -132,11 +142,11 @@ import {
132 142
 	import followRecord from "../tabs/followRecord/index";
133 143
 	import callRecord from "../tabs/callRecord/index";
134 144
 	export default {
135
-		components : {
145
+		components: {
136 146
 			clueInfo,
137 147
 			advertising,
138 148
 			followRecord,
139
-			callRecord
149
+			callRecord,
140 150
 		},
141 151
 		props: {
142 152
 			clueId: {
@@ -156,65 +166,74 @@ import {
156 166
 		},
157 167
 		data() {
158 168
 			return {
159
-				
160
-				showModal : false,
161
-				
169
+
170
+				showModal: false,
171
+
162 172
 				clueDetail: {},
163
-				
164
-				checkTags : [],
165
-				
173
+
174
+				checkTags: [],
175
+
166 176
 				clueTagGroupVoList: [],
167 177
 
168 178
 				crmHandelStatusDict: [],
169 179
 				crmCluePhaseDict: [],
170 180
 
171 181
 				tabs: [{
172
-					label: '基础信息',
173
-					slot: 'clueInfo'
174
-				}, {
175
-					label: '广告信息',
176
-					slot: 'advertising'
177
-				},{
178
-					label: '跟进记录',
179
-					slot: 'followRecord'
180
-				},
181
-				// {
182
-					// label: '通话记录',
183
-					// slot: 'callRecord'
184
-				// }
182
+						label: '基础信息',
183
+						slot: 'clueInfo'
184
+					}, {
185
+						label: '广告信息',
186
+						slot: 'advertising'
187
+					}, {
188
+						label: '跟进记录',
189
+						slot: 'followRecord'
190
+					},
191
+					{
192
+						label: '通话记录',
193
+						slot: 'callRecord'
194
+					}
185 195
 				],
186 196
 				activeIndex: 0,
187 197
 			}
188 198
 		},
189 199
 		methods: {
190
-			distributionSuccess(){
200
+			distributionSuccess() {
191 201
 				this.handleInit();
192 202
 			},
193
-			async hanldeTagClose(tag){
194
-				const { cfId : id , clueTags } = this.clueDetail;
195
-			 	const copyClueTags = cloneDeep(clueTags);
196
-				if(id == null){
197
-				  this.$message.error("修改异常");
198
-				  return;
203
+			async hanldeTagClose(tag) {
204
+				const {
205
+					cfId: id,
206
+					clueTags
207
+				} = this.clueDetail;
208
+				const copyClueTags = cloneDeep(clueTags);
209
+				if (id == null) {
210
+					this.$message.error("修改异常");
211
+					return;
199 212
 				}
200
-				const index = copyClueTags.findIndex(v=>v.id === tag.id);
201
-				if(index !== -1){
202
-				  copyClueTags.splice(index,1);
203
-				  const allTags = copyClueTags.map(v=>v.id).join(",");
204
-				  await uni.$u.api.updateClueFixedFieldsAllTags({ id , allTags });
205
-				  this.clueDetail.clueTags = copyClueTags;
206
-				  this.checkTags = this.clueDetail.clueTags.map(v=>v.id);
213
+				const index = copyClueTags.findIndex(v => v.id === tag.id);
214
+				if (index !== -1) {
215
+					copyClueTags.splice(index, 1);
216
+					const allTags = copyClueTags.map(v => v.id).join(",");
217
+					await uni.$u.api.updateClueFixedFieldsAllTags({
218
+						id,
219
+						allTags
220
+					});
221
+					this.clueDetail.clueTags = copyClueTags;
222
+					this.checkTags = this.clueDetail.clueTags.map(v => v.id);
207 223
 				}
208 224
 			},
209
-			handleDistribution(){
225
+			handleDistribution() {
210 226
 				this.$refs.distribution.show();
211 227
 			},
212
-			confirm(){
213
-				
228
+			confirm() {
229
+
214 230
 			},
215
-			async handleClueTagConfirm(){
231
+			async handleClueTagConfirm() {
216 232
 				const allTags = this.checkTags.join(",");
217
-				await uni.$u.api.updateClueFixedFieldsAllTags({ id : this.clueDetail.cfId , allTags });
233
+				await uni.$u.api.updateClueFixedFieldsAllTags({
234
+					id: this.clueDetail.cfId,
235
+					allTags
236
+				});
218 237
 				this.getDetail();
219 238
 			},
220 239
 			handleAddClueTag() {
@@ -276,7 +295,12 @@ import {
276 295
 			// 添加催记
277 296
 			handleAddFollow() {
278 297
 				uni.navigateTo({
279
-					url:`/pages/addFollow/index?clueId=${this.clueId}`
298
+					url: `/pages/addFollow/index?clueId=${this.clueId}`
299
+				})
300
+			},
301
+			handleUploadRecord() {
302
+				uni.navigateTo({
303
+					url: `/pages/uploadRecord/index?clueId=${this.clueId}`
280 304
 				})
281 305
 			},
282 306
 			// 初始化详情页
@@ -287,22 +311,37 @@ import {
287 311
 				this.$getDicts('crm_clue_phase').then((res => {
288 312
 					this.crmCluePhaseDict = res.filter(v => v.dictValue !== '6');
289 313
 				}))
290
-				uni.$u.api.getClueTagGroupVoList({ tagGroupApplication : '1' }).then(({data})=>{
314
+				uni.$u.api.getClueTagGroupVoList({
315
+					tagGroupApplication: '1'
316
+				}).then(({
317
+					data
318
+				}) => {
291 319
 					this.clueTagGroupVoList = data;
292 320
 				});
293 321
 				this.getDetail();
294 322
 			},
295
-			getDetail(){
323
+			getDetail() {
296 324
 				uni.$u.api.getClueMainInfoVoById({
297 325
 					id: this.clueId
298 326
 				}).then(res => {
299 327
 					this.clueDetail = res.data;
300
-					this.checkTags = this.clueDetail.clueTags.map(v=>v.id);
328
+					this.checkTags = this.clueDetail.clueTags.map(v => v.id);
301 329
 				});
302 330
 			},
303 331
 			// 标签点击事件
304 332
 			tabClick(index, item) {
305
-				// console.log("tabClick", index, item);
333
+				// // 根据索引调用对应组件的getData方法
334
+				// const componentRefs = [
335
+				// 	this.$refs.clueInfoRef,
336
+				// 	this.$refs.advertisingRef,
337
+				// 	this.$refs.followRecordRef,
338
+				// 	this.$refs.callRecordRef
339
+				// ];
340
+
341
+				// const currentComponent = componentRefs[index];
342
+				// if (currentComponent && typeof currentComponent.getData === 'function') {
343
+				// 	currentComponent.getData();
344
+				// }
306 345
 			},
307 346
 			// 标签切换事件
308 347
 			tabChange(index, item) {
@@ -316,18 +355,20 @@ import {
316 355
 </script>
317 356
 
318 357
 <style lang="scss" scoped>
319
-	.clueTagsSelect{
358
+	.clueTagsSelect {
320 359
 		height: 0;
321 360
 		overflow: hidden;
322 361
 	}
362
+
323 363
 	.clue_tag_wrap {
324 364
 		display: flex;
325 365
 		align-items: center;
326 366
 		background-color: #fff;
327
-		padding:0  10px;
367
+		padding: 0 10px;
328 368
 		margin: 20px 0;
329 369
 		flex-wrap: wrap;
330 370
 		min-height: 50px;
371
+
331 372
 		.clue_tag_add_btn {
332 373
 			font-size: 14px;
333 374
 			color: #108cff;
@@ -371,9 +412,11 @@ import {
371 412
 			background: #fff;
372 413
 			padding: 20px;
373 414
 			justify-content: space-between;
374
-			.left{
415
+
416
+			.left {
375 417
 				display: flex;
376 418
 			}
419
+
377 420
 			.copy_btn {
378 421
 				color: #4fa5fe;
379 422
 				margin-left: 10px;

+ 2 - 2
pages/clueDetail/tabs/advertising/index.vue

@@ -92,7 +92,7 @@
92 92
 					}
93 93
 				});
94 94
 			},
95
-			async getClueMainInfoById() {
95
+			async getData() {
96 96
 				const {
97 97
 					data
98 98
 				} = await uni.$u.api.getClueAdInfoByClueId({
@@ -102,7 +102,7 @@
102 102
 			}
103 103
 		},
104 104
 		created() {
105
-			this.getClueMainInfoById();
105
+			this.getData();
106 106
 		},
107 107
 		data() {
108 108
 			return {

+ 196 - 4
pages/clueDetail/tabs/callRecord/index.vue

@@ -1,12 +1,204 @@
1 1
 <template>
2
-	<view class="callRecord_wrap">12323</view>
2
+	<view class="callRecord_wrap">
3
+		<view v-if="dataList.length > 0" class="timeline-list">
4
+			<view v-for="(data, index) in dataList" :key="index" class="timeline-item">
5
+				<view class="timeline-icon">
6
+					<uni-icons type="phone" size="20" color="#6cc040"></uni-icons>
7
+				</view>
8
+				<view class="timeline-content">
9
+					<view class="timestamp">{{ titleText(data) }}</view>
10
+					<view class="info_top">
11
+						<view class="info_left">
12
+							<text class="name_text">{{ data.createNickName }} 打给 {{ clueDetail.name }}</text>
13
+						</view>
14
+						<view class="info_right">
15
+							{{ data.type === 1 ? data.followState : '' }}
16
+						</view>
17
+						<view class="delete-btn" @tap="handleDelete({ id: data.id })">
18
+							<uni-icons type="trash" size="20" color="#ff6666"></uni-icons>
19
+						</view>
20
+					</view>
21
+					<view class="file_name">{{data.fileName}}</view>
22
+					<view class="call_file">
23
+						<view class="audio-container">
24
+							<audio :src="data.fileUrl" controls ></audio>
25
+						</view>
26
+					</view>
27
+				</view>
28
+			</view>
29
+		</view>
30
+		<view v-else class="empty_wrap">
31
+			<uni-icons type="empty" size="60" color="#cccccc"></uni-icons>
32
+			<text class="empty-text">暂无通话记录</text>
33
+		</view>
34
+	</view>
3 35
 </template>
4
-
5 36
 <script>
37
+	export default {
38
+		props: {
39
+			clueId: {
40
+				type: [String, Number],
41
+				required: true
42
+			},
43
+			clueDetail: {
44
+				type: Object,
45
+				default: () => ({})
46
+			}
47
+		},
48
+		data() {
49
+			return {
50
+				dataList: []
51
+			}
52
+		},
53
+		methods: {
54
+			titleText(data) {
55
+				return `${data.createTime} 号码(${data.caller}) ${(data.type === 1 ?  data.hangupCauseName: '上传录音')}`
56
+			},
57
+			handleDelete({
58
+				id
59
+			}) {
60
+				uni.showModal({
61
+					title: '提示',
62
+					content: '是否确定删除?',
63
+					confirmColor: '#6cc040',
64
+					success: async (res) => {
65
+						if (res.confirm) {
66
+							try {
67
+								await uni.$u.api.deleteClueFile([id])
68
+								uni.showToast({
69
+									title: '删除成功',
70
+									icon: 'success',
71
+									duration: 2000
72
+								})
73
+								this.getData()
74
+							} catch (error) {
75
+								uni.showToast({
76
+									title: '删除失败',
77
+									icon: 'error',
78
+									duration: 2000
79
+								})
80
+							}
81
+						}
82
+					}
83
+				})
84
+			},
85
+			async getData() {
86
+				const {
87
+					data
88
+				} = await uni.$u.api.getCallClueFileByClueId({
89
+					clueId: this.clueId
90
+				});
91
+				this.dataList = data;
92
+			},
93
+		},
94
+		mounted() {
95
+			this.getData();
96
+		},
97
+		beforeDestroy(){
98
+			uni.$off('uploadRecordSuccess');  
99
+		},
100
+		created() {
101
+			uni.$on('uploadRecordSuccess',()=>{
102
+				this.getData();
103
+			});
104
+		}
105
+	}
6 106
 </script>
7
-
8 107
 <style lang="scss" scoped>
9
-	.callRecord_wrap{
108
+	.callRecord_wrap {
109
+		position: relative;
110
+		padding: 20rpx;
10 111
 		min-height: 400px;
112
+		box-sizing: border-box;
113
+
114
+		.loading-mask {
115
+			position: absolute;
116
+			top: 0;
117
+			left: 0;
118
+			right: 0;
119
+			bottom: 0;
120
+			background-color: rgba(255, 255, 255, 0.8);
121
+			display: flex;
122
+			justify-content: center;
123
+			align-items: center;
124
+			z-index: 999;
125
+		}
126
+
127
+		.timeline-list {
128
+			.timeline-item {
129
+				display: flex;
130
+				margin-bottom: 30rpx;
131
+
132
+				.timeline-icon {
133
+					width: 60rpx;
134
+					height: 60rpx;
135
+					border-radius: 50%;
136
+					background-color: #f1f3f4;
137
+					display: flex;
138
+					justify-content: center;
139
+					align-items: center;
140
+					margin-right: 20rpx;
141
+					flex-shrink: 0;
142
+				}
143
+
144
+				.timeline-content {
145
+					width:690rpx;
146
+
147
+					.timestamp {
148
+						font-size: 24rpx;
149
+						color: #999;
150
+						margin-bottom: 10rpx;
151
+					}
152
+
153
+					.info_top {
154
+						display: flex;
155
+						justify-content: space-between;
156
+						align-items: center;
157
+						margin-bottom: 20rpx;
158
+
159
+						.info_left {
160
+							.name_text {
161
+								font-size: 28rpx;
162
+								font-weight: 500;
163
+							}
164
+						}
165
+
166
+						.info_right {
167
+							color: #6cc040;
168
+							font-size: 26rpx;
169
+						}
170
+					}
171
+					.file_name{
172
+						color: #999;
173
+						font-size: 26rpx;
174
+					}
175
+
176
+					.call_file {
177
+						width: 100%;
178
+						border-radius: 12rpx;
179
+						display: flex;
180
+						align-items: center;
181
+						justify-content: space-between;
182
+						box-sizing: border-box;
183
+
184
+						.audio-container {
185
+							width: 100%;
186
+						}
187
+					}
188
+				}
189
+			}
190
+		}
191
+
192
+		.empty_wrap {
193
+			text-align: center;
194
+			padding: 100rpx 0;
195
+
196
+			.empty-text {
197
+				display: block;
198
+				margin-top: 30rpx;
199
+				color: #999;
200
+				font-size: 28rpx;
201
+			}
202
+		}
11 203
 	}
12 204
 </style>

+ 3 - 3
pages/clueDetail/tabs/clueInfo/index.vue

@@ -134,10 +134,10 @@
134 134
 				if (!isEqual(this.cloneClueDetail, this.clueDetail)) {
135 135
 					await uni.$u.api.updateClueMainInfo(this.clueDetail);
136 136
 					uni.$u.toast("修改成功");
137
-					this.getClueMainInfoById();
137
+					this.getData();
138 138
 				}
139 139
 			},
140
-			async getClueMainInfoById() {
140
+			async getData() {
141 141
 				const {
142 142
 					data
143 143
 				} = await uni.$u.api.getClueMainInfoById({
@@ -148,7 +148,7 @@
148 148
 			}
149 149
 		},
150 150
 		created() {
151
-			this.getClueMainInfoById();
151
+			this.getData();
152 152
 		},
153 153
 		data() {
154 154
 			return {

+ 4 - 4
pages/clueDetail/tabs/followRecord/index.vue

@@ -70,7 +70,7 @@
70 70
 				this.showModal = true;
71 71
 			},
72 72
 			isEmpty,
73
-			async getCaseFollowList() {
73
+			async getData() {
74 74
 				const {
75 75
 					data
76 76
 				} = await uni.$u.api.getClueFollowList({
@@ -81,19 +81,19 @@
81 81
 			async confirm() {
82 82
 				await uni.$u.api.deleteClueFollow([this.currentDeleteFollowId]);
83 83
 				uni.$u.toast("删除成功");
84
-				this.getCaseFollowList();
84
+				this.getData();
85 85
 				this.showModal = false;
86 86
 			},
87 87
 		},
88 88
 		mounted() {
89
-			this.getCaseFollowList();
89
+			this.getData();
90 90
 		},
91 91
 		beforeDestroy(){
92 92
 			uni.$off('addFollowSuccess');  
93 93
 		},
94 94
 		created() {
95 95
 			uni.$on('addFollowSuccess',()=>{
96
-				this.getCaseFollowList();
96
+				this.getData();
97 97
 			});
98 98
 		}
99 99
 	}

+ 7 - 18
pages/login/index - 副本 (2).vue

@@ -44,7 +44,7 @@
44 44
 		navigateToCallRecordingSettings,
45 45
 		jumpToPermissionPage,
46 46
 		makePhoneCall,
47
-		allRecorderFilesAction,
47
+		allRecorderFilesAction
48 48
 	} from '@/uni_modules/yao-lister';
49 49
 
50 50
 
@@ -102,7 +102,7 @@
102 102
 				})
103 103
 			},
104 104
 			makePhoneCall() {
105
-				makePhoneCall('15099989786')
105
+				makePhoneCall('13417106969')
106 106
 				// uni.makePhoneCall({
107 107
 				// 	phoneNumber: '13771854499', // 电话号码
108 108
 				// 	success: function() {
@@ -115,16 +115,12 @@
115 115
 			},
116 116
 			startListerPhone() {
117 117
 				startPhoneListener(res => {
118
-					uni.showToast({
119
-						icon: 'success',
120
-						title: '电话监听开启',
121
-						duration: 2000
122
-					});
123
-					this.phoneState = res + "012"
118
+					console.log(res,"监听结果==============================")
124 119
 				})
125 120
 			},
126 121
 			stopListerPhone() {
127 122
 				stopPhoneListener(res => {
123
+					console.log(res,"3123123123");
128 124
 					uni.showToast({
129 125
 						icon: 'success',
130 126
 						title: res,
@@ -135,7 +131,8 @@
135 131
 
136 132
 			async initPhoneStateListener() {
137 133
 				// 动态申请权限
138
-				const permissions = ["android.permission.READ_PHONE_STATE",
134
+				const permissions = [
135
+					"android.permission.READ_PHONE_STATE",
139 136
 					"android.permission.ANSWER_PHONE_CALLS", "android.permission.CALL_PHONE",
140 137
 					"android.permission.MANAGE_EXTERNAL_STORAGE",
141 138
 					"android.permission.READ_EXTERNAL_STORAGE",
@@ -143,15 +140,7 @@
143 140
 					"android.permission.READ_PHONE_NUMBERS",
144 141
 					"android.permission.FOREGROUND_SERVICE"
145 142
 				]
146
-				const result = await plus.android.requestPermissions(permissions, 1001)
147
-
148
-				if (result.granted) {
149
-					startListening()
150
-					// 监听原生事件
151
-					uni.$on('PHONE_STATE_CHANGE', (state) => {
152
-						this.phoneState = state
153
-					})
154
-				}
143
+				await plus.android.requestPermissions(permissions, 1001)
155 144
 			}
156 145
 		},
157 146
 	}

+ 233 - 0
pages/uploadRecord/index.vue

@@ -0,0 +1,233 @@
1
+<template>
2
+	<view class="follow_wrap">
3
+		<u-navbar placeholder :autoBack="true" title="上传录音" @rightClick="handleNavSaveClick">
4
+			<view class="u-nav-slot" slot="right">
5
+				保存
6
+			</view>
7
+		</u-navbar>
8
+		<view class="form_wrap">
9
+			<u--form labelPosition="left" labelWidth="80" :model="form" :rules="rules" ref="form" class="form_wrap">
10
+				<u-form-item label="操作人">
11
+					{{userInfo.nickName}}
12
+				</u-form-item>
13
+
14
+				<u-form-item label="主叫号码" prop="caller">
15
+					<u--input clearable v-model="form.caller" placeholder="请输入主叫号码"></u--input>
16
+				</u-form-item>
17
+
18
+				<u-form-item label="被叫号码" prop="callee">
19
+					<u--input clearable v-model="form.callee" placeholder="请输入被叫号码"></u--input>
20
+				</u-form-item>
21
+
22
+				<u-form-item label="录音" prop="fileUrl">
23
+					<ld-select :list="filelist" label-key="fileName" value-key="filePath" placeholder="请选择"
24
+						v-model="form.fileUrl" :border="false" @change="handleFileChange"></ld-select>
25
+					<u-icon slot="right" name="arrow-right"></u-icon>
26
+				</u-form-item>
27
+
28
+				<u-form-item label="备注" prop="remark">
29
+					<u--textarea v-model="form.remark" placeholder="请输入内容" count confirmType="done" maxlength="500">
30
+					</u--textarea>
31
+				</u-form-item>
32
+			</u--form>
33
+		</view>
34
+		
35
+		<!-- 录音确认对话框 -->
36
+		<u-popup :show="showConfirmDialog" mode="center" :closeable="false" :closeOnClickOverlay="false">
37
+			<view class="confirm-dialog">
38
+				<view class="dialog-title">确认录音</view>
39
+				<view class="dialog-content">
40
+					<view class="file-info">
41
+						<view class="file-name">{{selectedFile.fileName}}</view>
42
+					</view>
43
+					<view class="play-section">
44
+						<audio :src="selectedFile.filePath" controls class="audio-player"></audio>
45
+					</view>
46
+				</view>
47
+				<view class="dialog-buttons">
48
+					<u-button text="取消" @click="cancelConfirm" :plain="true"></u-button>
49
+					<u-button text="确认" @click="confirmUpload" type="primary"></u-button>
50
+				</view>
51
+			</view>
52
+		</u-popup>
53
+		
54
+		<!-- <drag-button :isDock="true" /> -->
55
+	</view>
56
+</template>
57
+
58
+<script>
59
+	export default {
60
+		computed: {
61
+			userInfo() {
62
+				return this.$store.state.user.userInfo;
63
+			},
64
+			filelist(){
65
+				return this.$store.state.call.filelist;
66
+			},
67
+		},
68
+		data() {
69
+			return {
70
+				rules: {
71
+					'caller': {
72
+						type: 'string',
73
+						required: true,
74
+						message: '请输入内容',
75
+						trigger: ['blur', 'change']
76
+					},
77
+					'callee': {
78
+						type: 'string',
79
+						required: true,
80
+						message: '请输入内容',
81
+						trigger: ['blur', 'change']
82
+					},
83
+					'fileUrl': {
84
+						type: 'string',
85
+						required: true,
86
+						message: '请输入内容',
87
+						trigger: ['blur', 'change']
88
+					},
89
+				},
90
+				form: {
91
+					clueId: undefined,
92
+					remark: '',
93
+					fileUrl : undefined,
94
+					fileName : undefined,
95
+					type : '3',
96
+					list : [],
97
+					caller : '',
98
+					callee : ''
99
+				},
100
+				// 对话框相关状态
101
+				showConfirmDialog: false,
102
+				selectedFile: {
103
+					filePath: '',
104
+					fileName: ''
105
+				},
106
+			}
107
+		},
108
+		methods: {
109
+			handleFileChange(file){
110
+				// {
111
+				    // "filePath": "/storage/emulated/0/Recordings/Record/Call/15099989786 2025-11-11 09-41-57.m4a",
112
+				    // "fileName": "15099989786 2025-11-11 09-41-57.m4a"
113
+				// }
114
+				
115
+				const {filePath ,  fileName} = file;
116
+				
117
+				// 显示确认对话框
118
+				this.selectedFile = { filePath, fileName };
119
+				this.showConfirmDialog = true;
120
+			},
121
+			
122
+			// 取消确认
123
+			cancelConfirm() {
124
+				this.showConfirmDialog = false;
125
+				this.selectedFile = { filePath: '', fileName: '' };
126
+				this.form.fileUrl = undefined;
127
+				this.form.fileName = undefined;
128
+			},
129
+			
130
+			// 确认上传
131
+			async confirmUpload() {
132
+				try {
133
+					// 调用上传接口
134
+					const result = await uni.$u.api.uploadFile(this.selectedFile.filePath);
135
+					if (result && result.data) {
136
+						result.data.remark = this.form.remark;
137
+						result.data.fileName = result.data.name;
138
+						result.data.fileUrl = result.data.url;
139
+						this.form.list = [result.data];
140
+					}
141
+					// 设置表单值
142
+					this.form.fileUrl = this.selectedFile.filePath;
143
+					this.form.fileName = this.selectedFile.fileName;
144
+					
145
+					// 关闭对话框
146
+					this.showConfirmDialog = false;
147
+				} catch (error) {
148
+					uni.hideLoading();
149
+					console.error('上传失败:', error);
150
+					uni.$u.toast('上传失败,请重试');
151
+				}
152
+			},
153
+			
154
+			handleNavSaveClick() {
155
+				this.$refs.form.validate().then(async () => {
156
+					await uni.$u.api.saveClueFile(this.form);
157
+					uni.$u.toast("保存成功");
158
+					this.timer = setTimeout(() => {
159
+						uni.$emit('uploadRecordSuccess');
160
+						uni.navigateBack();
161
+						clearTimeout(this.timer);
162
+					}, 1000)
163
+				})
164
+			},
165
+		},
166
+		onLoad(option) {
167
+			this.$store.dispatch("call/getFileList");
168
+			this.form.clueId = option.clueId;
169
+		}
170
+	}
171
+</script>
172
+
173
+<style lang="scss" scoped>
174
+	.form_wrap {
175
+		background-color: #fff;
176
+		margin: 20rpx 0;
177
+
178
+		.form_wrap {
179
+			::v-deep .u-form-item__body {
180
+				padding: 20rpx 40rpx;
181
+			}
182
+		}
183
+	}
184
+	
185
+	/* 录音确认对话框样式 */
186
+	.confirm-dialog {
187
+		padding: 40rpx;
188
+		min-width: 500rpx;
189
+		background: #fff;
190
+		border-radius: 20rpx;
191
+		
192
+		.dialog-title {
193
+			text-align: center;
194
+			font-size: 32rpx;
195
+			font-weight: bold;
196
+			color: #333;
197
+			margin-bottom: 40rpx;
198
+		}
199
+		
200
+		.dialog-content {
201
+			margin-bottom: 40rpx;
202
+			
203
+			.file-info {
204
+				margin-bottom: 30rpx;
205
+				
206
+				.file-name {
207
+					background: #f5f5f5;
208
+					padding: 20rpx;
209
+					border-radius: 10rpx;
210
+					font-size: 28rpx;
211
+					color: #333;
212
+					word-break: break-all;
213
+					text-align: center;
214
+				}
215
+			}
216
+			
217
+			.play-section {
218
+				.audio-player {
219
+					width: 100%;
220
+				}
221
+			}
222
+		}
223
+		
224
+		.dialog-buttons {
225
+			display: flex;
226
+			gap: 30rpx;
227
+			
228
+			::v-deep .u-button {
229
+				flex: 1;
230
+			}
231
+		}
232
+	}
233
+</style>

二进制
static/caseDetail/icon-record.png


+ 3 - 1
store/index.js

@@ -4,6 +4,7 @@ import app from "./modules/app"
4 4
 import user from "./modules/user"
5 5
 import dict from "./modules/dict"
6 6
 import follow from "./modules/follow"
7
+import call from "./modules/call.js"
7 8
 import getters from "./getters";
8 9
 import createPersistedState from 'vuex-persistedstate'
9 10
 import {
@@ -16,7 +17,8 @@ const store = new Vuex.Store({
16 17
 		app,
17 18
 		user,
18 19
 		dict,
19
-		follow
20
+		follow,
21
+		call
20 22
 	},
21 23
 	getters,
22 24
 	state: {

+ 240 - 0
store/modules/call.js

@@ -0,0 +1,240 @@
1
+import {
2
+	startPhoneListener,
3
+	stopPhoneListener,
4
+	checkIsAutoRecord,
5
+	toCallAutoRecorderPage,
6
+	navigateToCallRecordingSettings,
7
+	jumpToPermissionPage,
8
+	makePhoneCall,
9
+	allRecorderFilesAction
10
+} from '@/uni_modules/yao-lister';
11
+import permision from "@/js_sdk/wa-permission/permission.js";
12
+
13
+export default {
14
+	namespaced: true,
15
+	state: {
16
+		filelist: [],
17
+		isAutoRecord: false, // 通话自动录音状态
18
+		hasStoragePermission: false, // 文件访问权限状态
19
+		hasReadPhoneStatePermission: false, // 读取手机状态权限
20
+		hasCallPhonePermission: false, // 拨打电话权限
21
+		hasReadCallLogPermission: false, // 读取通话记录权限
22
+		hasReadPhoneNumbersPermission: false, // 读取电话号码权限
23
+		form: {
24
+			clueId: undefined,
25
+			fileUrl : undefined,
26
+			fileName : undefined,
27
+			type : '3',
28
+			list : [],
29
+			caller : '',
30
+			callee : ''
31
+		},
32
+	},
33
+	mutations: {
34
+		SET_FILE_LIST: (state, data) => {
35
+			state.filelist = data.data
36
+		},
37
+		SET_AUTO_RECORD: (state, value) => {
38
+			state.isAutoRecord = value;
39
+		},
40
+		SET_STORAGE_PERMISSION: (state, value) => {
41
+			state.hasStoragePermission = value;
42
+		},
43
+		SET_READ_PHONE_STATE_PERMISSION: (state, value) => {
44
+			state.hasReadPhoneStatePermission = value;
45
+		},
46
+		SET_CALL_PHONE_PERMISSION: (state, value) => {
47
+			state.hasCallPhonePermission = value;
48
+		},
49
+		SET_READ_CALL_LOG_PERMISSION: (state, value) => {
50
+			state.hasReadCallLogPermission = value;
51
+		},
52
+		SET_READ_PHONE_NUMBERS_PERMISSION: (state, value) => {
53
+			state.hasReadPhoneNumbersPermission = value;
54
+		},
55
+	},
56
+	actions: {
57
+		getFileList({dispatch,state, commit}) {
58
+			dispatch("checkStoragePermission").then(()=>{
59
+				if(state.hasStoragePermission){
60
+					allRecorderFilesAction(res => {
61
+						if(res && res.length > 0) {
62
+							// 将文件路径数组转换为包含filePath和fileName的对象数组
63
+							const fileListPromise = res.map(filePath => {
64
+								return new Promise((resolve) => {
65
+									// 从路径中提取文件名
66
+									const fileName = filePath.split('/').pop();
67
+									
68
+									// 获取文件信息,包括创建时间
69
+									plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
70
+										entry.getMetadata((metadata) => {
71
+											resolve({
72
+												filePath: filePath,
73
+												fileName: fileName,
74
+												createTime: metadata.modificationTime ? metadata.modificationTime : 0, // 文件创建时间
75
+											});
76
+										}, (error) => {
77
+											// 如果获取metadata失败,返回基本信息
78
+											resolve({
79
+												filePath: filePath,
80
+												fileName: fileName,
81
+												createTime: 0, // 设为0,排序时会在最前面
82
+											});
83
+										});
84
+									}, (error) => {
85
+										// 如果解析文件路径失败,返回基本信息
86
+										resolve({
87
+											filePath: filePath,
88
+											fileName: fileName,
89
+											createTime: 0,
90
+											size: 0
91
+										});
92
+									});
93
+								});
94
+							});
95
+							
96
+							// 等待所有文件信息获取完成后进行排序
97
+							Promise.all(fileListPromise).then((fileList) => {
98
+								// 按创建时间排序,最新的文件排在前面(降序)
99
+								fileList.sort((a, b) => {
100
+									return b.createTime - a.createTime;
101
+								});
102
+								
103
+								// 提交文件列表到state
104
+								commit('SET_FILE_LIST', { data: fileList });
105
+							}).catch((error) => {
106
+								console.error('获取文件列表失败:', error);
107
+								// 即使排序失败,也尝试提交原始列表
108
+								const fallbackFileList = res.map(filePath => {
109
+									const fileName = filePath.split('/').pop();
110
+									return {
111
+										filePath: filePath,
112
+										fileName: fileName
113
+									};
114
+								});
115
+								commit('SET_FILE_LIST', { data: fallbackFileList });
116
+							});
117
+						}
118
+					})
119
+				}else{
120
+					dispatch("requestStoragePermission");
121
+				}
122
+			})
123
+			
124
+		},
125
+
126
+		// 检查所有权限状态
127
+		checkAllPermissions({ commit }) {
128
+			return new Promise((resolve, reject) => {
129
+				try {
130
+					// 检查通话自动录音状态
131
+					const autoRecordStatus = checkIsAutoRecord();
132
+					commit('SET_AUTO_RECORD', autoRecordStatus);
133
+
134
+					// 检查文件访问权限
135
+					this.dispatch('call/checkStoragePermission').then(storagePermission => {
136
+						commit('SET_STORAGE_PERMISSION', storagePermission);
137
+
138
+						// 检查其他权限
139
+						const permissions = [
140
+							{
141
+								id: 'android.permission.READ_PHONE_STATE',
142
+								mutation: 'SET_READ_PHONE_STATE_PERMISSION'
143
+							},
144
+							{
145
+								id: 'android.permission.CALL_PHONE',
146
+								mutation: 'SET_CALL_PHONE_PERMISSION'
147
+							},
148
+							{
149
+								id: 'android.permission.READ_CALL_LOG',
150
+								mutation: 'SET_READ_CALL_LOG_PERMISSION'
151
+							},
152
+							{
153
+								id: 'android.permission.READ_PHONE_NUMBERS',
154
+								mutation: 'SET_READ_PHONE_NUMBERS_PERMISSION'
155
+							}
156
+						];
157
+
158
+						const permissionPromises = permissions.map(perm => {
159
+							return permision.requestAndroidPermission(perm.id).then(result => {
160
+								commit(perm.mutation, result === 1);
161
+								return { id: perm.id, granted: result === 1 };
162
+							});
163
+						});
164
+
165
+						Promise.all(permissionPromises).then(results => {
166
+								resolve({
167
+									autoRecordStatus,
168
+									storagePermission,
169
+									permissions: results
170
+								});
171
+						});
172
+					});
173
+				} catch (error) {
174
+					reject(error);
175
+				}
176
+			});
177
+		},
178
+
179
+		// 检查文件访问权限
180
+		checkStoragePermission({ commit }) {
181
+			return new Promise((resolve) => {
182
+				let hasPermission = false;
183
+				try {
184
+					// 获取Android版本号
185
+					let androidVersion = 0;
186
+					uni.getSystemInfo({
187
+						success: (res) => {
188
+							if (res.system && res.system.includes('Android')) {
189
+								const versionMatch = res.system.match(/Android (\d+\.?\d*)/);
190
+								if (versionMatch && versionMatch[1]) {
191
+									androidVersion = parseFloat(versionMatch[1]);
192
+								}
193
+							}
194
+
195
+							if (androidVersion >= 11) {
196
+								// Android 11及以上版本,使用MANAGE_EXTERNAL_STORAGE权限
197
+								const Environment = plus.android.importClass('android.os.Environment');
198
+								hasPermission = Environment.isExternalStorageManager();
199
+								commit('SET_STORAGE_PERMISSION', hasPermission);
200
+								resolve(hasPermission);
201
+							} else {
202
+								// Android 11以下版本,使用READ_EXTERNAL_STORAGE权限
203
+								permision.requestAndroidPermission('android.permission.READ_EXTERNAL_STORAGE')
204
+									.then(result => {
205
+										hasPermission = result === 1;
206
+										commit('SET_STORAGE_PERMISSION', hasPermission);
207
+										resolve(hasPermission);
208
+									});
209
+							}
210
+						}
211
+					});
212
+				} catch (e) {
213
+					hasPermission = false;
214
+					commit('SET_STORAGE_PERMISSION', hasPermission);
215
+					resolve(hasPermission);
216
+				}
217
+			});
218
+		},
219
+
220
+		// 请求文件访问权限
221
+		requestStoragePermission() {
222
+			jumpToPermissionPage();
223
+		},
224
+
225
+		// 跳转到通话录音设置页面
226
+		toCallRecorderSettings() {
227
+			toCallAutoRecorderPage();
228
+		},
229
+
230
+		// 跳转到系统通话录音界面
231
+		toSystemRecorderSettings() {
232
+			navigateToCallRecordingSettings();
233
+		},
234
+
235
+		// 打开系统设置
236
+		handleOpenSet() {
237
+			permision.gotoAppPermissionSetting('android.permission.RECORD_AUDIO');
238
+		}
239
+	},
240
+}

+ 3 - 3
store/modules/user.js

@@ -28,9 +28,9 @@ export default {
28 28
 		netConfig: {
29 29
 			// http://59.42.9.166:9520/proxy
30 30
 			// http://10.0.7.100:9500
31
-			ip : "https://crm.nanjingshiyu.com/prod-api", // ip
32
-			// ip: "http://172.16.7.200", // ip
33
-			port: "", // 端口
31
+			// ip : "https://crm.nanjingshiyu.com/prod-api", // ip
32
+			ip: "http://172.16.7.200", // ip
33
+			port: "9500", // 端口
34 34
 			isCallOff : "2", // 开关
35 35
 			folder: "", // 文件夹
36 36
 			webRtcIp : "", // 呼叫中心的ip

+ 4 - 1
utils/api.js

@@ -26,7 +26,7 @@ const install = (Vue, vm) => {
26 26
 		addCaseOutEquipment : (data = {})=>http.post("system/caseOutEquipment",data), // 申请mac地址授权
27 27
 		checkOutEquipment : (params = {},config)=>http.get("/system/caseOutEquipment/checkOutEquipment",{params,...config}), // 检测外访设备是否有申请通过记录
28 28
 		concatRecord : (files,params = {})=>http.upload('/fileVisit/concatRecord',{files, params , name: 'files', timeout: 1000 * 60 * 10, custom : { loadingText : "上传中" }}), // 合并录音并上传
29
-		uploadFile : (url,params = {})=> http.upload('/fileVisit/outVisitUpload',{filePath: url,params ,name : "file",timeout: 1000 * 60 * 10,custom : { loadingText : "上传中" }}), // 上传文件
29
+		uploadFile : (url,params = {})=> http.upload('/file/upload',{filePath: url,params ,name : "file",timeout: 1000 * 60 * 10,custom : { loadingText : "上传中" }}), // 上传文件
30 30
 		watermarkUpload : (url,params = {})=> http.upload('/fileVisit/watermarkUpload',{filePath: url, params, name : "file",timeout: 1000 * 60 * 10,custom : { loadingText : "上传中" }}), // 上传文件
31 31
 		getOutPolyline : (params = {})=>http.get("/system/caseOutPolyline/getOutPolyline",{ params , custom : { loading: false } }), // 请求外访轨迹列表
32 32
 		addOutPolyline : (data = {})=>http.post("/system/caseOutPolyline/addOutPolyline",data,{ custom : { loading: false } }), // 添加外访轨迹
@@ -67,10 +67,13 @@ const install = (Vue, vm) => {
67 67
 		getIdByName:(params,config={})=>http.get(store.state.user.path + "/clueMainInfo/getIdByName",{ params,...config }),
68 68
 		addClueMainInfo : (data,config = {})=>http.post(store.state.user.path + '/clueMainInfo/addClueMainInfo',data),
69 69
 		saveLog:(data,config={})=>http.post("/system/access/saveLog",data,{ custom : { loading: false } }),
70
+		saveClueFile:(data,config={})=>http.post(store.state.user.path + '/clueFile/saveClueFile',data),
70 71
 		updateClueMainInfo:(data,config={})=>http.post(store.state.user.path + '/clueMainInfo/updateClueMainInfo',data),
71 72
 		selectPromotionStats:(data,config={})=>http.post(store.state.user.path + '/promotionStats/selectPromotionStats',data),
72 73
 		firstLogin : (data)=> http.post('/system/user/firstLogin',data),
73 74
 		shiYuWxLoginGetInfo : (params)=> http.get('/auth/shiYuWxLoginGetInfo',{params, custom : { noAuth: true }}),
75
+		deleteClueFile : (data)=> http.delete(store.state.user.path + '/clueFile/deleteClueFile',data),
76
+		getCallClueFileByClueId : (params)=> http.get(store.state.user.path + '/clueFile/getCallClueFileByClueId',{params, custom : { noAuth: true }}),
74 77
 	}
75 78
 }
76 79