Selaa lähdekoodia

客户阶段数据-数据分析 & 阶段配置

xiaona 2 vuotta sitten
vanhempi
commit
5ea982f6c7

+ 33 - 45
.idea/workspace.xml

@@ -2,9 +2,11 @@
2 2
 <project version="4">
3 3
   <component name="ChangeListManager">
4 4
     <list default="true" id="c03feb23-5590-4b06-ae32-412ba98013a8" name="Default" comment="">
5
-      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
6
-      <change beforePath="$PROJECT_DIR$/project/src/components/dataBoard/playletData.vue" beforeDir="false" afterPath="$PROJECT_DIR$/project/src/components/dataBoard/playletData.vue" afterDir="false" />
5
+      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
6
+      <change beforePath="$PROJECT_DIR$/project/package-lock.json" afterPath="$PROJECT_DIR$/project/package-lock.json" />
7 7
     </list>
8
+    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
9
+    <option name="TRACKING_ENABLED" value="true" />
8 10
     <option name="SHOW_DIALOG" value="false" />
9 11
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
10 12
     <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -95,6 +97,8 @@
95 97
       <foldersAlwaysOnTop value="true" />
96 98
     </navigator>
97 99
     <panes>
100
+      <pane id="Scratches" />
101
+      <pane id="ProjectPane" />
98 102
       <pane id="Scope">
99 103
         <subPane subId="Project Files">
100 104
           <expand>
@@ -138,8 +142,6 @@
138 142
           <select />
139 143
         </subPane>
140 144
       </pane>
141
-      <pane id="Scratches" />
142
-      <pane id="ProjectPane" />
143 145
     </panes>
144 146
   </component>
145 147
   <component name="ProjectViewState">
@@ -152,10 +154,25 @@
152 154
     <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
153 155
     <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
154 156
     <property name="WebServerToolWindowFactoryState" value="false" />
155
-    <property name="last_opened_file_path" value="$PROJECT_DIR$" />
157
+    <property name="last_opened_file_path" value="$PROJECT_DIR$/project" />
156 158
     <property name="ts.external.directory.path" value="C:\Program Files (x86)\webstorm\WebStorm 2021.2.1\plugins\JavaScriptLanguage\jsLanguageServicesImpl\external" />
157 159
     <property name="vue.rearranger.settings.migration" value="true" />
158 160
   </component>
161
+  <component name="RunDashboard">
162
+    <option name="ruleStates">
163
+      <list>
164
+        <RuleState>
165
+          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
166
+        </RuleState>
167
+        <RuleState>
168
+          <option name="name" value="StatusDashboardGroupingRule" />
169
+        </RuleState>
170
+      </list>
171
+    </option>
172
+  </component>
173
+  <component name="ShelveChangesManager" show_recycled="false">
174
+    <option name="remove_strategy" value="false" />
175
+  </component>
159 176
   <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
160 177
   <component name="SvnConfiguration">
161 178
     <configuration />
@@ -195,17 +212,17 @@
195 212
       <workItem from="1661338111374" duration="432000" />
196 213
       <workItem from="1661392701143" duration="873000" />
197 214
       <workItem from="1661395150121" duration="2965000" />
215
+      <workItem from="1669605643306" duration="36000" />
198 216
     </task>
199 217
     <servers />
200 218
   </component>
201 219
   <component name="TimeTrackingManager">
202
-    <option name="totallyTimeSpent" value="18119000" />
220
+    <option name="totallyTimeSpent" value="18155000" />
203 221
   </component>
204 222
   <component name="ToolWindowManager">
205 223
     <frame x="-11" y="-11" width="1942" height="1042" extended-state="6" />
206
-    <editor active="true" />
207 224
     <layout>
208
-      <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.042417817" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
225
+      <window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.2518558" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
209 226
       <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
210 227
       <window_info id="Docker" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
211 228
       <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />
@@ -226,16 +243,20 @@
226 243
     </layout>
227 244
   </component>
228 245
   <component name="TypeScriptGeneratedFilesManager">
229
-    <option name="version" value="3" />
246
+    <option name="version" value="1" />
247
+  </component>
248
+  <component name="VcsContentAnnotationSettings">
249
+    <option name="myLimit" value="2678400000" />
250
+  </component>
251
+  <component name="XDebuggerManager">
252
+    <breakpoint-manager />
253
+    <watches-manager />
230 254
   </component>
231 255
   <component name="editorHistoryManager">
232 256
     <entry file="file://$PROJECT_DIR$/qwh5/src/router/index.ts">
233 257
       <provider selected="true" editor-type-id="text-editor">
234 258
         <state relative-caret-position="189">
235 259
           <caret line="31" column="30" lean-forward="false" selection-start-line="31" selection-start-column="30" selection-end-line="31" selection-end-column="30" />
236
-          <folding>
237
-            <element signature="e#0#81#0" expanded="false" />
238
-          </folding>
239 260
         </state>
240 261
       </provider>
241 262
     </entry>
@@ -243,7 +264,6 @@
243 264
       <provider selected="true" editor-type-id="text-editor">
244 265
         <state relative-caret-position="0">
245 266
           <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
246
-          <folding />
247 267
         </state>
248 268
       </provider>
249 269
     </entry>
@@ -251,10 +271,6 @@
251 271
       <provider selected="true" editor-type-id="text-editor">
252 272
         <state relative-caret-position="-567">
253 273
           <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
254
-          <folding>
255
-            <element signature="e#19673#20472#0" expanded="false" />
256
-            <element signature="e#20496#20817#0" expanded="false" />
257
-          </folding>
258 274
         </state>
259 275
       </provider>
260 276
     </entry>
@@ -262,7 +278,6 @@
262 278
       <provider selected="true" editor-type-id="text-editor">
263 279
         <state relative-caret-position="459">
264 280
           <caret line="56" column="79" lean-forward="true" selection-start-line="56" selection-start-column="56" selection-end-line="56" selection-end-column="79" />
265
-          <folding />
266 281
         </state>
267 282
       </provider>
268 283
     </entry>
@@ -270,7 +285,6 @@
270 285
       <provider selected="true" editor-type-id="text-editor">
271 286
         <state relative-caret-position="0">
272 287
           <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
273
-          <folding />
274 288
         </state>
275 289
       </provider>
276 290
     </entry>
@@ -278,9 +292,6 @@
278 292
       <provider selected="true" editor-type-id="text-editor">
279 293
         <state relative-caret-position="513">
280 294
           <caret line="22" column="16" lean-forward="false" selection-start-line="22" selection-start-column="16" selection-end-line="22" selection-end-column="16" />
281
-          <folding>
282
-            <element signature="e#0#28#0" expanded="false" />
283
-          </folding>
284 295
         </state>
285 296
       </provider>
286 297
     </entry>
@@ -288,14 +299,6 @@
288 299
       <provider selected="true" editor-type-id="text-editor">
289 300
         <state relative-caret-position="421">
290 301
           <caret line="86" column="17" lean-forward="true" selection-start-line="86" selection-start-column="17" selection-end-line="86" selection-end-column="17" />
291
-          <folding>
292
-            <element signature="n#style#0;n#template#0;n#!!top" expanded="false" />
293
-            <element signature="n#form#0;n#div#0;n#template#0;n#!!top" expanded="false" />
294
-            <element signature="n#van-tabs#0;n#div#0;n#template#0;n#!!top" expanded="false" />
295
-            <element signature="e#3035#3073#0" expanded="false" />
296
-            <element signature="e#5577#5588#0" expanded="false" />
297
-            <element signature="n#style#0;n#!!top" expanded="false" />
298
-          </folding>
299 302
         </state>
300 303
       </provider>
301 304
     </entry>
@@ -303,9 +306,6 @@
303 306
       <provider selected="true" editor-type-id="text-editor">
304 307
         <state relative-caret-position="244">
305 308
           <caret line="17" column="13" lean-forward="false" selection-start-line="17" selection-start-column="8" selection-end-line="17" selection-end-column="13" />
306
-          <folding>
307
-            <element signature="e#0#31#0" expanded="false" />
308
-          </folding>
309 309
         </state>
310 310
       </provider>
311 311
     </entry>
@@ -313,18 +313,6 @@
313 313
       <provider selected="true" editor-type-id="text-editor">
314 314
         <state relative-caret-position="311">
315 315
           <caret line="487" column="23" lean-forward="true" selection-start-line="487" selection-start-column="23" selection-end-line="487" selection-end-column="23" />
316
-          <folding>
317
-            <element signature="n#style#0;n#template#0;n#!!top" expanded="true" />
318
-            <element signature="n#template#1;n#div#1;n#div#0;n#div#0;n#van-list#0;n#van-collapse-item#0;n#template#0;n#van-collapse#0;n#div#0;n#van-tab#0;n#van-tabs#0;n#div#0;n#template#0;n#!!top" expanded="false" />
319
-            <element signature="n#template#2;n#div#1;n#div#0;n#div#0;n#van-list#0;n#van-collapse-item#0;n#template#0;n#van-collapse#0;n#div#0;n#van-tab#0;n#van-tabs#0;n#div#0;n#template#0;n#!!top" expanded="false" />
320
-            <element signature="n#div#0;n#div#1;n#div#0;n#div#0;n#van-list#0;n#van-collapse-item#0;n#template#0;n#van-collapse#0;n#div#0;n#van-tab#0;n#van-tabs#0;n#div#0;n#template#0;n#!!top" expanded="false" />
321
-            <element signature="n#template#1;n#div#1;n#div#0;n#div#0;n#van-list#0;n#van-tab#1;n#van-tabs#0;n#div#0;n#template#0;n#!!top" expanded="false" />
322
-            <element signature="n#template#2;n#div#1;n#div#0;n#div#0;n#van-list#0;n#van-tab#1;n#van-tabs#0;n#div#0;n#template#0;n#!!top" expanded="false" />
323
-            <element signature="n#div#0;n#div#1;n#div#0;n#div#0;n#van-list#0;n#van-tab#1;n#van-tabs#0;n#div#0;n#template#0;n#!!top" expanded="false" />
324
-            <element signature="n#style#0;n#div#0;n#div#0;n#van-action-sheet#0;n#div#0;n#template#0;n#!!top" expanded="true" />
325
-            <element signature="e#7708#7951#0" expanded="false" />
326
-            <element signature="e#9084#9241#0" expanded="false" />
327
-          </folding>
328 316
         </state>
329 317
       </provider>
330 318
     </entry>

+ 41 - 0
project/package-lock.json

@@ -1934,6 +1934,16 @@
1934 1934
       "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==",
1935 1935
       "dev": true
1936 1936
     },
1937
+    "clipboard": {
1938
+      "version": "2.0.11",
1939
+      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
1940
+      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
1941
+      "requires": {
1942
+        "good-listener": "^1.2.2",
1943
+        "select": "^1.1.2",
1944
+        "tiny-emitter": "^2.0.0"
1945
+      }
1946
+    },
1937 1947
     "cliui": {
1938 1948
       "version": "2.1.0",
1939 1949
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
@@ -3583,6 +3593,11 @@
3583 3593
       "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
3584 3594
       "dev": true
3585 3595
     },
3596
+    "delegate": {
3597
+      "version": "3.2.0",
3598
+      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
3599
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
3600
+    },
3586 3601
     "delegates": {
3587 3602
       "version": "1.0.0",
3588 3603
       "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -4842,6 +4857,14 @@
4842 4857
         "minimatch": "~3.0.2"
4843 4858
       }
4844 4859
     },
4860
+    "good-listener": {
4861
+      "version": "1.2.2",
4862
+      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
4863
+      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
4864
+      "requires": {
4865
+        "delegate": "^3.1.2"
4866
+      }
4867
+    },
4845 4868
     "graceful-fs": {
4846 4869
       "version": "4.2.8",
4847 4870
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
@@ -10649,6 +10672,11 @@
10649 10672
         }
10650 10673
       }
10651 10674
     },
10675
+    "select": {
10676
+      "version": "1.1.2",
10677
+      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
10678
+      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
10679
+    },
10652 10680
     "select-hose": {
10653 10681
       "version": "2.0.0",
10654 10682
       "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -11627,6 +11655,11 @@
11627 11655
       "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
11628 11656
       "dev": true
11629 11657
     },
11658
+    "tiny-emitter": {
11659
+      "version": "2.1.0",
11660
+      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
11661
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
11662
+    },
11630 11663
     "to-arraybuffer": {
11631 11664
       "version": "1.0.1",
11632 11665
       "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@@ -12194,6 +12227,14 @@
12194 12227
       "resolved": "https://registry.npmjs.org/vue-axios/-/vue-axios-3.2.5.tgz",
12195 12228
       "integrity": "sha512-V7XUzu0v3WOzd8PEF9m/ZoVivap+ToBPOXrhI8AsnczD93YXvGG+HkeQhJHAF/jeav8CsPDF3X/Z2vCqBbjsMQ=="
12196 12229
     },
12230
+    "vue-clipboard2": {
12231
+      "version": "0.3.3",
12232
+      "resolved": "https://registry.npmjs.org/vue-clipboard2/-/vue-clipboard2-0.3.3.tgz",
12233
+      "integrity": "sha512-aNWXIL2DKgJyY/1OOeITwAQz1fHaCIGvUFHf9h8UcoQBG5a74MkdhS/xqoYe7DNZdQmZRL+TAdIbtUs9OyVjbw==",
12234
+      "requires": {
12235
+        "clipboard": "^2.0.0"
12236
+      }
12237
+    },
12197 12238
     "vue-hot-reload-api": {
12198 12239
       "version": "2.3.4",
12199 12240
       "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",

+ 6 - 0
project/src/assets/config/interface_api.js

@@ -326,6 +326,12 @@ var api = {
326 326
   phaseDel: "/api/CustomerStage/stageRuleDel",
327 327
   phaseSort: "/api/CustomerStage/stageRuleSort",
328 328
   phaseSet: "/api/CustomerStage/setStageRule",
329
+  phaseSetConfirm: "/api/CustomerStage/confirmSet",
330
+
331
+  // 客户阶段分析
332
+  phaseAnalysis_data: "/api/CustomerStage/analysis",
333
+  phaseAnalysis_trend: "/api/CustomerStage/trend",
334
+  phaseAnalysis_situation: "/api/CustomerStage/situation",
329 335
 };
330 336
 
331 337
 export { api };

+ 361 - 4
project/src/components/customerAnalysis/stageData/index.vue

@@ -1,20 +1,377 @@
1 1
 <template>
2 2
   <div>
3
-    客户阶段分析
3
+    <!-- 数据列表 -->
4
+    <div class="dataList">
5
+      <div class="itemBox" v-for="(item,index) in dataList" :key="item.name" :class="item.rule_id == acIdx ? 'itemBoxAc' : ''" @click="switchDataItem(item,index)">
6
+        <div class="f24 ">{{item.cnt || '-'}}</div>
7
+        <div class="tMar5 f14 ">{{item.stage_title || '-' }}</div>
8
+      </div>
9
+    </div>
10
+
11
+    <!-- 阶段趋势 -->
12
+    <div class="trendBox" v-loading="trends_loading">
13
+      <div class="legendBox">
14
+        <div class="legendItem" v-for="(item,index) in legendList" :key="index" @click="changeLegend('legendList',item,index)">{{item.name}}
15
+          <div :class="['checkbox',item.selectFlag?'checkbox_active':'']" :style="item.selectFlag?`background: ${item.color};
16
+    border-color: ${item.color};`:''"><i class="el-icon-check"></i></div>
17
+        </div>
18
+      </div>
19
+      <div class="screenBox">
20
+        <date-picker title="自定义" quickType='select' :afferent_quick_list="[
21
+        { label: '近一周', value: '7', date: [$getDay(-8, false), $getDay(-1, false)] },
22
+        { label: '近15天', value: '15', date: [$getDay(-16, false), $getDay(-1, false)] },
23
+        { label: '近30天', value: '30', date: [$getDay(-31, false), $getDay(-1, false)] },
24
+        { label: '近90天', value: '90', date: [$getDay(-91, false), $getDay(-1, false)] }]" :quickFlag='true' :afferent_time="default_time" :clearFlag='false' @changeTime="changeTime" :pickerOptions='pickerOptions' :is_include_today='false'></date-picker>
25
+      </div>
26
+      <div id="trend" style="width:100%;height:250px"></div>
27
+      <noData class="noData" v-if="custTrends&&custTrends.length<=0&&!trends_loading"></noData>
28
+    </div>
29
+
30
+    <!-- 客户流转情况 -->
31
+    <div class="trendBox">
32
+      <div>客户流转情况</div>
33
+      <div class="screenBox">
34
+        <date-picker title="自定义" quickType='select' :afferent_quick_list="[
35
+        { label: '昨天', value: '1', date: [$getDay(-1, false), $getDay(-1, false)] },
36
+        { label: '近7天', value: '7', date: [$getDay(-7, false), $getDay(-1, false)] },
37
+        { label: '近30天', value: '30', date: [$getDay(-31, false), $getDay(-1, false)] }]" :quickFlag='true' :afferent_time="default_time" :clearFlag='false' @changeTime="changeTime" :pickerOptions='pickerOptions' :is_include_today='false'></date-picker>
38
+      </div>
39
+      <div id="trend_situation" style="width:100%;height:250px"></div>
40
+      <noData class="noData" v-if="custTrends&&custTrends.length<=0&&!trends_loading"></noData>
41
+    </div>
42
+
4 43
   </div>
5 44
 </template>
6 45
 
7 46
 <script>
47
+  import datePicker from '@/components/assembly/screen/datePicker.vue'
48
+  import noData from "@/components/assembly/noData";
49
+  import _lodash from 'lodash'
8 50
 export default {
9 51
   name: "stageData",
52
+  components: { datePicker, noData },
10 53
   data() {
11
-    return {}
54
+    return {
55
+      dataList:[],
56
+      acIdx:null,
57
+      trends_loading: false,
58
+      legendList: [
59
+        { name: '客户总数', key: 'cust_total_uc', color: '#2983DF', selectFlag: true },
60
+        { name: '客户新增', key: 'cust_add_uc', color: '#00B38A', selectFlag: true },
61
+        { name: '客户流失', key: 'cust_loss_uc', color: '#EB4315', selectFlag: true },
62
+        { name: '客户净增', key: 'cust_gain_uc', color: '#AED570', selectFlag: true },
63
+        { name: '公众号关注', key: 'official_account_uc', color: '#EFAF47', selectFlag: true },
64
+        { name: '付费客户数', key: 'cust_pay_uc', color: '#7366FF', selectFlag: true },
65
+        { name: '付费金额', key: 'cust_pay_amount', color: '#F28544', selectFlag: true },
66
+        { name: '付费用户客单价', key: 'charge_user_cost', color: '#E360C8', selectFlag: true },
67
+      ],
68
+      default_time: [this.$getDay(-8, false), this.$getDay(-1, false)],
69
+      myChart: null,
70
+      pickerOptions: {
71
+        disabledDate (time) {
72
+          return time > Date.now() - 8.64e7;
73
+        }
74
+      },
75
+      custTrends: [],
76
+      time: [],
77
+    }
78
+  },
79
+  created() {
80
+    this.time = this.default_time;
81
+    this.getAnalysisData()
12 82
   },
13
-  created() {},
14
-  methods: {}
83
+  methods: {
84
+
85
+    trendEvent (chartDataName) {
86
+      chartDataName === 'custTrends' && this.myChart && this.myChart.clear()
87
+
88
+      const _this = this;
89
+      let series = [], yAxis = []
90
+      let xArr = this[chartDataName].map((v) => {
91
+        return v.idate
92
+      });
93
+      this.legendList.forEach((item, index) => {
94
+        if (item.selectFlag) {
95
+          yAxis.push(
96
+            {
97
+              type: "value",
98
+              name: '',
99
+              show: false,
100
+              position: 'left',
101
+              axisTick: {
102
+                show: false
103
+              },
104
+              splitLine: {
105
+                lineStyle: {
106
+                  color: '#F2F2f2',
107
+                  type: "dashed"
108
+                }
109
+              },
110
+              axisLine: {
111
+                show: true,
112
+                lineStyle: {
113
+                  color: '#F2F2f2'
114
+                }
115
+              },
116
+              nameTextStyle: {
117
+                color: "#999999",
118
+                fontSize: 13,
119
+              },
120
+              axisLabel: {
121
+                color: '#999999',
122
+                fontSize: 12,
123
+                show: true,
124
+                formatter: function (params) {
125
+                  let result = params;
126
+                  let resultData;
127
+                  resultData = _this.$NumberHandle(result)
128
+                  return resultData;
129
+                }
130
+              }
131
+            })
132
+
133
+          // series
134
+          let data = this[chartDataName].map((v) => {
135
+            return v[item.key] || v[item.key] == 0 ? v[item.key] : '-'
136
+          });
137
+          series.push({
138
+            type: "line",
139
+            smooth: true,
140
+            name: item.name,
141
+            yAxisIndex: 0,
142
+            data: data,
143
+            lineStyle: {
144
+              width: 2
145
+            },
146
+            symbol: xArr.length == 1 ? 'emptyCircle' : 'none',
147
+            itemStyle: {
148
+              color: item.color,
149
+              borderType: "emptyCircle"
150
+            },
151
+          })
152
+        }
153
+      })
154
+      yAxis.forEach((item, index) => {
155
+        item.show = index == 0 ? true : false
156
+      })
157
+      series.forEach((item, index) => {
158
+        item.yAxisIndex = index
159
+      })
160
+      let option = {
161
+        title: '',
162
+        tooltip: {
163
+          trigger: 'axis',
164
+          show: true,
165
+          formatter: function (params) {
166
+            let result = params[0].name;
167
+            result += '<br/>'
168
+            params.forEach((item, index) => {
169
+              result +=
170
+                item.marker +
171
+                item.seriesName +
172
+                ":" + (_this.$formatNum(item.value)) +
173
+                "<br/>";
174
+            });
175
+            return result;
176
+          }
177
+        },
178
+        legend: {
179
+          itemWidth: 8,
180
+          itemHeight: 2,
181
+          icon: "plain",
182
+          show: false,
183
+          textStyle: {
184
+            fontSize: 12,
185
+            color: '#666666'
186
+          },
187
+        },
188
+        grid:
189
+          {
190
+            top: '6%',
191
+            left: '4%',
192
+            right: '4%',
193
+            bottom: '16%',
194
+            containLabel: false
195
+          },
196
+        xAxis: [
197
+          {
198
+            type: "category",
199
+            data: xArr,
200
+            boundaryGap: false,//设置数据从头开始
201
+            axisLine: {
202
+              show: true,
203
+              lineStyle: {
204
+                color: '#F2F2f2'
205
+              }
206
+            },
207
+            axisTick: {
208
+              show: false
209
+            },
210
+            splitLine: {
211
+              show: false
212
+            },
213
+            axisLabel: {
214
+              color: '#666',
215
+              fontSize: 10,
216
+              rotate: 30,
217
+            },
218
+          }
219
+        ],
220
+        yAxis: yAxis,
221
+        series: series
222
+      };
223
+      //初始化echarts实例
224
+      this.myChart = this.myChart ? this.myChart : this.$echarts.init(document.getElementById('trend'));
225
+      this.myChart.setOption(option);
226
+    },
227
+
228
+    changeTime (time) {//筛选时间变化
229
+      if (!time || time && time.length == 0) {
230
+        this.time = []
231
+      } else {
232
+        this.time = time
233
+      }
234
+      this.getCustTrends()
235
+    },
236
+
237
+    changeLegend (legendListName, data, index) {
238
+      let arr = this[legendListName].filter((v) => {
239
+        return v.selectFlag
240
+      })
241
+      if (arr.length == 1 && arr[0].key == data.key) {
242
+        this.$message({
243
+          message: '至少存在一条曲线',
244
+          type: "warning"
245
+        })
246
+        return
247
+      }
248
+      let item = data;
249
+      item.selectFlag = !item.selectFlag;
250
+      this.$set(this[legendListName], index, item)
251
+      this.$nextTick(() => {
252
+        legendListName === 'legendList' && this.trendEvent('custTrends')
253
+      })
254
+    },
255
+    // 获取趋势数据
256
+    getAnalysisData(){
257
+      this.$axios.get(this.URL.BASEURL + this.URL.phaseAnalysis_data).then((_res) => {
258
+        const res = _res.data
259
+        if (res && res.errno == 0) {
260
+          if(res.rst&&res.rst.length>0){
261
+            this.dataList = res.rst
262
+            this.acIdx = this.dataList[this.dataList.length-1].rule_id
263
+            this.getCustTrends()
264
+          }
265
+        } else if (res.errno != 4002) {
266
+          this.$message({
267
+            message: res.err,
268
+            type: "warning"
269
+          })
270
+        }
271
+      }).catch((err) => {
272
+        this.trends_loading = false
273
+      });
274
+    },
275
+
276
+    // 获取趋势数据
277
+    getCustTrends () {//客户数据趋势
278
+      this.$axios.get(this.URL.BASEURL + this.URL.phaseAnalysis_trend, {
279
+        params: {
280
+          rule_id: this.acIdx,
281
+          start_date: this.time[0],
282
+          end_date: this.time[1],
283
+        }
284
+      }).then((_res) => {
285
+        const res = _res.data
286
+        this.trends_loading = false
287
+        if (res && res.errno == 0) {
288
+          // 处理后端返回的数据 => 分离&提取新图表数据
289
+          this.custTrends = _lodash.cloneDeep(res.rst)
290
+          this.custTrends.forEach(item => {
291
+            delete item['total_cost']
292
+            delete item['total_cust_pay_amount']
293
+          })
294
+          this.custTrendsNew = _lodash.cloneDeep(res.rst).map(item => ({
295
+            idate: item['idate'],
296
+            total_cost: item['total_cost'],
297
+            total_cust_pay_amount: item['total_cust_pay_amount']
298
+          }))
299
+          this.$nextTick(() => {
300
+            this.trendEvent('custTrends') // 绘制图表
301
+          })
302
+        } else if (res.errno != 4002) {
303
+          this.$message({
304
+            message: res.err,
305
+            type: "warning"
306
+          })
307
+        }
308
+      }).catch((err) => {
309
+        this.trends_loading = false
310
+      });
311
+    },
312
+
313
+    //切换数据项
314
+    switchDataItem(item,index){
315
+      this.acIdx = item.rule_id
316
+      this.getCustTrends()
317
+    },
318
+  }
15 319
 }
16 320
 </script>
17 321
 
18 322
 <style lang="scss" scoped>
323
+.dataList{
324
+  display: flex;
325
+  align-items: center;
326
+  flex-wrap: nowrap;
327
+  overflow-x: auto;
328
+  .itemBox{
329
+    margin-right: 10px;
330
+    width: 240px;
331
+    height: 86px;
332
+    background: #FFFFFF;
333
+    border-radius: 6px 6px 0px 0px;
334
+    padding: 18px 40px;
335
+    color: #333;
336
+    box-sizing: border-box;
337
+    border-top: 6px solid #fff;
338
+    cursor: pointer;
339
+    flex-shrink: 0;
340
+  }
341
+  .itemBoxAc{
342
+    color: #02B389!important;
343
+    border-top: 6px solid #02B389;
344
+  }
345
+}
19 346
 
347
+.trendBox {
348
+  background: #ffffff;
349
+  padding: 22px 23px;
350
+  position: relative;
351
+  margin-top: 10px;
352
+  .noData {
353
+    position: absolute;
354
+    top: 100px;
355
+    left: 0;
356
+    right: 0;
357
+    margin: auto;
358
+  }
359
+  .legendBox {
360
+    display: flex;
361
+    align-items: center;
362
+    .legendItem {
363
+      color: #333333;
364
+      font-size: 14px;
365
+      line-height: 20px;
366
+      display: flex;
367
+      align-items: center;
368
+      margin-right: 30px;
369
+      cursor: pointer;
370
+      user-select: none;
371
+    }
372
+  }
373
+  .screenBox {
374
+    display: flex;
375
+  }
376
+}
20 377
 </style>

+ 129 - 76
project/src/components/phaseConfig/addEditDialog.vue

@@ -1,6 +1,6 @@
1 1
 <template>
2 2
   <div>
3
-    <el-dialog class="dialogCon_permission" :title="dialogTitle" :visible.sync="dialogVisible" :append-to-body="true" width="600px" @close="closeEvent">
3
+    <el-dialog class="dialogCon_permission" :title="dialogTitle" :visible.sync="dialogVisible" :append-to-body="true" width="660px" @close="closeEvent">
4 4
       <div class="dialogCon">
5 5
         <div class="itemBox" style="margin-top: 0">
6 6
           <div class="name"><em>*</em>阶段名称:</div>
@@ -17,23 +17,22 @@
17 17
         <div class="itemBox" style="align-items: baseline">
18 18
           <div class="name"><em>*</em>阶段规则:</div>
19 19
           <div class="ipt phaseBox">
20
-            <div class="flex-row bMar15">
21
-              <div class="chooseItem" v-for="item in phaseList" :key="item.value"
22
-                   :class="item.value == phaseVal ? 'acItem' : ''"
23
-                   @click.stop="phaseVal = item.value"
24
-                   >
25
-                <span class="checkBox" @click.stop="switchCheckBox(item.value)"
26
-                                     :class="choosePhaseArr.indexOf(item.value)>-1 ? 'acCheck' : ''"></span>
27
-                {{item.label}}
20
+            <div class="flex-base" v-for="(rItem,rIdx) in ruleList">
21
+              <span class="f13 flexShrink0">规则:</span>
22
+              <el-select style="width: 120px" :disabled="rItem.disabled" v-model="rItem.type" size="mini" placeholder="请选择">
23
+                <el-option v-for="item in phaseList" :key="item.value" :label="item.label" :value="item.value"></el-option>
24
+              </el-select>
25
+              <span class="lMar14 f13 flexShrink0">满足:</span>
26
+              <div>
27
+                <addTime v-if="rItem.type=='time'"></addTime>
28
+                <screen-pay :reset='resetFlag' title="" v-if="rItem.type=='pay'"></screen-pay>
29
+                <enterprise-tag :reset='resetFlag' title="" v-if="rItem.type=='label'"></enterprise-tag>
28 30
               </div>
31
+              <i class="el-icon-delete" v-if="ruleList&&ruleList.length > 1" @click="delePhaseEvent(rItem)"></i>
29 32
             </div>
30
-            <div>
31
-              <addTime v-show="phaseVal==1"></addTime>
32
-              <screen-pay :reset='resetFlag' title="" v-show="phaseVal==2"></screen-pay>
33
-              <enterprise-tag :reset='resetFlag' title="" v-show="phaseVal==3"></enterprise-tag>
33
+            <div class="addPhase" @click="addPhaseEvent">
34
+              <i class="el-icon-circle-plus "></i> 添加规则
34 35
             </div>
35
-
36
-
37 36
           </div>
38 37
         </div>
39 38
       </div>
@@ -57,23 +56,36 @@
57 56
         dialogTitle:'阶段配置',
58 57
         iptName:'',
59 58
         iptDesc:'',
60
-        phaseVal:1,
61 59
         resetFlag:false,
62 60
         phaseList:[
63
-          {label:'客户添加时间',value:1},
64
-          {label:'付费情况',value:2},
65
-          {label:'标签',value:3},
61
+          {label:'客户添加时间',value:'time'},
62
+          {label:'付费情况',value:'pay'},
63
+          {label:'标签',value:'label'},
66 64
         ],
67
-        choosePhaseArr:[],
65
+      /*
66
+        {type:'time',condition:{min:0,max:1}},
67
+        {type:'pay',condition:{
68
+          payStatus:'not - has',
69
+          payMin:'',
70
+          payMax:'',
71
+          payType:'number -  money'
72
+        }},
73
+        {type:'label',condition:{
74
+          tag:'1 - 2 - 3 ',
75
+            tag_id_list:'标签分割'
76
+        }}
77
+      * */
68 78
         ruleList:[
69
-          {type:'1',condition:'test'}
79
+          {type:'time',label:'客户添加时间',condition:{},disabled:false}
70 80
         ],//传给后台的格式和回显的规则
71
-      }
72
-    },
73
-    created () {
74
-
75 81
 
82
+        flag:true,// 校验是否通过
83
+        timeRef:'',
84
+        payRef:'',
85
+        tagRef:'',
86
+      }
76 87
     },
88
+    created () {},
77 89
     watch: {
78 90
       dialogVisible(isShow) {
79 91
         // 弹框显示 => 初始化表单数据
@@ -85,12 +97,46 @@
85 97
           }else{ //新增
86 98
             this.iptName = ''
87 99
             this.iptDesc = ''
88
-            this.phaseVal = 1
89 100
           }
90 101
         }
91 102
       },
92 103
     },
93 104
     methods: {
105
+      //删除规则
106
+      delePhaseEvent(rItem){
107
+        if(this.ruleList&&this.ruleList.length==1){
108
+          this.$message({
109
+            message: '最少需要一个规则',
110
+            type: "warning"
111
+          })
112
+          return
113
+        }
114
+        let idx = this.ruleList.findIndex(n=>n.type == rItem.type)
115
+        this.ruleList.splice(idx,1)
116
+        this.phaseList.push({
117
+          label:rItem.label,value:rItem.type
118
+        })
119
+        this.ruleList[this.ruleList.length-1].disabled = false
120
+      },
121
+      //添加规则
122
+      addPhaseEvent(){
123
+        if(this.ruleList&&this.ruleList.length==3){
124
+          this.$message({
125
+            message: '每个规则仅可添加一次',
126
+            type: "warning"
127
+          })
128
+          return
129
+        }
130
+        let idx = 0
131
+        this.ruleList.forEach(rItem=>{
132
+          rItem.disabled = true
133
+          idx = this.phaseList.findIndex(n=>n.value == rItem.type)
134
+        })
135
+        this.phaseList.splice(idx,1)
136
+        this.ruleList.push({
137
+          type:this.phaseList[0].value,label:this.phaseList[0].label,condition:{},disabled:false,
138
+        })
139
+      },
94 140
       // 添加编辑 规则
95 141
       addEditEvent(){
96 142
 
@@ -103,18 +149,22 @@
103 149
       closeEvent(){
104 150
         this.$emit('close')
105 151
       },
106
-      //选择规则
107
-      switchCheckBox(val){
108
-        let idx = this.choosePhaseArr.indexOf(val)
109
-        if(idx > -1){
110
-          this.choosePhaseArr.splice(idx,1)
111
-        }else{
112
-          this.phaseVal = val
113
-          this.choosePhaseArr.push(val)
114
-        }
152
+
153
+      //规则校验
154
+      handleAssignment(){
155
+        this.flag = true
156
+        console.log(this.$refs.timeRef.minVal);
157
+        // console.log(this.$refs.timeRef.handleVal());
158
+        // this.ruleList.forEach(rItem=>{
159
+        //   if(rItem.type == 'time'){
160
+        //    this.$nextTick(()=>{
161
+        //      console.log(this.$refs.timeRef.handleVal());
162
+        //    })
163
+        //   }
164
+        // })
115 165
       },
116 166
       //确定
117
-      confirmEvent(){
167
+      async confirmEvent (){
118 168
         if(this.iptName==''){
119 169
           this.$message({
120 170
             message: '规则标题必填',
@@ -122,27 +172,33 @@
122 172
           })
123 173
           return
124 174
         }
125
-        this.$axios.post(this.URL.BASEURL + this.URL.phaseSet, {
126
-          rule_id: this.echoObj ? this.echoObj.rule_id : '',
127
-          stage_title: this.iptName,
128
-          stage_desc:this.iptDesc,
129
-          stage_rule:this.ruleList
130
-        }).then((res) => {
131
-          var res = res.data
132
-          if (res && res.errno == 0) {
133
-            this.$message({
134
-              message: '添加成功',
135
-              type: "success"
136
-            })
137
-            this.$emit('close',true)
138
-          } else if (res.errno != 4002) {
139
-            this.$message({
140
-              message: res.err,
141
-              type: "warning"
142
-            })
143
-          }
144
-        }).catch((err) => {
145
-        });
175
+        await this.handleAssignment()
176
+        if(!this.flag){
177
+          return
178
+        }
179
+        console.log(this.ruleList);
180
+        return
181
+          this.$axios.post(this.URL.BASEURL + this.URL.phaseSet, {
182
+            rule_id: this.echoObj ? this.echoObj.rule_id : '',
183
+            stage_title: this.iptName,
184
+            stage_desc:this.iptDesc,
185
+            stage_rule:this.ruleList
186
+          }).then((res) => {
187
+            var res = res.data
188
+            if (res && res.errno == 0) {
189
+              this.$message({
190
+                message: '添加成功',
191
+                type: "success"
192
+              })
193
+              this.$emit('close',true)
194
+            } else if (res.errno != 4002) {
195
+              this.$message({
196
+                message: res.err,
197
+                type: "warning"
198
+              })
199
+            }
200
+          }).catch((err) => {
201
+          });
146 202
       }
147 203
 
148 204
     }
@@ -170,27 +226,24 @@
170 226
       .phaseBox{
171 227
         padding: 20px;
172 228
         background-color: #f8f8f8;
173
-        .chooseItem{
174
-          display: flex;
175
-          align-items: center;
229
+        .el-icon-delete{
230
+          color: #58BCA6;
231
+          margin-left: auto;
176 232
           cursor: pointer;
177
-          margin-right: 25px;
178
-          .checkBox{
179
-            display: inline-block;
180
-            width: 16px;
181
-            height: 16px;
182
-            border-radius: 3px;
183
-            border: 1px solid #d8d8d8;
184
-            margin-right: 6px;
185
-          }
186 233
         }
187
-        .acItem{
188
-          color: #00B38A;
189
-          font-weight: 600;
190
-        }
191
-        .acCheck{
192
-          background-color: #00B38A;
193
-          border: 1px solid #00B38A!important;
234
+        .addPhase{
235
+          margin-top: 10px;
236
+          display: inline-block;
237
+          .el-icon-circle-plus{
238
+            color: #58BCA6;
239
+            margin-right: 4px;
240
+          }
241
+          color: #58BCA6;
242
+          cursor: pointer;
243
+          font-size: 14px;
244
+          &:hover{
245
+
246
+          }
194 247
         }
195 248
       }
196 249
     }

+ 16 - 15
project/src/components/phaseConfig/addTime.vue

@@ -1,26 +1,24 @@
1 1
 <template>
2
-  <div class="timeLineBox " style="margin: 10px 0;">
3
-    <el-radio v-model="timeline" :label="24">24小时</el-radio>
4
-    <el-radio v-model="timeline" :label="168">7天</el-radio>
5
-    <el-radio v-model="timeline" :label="360">15天</el-radio>
6
-    <el-radio v-model="timeline" :label="-1">自定义</el-radio>
7
-    <date-picker v-if="timeline == -1" title="" width="355px" :timeFlag="true"
8
-                 :afferent_time='afferent_time' @changeTime="changeTime"
9
-                 style="margin:6px 0 8px 0">
10
-    </date-picker>
11
-    <!-- :reset='resetFlag'-->
2
+  <div class="timeLineBox " style="margin: 15px 0;">
3
+    <div class="flex-row tMar10">
4
+      <el-input v-model="minVal" size="mini" type="number" placeholder="开始值" style="width: 100px"></el-input>
5
+      <span class="lMar5">天</span>
6
+      <span class="lMar5 rMar5">-</span>
7
+      <el-input v-model="maxVal" size="mini" type="number" placeholder="结束值" style="width: 100px"></el-input>
8
+      <span class="lMar5">天</span>
9
+    </div>
12 10
   </div>
13 11
 </template>
14 12
 <script>
15
-  import datePicker from '@/components/assembly/screen/datePicker.vue'
16 13
   export default {
17 14
     name: 'addTime',
18
-    components: { datePicker },
19
-    props: ["afferent_time"],
15
+    components: {  },
16
+    props: ["propVal"],
20 17
     data () {
21 18
       return {
22
-        timeline:24,
23
-        resetFlag:false
19
+        resetFlag:false,
20
+        minVal:'',
21
+        maxVal:''
24 22
       }
25 23
     },
26 24
     created () {
@@ -29,6 +27,9 @@
29 27
     },
30 28
     methods: {
31 29
       //
30
+      handleVal(){
31
+        return { min: this.minVal ,max:this.maxVal }
32
+      },
32 33
       changeEvent(){
33 34
 
34 35
       },

+ 118 - 25
project/src/components/phaseConfig/index.vue

@@ -2,34 +2,45 @@
2 2
   <div>
3 3
     <div class="screenBox">
4 4
       <el-button type="primary" size="small" @click="addEditEvent">阶段配置</el-button>
5
-      <span class="f12 lMar8">*上下拖动调整阶段排列顺序</span>
5
+      <el-button type="primary" size="small" plain @click="confirmEvent">确认阶段配置</el-button>
6 6
     </div>
7 7
 
8
-    <el-table ref="phaseTable" :height="height" :data="tableData" tooltip-effect="dark" style="width: 100%;margin-top:10px" v-loading="loading">
9
-      <el-table-column min-width='160' v-for="(item,index) in desCol" :key="index+'des'" :prop="item.prop" :label="item.label" show-overflow-tooltip align="center" :fixed="item.prop=='date'?'left':false">
10
-        <template slot-scope="scope">
11
-          <template v-if="item.prop == 'enable'">
12
-            <span v-if="scope.row.enable==1" class="c-448AFF">启用</span>
13
-            <span v-else class="c-F03F5C">禁用</span>
8
+    <div class="tableBox">
9
+      <div class="f12 bMar15 pad015 c-00B38A">*上下拖动列表可以调整排序,请确保阶段排序为初级到高级的顺序,规则设置完成后点击【确认阶段配置】后开始同步数据</div>
10
+      <el-table ref="phaseTable" :height="height" :data="tableData" tooltip-effect="dark" style="width: 100%;margin-top:10px" v-loading="loading">
11
+        <el-table-column label="拖拽排序" width="80" align="center">
12
+          <template slot-scope="{ row }">
13
+            <div class="drag-handler">
14
+              <i class="el-icon-rank"  />
15
+            </div>
14 16
           </template>
15
-          <span v-else>{{scope.row[item.prop]}}</span>
16
-        </template>
17
-      </el-table-column>
18
-      <el-table-column label="操作" min-width="160" align="center">
19
-        <template slot-scope="scope">
20
-          <el-button type="primary" size="mini" @click="addEditEvent(scope.row)">编辑</el-button>
21
-          <el-popconfirm title="确定删除吗?" @confirm="deleEvent(scope.row.rule_id)">
22
-            <el-button slot="reference" type="danger" size="mini">删除</el-button>
23
-          </el-popconfirm>
24
-        </template>
25
-      </el-table-column>
26
-    </el-table>
17
+        </el-table-column>
18
+        <el-table-column min-width='160' v-for="(item,index) in desCol" :key="index+'des'" :prop="item.prop" :label="item.label" show-overflow-tooltip align="center" :fixed="item.prop=='date'?'left':false">
19
+          <template slot-scope="scope">
20
+            <template v-if="item.prop == 'enable'">
21
+              <span v-if="scope.row.enable==1" class="c-448AFF">启用</span>
22
+              <span v-else class="c-F03F5C">禁用</span>
23
+            </template>
24
+            <span v-else>{{scope.row[item.prop]}}</span>
25
+          </template>
26
+        </el-table-column>
27
+        <el-table-column label="操作" min-width="160" align="center">
28
+          <template slot-scope="scope">
29
+            <el-button type="primary" size="mini" @click="addEditEvent(scope.row)">编辑</el-button>
30
+            <el-popconfirm title="确定删除吗?" @confirm="deleEvent(scope.row.rule_id)">
31
+              <el-button slot="reference" type="danger" size="mini">删除</el-button>
32
+            </el-popconfirm>
33
+          </template>
34
+        </el-table-column>
35
+      </el-table>
27 36
 
28
-    <div class="pagination" v-show="total>0">
29
-      <el-pagination background :current-page="page" @current-change="handleCurrentChange" layout="prev, pager, next" :page-count='Number(pages)'>
30
-      </el-pagination>
37
+      <div class="pagination" v-show="total>0">
38
+        <el-pagination background :current-page="page" @current-change="handleCurrentChange" layout="prev, pager, next" :page-count='Number(pages)'>
39
+        </el-pagination>
40
+      </div>
31 41
     </div>
32 42
 
43
+
33 44
     <!--阶段配置弹框-->
34 45
     <addEditDialog
35 46
       :dialogVisible="dialogVisible"
@@ -40,13 +51,14 @@
40 51
 </template>
41 52
 <script>
42 53
   import addEditDialog from './addEditDialog.vue'
54
+  import Sortable from 'sortablejs'
43 55
   export default {
44 56
     name: 'phaseConfig',
45 57
     components: { addEditDialog },
46 58
     data () {
47 59
       return {
48 60
         desCol: [
49
-          { prop: "sort_order", label: "排序", min_width: 180, fixed: 'left' },
61
+          { prop: "rule_id", label: "规则id", min_width: 180,},
50 62
           { prop: "stage_title", label: "客户阶段", min_width: 180, },
51 63
           { prop: "stage_desc", label: "阶段描述", min_width: 180, },
52 64
           { prop: "stage_rule", label: "阶段规则", min_width: 180, },
@@ -59,14 +71,91 @@
59 71
         pages:0,
60 72
         page_size:20,
61 73
         loading:false,
62
-        echoObj:{}
74
+        echoObj:{},
75
+        operate_rule_id:'',//目标id
76
+        front_rule_id:'',//上
77
+        behind_rule_id:'',//下
63 78
       }
64 79
     },
65 80
     created () {
66 81
       this.height = document.documentElement.clientHeight - 280 > 400 ? document.documentElement.clientHeight - 280 : 400
67 82
       this.init()
68 83
     },
84
+    mounted() {
85
+      // 表格行拖拽
86
+      this.handleRowDrop()
87
+    },
69 88
     methods: {
89
+      // 注册表格行拖拽事件
90
+      handleRowDrop() {
91
+        const tbody = document.querySelector('.el-table__body-wrapper tbody')
92
+        const _this = this
93
+        Sortable.create(tbody, {
94
+          handle: '.drag-handler',
95
+          onEnd({ newIndex, oldIndex }) {
96
+            if (newIndex == oldIndex) return
97
+            _this.tableData.splice(
98
+              newIndex,
99
+              0,
100
+              _this.tableData.splice(oldIndex, 1)[0]
101
+            )
102
+            const newArray = _this.tableData.slice(0)
103
+            _this.tableData = []
104
+            _this.$nextTick( async () => {
105
+              _this.tableData = newArray
106
+              await _this.getRuleId(newIndex)
107
+              await _this.sortEvent()
108
+            })
109
+          }
110
+        })
111
+      },
112
+      //获取排序需要的id
113
+      getRuleId(newIndex){
114
+        const _this = this
115
+        _this.operate_rule_id = _this.tableData[newIndex].rule_id
116
+        if(newIndex == 0){ //列表第一位
117
+          _this.front_rule_id = ''
118
+          _this.behind_rule_id = _this.tableData[newIndex+1].rule_id
119
+        }else if(newIndex == _this.tableData.length-1){ //列表最后一位
120
+          _this.front_rule_id = _this.tableData[newIndex-1].rule_id
121
+          _this.behind_rule_id = ''
122
+        }else{ //中间
123
+          _this.front_rule_id = _this.tableData[newIndex-1].rule_id
124
+          _this.behind_rule_id = _this.tableData[newIndex+1].rule_id
125
+        }
126
+      },
127
+      //排序事件
128
+      sortEvent(){
129
+        this.$axios.post(this.URL.BASEURL + this.URL.phaseSort, {
130
+          operate_rule_id:this.operate_rule_id,//目标id
131
+          front_rule_id:this.front_rule_id,//上
132
+          behind_rule_id:this.behind_rule_id,//下
133
+        }).then((res) => {
134
+          var res = res.data
135
+          this.$message({
136
+            message: res.err,
137
+            type: "info"
138
+          })
139
+          //如果排序失败,就刷新一下页面,否则之后的排序都是错误的
140
+          if(res.errno !== '0'){
141
+            this.init()
142
+          }
143
+        })
144
+      },
145
+      // 确认阶段配置
146
+      confirmEvent(){
147
+        this.$axios.get(this.URL.BASEURL + this.URL.phaseSetConfirm).then((res) => {
148
+          var res = res.data
149
+          if (res && res.errno == 0) {
150
+            this.init(1)
151
+          } else {
152
+            this.$message({
153
+              message: res.err,
154
+              type: "warning"
155
+            })
156
+          }
157
+        })
158
+      },
70 159
       // 添加编辑 规则
71 160
       addEditEvent( row ){
72 161
         this.dialogVisible = true
@@ -132,5 +221,9 @@
132 221
   }
133 222
 </script>
134 223
 <style lang="scss">
135
-
224
+.tableBox{
225
+  background-color: #fff;
226
+  padding: 15px 0;
227
+  margin-top: 10px;
228
+}
136 229
 </style>

+ 1 - 1
project/src/components/phaseConfig/pay.vue

@@ -1,7 +1,7 @@
1 1
 <template>
2 2
   <div class="common-screen-item pay">
3 3
     <label class="common-screen-label" v-if="title&&title!=''">{{title}}</label>
4
-    <el-popover placement="bottom" trigger="click" v-model="visible" @hide="visibleHide">
4
+    <el-popover placement="bottom" size="mini" trigger="click" v-model="visible" @hide="visibleHide">
5 5
       <div :style="width?'width:'+width:''" :class="['common-screen-self-box','common-input-select',valueObj.radio!=null&&clearable?'common-input-select-hover':'']" slot="reference">
6 6
         <div :class="['common-screen-self-con',valueObj.radio!=0&&valueObj.radio==null?'common-screen-self-placeholder':'']" :style="width?'text-align: left;padding-left: 15px !important;':''">
7 7
           <div class="common-screen-self-con-div">

+ 10 - 0
project/src/style/self.scss

@@ -192,6 +192,9 @@
192 192
 .f22{
193 193
   font-size: 22px !important;
194 194
 }
195
+.f24{
196
+  font-size: 24px !important;
197
+}
195 198
 .f28{
196 199
   font-size: 28px;
197 200
 }
@@ -395,6 +398,10 @@
395 398
   display: flex;
396 399
   align-items: flex-start;
397 400
 }
401
+.flex-base{
402
+  display: flex;
403
+  align-items: baseline;
404
+}
398 405
 .clampTwo{
399 406
   text-overflow: -o-ellipsis-lastline;
400 407
   overflow: hidden;
@@ -440,3 +447,6 @@
440 447
   margin:10px 0;
441 448
   color:red;
442 449
 }
450
+.drag-handler {
451
+  cursor: move;
452
+}