Explorar o código

feat:价格趋势

zhangxin hai 2 semanas
pai
achega
bb33af96e6

+ 38 - 27
components/add-inquiry-dialog/index.scss

@@ -1,38 +1,49 @@
1 1
 ::v-deep .u-modal__content{
2 2
 	flex-direction: column;
3 3
 	gap: 30rpx;
4
-	.modal_wrap{
5
-		border-radius: 40rpx;
6
-		padding: 10rpx 20rpx;
7
-		box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
4
+	.title_wrap{
8 5
 		display: flex;
9 6
 		align-items: center;
10
-		.item{
11
-			width: 33%;
7
+		gap: 8rpx;
8
+		.title{
9
+			font-size: 26rpx;
10
+			color: #333;
11
+			white-space: nowrap;
12
+			flex-shrink: 0;
12 13
 		}
13
-		.brand{
14
-			font-size: 30rpx;
15
-			color: #111827;
16
-			text-align: center;
17
-			font-weight: 550;
18
-		}
19
-		.placeholder{
20
-			font-size:30rpx;
21
-			color: #c0c4cc;
22
-			text-align: center;
23
-		}
24
-		.divider{
25
-			color: #ddd;
26
-			font-size: 28rpx;
27
-		}
28
-		.uni-input-wrapper{
29
-			text-align: center;
30
-		}
31
-		.code-input {
32
-			.u-input__content__field-wrapper__field{
33
-				color: blueviolet !important;
14
+		.modal_wrap{
15
+			border-radius: 40rpx;
16
+			padding: 10rpx 20rpx;
17
+			box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
18
+			display: flex;
19
+			align-items: center;
20
+			.item{
21
+				width: 33%;
22
+			}
23
+			.brand{
24
+				font-size: 30rpx;
25
+				color: #111827;
26
+				text-align: center;
34 27
 				font-weight: 550;
35 28
 			}
29
+			.placeholder{
30
+				font-size:30rpx;
31
+				color: #c0c4cc;
32
+				text-align: center;
33
+			}
34
+			.divider{
35
+				color: #ddd;
36
+				font-size: 28rpx;
37
+			}
38
+			.uni-input-wrapper{
39
+				text-align: center;
40
+			}
41
+			.code-input {
42
+				.u-input__content__field-wrapper__field{
43
+					color: blueviolet !important;
44
+					font-weight: 550;
45
+				}
46
+			}
36 47
 		}
37 48
 	}
38 49
 	.img_wrap{

+ 17 - 8
components/add-inquiry-dialog/index.vue

@@ -1,12 +1,17 @@
1 1
 <template>
2 2
 	<view>
3
-		<u-modal :show="showModal" ref="uModal" :title="(editOrAdd === 'add' || editOrAdd === 'receptFormAdd') ? '新增' : '编辑'" :asyncClose="false" showCancelButton @cancel="closeDialog" cancelColor="#909399" :confirmText="'确定'" confirmColor="#2979ff" @confirm="confirm" @close="closeDialog" :closeOnClickOverlay="false">
4
-            <view class="modal_wrap">
5
-                <text @click="handleBrandClick" class="item" :class="info.dictLabel ? 'brand' : 'brand placeholder'">{{ info.dictLabel || '品牌' }}</text>
6
-                <text class="divider">|</text>
7
-                <u--input placeholder="型号" class="item" border="none" v-model="info.model" clearable></u--input>
8
-                <text class="divider">|</text>
9
-                <u--input class="code-input item" placeholder="编码" border="none" v-model="info.code" clearable></u--input>
3
+		<u-modal :show="showModal" ref="uModal" :asyncClose="false" showCancelButton @cancel="closeDialog" cancelColor="#909399" :confirmText="'确定'" confirmColor="#2979ff" @confirm="confirm" @close="closeDialog" :closeOnClickOverlay="false">
4
+            <view class="title_wrap">
5
+                <view class="title">
6
+                    {{ title }}
7
+                </view>
8
+                <view class="modal_wrap">
9
+                    <text @click="handleBrandClick" class="item" :class="info.dictLabel ? 'brand' : 'brand placeholder'">{{ info.dictLabel || '品牌' }}</text>
10
+                    <text class="divider">|</text>
11
+                    <u--input placeholder="型号" class="item" border="none" v-model="info.model" clearable></u--input>
12
+                    <text class="divider">|</text>
13
+                    <u--input class="code-input item" placeholder="编码" border="none" v-model="info.code" clearable></u--input>
14
+                </view>
10 15
             </view>
11 16
             <view class="img_wrap">
12 17
                 <imgs-row-scroll v-if="info.imgsUrl.length > 0" :isShowDeleteIcon="true" @deleteImgInfo="getDeleteImgInfo" imgMode="aspectFill" :totalWidth="400" :images="info.imgsUrl" :previewEnabled="true" :imageWidth="150" :imageHeight="150"></imgs-row-scroll>
@@ -57,6 +62,10 @@ export default {
57 62
             type: Boolean,
58 63
             default: false
59 64
         },
65
+        title:{
66
+            type: String,
67
+            default: ''
68
+        }
60 69
 	},
61 70
     emits: ['submitSuccess'],
62 71
     inject: {
@@ -172,7 +181,7 @@ export default {
172 181
                 type: this.type
173 182
             }
174 183
             uni.$u.api.addInquiry(data).then(res => {
175
-                uni.$u.toast(this.type == 1 ? "询价成功" : "核价成功")
184
+                uni.$u.toast('保存成功')
176 185
                 this.closeDialog()
177 186
                 this.$emit('submitSuccess')
178 187
                 if(this.isClue && this.refreshData) this.refreshData.resetData(); //线索公海页面需要刷新列表

+ 7 - 0
components/imgs-row-scroll/index.scss

@@ -15,6 +15,13 @@
15 15
 		overflow: hidden;
16 16
 		background-color: #f5f5f5;
17 17
 		position: relative;
18
+		border: 4rpx solid transparent;
19
+		transition: border-color 0.3s;
20
+		
21
+		&.is-active {
22
+			border-color: #3c9cff; // uView 的 primary 颜色
23
+			box-shadow: 0 0 12rpx rgba(60, 156, 255, 0.5);
24
+		}
18 25
 	}
19 26
 	
20 27
 	.img-content {

+ 28 - 0
components/imgs-row-scroll/index.vue

@@ -4,6 +4,7 @@
4 4
 			<view class="img-list">
5 5
 				<view 
6 6
 					class="img-item" 
7
+					:class="{ 'is-active': highlightActive && index === localActiveIndex }"
7 8
 					v-for="(item, index) in imageUrls" 
8 9
 					:key="index"
9 10
 					@click.stop="handleImageClick(item, index)"
@@ -100,6 +101,29 @@ export default {
100 101
 		isShowDeleteIcon: {
101 102
 			type: Boolean,
102 103
 			default: false
104
+		},
105
+		// 是否开启高亮当前选中项
106
+		highlightActive: {
107
+			type: Boolean,
108
+			default: false
109
+		},
110
+		// 当前选中的索引 (外部控制)
111
+		activeIndex: {
112
+			type: Number,
113
+			default: -1
114
+		}
115
+	},
116
+	data() {
117
+		return {
118
+			localActiveIndex: -1
119
+		}
120
+	},
121
+	watch: {
122
+		activeIndex: {
123
+			handler(val) {
124
+				this.localActiveIndex = val;
125
+			},
126
+			immediate: true
103 127
 		}
104 128
 	},
105 129
 	computed: {
@@ -133,6 +157,10 @@ export default {
133 157
 	},
134 158
 	methods: {
135 159
 		handleImageClick(item, index) {
160
+			if (this.highlightActive) {
161
+				this.localActiveIndex = index;
162
+			}
163
+			this.$emit('clickImg', { item, index });
136 164
 			if (!this.previewEnabled) {
137 165
 				return;
138 166
 			}

+ 1 - 1
components/inquiry-verification-list/index.vue

@@ -36,7 +36,7 @@
36 36
             </view>
37 37
         </scroll-view>
38 38
 
39
-        <add-inquiry-dialog ref="addInquiryDialog" editOrAdd="edit" :editInfo="editInfo" :type="type" @submitSuccess="onRefresh"/>
39
+        <add-inquiry-dialog ref="addInquiryDialog" editOrAdd="edit" :editInfo="editInfo" :type="type" :title="type == '1' ? '询价' : '核价'" @submitSuccess="onRefresh" />
40 40
     </view>
41 41
 </template>
42 42
 <script>

+ 1 - 0
components/inquiry-verification-list/mixins/inquiryVerificationList.js

@@ -51,6 +51,7 @@ export default {
51 51
         },
52 52
         handleClick(item) {
53 53
             this.editInfo = item;
54
+            this.editInfo.price = item.myPrice
54 55
             this.$nextTick(() => {
55 56
                 this.$refs.addInquiryDialog.showDialog();
56 57
             })

+ 303 - 0
components/remote-search-select/index.scss

@@ -0,0 +1,303 @@
1
+// 远程搜索选择组件样式
2
+.remote-search-select {
3
+  width: 100%;
4
+  position: relative;
5
+  .search-container {
6
+    display: flex;
7
+    gap: 10rpx;
8
+  }
9
+  // 搜索结果列表
10
+  .result-list {
11
+    height: 400rpx;
12
+    margin-top: 8rpx;
13
+    border-radius: 8px;
14
+    overflow-y: auto;
15
+    overflow-x: hidden;
16
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
17
+    position: absolute;
18
+    left: 0rpx;
19
+    right: 20rpx;
20
+    // top:0;
21
+    z-index: 20000;
22
+    background-color: #fff;
23
+    
24
+    ::v-deep .u-cell {
25
+      transition: background-color 0.2s ease;
26
+      
27
+      &:hover {
28
+        background-color: #f5f7fa;
29
+      }
30
+    }
31
+    
32
+    // 滚动条样式
33
+    &::-webkit-scrollbar {
34
+      width: 4px;
35
+    }
36
+    
37
+    &::-webkit-scrollbar-track {
38
+      background: #f1f1f1;
39
+      border-radius: 4px;
40
+    }
41
+    
42
+    &::-webkit-scrollbar-thumb {
43
+      background: #c1c1c1;
44
+      border-radius: 4px;
45
+    }
46
+    
47
+    &::-webkit-scrollbar-thumb:hover {
48
+      background: #a8a8a8;
49
+    }
50
+  }
51
+  
52
+  // 空结果提示
53
+  .empty-result {
54
+    margin-top: 8px;
55
+    padding: 20px;
56
+    border-radius: 8px;
57
+    background-color: #fff;
58
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
59
+    position: absolute;
60
+    left: 0;
61
+    right: 0;
62
+    z-index: 999;
63
+  }
64
+  
65
+  // 加载状态
66
+  .loading-state {
67
+    margin-top: 8px;
68
+    padding: 16px;
69
+    border-radius: 8px;
70
+    background-color: #fff;
71
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
72
+    position: absolute;
73
+    left: 0;
74
+    right: 0;
75
+    z-index: 999;
76
+    display: flex;
77
+    align-items: center;
78
+    justify-content: center;
79
+    
80
+    .loading-text {
81
+      margin-left: 8px;
82
+      font-size: 14px;
83
+      color: #909399;
84
+    }
85
+  }
86
+}
87
+.img-preview-container{
88
+  margin:70rpx 40rpx;
89
+  transition: all 0.3s ease;
90
+  
91
+  .image-section {
92
+    transition: all 0.3s ease;
93
+    margin-bottom: 30rpx;
94
+    
95
+    &.expanded {
96
+      margin-bottom: 40rpx;
97
+    }
98
+    
99
+    .image-container {
100
+      position: relative;
101
+      margin-bottom: 20rpx;
102
+      overflow: hidden;
103
+      border-radius: 10rpx;
104
+      
105
+      .preview-image {
106
+        width: 100%;
107
+        height: 300rpx;
108
+        object-fit: cover;
109
+        transition: all 0.3s ease;
110
+        
111
+        &.zoomed {
112
+          height: 500rpx;
113
+          box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15);
114
+        }
115
+      }
116
+      
117
+      .crop-overlay {
118
+        position: absolute;
119
+        top: 0;
120
+        left: 0;
121
+        right: 0;
122
+        bottom: 0;
123
+        background-color: rgba(0, 0, 0, 0.5);
124
+        display: flex;
125
+        flex-direction: column;
126
+        justify-content: center;
127
+        align-items: center;
128
+        
129
+        .crop-area {
130
+          position: absolute;
131
+          border: 2rpx solid #409eff;
132
+          background-color: rgba(64, 158, 255, 0.2);
133
+          cursor: move;
134
+          
135
+          .resize-handle {
136
+            position: absolute;
137
+            width: 20rpx;
138
+            height: 20rpx;
139
+            border: 2rpx solid #409eff;
140
+            background-color: #fff;
141
+            border-radius: 50%;
142
+            touch-action: none;
143
+            
144
+            &.top-left {
145
+              top: -10rpx;
146
+              left: -10rpx;
147
+              cursor: nw-resize;
148
+            }
149
+            
150
+            &.top-right {
151
+              top: -10rpx;
152
+              right: -10rpx;
153
+              cursor: ne-resize;
154
+            }
155
+            
156
+            &.bottom-left {
157
+              bottom: -10rpx;
158
+              left: -10rpx;
159
+              cursor: sw-resize;
160
+            }
161
+            
162
+            &.bottom-right {
163
+              bottom: -10rpx;
164
+              right: -10rpx;
165
+              cursor: se-resize;
166
+            }
167
+            
168
+            &.top {
169
+              top: -10rpx;
170
+              left: 50%;
171
+              transform: translateX(-50%);
172
+              cursor: n-resize;
173
+            }
174
+            
175
+            &.bottom {
176
+              bottom: -10rpx;
177
+              left: 50%;
178
+              transform: translateX(-50%);
179
+              cursor: s-resize;
180
+            }
181
+            
182
+            &.left {
183
+              left: -10rpx;
184
+              top: 50%;
185
+              transform: translateY(-50%);
186
+              cursor: w-resize;
187
+            }
188
+            
189
+            &.right {
190
+              right: -10rpx;
191
+              top: 50%;
192
+              transform: translateY(-50%);
193
+              cursor: e-resize;
194
+            }
195
+          }
196
+        }
197
+        
198
+        .crop-controls {
199
+          position: absolute;
200
+          top: 20rpx;
201
+          left: 0;
202
+          right: 0;
203
+          display: flex;
204
+          flex-direction: column;
205
+          align-items: center;
206
+          gap: 10rpx;
207
+          
208
+          .crop-hint {
209
+            color: #fff;
210
+            font-size: 24rpx;
211
+            margin-bottom: 10rpx;
212
+            background-color: rgba(0, 0, 0, 0.5);
213
+            padding: 5rpx 15rpx;
214
+            border-radius: 20rpx;
215
+          }
216
+        }
217
+        
218
+        
219
+      }
220
+      .confirm-crop-btn {
221
+        position: absolute;
222
+        bottom: 20rpx;
223
+        right: 20rpx;
224
+        z-index: 999;
225
+      }
226
+    }
227
+    .img-result-container{
228
+      display: flex;
229
+      justify-content: space-between;
230
+    }
231
+  }
232
+  
233
+  .img-result-list {
234
+    max-height: 1000rpx;
235
+    transition: all 0.3s ease;
236
+    
237
+    &.compressed {
238
+      max-height: 600rpx;
239
+    }
240
+    
241
+    ::v-deep .uni-scroll-view-content{
242
+      display: grid;
243
+      grid-template-columns: 1fr 1fr;
244
+      gap: 20rpx;
245
+      
246
+    }
247
+  }
248
+  
249
+  .img-result-item {
250
+    display: flex;
251
+    flex-direction: column;
252
+    align-items: center;
253
+    background-color: #f8f8f8;
254
+    border-radius: 10rpx;
255
+    padding-bottom: 20rpx;
256
+    text-align: center;
257
+    transition: transform 0.2s ease, box-shadow 0.2s ease;
258
+    
259
+    &:hover {
260
+      transform: translateY(-5rpx);
261
+      box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
262
+    }
263
+  }
264
+  
265
+  .img-result-thumb {
266
+    width: 100%;
267
+    height: 250rpx;
268
+    object-fit: cover;
269
+    border-top-left-radius: 10rpx;
270
+    border-top-right-radius: 10rpx;
271
+  }
272
+  
273
+  .img-result-info {
274
+    width: 100%;
275
+  }
276
+  
277
+  .img-result-title {
278
+    display: block;
279
+    background-color: #333;
280
+    color: white;
281
+    padding: 8rpx 12rpx;
282
+    font-size: 22rpx;
283
+    margin-bottom: 10rpx;
284
+  }
285
+  
286
+  .img-result-desc {
287
+    display: block;
288
+    font-size: 24rpx;
289
+    margin-bottom: 10rpx;
290
+    line-height: 1.3;
291
+    text-align: left;
292
+    padding: 0 20rpx;
293
+  }
294
+  
295
+  .img-result-price {
296
+    display: block;
297
+    font-size: 26rpx;
298
+    font-weight: bold;
299
+    color: #ff4d4f;
300
+    text-align: left;
301
+    padding: 0 20rpx;
302
+  }
303
+}

+ 322 - 0
components/remote-search-select/index.vue

@@ -0,0 +1,322 @@
1
+<template>
2
+    <view class="remote-search-select">
3
+        <view class="search-container">
4
+            <u-search :placeholder="placeholder" :value="value" :disabled="disabled" :maxlength="maxlength"
5
+            :show-action="showAction" :action-text="actionText" :shape="shape" :bg-color="bgColor"
6
+            :border-radius="borderRadius" :clearabled="clearabled" :prefix-icon="prefixIcon" :suffix-icon="suffixIcon"
7
+            @input="handleInput" @clear="handleClear" @focus="handleFocus" @blur="handleBlur"/>
8
+            <u-icon name="camera" v-if="cameraEnabled" @click="handleCameraClick" size="30"></u-icon>
9
+        </view>
10
+
11
+        <scroll-view v-if="resultsShow && searchResults.length > 0" class="result-list" scroll-y
12
+            @scrolltolower="handleScrollToLower">
13
+            <u-cell-group>
14
+                <u-cell v-for="(item, index) in searchResults" :key="index" :title="item.model"
15
+                    @click="handleSelectItem(item)" />
16
+            </u-cell-group>
17
+        </scroll-view>
18
+        <!-- 空 -->
19
+        <u-empty v-if="searchResults.length === 0 && !value"></u-empty>
20
+        <!-- 加载状态 -->
21
+        <view v-if="loading" class="loading-state">
22
+            <u-loading-icon mode="circle" size="20"></u-loading-icon>
23
+            <text class="loading-text">搜索中...</text>
24
+        </view>
25
+        <!-- 图片识别结果 -->
26
+        <u-popup title="图片识别结果" :show="show" @open="openImgPopup" mode="bottom" :round="10" :closeOnClickOverlay="false">
27
+            <view class="img-preview-container" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd">
28
+                <view class="image-section" :class="{ 'expanded': isImageExpanded }">
29
+                    <view class="image-container" v-if="currentImg" :style="isImageExpanded ? 'height: 500rpx; width: 670rpx;' : ''">
30
+                        <image v-show="!isImageExpanded" :src="currentImg" class="preview-image" :class="{ 'zoomed': isImageExpanded }" @load="handleImageLoad" id="previewImage"></image>
31
+                        <bt-cropper v-if="isImageExpanded" ref="cropper" :imageSrc="currentImg" :ratio="0" :fileType="fileType" :key="currentImg" :containerSize="{width: 670, height: 500}" @loadFail="handleCropLoadFail"></bt-cropper>
32
+                        <view v-if="isImageExpanded" class="confirm-crop-btn">
33
+                            <u-button type="primary" size="mini" @click="confirmCrop">确认裁剪</u-button>
34
+                        </view>
35
+                    </view>
36
+
37
+                    <view class="img-result-container">
38
+                        <imgsRowScroll :previewEnabled="false" :images="croppedImages" :highlightActive="true" :activeIndex="activeIndex" :imageWidth="150" :imageHeight="150" @clickImg="handleImgClick"></imgsRowScroll>
39
+                        <view @click="closeImgPopup">
40
+                            <u-icon name="close" size="30"></u-icon>
41
+                        </view>
42
+                    </view>
43
+                </view>
44
+                <scroll-view class="img-result-list" scroll-y @scrolltolower="handleScrollToLowerImg" :class="{ 'compressed': isImageExpanded }">
45
+                    <view v-for="(item, index) in imgResults" :key="index" class="img-result-item" @click="handleSelectImg(item)">
46
+                        <image :src="item.record.goodPicFileList[0] || ''" class="img-result-thumb"></image>
47
+                        <view class="img-result-info">
48
+                            <text class="img-result-title">{{ item.record.model || '-'}}</text>
49
+                            <text class="img-result-desc">{{ item.record.dictLabel || '-'}}</text>
50
+                            <text class="img-result-price">¥ {{ item.record.price || '-'}} 元</text>
51
+                        </view>
52
+                    </view>
53
+                </scroll-view>
54
+            </view>
55
+		</u-popup>
56
+    </view>
57
+</template>
58
+
59
+<script>
60
+import imgsRowScroll from '@/components/imgs-row-scroll/index.vue'
61
+export default {
62
+    name: 'RemoteSearchSelect',
63
+    components: {
64
+        imgsRowScroll
65
+    },
66
+    data() {
67
+        return {
68
+            searchValue: '',
69
+            show: false,
70
+            // 滚动相关
71
+            startY: 0,
72
+            currentY: 0,
73
+            isImageExpanded: false,
74
+            // 图片裁剪相关
75
+            croppedImages: [],
76
+            imageWidth: 0,
77
+            imageHeight: 0,
78
+            // 选中的索引
79
+            activeIndex: 0,
80
+            localCurrentImg: ''
81
+        }
82
+    },
83
+    props: {
84
+        // 搜索框属性
85
+        placeholder: {
86
+            type: String,
87
+            default: '请输入关键词搜索'
88
+        },
89
+        value: {
90
+            type: String,
91
+            default: ''
92
+        },
93
+        disabled: {
94
+            type: Boolean,
95
+            default: false
96
+        },
97
+        maxlength: {
98
+            type: [String, Number],
99
+            default: 100
100
+        },
101
+        showAction: {
102
+            type: Boolean,
103
+            default: false
104
+        },
105
+        actionText: {
106
+            type: String,
107
+            default: '搜索'
108
+        },
109
+        shape: {
110
+            type: String,
111
+            default: 'square'
112
+        },
113
+        bgColor: {
114
+            type: String,
115
+            default: '#f5f5f5'
116
+        },
117
+        borderRadius: {
118
+            type: [String, Number],
119
+            default: 4
120
+        },
121
+        clearabled: {
122
+            type: Boolean,
123
+            default: true
124
+        },
125
+        prefixIcon: {
126
+            type: String,
127
+            default: 'search'
128
+        },
129
+        suffixIcon: {
130
+            type: String,
131
+            default: ''
132
+        },
133
+        // 搜索结果
134
+        searchResults: {
135
+            type: Array,
136
+            default: () => []
137
+        },
138
+        resultsShow: {
139
+            type: Boolean,
140
+            default: false
141
+        },
142
+        // 加载状态
143
+        loading: {
144
+            type: Boolean,
145
+            default: false
146
+        },
147
+        cameraEnabled: {
148
+            type: Boolean,
149
+            default: false
150
+        },
151
+        currentUrl: {
152
+            type: String,
153
+            default: ''
154
+        },
155
+        imgResults: {
156
+            type: Array,
157
+            default: () => []
158
+        },
159
+        fileType: {
160
+            type: String,
161
+            default: 'png',
162
+            validator: (value) => ['png', 'jpg'].includes(value)
163
+        }
164
+    },
165
+    watch: {
166
+        currentUrl: {
167
+            handler(val) {
168
+                this.localCurrentImg = val
169
+            },
170
+            immediate: true
171
+        }
172
+    },
173
+    computed: {
174
+        currentImg:{
175
+            get(){
176
+                return this.localCurrentImg || ''
177
+            },
178
+            set(val){
179
+                this.localCurrentImg = val
180
+            }
181
+        }
182
+    },
183
+    emits: ['clear', 'confirm', 'select', 'search', 'load-more', 'focus', 'blur', 'upload', 'load-more-img','select-img','select-crop-img'],
184
+    methods: {
185
+        handleCameraClick() {
186
+            uni.chooseImage({
187
+                count: 1,
188
+                sizeType: ['compressed'],
189
+                sourceType: ['album', 'camera'],
190
+                success: (res) => {
191
+                    this.$emit('upload',res)
192
+                }
193
+            })
194
+        },
195
+        handleInput(val) {
196
+            this.searchValue = val
197
+            this.$emit('search', { keyword: val })
198
+        },
199
+        handleClear() {
200
+            this.searchValue = ''
201
+            this.$emit('clear')
202
+        },
203
+
204
+        // 选择结果项
205
+        handleSelectItem(item) {
206
+            this.searchValue = item.model
207
+            this.$emit('select', item)
208
+        },
209
+
210
+        // 处理滚动到底部事件
211
+        handleScrollToLower() {
212
+            this.$emit('load-more')
213
+        },
214
+        // 处理滚动到底部事件 图片列表
215
+        handleScrollToLowerImg() {
216
+            this.$emit('load-more-img')
217
+        },
218
+        // 处理获取焦点事件
219
+        handleFocus() {
220
+            this.$emit('focus')
221
+        },
222
+        // 处理失去焦点事件
223
+        handleBlur() {
224
+            this.$emit('blur')
225
+        },
226
+        // 打开图片识别结果弹窗
227
+        openImgPopup() {
228
+            this.show = true
229
+            this.$nextTick(()=>{
230
+                // 只有当列表为空时(说明是第一次打开或重新打开),才初始化列表
231
+                if (this.croppedImages.length == 0) {
232
+                    this.croppedImages = [this.currentUrl]
233
+                    this.activeIndex = 0
234
+                }
235
+            })
236
+        },
237
+        // 关闭图片识别结果弹窗
238
+        closeImgPopup() {
239
+            this.show = false
240
+            this.croppedImages = []
241
+        },
242
+        handleSelectImg(item) {
243
+            this.$emit('select-img', item)
244
+            this.closeImgPopup()
245
+        },
246
+        // 触摸开始事件
247
+        handleTouchStart(e) {
248
+            this.startY = e.touches[0].clientY
249
+        },
250
+        // 触摸移动事件
251
+        handleTouchMove(e) {
252
+            this.currentY = e.touches[0].clientY
253
+            const diffY = this.currentY - this.startY
254
+            
255
+            // 向下滑动超过100px时展开图片
256
+            if (diffY > 100 && !this.isImageExpanded) {
257
+                this.isImageExpanded = true
258
+            }
259
+            // 向上滑动超过50px时收起图片
260
+            else if (diffY < -50 && this.isImageExpanded) {
261
+                this.isImageExpanded = false
262
+            }
263
+        },
264
+        // 触摸结束事件
265
+        handleTouchEnd() {
266
+            this.startY = 0
267
+            this.currentY = 0
268
+        },
269
+        // 图片加载完成事件
270
+        handleImageLoad(e) {
271
+            this.imageWidth = e.detail.width
272
+            this.imageHeight = e.detail.height
273
+        },
274
+        handleCropLoadFail(err) {
275
+            console.error('bt-cropper loadFail:', err)
276
+            uni.showToast({
277
+                title: '裁剪器加载图片失败',
278
+                icon: 'none'
279
+            })
280
+        },
281
+        // 确认裁剪
282
+        async confirmCrop() {
283
+            try {
284
+                const res = await this.$refs.cropper.crop()
285
+                if (res) {
286
+                    this.croppedImages.push(res)
287
+                    this.activeIndex = this.croppedImages.length - 1
288
+                    this.currentImg = res
289
+                    
290
+                    // 将 blob URL 转为 File 对象
291
+                    // const response = await fetch(res)
292
+                    // const blob = await response.blob()
293
+                    // const fileName = `crop_${Date.now()}.${this.fileType || 'png'}`
294
+                    // const file = new File([blob], fileName, { type: blob.type })
295
+                    // console.log(response,blob,fileName,file)
296
+                    console.log(res)
297
+                    this.$emit('select-crop-img', res)
298
+                } else {
299
+                    uni.showToast({
300
+                        title: '裁剪失败',
301
+                        icon: 'none'
302
+                    })
303
+                }
304
+            } catch (err) {
305
+                console.error('裁剪失败:', err)
306
+                uni.showToast({
307
+                    title: '裁剪失败',
308
+                    icon: 'none'
309
+                })
310
+            }
311
+        },
312
+        handleImgClick({ item, index }) {
313
+            this.activeIndex = index
314
+            this.currentImg = item
315
+        }
316
+    }
317
+}
318
+</script>
319
+
320
+<style lang="scss" scoped>
321
+@import './index.scss';
322
+</style>

+ 1 - 1
pages.json

@@ -153,7 +153,7 @@
153 153
 			"path": "pages/publicClue/index",
154 154
 			"style": {
155 155
 				"navigationBarTitleText": "线索公海",
156
-				"enablePullDownRefresh": true,
156
+				"enablePullDownRefresh": false,
157 157
 				"navigationStyle": "custom"
158 158
 			}
159 159
 		},

+ 287 - 113
pages/clue/components/trend.vue

@@ -1,12 +1,12 @@
1 1
 <template>
2
-    <u-modal :show="trendModal" title="价格趋势" @confirm="closeTrendModal" confirmText="关闭弹窗" height="80%">
3
-        <view class="trend_modal">
4
-            <u-search placeholder="请输入型号" v-model="model" @blur="searchTrend" @custom="searchTrend"></u-search>
2
+    <u-modal :show="trendModal" title="价格趋势" @confirm="closeTrendModal" confirmText="关闭弹窗" height="93%"><view class="trend_modal">
3
+            <RemoteSearchSelect ref="searchSelect" :value="model" placeholder="请输入关键词" :resultsShow="resultsShow" :currentUrl="currentImg" :searchResults="searchResults" :imgResults="imgResults" :loading="loading"
4
+                :cameraEnabled="true" @search="handleSearch" @select="handleSelect" @select-img="handleSelectImg" @select-crop-img="handleSelectCropImg" @load-more="loadMore" @load-more-img="loadMoreImg" @clear="handleClear" @focus="handleFocus" @upload="uploadImage"></RemoteSearchSelect>
5 5
             <view class="charts_box" v-if="chartShow">
6 6
                 <u--text type="primary" :text="`最大价格:${Math.max(...maxPrice)}元`"></u--text>
7 7
                 <u--text type="warning" :text="`最小价格:${Math.min(...minPrice)}元`"></u--text>
8 8
                 <qiun-data-charts type="line" :chartData="chartData" canvasId="trendChart" :opts="opts" :ontouch="true"
9
-                    tooltipFormat="tooltipFormatPrice" width="700rpx" height="500rpx" backgroundColor="#ffffff"
9
+                    tooltipFormat="tooltipFormatPrice" width="700rpx" height="600rpx" backgroundColor="#ffffff"
10 10
                     @getIndex="handleChartClick" />
11 11
             </view>
12 12
 
@@ -19,7 +19,7 @@
19 19
                     </view>
20 20
                     <view class="card-row">
21 21
                         <span class="card-label">日期:</span>
22
-                        <span class="card-value">{{ formatTime(item.recycleTime) }}</span>
22
+                        <span class="card-value">{{ item.recycleTime }}</span>
23 23
                     </view>
24 24
                     <view class="card-row">
25 25
                         <span class="card-label">价格:</span>
@@ -27,7 +27,7 @@
27 27
                     </view>
28 28
                     <view class="card-row">
29 29
                         <span class="card-label">回收情况:</span>
30
-                        <span class="card-value">{{ formatRecycleSituation(item.recycleSituation) }}</span>
30
+                        <span class="card-value">{{ item.recycleSituation }}</span>
31 31
                     </view>
32 32
                     <view class="card-row">
33 33
                         <span class="card-label">备注:</span>
@@ -41,8 +41,12 @@
41 41
 
42 42
 <script>
43 43
 import { recycleSituationList } from '@/pages/wareHouse/js/public.js'
44
+import RemoteSearchSelect from '@/components/remote-search-select/index.vue'
44 45
 export default {
45 46
     name: 'ComponentName',
47
+    components: {
48
+        RemoteSearchSelect,
49
+    },
46 50
     data() {
47 51
         return {
48 52
             trendModal: false,
@@ -50,7 +54,7 @@ export default {
50 54
             color: [],
51 55
             opts: {
52 56
                 color: this.color,
53
-                padding: [20, 10, 20, 0],
57
+                padding: [20, 10, 40, 0],
54 58
                 dataLabel: true,
55 59
                 dataPointShape: true,
56 60
                 enableScroll: true,
@@ -100,86 +104,247 @@ export default {
100 104
             minPrice: [],
101 105
             chartShow: false,
102 106
             cardData: [],
103
-            date: '',
107
+            // date: '',
104 108
             recycleSituationList: recycleSituationList,
109
+            searchResults: [],
110
+            loading: false,
111
+            pageNum: 1,
112
+            total: 0,
113
+            resultsShow: false,
114
+            isSelecting: false,
115
+            imgResults: [],
116
+            currentImg: '',
117
+            imgPageNum: 1,
118
+            imgTotal: 0,
119
+            searchImgInfo: {},
105 120
         }
106 121
     },
107 122
     props: {
108 123
     },
109 124
     emits: [],
110 125
     methods: {
111
-        openTrendModal() {
112
-            this.trendModal = true;
126
+        handleSearch({ keyword }) {
127
+            if (this.isSelecting) {
128
+                this.isSelecting = false
129
+                return
130
+            }
131
+            if (!keyword) {
132
+                this.searchResults = []
133
+                return
134
+            }
135
+            this.model = keyword
136
+            this.loading = true
137
+            uni.$u.api.selectModelList({
138
+                inputValue: keyword,
139
+                pageNum: this.pageNum,
140
+                pageSize: 10,
141
+            }).then(res=>{
142
+                this.loading = false
143
+                this.total = Number(res.total) || 0
144
+                if (this.pageNum == 1){
145
+                    this.searchResults = res.rows || []
146
+                }else{
147
+                    this.searchResults = [...this.searchResults, ...res.rows || []]
148
+                }
149
+                this.resultsShow = true
150
+            })
113 151
         },
114
-        closeTrendModal() {
152
+        handleFocus() {
153
+            this.resultsShow = true
154
+            this.handleSearch({ keyword: this.model })
155
+        },
156
+        handleClear() {
157
+            this.resultsShow = false
158
+            this.searchResults = []
159
+            this.total = 0
160
+            this.pageNum = 1
161
+            this.imgResults = []
162
+            this.imgTotal = 0
163
+            this.imgPageNum = 1
164
+            this.currentImg = ''
115 165
             this.model = ""
116
-            this.date = ""
166
+            this.minPrice = []
167
+            this.maxPrice = []
168
+            this.chartData = []
117 169
             this.cardData = []
118 170
             this.chartShow = false
171
+        },
172
+        loadMore() {
173
+            if (this.searchResults.length >= this.total) {
174
+                uni.$u.toast("没有更多数据了")
175
+                return
176
+            }
177
+            this.pageNum++
178
+            this.handleSearch({ keyword: this.model })
179
+        },
180
+        // 处理图片列表滚动到底部事件
181
+        loadMoreImg() {
182
+            if (this.imgResults.length >= this.imgTotal) {
183
+                uni.$u.toast("没有更多数据了")
184
+                return
185
+            }
186
+            this.imgPageNum++
187
+            this.uploadImage(this.searchImgInfo)
188
+        },
189
+        handleSelect(item) {
190
+            this.isSelecting = true
191
+            this.model = item.model
192
+            this.resultsShow = false
193
+            this.chartShow = false
194
+            this.$nextTick(() => {
195
+                this.searchTrend(item.model)
196
+            })
197
+        },
198
+        handleSelectImg(item) {
199
+            this.model = item.record.model
200
+            this.chartShow = false
201
+            this.isSelecting = true
202
+            this.pageNum = 1
203
+            this.total = 0
204
+            this.$nextTick(() => {
205
+                this.searchTrend(item.record.model)
206
+            })
207
+        },
208
+        handleSelectCropImg(file) {
209
+            this.imgPageNum = 1
210
+            this.imgTotal = 0
211
+            console.log(file);
212
+            
213
+            const data = {
214
+                tempFilePaths:[file]
215
+            }
216
+            this.uploadImage(data)
217
+        },
218
+        openTrendModal() {
219
+            this.trendModal = true;
220
+        },
221
+        closeTrendModal() {
222
+            this.handleClear()
119 223
             this.trendModal = false;
120 224
         },
121 225
         searchTrend(val) {
122
-                if (val !== '') {
123
-                    uni.$u.api.inquiryChart({
124
-                        model: val,
125
-                    }).then(res => {
126
-                        if (res.data.length == 0) {
127
-                            uni.$u.toast("暂无数据")
128
-                            this.minPrice = []
129
-                            this.maxPrice = []
130
-                            this.chartShow = false
131
-                            return
132
-                        }
133
-                        const response = res.data
134
-                        this.maxPrice = []
226
+            if (val !== '') {
227
+                uni.$u.api.inquiryChart({
228
+                    model: val,
229
+                }).then(res => {
230
+                    if (res.data.length == 0) {
231
+                        uni.$u.toast("暂无数据")
135 232
                         this.minPrice = []
136
-                        this.color = []
137
-                        const categories = []
138
-                        const dateMap = {}
139
-                        response.forEach(item => {
140
-                            this.maxPrice.push(item.max)
141
-                            this.minPrice.push(item.min)
142
-                            item.list.forEach(i => {
143
-                                i.recycleTime = this.formatTime(i.recycleTime)
144
-                                if (!dateMap[i.recycleTime]) {
145
-                                    dateMap[i.recycleTime] = true
146
-                                    categories.push(i.recycleTime)
147
-                                }
148
-                            })
149
-                        })
150
-                        const series = response.map((item) => {
151
-                            const color = this.getRandomColor()
152
-                            this.color.push(color)
153
-                            const data = categories.map(date => {
154
-                                const itemData = item.list.find(i => this.formatTime(i.recycleTime) == date)
155
-                                return itemData ? itemData.price : null
156
-                            })
157
-                            return {
158
-                                name: item.model,
159
-                                data: data,
160
-                                list: item.list,
161
-                                setShadow: [
162
-                                    3,
163
-                                    8,
164
-                                    15,
165
-                                    color
166
-                                ],
233
+                        this.maxPrice = []
234
+                        this.chartShow = false
235
+                        this.cardData = []
236
+                        return
237
+                    }
238
+                    this.maxPrice = []
239
+                    this.minPrice = []
240
+                    this.color = []
241
+                    const categories = []
242
+                    const dateMap = {}
243
+                    const recycleData = res.data[0].list
244
+                    recycleData.forEach(i => {
245
+                        i.recycleTime = this.formatTime(i.recycleTime)
246
+                        if (!dateMap[i.recycleTime]) {
247
+                            dateMap[i.recycleTime] = true
248
+                            categories.push(i.recycleTime)
249
+                        }
250
+                    })
251
+                    const situationMap = {}
252
+                    recycleData.forEach(i => {
253
+                        const key = `${i.recycleTime}-${i.recycleSituation}`
254
+                        if (!situationMap[key]) {
255
+                            situationMap[key] = {
256
+                                recycleTime: i.recycleTime,
257
+                                recycleSituation: i.recycleSituation,
258
+                                prices: []
167 259
                             }
260
+                        }
261
+                        situationMap[key].prices.push(i.price)
262
+                        this.maxPrice.push(i.price)
263
+                        this.minPrice.push(i.price)
264
+                    })
265
+                    const situationGroups = {}
266
+                    Object.values(situationMap).forEach(item => {
267
+                        const avgPrice = item.prices.reduce((sum, price) => sum + price, 0) / item.prices.length
268
+                        if (!situationGroups[item.recycleSituation]) {
269
+                            situationGroups[item.recycleSituation] = []
270
+                        }
271
+                        situationGroups[item.recycleSituation].push({
272
+                            recycleTime: item.recycleTime,
273
+                            price: avgPrice
274
+                        })
275
+                    })
276
+                    const series = Object.entries(situationGroups).map(([situation, data]) => {
277
+                        const color = this.getRandomColor()
278
+                        this.color.push(color)
279
+                        const seriesData = categories.map(date => {
280
+                            const itemData = data.find(i => i.recycleTime == date)
281
+                            return itemData ? itemData.price : null
168 282
                         })
169
-                        this.opts.color = this.color
170
-                        const chartData = {
171
-                            categories: categories,
172
-                            series: series
283
+                        return {
284
+                            name: this.formatRecycleSituation(situation),
285
+                            data: seriesData,
286
+                            list: data,
287
+                            setShadow: [
288
+                                3,
289
+                                8,
290
+                                15,
291
+                                color
292
+                            ],
173 293
                         }
174
-                        this.chartData = JSON.parse(JSON.stringify(chartData))
175
-                        this.cardData = []
176
-                        this.chartShow = true
177
-                    }).catch((err) => {
178
-                        uni.$u.toast(err)
179 294
                     })
180
-                }
181
-            },
182
-        formatTime(date){
295
+                    this.opts = {
296
+                        ...this.opts,
297
+                        color: [...this.color]
298
+                    }
299
+                    const chartData = {
300
+                        categories: categories,
301
+                        series: series
302
+                    }
303
+                    this.chartData = chartData
304
+                    this.cardData = []
305
+                    this.chartShow = true
306
+                }).catch((err) => {
307
+                    uni.$u.toast(err)
308
+                })
309
+            } else {
310
+                this.minPrice = []
311
+                this.maxPrice = []
312
+                this.chartShow = false
313
+                this.cardData = []
314
+            }
315
+        },
316
+        uploadImage(res) {
317
+            this.$nextTick(()=>{
318
+                this.searchImgInfo = res;
319
+                console.log(res,this.searchImgInfo.tempFilePaths[0]);
320
+                uni.$u.api.searchModelByImage(
321
+                    this.searchImgInfo.tempFilePaths[0],
322
+                    {
323
+                        pageNum: this.imgPageNum,
324
+                        pageSize: 10
325
+                    }
326
+                ).then(res => {
327
+                    if (res.data) {
328
+                        this.imgTotal = Number(res.data.total)
329
+                        this.currentImg = this.searchImgInfo.tempFilePaths[0]
330
+                        if(this.imgPageNum == 1){
331
+                            this.imgResults = res.data.rows
332
+                        }else{
333
+                            this.imgResults = [...this.imgResults, ...res.data.rows]
334
+                        }
335
+                        this.$refs.searchSelect.openImgPopup()
336
+                    } else {
337
+                        uni.$u.toast('未匹配到型号,请换一张图或手动输入');
338
+                    }
339
+                }).catch((err) => {
340
+    
341
+                    console.log(err);
342
+                    
343
+                    uni.$u.toast('识别失败,请重试');
344
+                });
345
+            })
346
+        },
347
+        formatTime(date) {
183 348
             return this.$dayjs(date).format('YYYY-MM-DD')
184 349
         },
185 350
         formatRecycleSituation(situation) {
@@ -190,20 +355,19 @@ export default {
190 355
             const index = event.currentIndex.index;
191 356
             // 获取点击的日期
192 357
             const date = this.chartData.categories[index];
193
-            this.date = date;
358
+            // this.date = date;
194 359
 
195
-            // 构建该日期的所有型号价格数据
360
+            // 构建该日期的所有回收情况价格数据
196 361
             const cardData = [];
197 362
             this.chartData.series.forEach(series => {
198 363
                 const price = series.data[index];
199 364
                 if (price !== null) {
200
-                    // 查找对应日期的完整数据
201
-                    const itemData = series.list.find(i => i.recycleTime === date);
202 365
                     cardData.push({
203
-                        model: series.name,
366
+                        model: this.model,
204 367
                         price: price,
205
-                        recycleSituation: itemData?.recycleSituation || '',
206
-                        remark: itemData?.remark || ''
368
+                        recycleSituation: series.name,
369
+                        remark: '',
370
+                        recycleTime: date,
207 371
                     });
208 372
                 }
209 373
             });
@@ -223,58 +387,68 @@ export default {
223 387
 
224 388
 <style lang="scss" scoped>
225 389
 .trend_modal {
226
-	width: 100%;
227
-	display: flex;
228
-	flex-direction: column;
229
-	gap: 20rpx;
390
+    width: 100%;
391
+    min-height: 500rpx;
392
+    max-height: 100%;
393
+    height: 100%;
394
+    display: flex;
395
+    flex-direction: column;
396
+    gap: 20rpx;
397
+    overflow-y: auto;
398
+}
399
+
400
+.charts_box {
401
+    width: 100%;
402
+    max-height: 600rpx;
403
+    overflow: hidden;
230 404
 }
231 405
 
232 406
 .card-container {
233
-	display: flex;
234
-	flex-direction: column;
235
-	gap: 16rpx;
236
-	max-height: 500rpx;
237
-	overflow-y: auto;
238
-    font-size:26rpx;
407
+    display: flex;
408
+    flex-direction: column;
409
+    gap: 16rpx;
410
+    max-height: 200rpx;
411
+    overflow-y: auto;
412
+    font-size: 26rpx;
239 413
 
240
-	&::-webkit-scrollbar {
241
-		width: 6rpx;
242
-	}
414
+    &::-webkit-scrollbar {
415
+        width: 6rpx;
416
+    }
243 417
 
244
-	&::-webkit-scrollbar-track {
245
-		background: #f1f1f1;
246
-		border-radius: 3rpx;
247
-	}
418
+    &::-webkit-scrollbar-track {
419
+        background: #f1f1f1;
420
+        border-radius: 3rpx;
421
+    }
248 422
 
249
-	&::-webkit-scrollbar-thumb {
250
-		background: #c1c1c1;
251
-		border-radius: 3rpx;
423
+    &::-webkit-scrollbar-thumb {
424
+        background: #c1c1c1;
425
+        border-radius: 3rpx;
252 426
 
253
-		&:hover {
254
-			background: #a8a8a8;
255
-		}
256
-	}
427
+        &:hover {
428
+            background: #a8a8a8;
429
+        }
430
+    }
257 431
 }
258 432
 
259 433
 .card-item {
260
-	background-color: #f5f5f5;
261
-	border-radius: 8rpx;
262
-	padding: 16rpx;
263
-	box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
434
+    background-color: #f5f5f5;
435
+    border-radius: 8rpx;
436
+    padding: 16rpx;
437
+    box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
264 438
 }
265 439
 
266 440
 .card-row {
267
-	display: flex;
268
-	margin-bottom: 10rpx;
269
-	align-items: flex-start;
441
+    display: flex;
442
+    margin-bottom: 10rpx;
443
+    align-items: flex-start;
270 444
 
271
-	.card-label {
272
-		flex-shrink: 0;
273
-	}
445
+    .card-label {
446
+        flex-shrink: 0;
447
+    }
274 448
 
275
-	.card-value {
276
-		flex: 1;
277
-		word-break: break-all;
278
-	}
449
+    .card-value {
450
+        flex: 1;
451
+        word-break: break-all;
452
+    }
279 453
 }
280 454
 </style>

+ 70 - 50
pages/clue/post/index.vue

@@ -6,6 +6,9 @@
6 6
 				<view v-if="item.price" class="price">
7 7
 					<u-icon name="rmb-circle"></u-icon>
8 8
 					{{ item.price }}
9
+					<view class="more" v-if="item.hasMoreInquiryPrice" @click.stop="handleInquiry(item)">
10
+						更多
11
+					</view>
9 12
 				</view>
10 13
 			</view>
11 14
 			<view class="top_right">{{ item.assignStateCode === '1' ? "已分配" : "未分配" }}</view>
@@ -13,7 +16,7 @@
13 16
 		<view class="post_info">
14 17
 			<view class="telPhone">
15 18
 				<view class="phone">
16
-					<text>电话: </text>
19
+					<text>电话:</text>
17 20
 					<show-real-text :real="item.telephone" :type='type'
18 21
 						:style="{ color: item.repetitionOperationName ? 'red' : 'black' }"></show-real-text>
19 22
 					<template v-if="item.telAddr">
@@ -23,8 +26,8 @@
23 26
 				</view>
24 27
 				<view class="copy_btn" @click.stop="handleCopy(item)" v-if="type != '1'">复制</view>
25 28
 			</view>
26
-			<view class="telPhone" v-if="item.weixin">
27
-				<text>微信: </text>
29
+			<view class="info" v-if="item.weixin">
30
+				<text>微信:</text>
28 31
 				<view class="phone">
29 32
 					<text :style="{ color: item.repetitionOperWeixinName ? 'red' : 'black' }">{{ item.weixin }}</text>
30 33
 					<text v-if="item.repetitionOperWeixinName">( 撞 : {{ item.repetitionOperWeixinName }})</text>
@@ -37,10 +40,10 @@
37 40
 				<view>{{ item.appName }}</view>
38 41
 			</view>
39 42
 			<view class="info">
40
-				<view class="owner">所属人 {{ item.clueOwnerName ? item.clueOwnerName : "-" }}</view>
43
+				<view class="owner">所属人:{{ item.clueOwnerName ? item.clueOwnerName : "-" }}</view>
41 44
 			</view>
42 45
 			<view class="info">
43
-				<view class="operation">运营人 {{ item.clueOperationName ? item.clueOperationName : "-" }}</view>
46
+				<view class="operation">运营人:{{ item.clueOperationName ? item.clueOperationName : "-" }}</view>
44 47
 			</view>
45 48
 			<view class="info latest_follow_row" @click.stop="handleShowFollowDetail(item)">
46 49
 				<view class="label">最新跟进记录:</view>
@@ -64,15 +67,17 @@
64 67
 				</view>
65 68
 			</view>
66 69
 		</view>
67
-		<view class="sendOrder inquiry" @click.stop="handleInquiry(item)">
68
-			<image src='/static/publicClue/inquiry.png' mode="aspectFit" class="sendOrder_img"></image>
69
-			<view>询价</view>
70
-		</view>
71
-		<view class="sendOrder" @click.stop="handleSendOrder(item)">
72
-			<image src='/static/publicClue/littlePlane.png' mode="aspectFit" class="sendOrder_img"></image>
73
-			<view>发单</view>
70
+		<view class="sendOrder_wrap">
71
+			<view class="sendOrder inquiry" @click.stop="handleInquiry(item)">
72
+				<image src='/static/publicClue/inquiry.png' mode="aspectFit" class="sendOrder_img"></image>
73
+				<view>询价</view>
74
+			</view>
75
+			<view class="sendOrder" @click.stop="handleSendOrder(item)">
76
+				<image src='/static/publicClue/littlePlane.png' mode="aspectFit" class="sendOrder_img"></image>
77
+				<view>发单</view>
78
+			</view>
74 79
 		</view>
75
-		<add-inquiry-dialog ref="inquiryDialog" :clueId="clueId" :isClue="true" :editOrAdd="editOrAdd" :editInfo="editInfo" @cancel="handleInquiryCancel" :type="1"/>
80
+		<add-inquiry-dialog ref="inquiryDialog" :clueId="clueId" :isClue="true" :editOrAdd="editOrAdd" :editInfo="editInfo" title="询价" @cancel="handleInquiryCancel" :type="1"/>
76 81
 		<!-- 最新跟进记录详情弹窗;关闭时记时间戳,避免蒙层点击冒泡触发详情跳转 -->
77 82
 		<u-popup :show="followPopupVisible" mode="bottom" round="16" @close="onFollowPopupClose"
78 83
 			:closeOnClickOverlay="true" @open="loadFollowList">
@@ -296,6 +301,14 @@ export default {
296 301
 			.price{
297 302
 				display: flex;
298 303
 				align-items: center;
304
+				gap: 8rpx;
305
+				.more{
306
+					font-size: 20rpx;
307
+					border: 1px solid #3c9cff;
308
+					border-radius:10rpx;
309
+					padding: 3rpx 8rpx;
310
+					color: #3c9cff;
311
+				}
299 312
 			}
300 313
 		}
301 314
 
@@ -306,10 +319,12 @@ export default {
306 319
 
307 320
 	.post_info {
308 321
 		font-size: 14px;
309
-
322
+		display: grid;
323
+		grid-template-columns: 1fr 1fr;
324
+		gap: 10rpx;
310 325
 		.telPhone {
311 326
 			display: flex;
312
-			margin-bottom: 6px;
327
+			grid-column: 1 / -1;
313 328
 
314 329
 			.copy_btn {
315 330
 				color: #4fa5fe;
@@ -319,7 +334,15 @@ export default {
319 334
 
320 335
 		.info {
321 336
 			display: flex;
322
-			margin-bottom: 10px;
337
+			// margin-bottom: 10px;
338
+			overflow: hidden;
339
+
340
+			> view {
341
+				white-space: nowrap;
342
+				overflow: hidden;
343
+				text-overflow: ellipsis;
344
+				flex: 1;
345
+			}
323 346
 
324 347
 			.createTime {
325 348
 				margin-right: 10px;
@@ -334,29 +357,27 @@ export default {
334 357
 		.latest_follow_row {
335 358
 			align-items: flex-start;
336 359
 			flex-wrap: wrap;
360
+			grid-column: 1 / -1;
337 361
 
338 362
 			.label {
339 363
 				flex: 0 0 90px;
340
-				color: #666;
341 364
 			}
342 365
 
343 366
 			.latest_follow_content {
344 367
 				flex: 1;
345 368
 				min-width: 0;
346 369
 				display: flex;
347
-				flex-direction: column;
348 370
 				gap: 4px;
371
+				flex-direction: column;
349 372
 			}
350 373
 
351 374
 			.latest_follow_text {
352 375
 				font-size: 13px;
353
-				color: #333;
354 376
 				word-break: break-all;
355 377
 			}
356 378
 
357 379
 			.latest_follow_time {
358 380
 				font-size: 12px;
359
-				color: #999;
360 381
 			}
361 382
 
362 383
 			.latest_follow_add {
@@ -390,39 +411,38 @@ export default {
390 411
 	}
391 412
 
392 413
 
393
-
394
-	.sendOrder {
395
-		width: 50px;
396
-		height: 50px;
397
-		background-color: rgb(36, 98, 234);
398
-		color: #fff;
399
-		border-radius: 50%;
414
+	.sendOrder_wrap {
400 415
 		display: flex;
401
-		flex-direction: column;
402
-		justify-content: center;
403
-		align-items: center;
404
-
405
-		font-size: 10px;
406
-		font-weight: 700;
407
-		font-family: "uicon";
408
-
409
-		position: absolute;
410
-		right: 20px;
411
-		bottom: 115px;
412
-
413
-
414
-		.sendOrder_img {
415
-			width: 20px;
416
-			height: 20px;
416
+		justify-content: space-between;
417
+		gap: 20rpx;
418
+		.sendOrder {
419
+			width: 50%;
420
+			height: 40px;
421
+			background-color: rgb(36, 98, 234);
422
+			color: #fff;
423
+			border-radius: 20rpx;
424
+			display: flex;
425
+			justify-content: center;
426
+			align-items: center;
427
+			gap: 20rpx;
428
+			font-size: 28rpx;
429
+			font-weight: 700;
430
+			font-family: "uicon";
431
+			margin-top: 10rpx;
432
+			.sendOrder_img {
433
+				width: 20px;
434
+				height: 20px;
435
+			}
436
+		}
437
+	
438
+		.inquiry {
439
+			right: 140rpx;
440
+			color: #2563eb;
441
+			border: 1px solid #2563eb;
442
+			background-color: #fff;
417 443
 		}
418 444
 	}
419
-
420
-	.inquiry {
421
-		right: 140rpx;
422
-		color: #2563eb;
423
-		border: 1px solid #2563eb;
424
-		background-color: #fff;
425
-	}
445
+	
426 446
 }
427 447
 
428 448
 .follow_popup {

+ 3 - 3
pages/orderDetailRefactored/components/PageThree.vue

@@ -153,7 +153,7 @@
153 153
 
154 154
     <!-- 核价对话框 -->
155 155
     <add-inquiry-dialog ref="pricingDialog" :clueId="pricingClueId" :editOrAdd="pricingEditOrAdd"
156
-      :editInfo="pricingEditInfo" :type="2" />
156
+      :editInfo="pricingEditInfo" :type="2" title="询价"/>
157 157
 
158 158
     <u-modal :show="payNowModalVisible" title="确认支付信息" :showConfirmButton="false">
159 159
       <view class="modal-content">
@@ -869,7 +869,7 @@ export default {
869 869
         const res = await uni.$u.api.inquiryDetail(data)
870 870
         if (res.code === 200 && res.data) {
871 871
           // 编辑模式:保留原有数据,但更新品牌和图片
872
-          this.pricingEditOrAdd = 'editForm'
872
+          this.pricingEditOrAdd = 'edit'
873 873
           this.pricingEditInfo = {
874 874
             ...res.data,
875 875
             dictLabel: brandDictLabel || res.data.dictLabel,
@@ -881,7 +881,7 @@ export default {
881 881
           })
882 882
         } else {
883 883
           // 新增模式:设置品牌和图片
884
-          this.pricingEditOrAdd = 'editForm'
884
+          this.pricingEditOrAdd = 'edit'
885 885
           this.pricingEditInfo = {
886 886
             dictLabel: brandDictLabel,
887 887
             dictValue: brandDictValue,

+ 4 - 0
pages/wareHouse/index.vue

@@ -55,6 +55,9 @@
55 55
 						<text class="price-text">价格可见</text>
56 56
 					</view>
57 57
 					<view class="dept-btn" @click="recycleDeptClick">
58
+						<view class="dept-name" v-if="deptName">
59
+							{{ deptName }}
60
+						</view>
58 61
 						<image src="/static/icons/dept.png" mode="aspectFill"></image>
59 62
 					</view>
60 63
 					<ba-tree-picker :selectParent="true" ref="recycleDeptRef" :multiple='false' @select-change="deptSeletchang"
@@ -496,6 +499,7 @@ export default {
496 499
 			return false;
497 500
 		},
498 501
 	},
502
+	
499 503
 	onShow() {
500 504
 		// 尝试恢复页面状态
501 505
 		if (!this.restorePageState()) {

+ 7 - 0
pages/wareHouse/styles/index.scss

@@ -248,10 +248,17 @@
248 248
     }
249 249
     .dept-btn {
250 250
       margin-left: 18rpx;
251
+      display: flex;
252
+      align-items: center;
253
+      gap: 8rpx;
251 254
       image {
252 255
         width: 30rpx;
253 256
         height: 30rpx;
254 257
       }
258
+      .dept-name{
259
+        color: #333;
260
+        font-size: 24rpx;
261
+      }
255 262
     }
256 263
   }
257 264
 }

+ 29 - 0
uni_modules/bt-cropper/changelog.md

@@ -0,0 +1,29 @@
1
+## 3.0.2(2023-06-06)
2
+更新了文档
3
+
4
+## 3.0.1(2022-11-03)
5
+修复 撤销和重做不生效的问题
6
+## 3.0.0(2022-11-03)
7
+使用wxs重构代码,性能大提升
8
+新增 支持蒙版裁剪,可以裁剪任何形状的图形(详情见demo示例)
9
+新增 支持在弹窗中使用(详情见demo示例)
10
+移除 由于插槽会导致许多问题,实际上开发者自己封装组件反而更简单,所以3.0版本以后移除插槽,2.0迁移教程见 demo:全屏裁剪
11
+## 2.0.3(2022-08-21)
12
+修复 在vue3 程序中报错的问题
13
+新增 新增了图片初始化完成和加载失败的事件
14
+## 2.0.2(2022-08-18)
15
+新增 增加了原像素裁剪功能,即使用用户在裁剪框取景的大小作为输出像素,换句话说,输出的图片分辨率与输入图片分辨率一样
16
+新增 增加了change事件,会在图像和裁剪框位置变化后触发
17
+新增 增加了compress参数 压缩图片,压缩图片是为了提升流畅度,所以只会针对用户拖动的那张图片进行压缩,最终输出的图像品质并不会受到影响
18
+修复 用户在没有传入图像时报错的问题
19
+修复 ios在某些机型上拖动出现残留的问题
20
+## 2.0.1(2022-07-20)
21
+修复:ios打包成app的时候有几率裁剪不成功的问题
22
+## 2.0.0(2022-07-13)
23
+更新了2.0版本,增加了图片放大功能
24
+## bt-cropper 图片裁切
25
+========
26
+### 2022年7月13日 发布2.0版本
27
+* 1.完全重构了代码,并且优化了性能
28
+* 2.支持app
29
+* 3.修复demo在H5的情况下高度错误的问题

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1095 - 0
uni_modules/bt-cropper/components/bt-cropper/bt-cropper.vue


+ 22 - 0
uni_modules/bt-cropper/components/bt-cropper/iconfont.css

@@ -0,0 +1,22 @@
1
+@font-face {
2
+  font-family: "iconfont"; /* Project id 3311610 */
3
+  src: url('//at.alicdn.com/t/font_3311610_7wh8injedpd.woff2?t=1649382821379') format('woff2'),
4
+       url('//at.alicdn.com/t/font_3311610_7wh8injedpd.woff?t=1649382821379') format('woff'),
5
+       url('//at.alicdn.com/t/font_3311610_7wh8injedpd.ttf?t=1649382821379') format('truetype');
6
+}
7
+
8
+.iconfont {
9
+  font-family: "iconfont" !important;
10
+  font-size: 16px;
11
+  font-style: normal;
12
+  -webkit-font-smoothing: antialiased;
13
+  -moz-osx-font-smoothing: grayscale;
14
+}
15
+
16
+.icon-reset:before {
17
+  content: "\e611";
18
+}
19
+
20
+.icon-move:before {
21
+  content: "\e67b";
22
+}

+ 253 - 0
uni_modules/bt-cropper/components/bt-cropper/js/touchs.js

@@ -0,0 +1,253 @@
1
+var startTouchs = [];
2
+var touchType = ''
3
+var startDistance = 0;
4
+var touchCenter = [];
5
+var cropperRect = null;
6
+var imageRect = null;
7
+var directionX = 0;
8
+var directionY = 0;
9
+var ratio = 0;
10
+// 操作时改变的对象
11
+var changes = {
12
+	imageRect: null,
13
+	cropperRect: null
14
+}
15
+// 计算旋转后真实的图片大小
16
+function getRealSize(){
17
+	var w = changes.imageRect.width
18
+	var h = changes.imageRect.height
19
+	var l =  changes.imageRect.left
20
+	var t =  changes.imageRect.top
21
+	// 内斜边
22
+	var R = Math.sqrt(w*w+h*h)
23
+	var angle = Math.atan(h/w) / Math.PI * 180
24
+	var rorate = rotateAngle%90
25
+	var direct = Math.floor(rotateAngle/90)
26
+	var width = R*Math.cos(ang2deg(angle-rorate))
27
+	var height = R*Math.sin(ang2deg(angle+rorate))
28
+	if(direct % 2 === 1){
29
+		var temp = width
30
+		width = height
31
+		height = temp
32
+	}
33
+	return {
34
+		width: width,
35
+		height: height,
36
+		left: l - (width - w)/2,
37
+		top: t - (height - h)/2,
38
+		dw: width - w,
39
+		dh: height - h
40
+	}
41
+}
42
+export default {
43
+	computed: {
44
+		imageStyle() {
45
+			const imageRect = this.imageRect
46
+			if (imageRect) {
47
+				return {
48
+					left: imageRect.left + 'px',
49
+					top: imageRect.top + 'px',
50
+					width: imageRect.width + 'px',
51
+					height: imageRect.height + 'px'
52
+				}
53
+			} else {
54
+				return {}
55
+			}
56
+		},
57
+		cropperStyle() {
58
+			const cropperRect = this.cropperRect
59
+			if (cropperRect) {
60
+				return {
61
+					left: cropperRect.left + 'px',
62
+					top: cropperRect.top + 'px',
63
+					width: cropperRect.width + 'px',
64
+					height: cropperRect.height + 'px'
65
+				}
66
+			} else {
67
+				return {}
68
+			}
69
+		}
70
+	},
71
+	methods: {
72
+		touchStart() {
73
+			let ev;
74
+			if (arguments.length == 3) {
75
+				directionX = arguments[0];
76
+				directionY = arguments[1];
77
+				ev = arguments[2];
78
+				touchType = "controller";
79
+			} else {
80
+				touchType = "image";
81
+				ev = arguments[0];
82
+			}
83
+			startTouchs = ev.touches;
84
+			changes = {
85
+				imageRect: this.imageRect,
86
+				cropperRect: this.cropperRect
87
+			};
88
+			ratio = this.ratio;
89
+			cropperRect = {
90
+				...changes.cropperRect
91
+			}
92
+			imageRect = {
93
+				...changes.imageRect
94
+			}
95
+			if (startTouchs.length == 2) {
96
+				const imageRect = this.imageRect
97
+				var x1 = startTouchs[0].clientX
98
+				var y1 = startTouchs[0].clientY
99
+				var x2 = startTouchs[1].clientX
100
+				var y2 = startTouchs[1].clientY
101
+				var distance = Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)
102
+				startDistance = Math.sqrt(distance)
103
+				var leftPercent = ((x1 + x2) / 2 - imageRect.left) / imageRect.width
104
+				var topPercent = ((y1 + y2) / 2 - imageRect.top) / imageRect.height
105
+				touchCenter = [leftPercent, topPercent]
106
+			}
107
+		},
108
+		touchMove(ev) {
109
+			if(startTouchs.length!==ev.touches.length) return
110
+			var touches = ev.touches;
111
+			var changeX1 = touches[0].clientX - startTouchs[0].clientX;
112
+			var changeY1 = touches[0].clientY - startTouchs[0].clientY;
113
+			if (startTouchs.length == 1) {
114
+				if (touchType === 'image') {
115
+					changes.imageRect.left = imageRect.left + changeX1;
116
+					changes.imageRect.top = imageRect.top + changeY1;
117
+					// console.log(startTouchs.length,ev.touches.length)
118
+				} else if (touchType === 'controller') {
119
+					var changeX = changeX1 * directionX;
120
+					var changeY = changeY1 * directionY;
121
+					// 比例缩放控制
122
+					if (ratio !== 0) {
123
+						if (directionX * directionY !== 0) {
124
+							if (changeX / ratio > changeY) {
125
+								changeY = changeX / ratio
126
+								changeX = changeY * ratio
127
+							} else {
128
+								changeX = changeY * ratio
129
+								changeY = changeX / ratio
130
+							}
131
+						} else {
132
+							if (directionX == 0) {
133
+								changeX = changeY * ratio
134
+							} else {
135
+								changeY = changeX / ratio
136
+							}
137
+						}
138
+					}
139
+					var realSize = getRealSize()
140
+					var width = cropperRect.width + changeX
141
+					var height = cropperRect.height + changeY
142
+					var imageRight = realSize.left+realSize.width
143
+					var imageBottom = realSize.top+realSize.height
144
+					if (directionX != -1) {
145
+						if (cropperRect.left + width > imageRight) {
146
+							width = imageRight - cropperRect.left
147
+							if (ratio !== 0) {
148
+								height = width / ratio
149
+							}
150
+						}
151
+					} else {
152
+						var cLeft = cropperRect.left - changeX
153
+						if (cLeft < realSize.left) {
154
+							width = cropperRect.left + cropperRect.width - realSize.left
155
+							if (ratio !== 0) {
156
+								height = width / ratio
157
+							}
158
+						}
159
+					}
160
+					// 判断是否触底
161
+					if (directionY != -1) {
162
+						if (cropperRect.top + height > imageBottom) {
163
+							height = imageBottom - cropperRect.top
164
+							if (ratio !== 0) {
165
+								width = height * ratio
166
+							}
167
+						}
168
+					} else {
169
+						var cTop = cropperRect.top - changeY
170
+						if (cTop < realSize.top) {
171
+							height = cropperRect.top + cropperRect.height - realSize.top
172
+							if (ratio !== 0) {
173
+								width = height * ratio
174
+							}
175
+						}
176
+					}
177
+					if (directionX == -1) {
178
+						changes.cropperRect.left = cropperRect.left + cropperRect.width - width
179
+					}
180
+					if (directionY == -1) {
181
+						changes.cropperRect.top = cropperRect.top + cropperRect.height - height
182
+					}
183
+					// 边界控制
184
+					changes.cropperRect.width = width
185
+					changes.cropperRect.height = height
186
+				}
187
+			} else if (touches.length == 2 && startTouchs.length == 2) {
188
+				var changeX2 = touches[0].clientX - touches[1].clientX;
189
+				var changeY2 = touches[0].clientY - touches[1].clientY;
190
+				var distance = Math.pow(changeX2, 2) + Math.pow(changeY2, 2)
191
+				distance = Math.sqrt(distance)
192
+				// 放大比例
193
+				var scaleRate = distance / startDistance
194
+				this.imageScale(scaleRate)
195
+			}
196
+		},
197
+		touchEnd(ev) {
198
+			// console.log('end',ev)
199
+			if(ev.touches.length!==0) return
200
+			if (touchType === "image") {
201
+				var cropperLeft = cropperRect.left
202
+				var cropperRight = cropperRect.left + cropperRect.width
203
+				var cropperTop = cropperRect.top
204
+				var cropperBottom = cropperTop + cropperRect.height
205
+				var cropperRate = cropperRect.width / cropperRect.height
206
+				var realSize = getRealSize()
207
+				var rate = realSize.width / realSize.height
208
+				if (realSize.width < cropperRect.width || realSize.height < cropperRect.height) {
209
+					var scale = 1
210
+					if (rate < cropperRate) {
211
+						scale = cropperRect.width / realSize.width
212
+					} else {
213
+						scale = cropperRect.height / realSize.height
214
+					}
215
+					imageRect.width = changes.imageRect.width
216
+					imageRect.height = changes.imageRect.height
217
+					this.imageScale(scale)
218
+				}
219
+				// 边界控制start
220
+				if (cropperLeft < realSize.left) {
221
+					changes.imageRect.left = cropperLeft + realSize.dw/2
222
+				}
223
+				if (cropperRight > realSize.left + realSize.width) {
224
+					changes.imageRect.left = cropperRight - realSize.width + realSize.dw/2
225
+				}
226
+				if (cropperTop < realSize.top) {
227
+					changes.imageRect.top = cropperTop + realSize.dh/2
228
+				}
229
+				if (cropperBottom > realSize.top + realSize.height) {
230
+					changes.imageRect.top = cropperBottom - realSize.height + realSize.dh/2
231
+				}
232
+				// 边界控制end
233
+			}
234
+			this.updateData({
235
+				cropperRect: changes.cropperRect,
236
+				imageRect: changes.imageRect,
237
+			})
238
+			touchType = ""
239
+			startTouchs = []
240
+			return false;
241
+		},
242
+		imageScale(scaleRate) {
243
+			var cw = imageRect.width * (scaleRate - 1)
244
+			var ch = imageRect.height * (scaleRate - 1)
245
+			changes.imageRect = {
246
+				width: imageRect.width + cw,
247
+				height: imageRect.height + ch,
248
+				left: imageRect.left - cw * (touchCenter[0]),
249
+				top: imageRect.top - ch * (touchCenter[1])
250
+			}
251
+		}
252
+	}
253
+}

+ 43 - 0
uni_modules/bt-cropper/components/bt-cropper/utils/tools.js

@@ -0,0 +1,43 @@
1
+export function getTouchPoints(touchs) {
2
+	return Array.from(touchs).map(ev => {
3
+		return [ev.clientX, ev.clientY]
4
+	})
5
+}
6
+// 函数防抖
7
+export function debounce(fn, wait = 200) {
8
+	var timer = null;
9
+	return function (){
10
+		if (timer !== null) {
11
+			clearTimeout(timer);
12
+		}
13
+		timer = setTimeout(fn.bind(this), wait);
14
+	}
15
+}
16
+
17
+/**
18
+ * @description 睡眠
19
+ * @param {number} time 等待时间毫秒数
20
+ */
21
+export function sleep(time = 200) {
22
+	return new Promise(resolve => {
23
+		setTimeout(resolve, time)
24
+	})
25
+}
26
+const systemInfo = uni.getSystemInfoSync();
27
+
28
+export function parseUnit(size){
29
+	if(typeof size == 'number' || !isNaN(Number(size))){
30
+		return uni.upx2px(size)
31
+	}else if(typeof size === 'string') {
32
+		if(size.endsWith('rpx')){
33
+			return parseUnit(size.replace('rpx',''))
34
+		}else if(size.endsWith('px')){
35
+			return Number(size.replace('px',''))
36
+		}else if(size.endsWith('vw')){
37
+			return Number(size.replace('vw',''))*systemInfo.screenWidth/100
38
+		}else if(size.endsWith('vh')){
39
+			return Number(size.replace('vh',''))*systemInfo.screenHeight/100
40
+		}
41
+	}
42
+	return 0
43
+}

+ 81 - 0
uni_modules/bt-cropper/package.json

@@ -0,0 +1,81 @@
1
+{
2
+	"id": "bt-cropper",
3
+	"displayName": "bt-cropper图片裁剪插件",
4
+	"version": "3.0.2",
5
+	"description": "一款好用的图片裁剪插件",
6
+	"keywords": [
7
+        "图片",
8
+        "图片裁剪",
9
+        "图片裁剪",
10
+        "头像裁剪",
11
+        "cropper"
12
+    ],
13
+	"repository": "",
14
+	"engines": {
15
+		"HBuilderX": "^3.4.9"
16
+	},
17
+    "dcloudext": {
18
+        "sale": {
19
+			"regular": {
20
+				"price": "0.00"
21
+			},
22
+			"sourcecode": {
23
+				"price": "0.00"
24
+			}
25
+		},
26
+		"contact": {
27
+			"qq": "1097122362"
28
+		},
29
+		"declaration": {
30
+			"ads": "无",
31
+			"data": "插件不采集任何数据",
32
+			"permissions": "无"
33
+		},
34
+        "npmurl": "",
35
+        "type": "component-vue"
36
+	},
37
+	"uni_modules": {
38
+		"dependencies": [],
39
+		"encrypt": [],
40
+		"platforms": {
41
+			"cloud": {
42
+				"tcb": "y",
43
+				"aliyun": "y"
44
+			},
45
+			"client": {
46
+				"Vue": {
47
+					"vue2": "y",
48
+					"vue3": "y"
49
+				},
50
+				"App": {
51
+					"app-vue": "y",
52
+					"app-nvue": "n"
53
+				},
54
+				"H5-mobile": {
55
+					"Safari": "y",
56
+					"Android Browser": "y",
57
+					"微信浏览器(Android)": "y",
58
+					"QQ浏览器(Android)": "y"
59
+				},
60
+				"H5-pc": {
61
+					"Chrome": "n",
62
+					"IE": "n",
63
+					"Edge": "n",
64
+					"Firefox": "n",
65
+					"Safari": "n"
66
+				},
67
+				"小程序": {
68
+					"微信": "y",
69
+					"阿里": "u",
70
+					"百度": "u",
71
+					"字节跳动": "y",
72
+					"QQ": "y"
73
+				},
74
+				"快应用": {
75
+					"华为": "n",
76
+					"联盟": "n"
77
+				}
78
+			}
79
+		}
80
+	}
81
+}

+ 108 - 0
uni_modules/bt-cropper/readme.md

@@ -0,0 +1,108 @@
1
+
2
+
3
+## bt-cropper 图片裁切
4
+> **组件名:bt-cropper**
5
+
6
+图片裁切组件,在页面中裁切图片,输出裁切后的图片,支持app,小程序,H5
7
+### [在线体验](https://static-mp-8cc5bb7c-7831-45bf-a4e1-a71b10d3319c.next.bspapp.com/h5)
8
+
9
+## 将demo编译为微信小程序的时候,一定要填写appid!!!不然无法使用!!
10
+ 
11
+
12
+> **注意事项**
13
+> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
14
+> - 组件需要依赖 `sass` 插件 ,请自行手动安装
15
+> - 只测试了头条小程序,app-vue 安卓,微信小程序和H5 大部分平台应该都没问题了
16
+> - 包裹层或裁剪器需要手动指定高度和宽度,推荐手动指定裁剪器的大小,尤其是头条小程序,js有时候获取不到容器的大小
17
+> - 如使用过程中有任何问题,或者您有一些好的建议,欢迎联系作者微信:1097122362
18
+
19
+
20
+
21
+### 安装方式
22
+
23
+本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
24
+
25
+### 基本用法 
26
+
27
+**示例**
28
+
29
+```html
30
+<template>
31
+	<view class="container">
32
+		<bt-cropper ref="cropper" :imageSrc="imageSrc"></bt-cropper>
33
+		<button @click="crop">裁切</button>
34
+	</view>
35
+</template>
36
+<style>
37
+	.container{
38
+		/** 外层一定要指定大小 */
39
+		height:100vh;
40
+	}
41
+</style>
42
+```
43
+
44
+
45
+```javascript
46
+export default {
47
+   methods:{
48
+      crop(){
49
+        // 通过组件定义的ref调用cropper方法,返回一个promise对象
50
+        this.$refs.cropper.crop().then((res)=>{
51
+			console.log(res)
52
+		})
53
+      }
54
+   }
55
+}
56
+
57
+```
58
+
59
+### 限定裁切比例
60
+
61
+bt-cropper,指定ratio即可设置裁切框的宽高比,如果你想让用户自由缩放,将ratio设置为0即可
62
+
63
+**示例**
64
+
65
+```html
66
+<bt-cropper ref="cropper" :ratio="16/9":imageSrc="imageSrc"></bt-cropper>
67
+```
68
+
69
+## API
70
+
71
+### cropper Props 
72
+
73
+|属性名|类型|默认值|说明|
74
+|:-:|:-:|:-:|:-:|
75
+|ratio|number|0|裁切图像的宽高比,0表示自由比例|
76
+|dWidth|number|0|生成的图片的宽度,单位:px,如果传入0的话就是按原像素的比例裁剪,也就是说,输出图片的清晰度和输入图片的清晰度一样|
77
+|imageSrc|String|''|原图的路径,支持本地路径和网络路径,如果是网络路径,小程序要注意配置下载域名,H5要注意跨域问题|
78
+|mask|String|''| 裁剪的蒙版url,配合蒙版可以裁剪出任何形状的图形 (示例见全屏裁剪demo) |
79
+|fileType|String|'jpg'|目标文件的类型,只支持 'jpg' 或 'png'。默认为 'jpg'|
80
+|quality|Number|1|图片的质量,取值范围为 (0, 1],不在范围内时当作1.0处理|
81
+|showGrid|Boolean|false|是否显示中心网格线,默认不显示|
82
+|initPosition|object|null|图片自定义的初始的位置,内容格式见change事件|
83
+|autoZoom|Boolean|true|是否开启操作结束后自动放大到窗口大小|
84
+|containerSize|object|null|手动指定容器大小,如果裁剪器放在大小会移动或缩放的dom中,则必须手动指定大小,可以带上单位,如果不带单位默认px,支持的单位有:rpx,px,vw,vm,示例:{width:100,height:1100}或者:{width:'100rpx',height:'100rpx'}|
85
+|canvas2d|Boolean|false| 是开启 2d canvas |
86
+
87
+
88
+
89
+### cropper Methods
90
+
91
+|方法名称|说明|参数|
92
+|:-:|:-:|:-:|
93
+|crop|裁剪图片|开始绘制并开始裁剪图片,返回Promise对象|
94
+|init|初始化,图片路径变化的时候自动调用此方法,如果包裹的dom大小变化了,你需要手动调用此方法|-|
95
+|initCropper|重置裁剪框和图片的位置和大小到初始的位置和大小|-|
96
+|undo|撤销,最多可以回退10步|-|
97
+|resume|重做|-|
98
+
99
+
100
+### cropper Events
101
+
102
+|方法名称|说明|返回值|
103
+|:-:|:-:|:-:|
104
+|change|当裁剪框和图片的相对位置发生变化的时候触发,返回裁剪框与图片的相对位置|ev={left:number,top:number,width:number,height:number}|
105
+|loadFail|当图片加载失败时触发| - |
106
+|cropStart|当裁开始时触发| - |
107
+## 帮助
108
+在使用中如遇到无法解决的问题,请提 [Issues](https://gitee.com/xiaojiang1996/better-uni-cropper/issues) 或者加我 微信:1097122362。

+ 2 - 0
utils/api.js

@@ -144,6 +144,8 @@ const install = (Vue, vm) => {
144 144
 		wareHouseOrderUpdate:(data)=>http.put(store.state.user.path+'/wareHouseOrder',data),//开单编辑
145 145
 		wareHouseDelete:(data)=>http.delete(store.state.user.path+'/warehouse/' + data.id ),//仓库删除
146 146
 		saveOrderFileAndTransfer: (data = {}) => http.post(store.state.user.path + '/storeInfo/saveOrderFileAndTransfer', data),
147
+		selectModelList: (data) => http.post(store.state.user.path+'/warehouse/selectModelList' , data),// 价格趋势筛选
148
+		searchModelByImage:(filePath, params = {})=>http.upload(store.state.user.path+'/warehouse/searchModelByImage',{ filePath, name: "file", params, timeout: 60000, custom: { loadingText: '识别中...' } }),//询价中心-以图搜型号
147 149
 
148 150
 		// 删除订单分成数据
149 151
 		deleteClueCommissionForm: (id, config = {}) => http.delete(store.state.user.path + '/clueCommissionForm/' + id),