From b515fae43490ab20977d559e19d4e5f63a4fd96d Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期五, 25 四月 2025 16:42:39 +0800
Subject: [PATCH] 应急线索模块

---
 pages/usercenter/menu.js                     |   43 
 pages/cluetask/manage/index.js               |   55 ++
 pages/cluetask/manage/question-proxy.js      |   79 +++
 pages/cluetask/conclusion/index.wxss         |    3 
 pages/cluetask/clue-item.js                  |   40 +
 custom-tab-bar/index.js                      |   31 
 pages/cluetask/question/index.wxss           |    9 
 components/descriptions-list-item/index.wxml |    6 
 pages/cluetask/conclusion/index.json         |   10 
 pages/cluetask/question/index.js             |  220 ++++++++
 components/descriptions-list/index.wxml      |    9 
 model/clue/clue.js                           |   14 
 pages/cluetask/manage/index.json             |   13 
 model/clue/clueTask.js                       |    3 
 pages/cluetask/home/index.js                 |   15 
 pages/cluetask/manage/conclusion-proxy.js    |   35 +
 pages/cluetask/conclusion/index.js           |   80 +++
 pages/cluetask/manage/conclusion.wxml        |   21 
 common/clue/dataConclusionType.js            |   32 +
 config/index.js                              |    9 
 project.private.config.json                  |    6 
 components/descriptions-list-item/index.js   |   30 +
 components/descriptions-list-item/index.json |    4 
 components/empty-page/index.wxml             |    7 
 custom-tab-bar/data.js                       |   64 ++
 pages/cluetask/manage/question.wxml          |   48 +
 pages/cluetask/question/index.wxml           |   47 +
 components/form/form-util.js                 |    8 
 pages/cluetask/manage/index.wxss             |   24 
 components/form/index.wxml                   |   18 
 pages/cluetask/home/tasks.wxml               |   39 +
 pages/usercenter/index.wxml                  |    1 
 pages/cluetask/conclusion/index.wxml         |   12 
 services/clue/fetchClue.js                   |  266 ++++++---
 components/empty-page/index.js               |    1 
 pages/cluetask/home/tasks-proxy.js           |   18 
 components/descriptions-list-item/index.wxss |   21 
 components/descriptions-list/index.wxss      |   22 
 pages/cluetask/question/index.json           |   10 
 app.json                                     |    9 
 components/descriptions-list/index.js        |   26 +
 model/clue/clueQuestion.js                   |    2 
 components/form/index.js                     |   22 
 pages/cluetask/manage/index.wxml             |   18 
 components/descriptions-list/index.json      |    4 
 common/dataTowns.js                          |    8 
 components/form/index.wxss                   |    6 
 pages/cluetask/home/tasks.wxss               |   41 +
 pages/usercenter/index.js                    |    9 
 49 files changed, 1,356 insertions(+), 162 deletions(-)

diff --git a/app.json b/app.json
index 4599fd1..3598588 100644
--- a/app.json
+++ b/app.json
@@ -3,6 +3,7 @@
     "pages/usercenter/login/login-home/index",
     "pages/home/index",
     "pages/supervision/index",
+    "pages/cluetask/home/index",
     "pages/assist/index",
     "pages/usercenter/index",
     "pages/enterprise/search/index",
@@ -37,7 +38,9 @@
     "pages/inspection/scene/info/device-status/index",
     "pages/simple-home/index",
     "pages/inspection/ranking/search/index",
-    "pages/cluetask/home/index"
+    "pages/cluetask/manage/index",
+    "pages/cluetask/question/index",
+    "pages/cluetask/conclusion/index"
   ],
   "tabBar": {
     "custom": true,
@@ -53,10 +56,6 @@
       {
         "pagePath": "pages/supervision/index",
         "text": "绮剧粏鍖栫洃绠�"
-      },
-      {
-        "pagePath": "pages/cluetask/home/index",
-        "text": "搴旀�ョ嚎绱�"
       },
       {
         "pagePath": "pages/selfpatrol/index",
diff --git a/common/clue/dataConclusionType.js b/common/clue/dataConclusionType.js
new file mode 100644
index 0000000..c4cb17b
--- /dev/null
+++ b/common/clue/dataConclusionType.js
@@ -0,0 +1,32 @@
+// 搴旀�ョ嚎绱换鍔″搷搴旂骇鍒�
+const ConclusionType = [
+  { value: '鏈夐棶棰�', label: '鏈夐棶棰�' },
+  { value: '鏃犻棶棰�', label: '鏃犻棶棰�' },
+  { value: '宸茶В鍐�', label: '宸茶В鍐�' },
+];
+
+export default {
+  ConclusionType,
+  toLabel(value) {
+    let r = ConclusionType.find(item => {
+      return item.value == value;
+    });
+    if (!r) r = ConclusionType[0];
+    return r.label;
+  },
+
+  toLabel2(value) {
+    let l = this.toLabel(value);
+    if (l == ConclusionType[0].label) {
+      l = '/';
+    }
+    return l;
+  },
+
+  toValue(label) {
+    const r = ConclusionType.find(item => {
+      return item.label == label;
+    });
+    return r.value;
+  },
+};
diff --git a/common/dataTowns.js b/common/dataTowns.js
index e7b43d0..e53cd8e 100644
--- a/common/dataTowns.js
+++ b/common/dataTowns.js
@@ -119,8 +119,8 @@
 }
 
 function toLabel(value) {
-  for (const iterator of Towns) {
-    let r = iterator.value.find(item => {
+  for (const key in Towns) {
+    let r = Towns[key].find(item => {
       return item.value == value;
     });
     if (r) {
@@ -130,8 +130,8 @@
 }
 
 function toValue(label) {
-  for (const iterator of Towns) {
-    let r = iterator.value.find(item => {
+  for (const key in Towns) {
+    let r = Towns[key].find(item => {
       return item.label == label;
     });
     if (r) {
diff --git a/components/descriptions-list-item/index.js b/components/descriptions-list-item/index.js
new file mode 100644
index 0000000..5280d5a
--- /dev/null
+++ b/components/descriptions-list-item/index.js
@@ -0,0 +1,30 @@
+// components/descriptions-list-item/index.js
+Component({
+  options: {
+    addGlobalClass: true,
+    multipleSlots: true,
+  },
+  /**
+   * 缁勪欢鐨勫睘鎬у垪琛�
+   */
+  properties: {
+    label: {
+      type: String,
+      value: undefined,
+    },
+    content: {
+      type: String,
+      value: undefined,
+    },
+  },
+
+  /**
+   * 缁勪欢鐨勫垵濮嬫暟鎹�
+   */
+  data: {},
+
+  /**
+   * 缁勪欢鐨勬柟娉曞垪琛�
+   */
+  methods: {},
+});
diff --git a/components/descriptions-list-item/index.json b/components/descriptions-list-item/index.json
new file mode 100644
index 0000000..e8cfaaf
--- /dev/null
+++ b/components/descriptions-list-item/index.json
@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}
\ No newline at end of file
diff --git a/components/descriptions-list-item/index.wxml b/components/descriptions-list-item/index.wxml
new file mode 100644
index 0000000..c864cba
--- /dev/null
+++ b/components/descriptions-list-item/index.wxml
@@ -0,0 +1,6 @@
+<view class="tr">
+  <view wx:if="{{label}}" class="td td-1">{{ label }}</view>
+  <view wx:else class="td td-1"><slot name="label"></slot></view>
+  <view wx:if="{{content}}" class="td td-2">{{ content }}</view>
+  <view wx:else class="td td-2"><slot name="content"></slot></view>
+</view>
diff --git a/components/descriptions-list-item/index.wxss b/components/descriptions-list-item/index.wxss
new file mode 100644
index 0000000..f59f616
--- /dev/null
+++ b/components/descriptions-list-item/index.wxss
@@ -0,0 +1,21 @@
+.tr {
+  display: flex;
+  font-size: var(--td-font-size-s);
+  margin-top: -1px;
+}
+.td {
+  border: 1px solid var(--td-gray-color-3);
+  padding: 2px 6px;
+}
+.td-1 {
+  width: 68px;
+  background-color: var(--td-primary-color-1);
+  color: var(--td-text-color-secondary);
+}
+
+.td-2 {
+  display: inline-block;
+  width: 100%;
+  color: var(--td-text-color-primary);
+  margin-left: -1px;
+}
\ No newline at end of file
diff --git a/components/descriptions-list/index.js b/components/descriptions-list/index.js
new file mode 100644
index 0000000..b6f01fc
--- /dev/null
+++ b/components/descriptions-list/index.js
@@ -0,0 +1,26 @@
+// components/descriptions-list/index.js
+Component({
+  options: {
+    addGlobalClass: true,
+    multipleSlots: true,
+  },
+  /**
+   * 缁勪欢鐨勫睘鎬у垪琛�
+   */
+  properties: {
+    title: {
+      type: String,
+      value: undefined,
+    },
+  },
+
+  /**
+   * 缁勪欢鐨勫垵濮嬫暟鎹�
+   */
+  data: {},
+
+  /**
+   * 缁勪欢鐨勬柟娉曞垪琛�
+   */
+  methods: {},
+});
diff --git a/components/descriptions-list/index.json b/components/descriptions-list/index.json
new file mode 100644
index 0000000..e8cfaaf
--- /dev/null
+++ b/components/descriptions-list/index.json
@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}
\ No newline at end of file
diff --git a/components/descriptions-list/index.wxml b/components/descriptions-list/index.wxml
new file mode 100644
index 0000000..8d289fd
--- /dev/null
+++ b/components/descriptions-list/index.wxml
@@ -0,0 +1,9 @@
+<view class="wrapper">
+  <view class="title-wrapper">
+    <view v-if="{{title}}" class="descriptions-list__title">{{ title }}</view>
+    <slot name="extra"></slot>
+  </view>
+  <view>
+    <slot></slot>
+  </view>
+</view>
diff --git a/components/descriptions-list/index.wxss b/components/descriptions-list/index.wxss
new file mode 100644
index 0000000..d82dba0
--- /dev/null
+++ b/components/descriptions-list/index.wxss
@@ -0,0 +1,22 @@
+.wrapper {
+  background-color: var(--td-gray-color-1);
+  padding: 4px;
+  border-radius: var(--td-border-radius);
+  box-shadow: var(--td-shadow-4);
+  margin-bottom: 8px;
+}
+
+.title-wrapper {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.descriptions-list__title {
+  padding: 8px 8px;
+  font-size: var(--td-font-size-m);
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-weight: 600;
+}
\ No newline at end of file
diff --git a/components/empty-page/index.js b/components/empty-page/index.js
index 603909c..814d332 100644
--- a/components/empty-page/index.js
+++ b/components/empty-page/index.js
@@ -2,6 +2,7 @@
 Component({
   options: {
     addGlobalClass: true,
+    multipleSlots: true,
   },
   /**
    * 缁勪欢鐨勫睘鎬у垪琛�
diff --git a/components/empty-page/index.wxml b/components/empty-page/index.wxml
index 0bada45..6dc2f88 100644
--- a/components/empty-page/index.wxml
+++ b/components/empty-page/index.wxml
@@ -1,6 +1,9 @@
 <!--components/empty-page/index.wxml-->
-<t-empty description="{{description}}" >
+<t-empty description="{{description}}">
   <view slot="image">
-    <t-image src="/res/nodata.png" mode="aspectFill" width="180rpx" height="180rpx"/>
+    <t-image src="/res/nodata.png" mode="aspectFill" width="180rpx" height="180rpx" />
+  </view>
+  <view slot="action">
+    <slot name="action" />
   </view>
 </t-empty>
diff --git a/components/form/form-util.js b/components/form/form-util.js
index 255166f..798e321 100644
--- a/components/form/form-util.js
+++ b/components/form/form-util.js
@@ -3,7 +3,7 @@
  * @param {String} _label 鏍囩鍚嶇О
  * @param {String} _name 瀛楁鍚嶇О
  * @param {Boolean} _required 鏄惁涓哄繀濉」
- * @param {String} _type 杈撳叆绫诲瀷 锛坱ext: 杈撳叆妗�; switch: 鍒囨崲鎸夐挳; picker: 涓嬫媺妗嗛�夐」; cascader: 绾ц仈閫夋嫨锛�
+ * @param {String} _type 杈撳叆绫诲瀷 锛坱ext: 杈撳叆妗�; textarea: 澶氳杈撳叆妗�; switch: 鍒囨崲鎸夐挳; picker: 涓嬫媺妗嗛�夐」; cascader: 绾ц仈閫夋嫨锛�
  * @param {Array} _options 褰撹緭鍏ョ被鍨嬩负picker鎴朿ascader鏃讹紝鎻愪緵鍙�夐」
  * @param {Array} cascaderTitles 褰撹緭鍏ョ被鍨嬩负cascader鏃讹紝鎻愪緵姣忓眰閫夐」鐨勬爣棰�
  * @param {Array} referItems 褰撹緭鍏ョ被鍨嬩负cascader鏃讹紝鎻愪緵鍏宠仈鐨勫睘鎬ame
@@ -20,7 +20,7 @@
   return {
     required: _required,
     label: _label,
-    placeholder: (_type == 'text' ? '璇疯緭鍏�' : '璇烽�夋嫨') + _label,
+    placeholder: (_type == 'text' || _type == 'textarea' ? '璇疯緭鍏�' : '璇烽�夋嫨') + _label,
     name: _name,
     value: null,
     status: 'success',
@@ -50,10 +50,10 @@
  * @param {Object} defaultValue 鍒濆鏁版嵁
  */
 function setDefaultValue(items, defaultValue) {
-  if (typeof defaultValue === 'object') {
+  if (defaultValue && typeof defaultValue === 'object') {
     items.forEach(e => {
       // 鏂囨湰鍜屽紑鍏崇被鍨嬬洿鎺ヨ祴鍊�
-      if (e.inputType == 'text' || e.inputType == 'switch') {
+      if (e.inputType == 'text' || e.inputType == 'switch' || e.inputType == 'textarea') {
         if (defaultValue.hasOwnProperty(e.name)) {
           e.value = defaultValue[e.name];
         }
diff --git a/components/form/index.js b/components/form/index.js
index ef2114e..a149651 100644
--- a/components/form/index.js
+++ b/components/form/index.js
@@ -26,13 +26,25 @@
         this.setData({ formArray: v });
       },
     },
+    // 纭鏂囨湰
     submitText: {
       type: String,
       value: '淇濆瓨',
     },
+    // 鍙栨秷鏂囨湰
     cancelText: {
       type: String,
       value: '鍙栨秷',
+    },
+    // 鏄惁鎵ц鏍¢獙
+    validated: {
+      type: Boolean,
+      value: false,
+    },
+    // 鏄惁鍙紪杈�
+    editable: {
+      type: Boolean,
+      value: true,
     },
   },
 
@@ -101,7 +113,13 @@
     // 淇濆瓨
     onSubmit() {
       const formObj = {};
+      let fail = false;
       this.data.formArray.forEach(e => {
+        if (e.required && !e.value) {
+          fail = true;
+          this.setData({ validated: true });
+          return;
+        }
         if (e.inputType == 'picker') {
           formObj[e.name] = e.value;
         } else if (e.inputType == 'cascader') {
@@ -112,7 +130,9 @@
           formObj[e.name] = e.value;
         }
       });
-      this.triggerEvent('submit', formObj);
+      if (!fail) {
+        this.triggerEvent('submit', formObj);
+      }
     },
     // 鍙栨秷
     onCancel() {
diff --git a/components/form/index.wxml b/components/form/index.wxml
index cf20497..7073cb5 100644
--- a/components/form/index.wxml
+++ b/components/form/index.wxml
@@ -9,6 +9,19 @@
         bind:change="onInputChange"
       />
     </block>
+    <block wx:elif="{{item.inputType == 'textarea'}}">
+      <t-textarea
+        label="{{item.label}}"
+        model:value="{{item.value}}"
+        bordered
+        autofocus
+        autosize
+        confirm-type="next"
+        data-index="{{index}}"
+        placeholder="{{item.placeholder}}"
+        bind:change="onInputChange"
+      ></t-textarea>
+    </block>
     <block wx:elif="{{item.inputType == 'switch'}}">
       <t-cell title="{{item.label}}">
         <t-switch
@@ -61,10 +74,13 @@
         bind:change="onCascaderChange"
       />
     </block>
+    <block wx:if="{{validated && item.required && !item.value}}">
+      <view class="tips">{{item.tips}}</view>
+    </block>
   </block>
 </block>
 <slot></slot>
-<view class="btn-group">
+<view wx:if="editable" class="btn-group">
   <t-button
     block
     theme="light"
diff --git a/components/form/index.wxss b/components/form/index.wxss
index 801b34c..97a6140 100644
--- a/components/form/index.wxss
+++ b/components/form/index.wxss
@@ -3,4 +3,10 @@
   justify-content: space-between;
   padding: 8px;
   gap: 8px;
+}
+
+.tips {
+  margin-left: 16px;
+  color: var(--td-error-color);
+  font-size: var(--td-font-size-s);
 }
\ No newline at end of file
diff --git a/config/index.js b/config/index.js
index e80d281..6894fb8 100644
--- a/config/index.js
+++ b/config/index.js
@@ -18,14 +18,15 @@
 // const inspectPicUrl = `${inspectUrl}/images/`;
 
 // 閬撹矾搴旀�ョ嚎绱�
-// const clueUrl = 'https://fyami.com.cn:448/';
-const clueUrl = 'http://192.168.0.110:8084/';
+const clueUrl = 'https://fyami.com.cn:448/';
+// const clueUrl = 'http://192.168.0.110:8080/';
 const cu = 'https://fyami.com.cn:448';
+// const cu = 'http://192.168.0.110:8080';
 const cluePicUrl = `${cu}/images/`;
 
 // 杩愯妯″紡
-const mode = 'debug';
-// const mode = 'prod';
+// const mode = 'debug';
+const mode = 'prod';
 
 export {
   basePicUrl,
diff --git a/custom-tab-bar/data.js b/custom-tab-bar/data.js
index e7af7d4..202ac0f 100644
--- a/custom-tab-bar/data.js
+++ b/custom-tab-bar/data.js
@@ -11,14 +11,13 @@
     url: 'pages/supervision/index',
     level: 2,
   },
+  // {
+  //   icon: 'fact-check',
+  //   text: '搴旀�ョ嚎绱�',
+  //   url: 'pages/cluetask/home/index',
+  //   level: 1,
+  // },
   {
-    icon: 'fact-check',
-    text: '搴旀�ョ嚎绱�',
-    url: 'pages/cluetask/home/index',
-    level: 1,
-  },
-  {
-    // icon: `${baseIconUrl}tab-slef-patrol.png`,
     icon: 'root-list',
     text: '搴旀�ヨ嚜宸℃煡',
     url: 'pages/selfpatrol/index',
@@ -37,3 +36,54 @@
     level: 2,
   },
 ];
+
+// const menu = [
+//   {
+//     icon: 'home',
+//     text: '棣栭〉',
+//     url: 'pages/home/index',
+//     level: 2,
+//   },
+//   {
+//     icon: 'app',
+//     text: '绮剧粏鍖栫洃绠�',
+//     url: 'pages/supervision/index',
+//     level: 2,
+//   },
+//   {
+//     icon: 'fact-check',
+//     text: '搴旀�ョ嚎绱�',
+//     url: 'pages/cluetask/home/index',
+//     level: 1,
+//   },
+//   {
+//     icon: 'root-list',
+//     text: '搴旀�ヨ嚜宸℃煡',
+//     url: 'pages/selfpatrol/index',
+//     level: 2,
+//   },
+//   // {
+//   //   icon: 'system-device',
+//   //   text: '璁惧绠$悊',
+//   //   url: 'pages/inspection/scene/index',
+//   //   level: 1,
+//   // },
+//   {
+//     icon: 'user',
+//     text: '涓汉涓績',
+//     url: 'pages/usercenter/index',
+//     level: 2,
+//   },
+// ];
+
+// function getMenu(usertypeid) {
+//   const menu = [];
+//   menu.forEach(v => {
+//     if (app.globalData.userInfo.usertypeid <= v.level) {
+//       menu.push(v);
+//     }
+//   });
+//   return menu;
+// }
+
+// export { getMenu };
diff --git a/custom-tab-bar/index.js b/custom-tab-bar/index.js
index a6ff3b7..5933a2c 100644
--- a/custom-tab-bar/index.js
+++ b/custom-tab-bar/index.js
@@ -1,23 +1,34 @@
 import TabMenu from './data';
+// import { getMenu } from './data';
+
 const app = getApp();
 
 Component({
   data: {
     active: 0,
     list: TabMenu,
+    // list: getMenu(app.globalData.userInfo.usertypeid),
   },
   attached() {
-    const menu = []
-    TabMenu.forEach(v => {
-      if (app.globalData.userInfo.usertypeid <= v.level) {
-        menu.push(v)
-      }
-    });
-    // TabMenu.map(v => {
-    //   v.visible = app.globalData.userInfo.usertypeid <= v.level;
-    //   return v;
+    // const menu = [];
+    // TabMenu.forEach(v => {
+    //   if (app.globalData.userInfo.usertypeid <= v.level) {
+    //     menu.push(v);
+    //   }
     // });
-    this.setData({ list: menu });
+    // this.setData({ list: menu });
+    // const index = [];
+    // TabMenu.forEach((v, i) => {
+    //   if (app.globalData.userInfo.usertypeid > v.level) {
+    //     index.push(i);
+    //   }
+    // });
+    // let offset = 0;
+    // index.forEach(e => {
+    //   TabMenu.splice(e + offset, 1);
+    //   offset--;
+    // });
+    // this.setData({ list: TabMenu });
   },
   methods: {
     onChange(event) {
diff --git a/model/clue/clue.js b/model/clue/clue.js
new file mode 100644
index 0000000..dc60950
--- /dev/null
+++ b/model/clue/clue.js
@@ -0,0 +1,14 @@
+import dayjs from 'dayjs';
+
+function getClue(data) {
+  data._releaseTimeTxt = dayjs(data.creleaseTime).format('YYYY-MM-DD HH:mm:ss');
+  return data;
+}
+
+function getClueList(dataList) {
+  return dataList.map(v => {
+    return getClue(v);
+  });
+}
+
+export { getClue, getClueList };
diff --git a/model/clue/clueQuestion.js b/model/clue/clueQuestion.js
index 63b61c7..3e34f00 100644
--- a/model/clue/clueQuestion.js
+++ b/model/clue/clueQuestion.js
@@ -1,7 +1,7 @@
 import { cluePicUrl } from '../../config/index';
 
 function getClueQuestion(data) {
-  data.cqFilePath = data.cqFilePath.split(';').map((val) => {
+  data._filePath = data.cqFilePath.split(';').map((val) => {
     return cluePicUrl + val;
   });
   return data;
diff --git a/model/clue/clueTask.js b/model/clue/clueTask.js
index 85ad441..1da0d25 100644
--- a/model/clue/clueTask.js
+++ b/model/clue/clueTask.js
@@ -17,16 +17,19 @@
       name: '鎬昏',
       value: total,
       diff: '',
+      clickable: false,
     },
     {
       name: '宸插畬鎴�',
       value: finished,
       diff: total == 0 ? '0%' : `${p1}%`,
+      clickable: false,
     },
     {
       name: '寰呭畬鎴�',
       value: unfinished,
       diff: total == 0 ? '0%' : `${p2}%`,
+      clickable: false,
     },
   ];
 }
diff --git a/pages/cluetask/clue-item.js b/pages/cluetask/clue-item.js
new file mode 100644
index 0000000..122c12c
--- /dev/null
+++ b/pages/cluetask/clue-item.js
@@ -0,0 +1,40 @@
+import { baseInputItem, hideInputItem, setDefaultValue } from '../../components/form/form-util.js';
+import dataConclusionType from '../../common/clue/dataConclusionType';
+import { Towns } from '../../common/dataTowns';
+
+export function conclusionForm(defaultValue) {
+  const items = [
+    hideInputItem('id', 'ccId'),
+    hideInputItem('绾跨储id', 'cid'),
+
+    baseInputItem('闂绫诲瀷', 'ccQuestionType', true, 'picker', dataConclusionType.ConclusionType),
+    baseInputItem('绾跨储缁撹', 'ccConclusion', true),
+    baseInputItem('璇︾粏鎻忚堪', 'ccDetails', true, 'textarea'),
+  ];
+
+  // 濉厖榛樿鏁版嵁
+  setDefaultValue(items, defaultValue);
+
+  return items;
+}
+
+export function questionForm(defaultValue) {
+  const items = [
+    hideInputItem('id', 'cqId'),
+    hideInputItem('闂缂栧彿', 'cqUid'),
+    hideInputItem('绾跨储id', 'cid'),
+    hideInputItem('闂鍥剧墖', 'cqFilePath'),
+
+    baseInputItem('闂鍚嶇О', 'cqName', true),
+    baseInputItem('闂鎻忚堪', 'cqDescription', true, 'textarea'),
+    baseInputItem('鎵�鍦ㄨ闀�', 'cqStreet', true, 'picker', Towns[310106]),
+
+    // baseInputItem('璇︾粏鍦板潃', 'cqAddress', true),
+    // baseInputItem('鍧愭爣', '_coordinate', true),
+  ];
+
+  // 濉厖榛樿鏁版嵁
+  setDefaultValue(items, defaultValue);
+
+  return items;
+}
diff --git a/pages/cluetask/conclusion/index.js b/pages/cluetask/conclusion/index.js
new file mode 100644
index 0000000..95383e5
--- /dev/null
+++ b/pages/cluetask/conclusion/index.js
@@ -0,0 +1,80 @@
+import { conclusionForm } from '../clue-item.js';
+
+import { uploadConclusion } from '../../../services/clue/fetchClue';
+Page({
+  /**
+   * 椤甸潰鐨勫垵濮嬫暟鎹�
+   */
+  data: {
+    formArray: [],
+    // 妯″紡锛宎dd: 鏂板锛泆pdate锛氭洿鏂�
+    mode: 'add',
+    submitText: '淇濆瓨',
+  },
+
+  /**
+   * 鐢熷懡鍛ㄦ湡鍑芥暟--鐩戝惉椤甸潰鍔犺浇
+   */
+  onLoad(options) {
+    this.getOpenerEventChannel().on('acceptConclusionData', data => {
+      const { conclusion, clue, isInternal } = data;
+      const formArray = conclusionForm(conclusion);
+      const mode = conclusion ? 'update' : 'add';
+
+      this.setData({ formArray, mode, clue, isInternal });
+    });
+  },
+
+  // 鎻愪氦琛ㄥ崟
+  submit(e) {
+    const { mode, clue, isInternal } = this.data;
+
+    const formObj = e.detail;
+    formObj.cid = clue.cid;
+    formObj.ccInternal = isInternal;
+    if (mode == 'add') {
+      this.create(formObj);
+    } else {
+      this.update(formObj);
+    }
+  },
+
+  // 鍙栨秷琛ㄥ崟
+  cancel() {
+    wx.navigateBack({
+      delta: 1,
+    });
+  },
+
+  // 鏂板
+  create(formObj) {
+    uploadConclusion(formObj).then(res => {
+      if (res.success) {
+        this.getOpenerEventChannel().emit('uploadOver');
+        wx.navigateBack({
+          delta: 1,
+          success: () => {
+            wx.showToast({
+              title: '缁撹鎻愪氦鎴愬姛',
+              duration: 2000,
+              icon: 'success',
+              mask: true,
+            });
+          },
+        });
+      } else {
+        wx.showToast({
+          title: res.message,
+          duration: 2000,
+          icon: 'error',
+          mask: true,
+        });
+      }
+    });
+  },
+
+  // 鏇存柊
+  update(formObj) {
+    this.create(formObj);
+  },
+});
diff --git a/pages/cluetask/conclusion/index.json b/pages/cluetask/conclusion/index.json
new file mode 100644
index 0000000..7e5fecb
--- /dev/null
+++ b/pages/cluetask/conclusion/index.json
@@ -0,0 +1,10 @@
+{
+  "navigationBarTitleText": "绾跨储缁撹",
+  "onReachBottomDistance": 10,
+  "backgroundTextStyle": "light",
+  "navigationBarTextStyle": "white",
+  "navigationBarBackgroundColor": "#389AFF",
+  "usingComponents": {
+    "t-form": "/components/form/index"
+  }
+}
\ No newline at end of file
diff --git a/pages/cluetask/conclusion/index.wxml b/pages/cluetask/conclusion/index.wxml
new file mode 100644
index 0000000..41d5bd2
--- /dev/null
+++ b/pages/cluetask/conclusion/index.wxml
@@ -0,0 +1,12 @@
+<view class="page">
+  <view class="page-header"> </view>
+  <view class="page-container">
+    <t-form
+      formArr="{{formArray}}"
+      submit-text="{{submitText}}"
+      bind:submit="submit"
+      bind:cancel="cancel"
+    ></t-form>
+  </view>
+  <view class="page-footer"></view>
+</view>
diff --git a/pages/cluetask/conclusion/index.wxss b/pages/cluetask/conclusion/index.wxss
new file mode 100644
index 0000000..4d754fc
--- /dev/null
+++ b/pages/cluetask/conclusion/index.wxss
@@ -0,0 +1,3 @@
+.page .page-container {
+  padding: 0;
+}
\ No newline at end of file
diff --git a/pages/cluetask/home/index.js b/pages/cluetask/home/index.js
index 7af513c..1069fa1 100644
--- a/pages/cluetask/home/index.js
+++ b/pages/cluetask/home/index.js
@@ -3,7 +3,7 @@
 import { useStatistic } from './statistic-proxy.js';
 import { useTasks } from './tasks-proxy.js';
 
-import clueApi from '../../../services/clue/fetchClue';
+import { fetchClueTask } from '../../../services/clue/fetchClue';
 
 const app = getApp();
 
@@ -18,7 +18,10 @@
   onLoad(options) {},
 
   onShow() {
-    this.getTabBar().init();
+    if (this.getTabBar()) {
+      this.getTabBar().init();      
+    }
+    this.refresh();
   },
 
   onPullDownRefresh() {
@@ -38,11 +41,15 @@
   optionsCount: 0,
   init() {
     this.optionsCount++;
-    if (this.optionsCount == 2) this._startLoad();
+    this.refresh();
+  },
+
+  refresh() {
+    this._startLoad();
   },
 
   _fetchData(page) {
-    return clueApi.fetchClueTask({}).then(res => {
+    return fetchClueTask({}).then(res => {
       this.setData({ clueTaskList: res.data });
       this.calClueCount();
       this.formatClueTask();
diff --git a/pages/cluetask/home/tasks-proxy.js b/pages/cluetask/home/tasks-proxy.js
index cefb31b..7bde071 100644
--- a/pages/cluetask/home/tasks-proxy.js
+++ b/pages/cluetask/home/tasks-proxy.js
@@ -14,7 +14,7 @@
     formatClueTask() {
       const { clueTaskList } = this.data;
       clueTaskList.forEach(t => {
-        t._taskTime = dayjs(t.taskTime).format('YYYY-MM-DD')
+        t._taskTime = dayjs(t.taskTime).format('YYYY-MM-DD');
         let note = '';
         note += t.provinceName ? t.provinceName : '';
         note += t.provinceName == t.cityName ? '' : t.cityName ? `/${t.cityName}` : '';
@@ -27,5 +27,21 @@
       });
       this.setData({ clueTaskList });
     },
+
+    handleClueTaskClick(e) {
+      const { index } = e.currentTarget.dataset;
+      const clueTask = this.data.clueTaskList[index];
+      wx.navigateTo({
+        url: '/pages/cluetask/manage/index',
+        events: {
+          uploadOver: () => {
+            // 绾跨储鎻愪氦瀹屾垚鍚庢洿鏂扮姸鎬�
+          },
+        },
+        success: res => {
+          res.eventChannel.emit('acceptClueTask', clueTask);
+        },
+      });
+    },
   },
 });
diff --git a/pages/cluetask/home/tasks.wxml b/pages/cluetask/home/tasks.wxml
index 3b89fab..60fafba 100644
--- a/pages/cluetask/home/tasks.wxml
+++ b/pages/cluetask/home/tasks.wxml
@@ -1,10 +1,35 @@
 <view>
-  <view wx:for="{{clueTaskList}}" wx:key="index" wx:for-index="index"> 
-  <t-cell title="浠诲姟鏃堕棿" note="{{item._taskTime}}" />
-  <t-cell title="浠诲姟浣嶇疆" note="{{item._location}}"/>
-  <t-cell title="鍝嶅簲鏃堕棿" note="{{item._resLevelTxt}}"/>
-  <t-cell title="鍑鸿鏂瑰紡" note="{{item._travelModeTxt}}"/>
-  <t-cell title="鏃犱汉鏈�" note="{{item._hasUavTxt}}"/>
-
+  <view
+    class="clue-task__wrap"
+    wx:for="{{clueTaskList}}"
+    wx:key="index"
+    wx:for-index="index"
+    data-index="{{index}}"
+    bindtap="handleClueTaskClick"
+  >
+    <view class="clue-task__title-wrap">
+      <view class="clue-task__title">{{item._taskTime}}</view>
+      <view class="clue-task__abstract">
+        搴旀�ュ贰鏌�
+        <t-icon name="chevron-right" size="var(--icon-width)" color="black" />
+      </view>
+    </view>
+    <view class="clue-task__abstract"> 绾跨储缂栧彿锛歿{item.clueId}} </view>
+    <view class="clue-task__abstract"> 浠诲姟浣嶇疆锛歿{item._location}} </view>
+    <t-divider />
+    <view class="clue-task__tags">
+      <view class="clue-task__tag-item">
+        <span>{{item._resLevelTxt}}</span>
+        <span class="clue-task__label">鍝嶅簲鏃堕棿</span>
+      </view>
+      <view class="clue-task__tag-item">
+        <span>{{item._travelModeTxt}}</span>
+        <span class="clue-task__label">鍑鸿鏂瑰紡</span>
+      </view>
+      <view class="clue-task__tag-item">
+        <span>{{item._hasUavTxt}}</span>
+        <span class="clue-task__label">鏃犱汉鏈�</span>
+      </view>
+    </view>
   </view>
 </view>
diff --git a/pages/cluetask/home/tasks.wxss b/pages/cluetask/home/tasks.wxss
index e69de29..6c22bd9 100644
--- a/pages/cluetask/home/tasks.wxss
+++ b/pages/cluetask/home/tasks.wxss
@@ -0,0 +1,41 @@
+.clue-task__wrap {
+  border-radius: var(--td-border-radius);
+  background-color: var(--td-bg-color-block);
+  padding: var(--td-spacer-1);
+  margin-bottom: var(--td-spacer);
+}
+
+.clue-task__title-wrap {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.clue-task__title {
+  font-weight: 550;
+  font-size: var(--td-font-size-m);
+}
+
+.clue-task__abstract {
+  font-size: var(--td-font-size-s);
+  color: var(--td-text-color-secondary);
+  display: flex;
+  align-items: center;
+}
+
+.clue-task__tags {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.clue-task__tags .clue-task__tag-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.clue-task__tag-item .clue-task__label {
+  font-size: var(--td-font-size-s);
+  color: var(--td-text-color-secondary);
+}
\ No newline at end of file
diff --git a/pages/cluetask/manage/conclusion-proxy.js b/pages/cluetask/manage/conclusion-proxy.js
new file mode 100644
index 0000000..a9e9b95
--- /dev/null
+++ b/pages/cluetask/manage/conclusion-proxy.js
@@ -0,0 +1,35 @@
+import { getConclusion } from '../../../services/clue/fetchClue';
+
+export const useConclusion = Behavior({
+  data: {
+    conclusion: undefined,
+  },
+  methods: {
+    fetchClueConclusion() {
+      const { clueTask } = this.data;
+      getConclusion(clueTask.clueId, clueTask.internalTask).then(res => {
+        this.setData({ conclusion: res.data });
+      });
+    },
+
+    navToConclusion() {
+      wx.navigateTo({
+        url: '/pages/cluetask/conclusion/index',
+        events: {
+          uploadOver: () => {
+            // 缁撹鎻愪氦瀹屾垚鍚庢洿鏂扮姸鎬�
+            this.fetchClueConclusion();
+          },
+        },
+        success: res => {
+          const { conclusion, clue, clueTask } = this.data;
+          res.eventChannel.emit('acceptConclusionData', {
+            conclusion: conclusion,
+            clue: clue,
+            isInternal: clueTask.internalTask,
+          });
+        },
+      });
+    },
+  },
+});
diff --git a/pages/cluetask/manage/conclusion.wxml b/pages/cluetask/manage/conclusion.wxml
new file mode 100644
index 0000000..9721077
--- /dev/null
+++ b/pages/cluetask/manage/conclusion.wxml
@@ -0,0 +1,21 @@
+<DescriptionsList wx:if="{{conclusion}}" title="绾跨储缁撹">
+  <view slot="extra">
+    <t-button
+      theme="primary"
+      size="extra-small"
+      disabled="{{conclusion.ccUploaded}}"
+      bind:tap="navToConclusion"
+      >淇敼缁撹</t-button
+    >
+  </view>
+  <DescriptionsListItem label="闂绫诲瀷" content="{{conclusion.ccQuestionType}}" />
+  <DescriptionsListItem label="绾跨储缁撹" content="{{conclusion.ccConclusion}}" />
+  <DescriptionsListItem label="璇︾粏鎻忚堪" content="{{conclusion.ccDetails}}" />
+</DescriptionsList>
+<view wx:else class="dashed-border">
+  <t-empty-page description="绾跨储缁撹鏈笂浼�">
+    <t-button slot="action" size="small" theme="primary" bind:tap="navToConclusion"
+      >鍙嶉涓婃姤</t-button
+    >
+  </t-empty-page>
+</view>
diff --git a/pages/cluetask/manage/index.js b/pages/cluetask/manage/index.js
new file mode 100644
index 0000000..b6fe6ef
--- /dev/null
+++ b/pages/cluetask/manage/index.js
@@ -0,0 +1,55 @@
+import { useLoading } from '../../../behaviors/loading';
+import { useConclusion } from './conclusion-proxy.js';
+import { useQuestion } from './question-proxy.js';
+
+import { fetchClue, fetchClueInternal } from '../../../services/clue/fetchClue';
+
+Page({
+  behaviors: [useLoading, useConclusion, useQuestion],
+
+  data: {
+    clue: {},
+  },
+
+  onLoad(options) {
+    this.getOpenerEventChannel().on('acceptClueTask', data => {
+      this.setData({
+        clueTask: data,
+      });
+      this._startLoad();
+    });
+  },
+
+  onPullDownRefresh() {
+    this._startLoad();
+  },
+
+  onReachBottom() {
+    this._loadMore();
+  },
+
+  /*********************************************************************************** */
+  _fetchData(page) {
+    const array = [];
+    array.push(this.fetchClueQuestion());
+    if (page == 1) {
+      array.push(this.fetchClueData());
+      array.push(this.fetchClueConclusion());
+    }
+    return Promise.all(array).then(res => {
+      return res[0];
+    });
+  },
+
+  fetchClueData() {
+    const { clueTask } = this.data;
+    const func = clueTask.internalTask ? fetchClueInternal : fetchClue;
+    func({ cid: clueTask.clueId }).then(res => {
+      if (res.data.length > 0) {
+        this.setData({ clue: res.data[0] });
+      }
+    });
+  },
+
+  /*********************************************************************************** */
+});
diff --git a/pages/cluetask/manage/index.json b/pages/cluetask/manage/index.json
new file mode 100644
index 0000000..2f5bb06
--- /dev/null
+++ b/pages/cluetask/manage/index.json
@@ -0,0 +1,13 @@
+{
+  "navigationBarTitleText": "搴旀�ョ嚎绱�",
+  "onReachBottomDistance": 10,
+  "backgroundTextStyle": "light",
+  "enablePullDownRefresh": true,
+  "navigationBarTextStyle": "white",
+  "navigationBarBackgroundColor": "#389AFF",
+  "usingComponents": {
+    "DescriptionsList": "/components/descriptions-list/index",
+    "DescriptionsListItem": "/components/descriptions-list-item/index",
+    "t-empty-page": "/components/empty-page/index"
+  }
+}
\ No newline at end of file
diff --git a/pages/cluetask/manage/index.wxml b/pages/cluetask/manage/index.wxml
new file mode 100644
index 0000000..ca908b8
--- /dev/null
+++ b/pages/cluetask/manage/index.wxml
@@ -0,0 +1,18 @@
+<import src="/pages/common/template/template-loading.wxml" />
+
+<view class="page">
+  <template is="pulldown-loading" wx:if="{{pageLoading}}" />
+  <view class="page-header">
+    <DescriptionsList title="绾跨储娓呭崟璇︽儏">
+      <DescriptionsListItem label="鏄惁涓婁紶" content="{{clue.cuploaded ? '宸蹭笂浼�' : '鏈笂浼�'}}" />
+      <DescriptionsListItem label="绾跨储缂栧彿" content="{{clue.cid}}" />
+      <DescriptionsListItem label="绾跨储鍚嶇О" content="{{clue.cclueName}}" />
+      <DescriptionsListItem label="涓嬪彂鏃堕棿" content="{{clue._releaseTimeTxt}}" />
+    </DescriptionsList>
+  </view>
+  <view class="page-container">
+    <include src="./conclusion.wxml" />
+    <include src="./question.wxml" />
+  </view>
+  <view class="page-footer"></view>
+</view>
diff --git a/pages/cluetask/manage/index.wxss b/pages/cluetask/manage/index.wxss
new file mode 100644
index 0000000..dc361b0
--- /dev/null
+++ b/pages/cluetask/manage/index.wxss
@@ -0,0 +1,24 @@
+page {
+  --header-bottom-padding: 600rpx;
+}
+
+.page .page-header {
+  background: linear-gradient(var(--td-primary-color-7), var(--td-bg-color));
+  padding-bottom: var(--header-bottom-padding);
+}
+
+.page .page-container {
+  margin-top: calc(0rpx - var(--header-bottom-padding));
+}
+
+.btn-wrap {
+  display: flex;
+  justify-content: center;
+  padding: 16px;
+}
+
+.dashed-border {
+  border: 1px dashed var(--td-font-gray-3);
+  border-radius: var(--td-border-radius);
+  padding-bottom: 4px;
+}
diff --git a/pages/cluetask/manage/question-proxy.js b/pages/cluetask/manage/question-proxy.js
new file mode 100644
index 0000000..7a714f6
--- /dev/null
+++ b/pages/cluetask/manage/question-proxy.js
@@ -0,0 +1,79 @@
+import { getQuestion, deleteQuestion } from '../../../services/clue/fetchClue';
+
+export const useQuestion = Behavior({
+  data: {
+    questionList: [],
+  },
+  methods: {
+    fetchClueQuestion() {
+      const { clueTask } = this.data;
+      getQuestion(clueTask.clueId, clueTask.internalTask).then(res => {
+        this.setData({ questionList: res.data });
+      });
+    },
+
+    deleteQuestion(e) {
+      const { index } = e.currentTarget.dataset;
+      this.setData({ showDelete: true, currentIndex: index });
+    },
+
+    onDeleteConfirm() {
+      const { currentIndex, questionList } = this.data;
+      const q = questionList[currentIndex];
+
+      deleteQuestion(q.cqId)
+        .then(res => {
+          if (res.success) {
+            const index = questionList.indexOf(q);
+            questionList.splice(index, 1);
+            this.setData({ questionList });
+          }
+        })
+        .finally(() => this.closeDialog());
+    },
+
+    checkQuestion(e) {
+      const { index } = e.currentTarget.dataset;
+      const question = this.data.questionList[index];
+
+      wx.navigateTo({
+        url: '/pages/cluetask/question/index',
+        events: {
+          uploadOver: () => {
+            // 闂淇敼瀹屾垚鍚庢洿鏂扮姸鎬�
+            this.fetchClueQuestion();
+          },
+        },
+        success: res => {
+          res.eventChannel.emit('acceptClueQuestionData', {
+            question,
+            clue: this.data.clue,
+            isInternal: this.data.clueTask.internalTask,
+          });
+        },
+      });
+    },
+
+    addQuestion() {
+      wx.navigateTo({
+        url: '/pages/cluetask/question/index',
+        events: {
+          uploadOver: () => {
+            // 闂鎻愪氦瀹屾垚鍚庢洿鏂扮姸鎬�
+            this.fetchClueQuestion();
+          },
+        },
+        success: res => {
+          res.eventChannel.emit('acceptClueQuestionData', {
+            clue: this.data.clue,
+            isInternal: this.data.clueTask.internalTask,
+          });
+        },
+      });
+    },
+
+    closeDialog() {
+      this.setData({ showDelete: false });
+    },
+  },
+});
diff --git a/pages/cluetask/manage/question.wxml b/pages/cluetask/manage/question.wxml
new file mode 100644
index 0000000..88c1224
--- /dev/null
+++ b/pages/cluetask/manage/question.wxml
@@ -0,0 +1,48 @@
+<view class="title-primary-1">绾跨储闂</view>
+<block wx:if="{{questionList.length > 0}}">
+  <block wx:for="{{questionList}}" wx:key="index" wx:for-index="index">
+    <DescriptionsList title="{{item.cqUid}}">
+      <view slot="extra">
+        <t-button
+          wx:if="{{!clue.cuploaded}}"
+          theme="danger"
+          size="extra-small"
+          icon="delete"
+          shape="square"
+          data-index="{{index}}"
+          bind:tap="deleteQuestion"
+        ></t-button>
+        <t-button
+          style="margin-left: 4px"
+          theme="primary"
+          size="extra-small"
+          data-index="{{index}}"
+          bind:tap="checkQuestion"
+          >{{ clue.cuploaded ? '闂璇︽儏' : '淇敼闂' }}</t-button
+        >
+      </view>
+      <DescriptionsListItem label="闂鍚嶇О" content="{{item.cqName}}" />
+      <DescriptionsListItem label="闂鎻忚堪" content="{{item.cqDescription}}" />
+      <DescriptionsListItem label="璇︾粏鍦板潃" content="{{item.cqAddress}}" />
+    </DescriptionsList>
+  </block>
+  <view class="btn-wrap">
+    <t-button wx:if="{{!clue.cuploaded}}" size="extra-small" theme="primary" bind:tap="addQuestion"
+      >娣诲姞闂</t-button
+    >
+  </view>
+</block>
+<view wx:else class="dashed-border">
+  <t-empty-page description="鏃犵嚎绱㈤棶棰�">
+    <t-button slot="action" size="small" theme="primary" bind:tap="addQuestion">鍙嶉涓婃姤</t-button>
+  </t-empty-page>
+</view>
+<t-dialog
+  visible="{{showDelete}}"
+  title="鍒犻櫎纭"
+  content=""
+  confirm-btn="{{ { content: '纭畾', variant: 'base' } }}"
+  cancel-btn="鍙栨秷"
+  bind:confirm="onDeleteConfirm"
+  bind:cancel="closeDialog"
+/>
diff --git a/pages/cluetask/question/index.js b/pages/cluetask/question/index.js
new file mode 100644
index 0000000..1762113
--- /dev/null
+++ b/pages/cluetask/question/index.js
@@ -0,0 +1,220 @@
+import { cluePicUrl } from '../../../config/index';
+import { questionForm } from '../clue-item.js';
+import { toLabel, toValue } from '../../../common/dataTowns';
+
+import { uploadQuestion, updateQuestion } from '../../../services/clue/fetchClue';
+
+Page({
+  /**
+   * 椤甸潰鐨勫垵濮嬫暟鎹�
+   */
+  data: {
+    validated: false,
+    formArray: [],
+    // 妯″紡锛宎dd: 鏂板锛泆pdate锛氭洿鏂�
+    mode: 'add',
+    submitText: '淇濆瓨',
+
+    // 鍥剧墖涓婁紶鍒楄〃
+    fileList: [],
+    // 鏇存柊妯″紡涓嬶紝璁板綍琚垹闄ょ殑鍘熸湁鍥剧墖
+    deletedFileList: [],
+    // 鍥剧墖灞曠ず鏂瑰紡
+    gridConfig: {
+      column: 3,
+      width: 210,
+      height: 210,
+    },
+    // 鍥剧墖闄愬埗澶у皬
+    sizeLimit: { size: 5, unit: 'MB', message: '鍥剧墖澶у皬涓嶈秴杩�5MB' },
+
+    // 瀹氫綅淇℃伅
+    locations: {
+      // 浣嶇疆鍚嶇О
+      name: '',
+      address: '',
+      // 浣跨敤 gcj02 鍥芥祴灞�鍧愭爣绯�
+      longitude: '',
+      latitude: '',
+      coorTxt: '',
+    },
+  },
+
+  /**
+   * 鐢熷懡鍛ㄦ湡鍑芥暟--鐩戝惉椤甸潰鍔犺浇
+   */
+  onLoad(options) {
+    this.getOpenerEventChannel().on('acceptClueQuestionData', data => {
+      const { question, clue, isInternal } = data;
+      if (question) {
+        this.setData({ uploaded: question.cqUploaded });
+        question.cqStreet = toValue(question.cqStreet);
+        this.setData({
+          locations: {
+            address: question.cqAddress,
+            longitude: question.cqLongitude,
+            latitude: question.cqLatitude,
+            coorTxt: question.cqLongitude + ',' + question.cqLatitude,
+          },
+          fileList: question._filePath.map(v => {
+            return {
+              url: v,
+            };
+          }),
+        });
+      }
+      const formArray = questionForm(question);
+      const mode = question ? 'update' : 'add';
+
+      this.setData({ formArray, mode, clue, isInternal });
+    });
+  },
+
+  // 鎻愪氦琛ㄥ崟
+  submit(e) {
+    // 棰濆鐨勫畾浣嶄俊鎭拰鍥剧墖鏍¢獙
+    this.setData({ validated: true });
+    const { fileList, locations } = this.data;
+    if (locations.address == '' || !fileList || fileList.length == 0) {
+      return;
+    }
+
+    const formObj = e.detail;
+    formObj.cId = this.data.clue.cid;
+    formObj.cqStreet = toLabel(formObj.cqStreet);
+    formObj.cqAddress = locations.address;
+    formObj.cqLongitude = locations.longitude;
+    formObj.cqLatitude = locations.latitude;
+    formObj.cqInternal = this.data.isInternal;
+
+    const images = [];
+    // 绛涢�夋柊澧炵殑鏈湴鍥剧墖浣滀负涓婁紶鍥剧墖
+    fileList.forEach(v => {
+      if (v.url.indexOf(cluePicUrl) == -1) {
+        images.push(v.url);
+      }
+    });
+
+    const { mode } = this.data;
+    if (mode == 'add') {
+      this.create(formObj, images);
+    } else {
+      this.update(formObj, images);
+    }
+  },
+
+  // 鍙栨秷琛ㄥ崟
+  cancel() {
+    wx.navigateBack({
+      delta: 1,
+    });
+  },
+
+  // 鏂板
+  create(formObj, images) {
+    uploadQuestion(formObj, images).then(res => this.modifySuccess(res));
+  },
+
+  // 鏇存柊
+  update(formObj, images) {
+    // wx.showToast({
+    //   title: '闂涓嶅厑璁告洿鏂�',
+    //   duration: 2000,
+    //   icon: 'error',
+    //   mask: true,
+    // });
+    // return;
+
+    const deleteImgUrl = this.data.deletedFileList.join(';');
+    updateQuestion(formObj, deleteImgUrl, images).then(res => this.modifySuccess(res));
+  },
+
+  // 闂鏂板鎴栦慨鏀规垚鍔熷悗
+  modifySuccess(res) {
+    if (res.success) {
+      this.getOpenerEventChannel().emit('uploadOver');
+      wx.navigateBack({
+        delta: 1,
+        success: () => {
+          wx.showToast({
+            title: this.data.mode == 'add' ? '闂鎻愪氦鎴愬姛' : '闂鏇存柊鎴愬姛',
+            duration: 2000,
+            icon: 'success',
+            mask: true,
+          });
+        },
+      });
+    } else {
+      wx.showToast({
+        title: res.message,
+        duration: 2000,
+        icon: 'error',
+        mask: true,
+      });
+    }
+  },
+
+  /************************************************************** */
+  // 鎵嬪姩淇敼瀹氫綅浣嶇疆鎻忚堪
+  handleLocation(e) {
+    const { value } = e.detail;
+    // console.log(e);
+    // console.log(this.data.locations);
+    const { location } = this.data;
+    location.address = value;
+    this.setData({ locations });
+  },
+
+  // handleCoorTxt(e) {
+  //   console.log(e);
+  //   const { value } = e.detail;
+  // },
+
+  // 浠庡湴鍥鹃�夋嫨瀹氫綅
+  chooseLocation() {
+    const { locations } = this.data;
+    wx.chooseLocation({
+      longitude: locations.longitude,
+      latitude: locations.latitude,
+      success: res => {
+        this.setData({
+          locations: {
+            ...res,
+            coorTxt: `${res.longitude},${res.latitude}`,
+          },
+        });
+      },
+    });
+  },
+
+  // 娣诲姞鍥剧墖
+  handleAddImg(e) {
+    const { fileList } = this.data;
+    const { files } = e.detail;
+    this.setData({
+      fileList: [...fileList, ...files],
+    });
+  },
+  // 绉婚櫎鍥剧墖
+  handleRemoveImg(e) {
+    const { index } = e.detail;
+    const { fileList, mode } = this.data;
+
+    const [deletedfile] = fileList.splice(index, 1);
+
+    // 鍦ㄦ洿鏂版ā寮忎笅锛岃褰曞垹闄ょ殑鍘熸湁鍥剧墖锛岀敤浜庢洿鏂伴棶棰樻帴鍙g殑鍙傛暟
+    if (mode == 'update') {
+      // 閫氳繃鏂囦欢璺緞鍒ゆ柇鏄惁涓鸿繙绋媓ttp璺緞锛�
+      if (deletedfile.url.indexOf(cluePicUrl) != -1) {
+        const originUrl = deletedfile.url.replace(cluePicUrl, '');
+        const { deletedFileList } = this.data;
+        deletedFileList.push(originUrl);
+
+        console.log(deletedFileList);
+      }
+    }
+    this.setData({
+      fileList,
+    });
+  },
+});
diff --git a/pages/cluetask/question/index.json b/pages/cluetask/question/index.json
new file mode 100644
index 0000000..f198665
--- /dev/null
+++ b/pages/cluetask/question/index.json
@@ -0,0 +1,10 @@
+{
+  "navigationBarTitleText": "绾跨储闂",
+  "onReachBottomDistance": 10,
+  "backgroundTextStyle": "light",
+  "navigationBarTextStyle": "white",
+  "navigationBarBackgroundColor": "#389AFF",
+  "usingComponents": {
+    "t-form": "/components/form/index"
+  }
+}
\ No newline at end of file
diff --git a/pages/cluetask/question/index.wxml b/pages/cluetask/question/index.wxml
new file mode 100644
index 0000000..3fbb8d1
--- /dev/null
+++ b/pages/cluetask/question/index.wxml
@@ -0,0 +1,47 @@
+<view class="page">
+  <view class="page-header"> </view>
+  <view class="page-container">
+    <t-form
+      editable="{{!uploaded}}"
+      formArr="{{formArray}}"
+      submit-text="{{submitText}}"
+      bind:submit="submit"
+      bind:cancel="cancel"
+    >
+      <t-input
+        label="闂瀹氫綅"
+        placeholder="璇疯緭鍏ラ棶棰樺畾浣�"
+        value="{{locations.address}}"
+        bind:change="handleLocation"
+      >
+      </t-input>
+      <t-input
+        disabled
+        label="闂鍧愭爣"
+        placeholder="璇疯緭鍏ラ棶棰樺潗鏍囷紝鐢ㄩ�楀彿鍒嗗壊"
+        value="{{locations.coorTxt}}"
+      >
+        <t-button slot="suffix" theme="primary" size="extra-small" bindtap="chooseLocation">
+          鍦板浘瀹氫綅
+        </t-button>
+      </t-input>
+      <view wx:if="{{validated && locations.address == ''}}" class="tips"> 闂瀹氫綅涓嶈兘涓虹┖ </view>
+      <t-cell title="闂鍥剧墖">
+        <t-upload
+          slot="description"
+          media-type="{{['image']}}"
+          files="{{fileList}}"
+          gridConfig="{{gridConfig}}"
+          max="{{3}}"
+          size-limit="{{sizeLimit}}"
+          bind:add="handleAddImg"
+          bind:remove="handleRemoveImg"
+        />
+      </t-cell>
+      <view wx:if="{{validated && (!fileList || fileList.length == 0)}}" class="tips">
+          闂鍥剧墖鑷冲皯閫変竴寮�
+        </view>
+    </t-form>
+  </view>
+  <view class="page-footer"></view>
+</view>
diff --git a/pages/cluetask/question/index.wxss b/pages/cluetask/question/index.wxss
new file mode 100644
index 0000000..d65d5f2
--- /dev/null
+++ b/pages/cluetask/question/index.wxss
@@ -0,0 +1,9 @@
+.page .page-container {
+  padding: 0;
+}
+
+.tips {
+  margin-left: 16px;
+  color: var(--td-error-color);
+  font-size: var(--td-font-size-s);
+}
\ No newline at end of file
diff --git a/pages/usercenter/index.js b/pages/usercenter/index.js
index 82e78fd..bc809cf 100644
--- a/pages/usercenter/index.js
+++ b/pages/usercenter/index.js
@@ -49,9 +49,14 @@
   },
 
   onClickCell({ currentTarget }) {
-    const { type } = currentTarget.dataset;
-
+    const { type, url } = currentTarget.dataset;
+    wx.navigateTo({ url });
+    return
     switch (type) {
+      case 'clue': {
+        wx.navigateTo({ url: '/pages/cluetask/home/index' });
+        break;
+      }
       case 'supervision': {
         wx.navigateTo({ url: '/pages/inspection/scene/index' });
         break;
diff --git a/pages/usercenter/index.wxml b/pages/usercenter/index.wxml
index b5b2633..26bab38 100644
--- a/pages/usercenter/index.wxml
+++ b/pages/usercenter/index.wxml
@@ -14,6 +14,7 @@
             arrow="{{!xitem.icon}}"
             note="{{xitem.tit}}"
             data-type="{{xitem.type}}"
+            data-url="{{xitem.url}}"
             bordered="{{false}}"
             bind:click="onClickCell"
             t-class="t-cell-padding"
diff --git a/pages/usercenter/menu.js b/pages/usercenter/menu.js
index 27b9e61..493dc89 100644
--- a/pages/usercenter/menu.js
+++ b/pages/usercenter/menu.js
@@ -1,55 +1,62 @@
 export const menuData = [
   [
     {
+      title: '搴旀�ョ嚎绱�',
+      tit: '',
+      url: '/pages/cluetask/home/index',
+      type: 'clue',
+      level: 1,
+    },
+    {
       title: '璁惧绠$悊',
       tit: '',
-      url: '',
+      url: '/pages/inspection/scene/index',
       type: 'supervision',
-      level: 1
+      level: 1,
     },
     {
       title: '閫氱煡绠$悊',
       tit: '',
-      url: '',
+      url: '/pages/usercenter/notice/index',
       type: 'notice',
-      level: 2
+      level: 2,
     },
     {
       title: '鎵ф硶鍔ㄦ��',
       tit: '',
-      url: '',
+      url: '/pages/news/law-enforcement/index',
       type: 'info',
-      level: 2
+      level: 2,
     },
     // {
     //   title: '鑷贰鏌ョ鐞�',
     //   tit: '',
-    //   url: '',
+    //   url: '/pages/selfpatrol/index',
     //   type: 'patrol',
     // },
-    {
-      title: '鐜繚鏃ョ▼',
-      tit: '',
-      url: '',
-      type: 'schedule',
-      level: 2
-    },
+    // {
+    //   title: '鐜繚鏃ョ▼',
+    //   tit: '',
+    //   url: '',
+    //   type: 'schedule',
+    //   level: 2
+    // },
   ],
   [
     {
       title: '鎶�鏈敮鎸�',
       tit: '',
-      url: '',
+      url: '/pages/usercenter/support/index',
       type: 'support',
-      level: 3
+      level: 3,
     },
     {
       title: '鍏充簬',
       tit: '',
-      url: '',
+      url: '/pages/usercenter/about/index',
       type: 'about',
       // icon: 'service',
-      level: 3
+      level: 3,
     },
   ],
 ];
diff --git a/project.private.config.json b/project.private.config.json
index 50b824b..ce2e311 100644
--- a/project.private.config.json
+++ b/project.private.config.json
@@ -2,8 +2,8 @@
   "description": "椤圭洰绉佹湁閰嶇疆鏂囦欢銆傛鏂囦欢涓殑鍐呭灏嗚鐩� project.config.json 涓殑鐩稿悓瀛楁銆傞」鐩殑鏀瑰姩浼樺厛鍚屾鍒版鏂囦欢涓�傝瑙佹枃妗o細https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
   "projectname": "ep-law-abiding-manage-weixin",
   "setting": {
-    "compileHotReLoad": true,
-    "urlCheck": false
+    "compileHotReLoad": false,
+    "urlCheck": true
   },
-  "libVersion": "3.5.3"
+  "libVersion": "3.7.12"
 }
\ No newline at end of file
diff --git a/services/clue/fetchClue.js b/services/clue/fetchClue.js
index 4a50ed4..be15df5 100644
--- a/services/clue/fetchClue.js
+++ b/services/clue/fetchClue.js
@@ -5,110 +5,200 @@
 import { get, post, _delete } from '../baseRequset';
 import { clueUrl, cluePicUrl } from '../../config/index';
 import { getClueQuestionList } from '../../model/clue/clueQuestion';
+import { getClueList } from '../../model/clue/clue';
 
-export default {
-  /******************************************************************************* */
-  /**
-   * 鏌ヨ绾跨储浠诲姟
-   * @param {*} clueTask
-   * @returns
-   */
-  fetchClueTask(clueTask) {
-    return post(
-      {
-        url: `clue/task/fetch`,
-        data: clueTask,
-      },
-      clueUrl,
-    ).then(res => {
-      return res.data;
-    });
-  },
+/******************************************************************************* */
+/**
+ * 鏌ヨ绾跨储
+ * @param {*} clue
+ * @returns
+ */
+function fetchClue(clue) {
+  return post(
+    {
+      url: `clue/search`,
+      data: clue,
+    },
+    clueUrl,
+  ).then(res => {
+    res.data.data = getClueList(res.data.data);
+    return res.data;
+  });
+}
 
-  /******************************************************************************* */
-  /**
-   * 鑾峰彇绾跨储缁撹
-   * @param {string} clueId 绾跨储id
-   */
-  getConclusion(clueId) {
-    return get({
+/**
+ * 鏌ヨ绾跨储
+ * @param {*} clue
+ * @returns
+ */
+function fetchClueInternal(clue) {
+  return post(
+    {
+      url: `clue/internal/search`,
+      data: clue,
+    },
+    clueUrl,
+  ).then(res => {
+    res.data.data = getClueList(res.data.data);
+    return res.data;
+  });
+}
+/******************************************************************************* */
+/**
+ * 鏌ヨ绾跨储浠诲姟
+ * @param {*} clueTask
+ * @returns
+ */
+function fetchClueTask(clueTask) {
+  return post(
+    {
+      url: `clue/task/fetch`,
+      data: clueTask,
+    },
+    clueUrl,
+  ).then(res => {
+    return res.data;
+  });
+}
+
+/******************************************************************************* */
+/**
+ * 鑾峰彇绾跨储缁撹
+ * @param {string} clueId 绾跨储id
+ */
+function getConclusion(clueId, internal) {
+  return get(
+    {
       url: `clue/conclusion/fetch`,
       params: {
-        clueId: clueId,
+        clueId,
+        internal,
       },
-      clueUrl,
-    }).then(res => res.data);
-  },
+    },
+    clueUrl,
+  ).then(res => res.data);
+}
 
-  /**
-   * 鎻愪氦绾跨储缁撹
-   * @param {object} conclusion 绾跨储
-   * @returns
-   */
-  uploadConclusion(conclusion) {
-    return post(
-      {
-        url: `clue/conclusion/upload`,
-        data: conclusion,
-      },
-      clueUrl,
-    ).then(res => res.data);
-  },
+/**
+ * 鎻愪氦绾跨储缁撹
+ * @param {object} conclusion 绾跨储
+ * @returns
+ */
+function uploadConclusion(conclusion) {
+  return post(
+    {
+      url: `clue/conclusion/upload`,
+      data: conclusion,
+    },
+    clueUrl,
+  ).then(res => res.data);
+}
 
-  /******************************************************************************* */
-  /**
-   * 鑾峰彇宸叉彁浜ょ殑绾跨储闂
-   * @param {string} clueId 绾跨储id
-   */
-  getQuestion(clueId) {
-    return get({
+/******************************************************************************* */
+/**
+ * 鑾峰彇宸叉彁浜ょ殑绾跨储闂
+ * @param {string} clueId 绾跨储id
+ */
+function getQuestion(clueId, internal) {
+  return get(
+    {
       url: `clue/question/fetch`,
       params: {
-        clueId: clueId,
+        clueId,
+        internal,
       },
-      clueUrl,
-    }).then(res => {
-      return getClueQuestionList(res.data);
+    },
+    clueUrl,
+  ).then(res => {
+    res.data.data = getClueQuestionList(res.data.data);
+    return res.data;
+  });
+}
+
+/**
+ * 涓婁紶绾跨储闂
+ * @param {object} question 闂鎻忚堪
+ * @param {*} files 闂鍥剧墖
+ * @returns
+ */
+function uploadQuestion(question, files) {
+  const fields = [
+    {
+      name: 'question',
+      value: JSON.stringify(question),
+    },
+  ];
+  const images = files.map(f => {
+    return {
+      name: 'images',
+      filePath: f,
+    };
+  });
+
+  return new Multipart({
+    fields,
+    files: images,
+  })
+    .submit(clueUrl + `/clue/question/upload`)
+    .then(res => {
+      return res.data;
     });
-  },
+}
 
-  /**
-   * 涓婁紶绾跨储闂
-   * @param {object} question 闂鎻忚堪
-   * @param {*} files 闂鍥剧墖
-   * @returns
-   */
-  uploadQuestion(question, files) {
-    const fields = [
-      {
-        name: 'question',
-        value: JSON.stringify(question),
-      },
-    ];
-    const images = files.map(f => {
-      return {
-        name: 'images',
-        filePath: f,
-      };
+/**
+ * 鏇存柊绾跨储闂
+ * @param {object} question 闂鎻忚堪
+ * @param {Sting} deleteImgUrl 鍒犻櫎鐨勫浘鐗囩浉瀵硅矾寰勶紝鐢�;鍒嗗壊
+ * @param {*} files 闂鍥剧墖
+ * @returns
+ */
+function updateQuestion(question, deleteImgUrl, files) {
+  const fields = [
+    {
+      name: 'question',
+      value: JSON.stringify(question),
+    },
+    {
+      name: 'deleteImg',
+      value: deleteImgUrl,
+    },
+  ];
+  const images = files.map(f => {
+    return {
+      name: 'images',
+      filePath: f,
+    };
+  });
+
+  return new Multipart({
+    fields,
+    files: images,
+  })
+    .submit(clueUrl + `/clue/question/update`)
+    .then(res => {
+      return res.data;
     });
+}
 
-    return new Multipart({
-      fields,
-      images,
-    })
-      .submit(clueUrl + `/clue/question/upload`)
-      .then(res => {
-        return res.data;
-      });
-  },
-
-  deleteQuestion(questionId) {
-    return _delete({
+function deleteQuestion(questionId) {
+  return _delete(
+    {
       url: `clue/question`,
       params: {
         questionId: questionId,
       },
-      clueUrl,
-    }).then(res => res.data);
-  },
+    },
+    clueUrl,
+  ).then(res => res.data);
+}
+export {
+  fetchClue,
+  fetchClueInternal,
+  fetchClueTask,
+  getConclusion,
+  uploadConclusion,
+  updateQuestion,
+  getQuestion,
+  uploadQuestion,
+  deleteQuestion,
 };

--
Gitblit v1.9.3