riku
2025-04-23 f1f26b166b71371e0a8dfaf9b9f575d2d79feefc
完成线索任务的发布功能
已修改8个文件
已添加9个文件
1105 ■■■■■ 文件已修改
src/api/clue/clueTaskApi.js 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/userApi.js 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components.d.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/OptionExecutors.vue 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/OptionLocation.vue 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/OptionResponseLevel.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/OptionTravelMode.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constant/location.js 257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constant/response-level.js 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constant/travel-mode.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/model/clueQuestion.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/overlay-clue/ClueLayout.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/overlay-clue/list/ClueManage.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/overlay-clue/list/components/ClueList.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/overlay-clue/report/ClueReport.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/overlay-clue/task/ClueTaskEdit.vue 287 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/clue/clueTaskApi.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
import { $clue } from '../index';
export default {
  /**
   * åˆ›å»ºçº¿ç´¢ä»»åŠ¡
   * @param {*} clueTask
   * @returns
   */
  createClueTask(clueTask) {
    return $clue
      .put(`clue/task/create`, clueTask)
  },
  /**
   * æ›´æ–°çº¿ç´¢ä»»åŠ¡
   * @param {*} clueTask
   * @returns
   */
  updateClueTask(clueTask) {
    return $clue
      .post(`clue/task/update`, clueTask)
  },
  /**
   * æŸ¥è¯¢çº¿ç´¢ä»»åŠ¡
   * @param {*} clueTask
   * @returns
   */
  fetchClueTask(clueTask) {
    return $clue
      .post(`clue/task/fetch`, clueTask)
  }
};
src/api/fysp/userApi.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
import { $fysp } from '../index';
export default {
  /**
   * èŽ·å–ç”¨æˆ·è¯¦æƒ…
   */
  getUserById(id) {
    return $fysp.get(`userinfo/${id}`).then((res) => res.data);
  },
  /**
   * æ›´æ–°ç”¨æˆ·è¯¦æƒ…
   */
  updateUser(user) {
    return $fysp.post(`userinfo`, user).then((res) => res.data);
  },
  /**
   * èŽ·å–åœºæ™¯çš„ç”¨æˆ·è¯¦æƒ…
   */
  getUserByScene(sId) {
    return $fysp
      .get(`userinfo/scene/get?sceneId=${sId}`)
      .then((res) => res.data);
  },
  /**
   * è‡ªåŠ¨åˆ›å»ºè´¦æˆ·
   */
  autoCreateAccount(sId) {
    return $fysp.post(`userinfo/create?sceneId=${sId}`).then((res) => res.data);
  },
  /**
   * èŽ·å–åœºæ™¯å¯¹åº”çš„é£žç¾½çŽ¯å¢ƒç³»ç»Ÿç”¨æˆ·id
   */
  getTzId(sceneId) {
    return $fysp.get(`usermap?sceneId=${sceneId}`).then((res) => res.data);
  },
  /**
   * æŒ‰ç”¨æˆ·ç±»åž‹èŽ·å–ç”¨æˆ·ä¿¡æ¯
   */
  getUserByType(typeId, enable = true) {
    return $fysp
      .get(`userinfo/type/get`, { params: { typeId, enable } })
      .then((res) => res.data);
  }
};
src/api/index.js
@@ -1,17 +1,37 @@
import axios from 'axios';
import { setInterceptors } from "./config";
// const url = 'http://47.100.191.150:9031/';
const url = 'http://192.168.0.110:8084/';
// const imgUrl = 'http://47.100.191.150:9031/images/';
const imgUrl = 'http://192.168.0.110:8084/images/';
const debug = true;
// let url1 = 'http://47.100.191.150:9031/';
// let url1_file = 'http://47.100.191.150:9031';
let url1 = 'https://fyami.com.cn:448/';
let url1_file = 'https://fyami.com.cn:448/';
let url2 = 'http://47.100.191.150:9005/';
let url2_file = 'http://47.100.191.150:9005/';
if (debug) {
  url1 = 'http://192.168.0.110:8084/';
  url1_file = 'http://192.168.0.110:8084/';
  // url2 = 'http://192.168.0.110:9001/';
  // url2_file = 'http://192.168.0.138:8080/';
}
//飞羽监管
const $clue = axios.create({
  baseURL: url,
  timeout: 10000
  baseURL: url1,
  timeout: 20000
  // headers: addHeaders()
});
$clue.imgUrl = `${url1_file}images/`;
//飞羽监管
const $fysp = axios.create({
  baseURL: url2,
  timeout: 20000
});
$fysp.imgUrl = `${url2_file}images/`;
$fysp.downloadUrl = `${url2_file}files/`;
// function getHeaders() {
//   const token = 'e6dc8bb9e1ff0ce973fb92b4af2e4c3f';
@@ -30,5 +50,6 @@
//添加拦截器
setInterceptors($clue)
setInterceptors($fysp)
export { $clue, imgUrl };
export { $clue, $fysp };
src/components.d.ts
@@ -13,6 +13,7 @@
    DescriptionsList: typeof import('./components/list/DescriptionsList.vue')['default']
    DescriptionsListItem: typeof import('./components/list/DescriptionsListItem.vue')['default']
    ElButton: typeof import('element-plus/es')['ElButton']
    ElCascader: typeof import('element-plus/es')['ElCascader']
    ElCol: typeof import('element-plus/es')['ElCol']
    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
@@ -33,11 +34,16 @@
    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
    ElSelect: typeof import('element-plus/es')['ElSelect']
    ElSpace: typeof import('element-plus/es')['ElSpace']
    ElSwitch: typeof import('element-plus/es')['ElSwitch']
    ElTag: typeof import('element-plus/es')['ElTag']
    ElText: typeof import('element-plus/es')['ElText']
    ElUpload: typeof import('element-plus/es')['ElUpload']
    MapSearch: typeof import('./components/map/MapSearch.vue')['default']
    OptionExecutors: typeof import('./components/search-option/OptionExecutors.vue')['default']
    OptionLocation: typeof import('./components/search-option/OptionLocation.vue')['default']
    OptionResponseLevel: typeof import('./components/search-option/OptionResponseLevel.vue')['default']
    OptionTime: typeof import('./components/search-option/OptionTime.vue')['default']
    OptionTravelMode: typeof import('./components/search-option/OptionTravelMode.vue')['default']
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
  }
src/components/search-option/OptionExecutors.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
<template>
  <el-select
    :model-value="modelValue"
    @update:model-value="handleChange"
    multiple
    clearable
    placeholder="选择执行人"
    class="w-300"
  >
    <el-option
      v-for="(s, i) in executorOptions"
      :key="i"
      :label="s.label"
      :value="s.value"
    />
  </el-select>
</template>
<script>
/**
 * çº¿ç´¢ä»»åŠ¡æ‰§è¡Œäººé€‰é¡¹
 */
import userApi from '@/api/fysp/userApi';
export default {
  props: {
    modelValue: {
      type: Array,
      default: () => []
    },
    userType: {
      type: Number,
      default: 1
    }
  },
  emits: ['update:modelValue', 'initOver'],
  data() {
    return {
      executorOptions: []
    };
  },
  methods: {
    getOptions() {
      return userApi.getUserByType(this.userType).then((res) => {
        res.forEach((v) => {
          this.executorOptions.push({
            label: v.realname,
            value: v.guid
            // data: v
          });
        });
        // this.executorOptions = res.map((v) => {
        //   return {
        //     label: v.realname,
        //     value: v.guid
        //     // data: v
        //   };
        // });
      });
    },
    handleChange(value) {
      this.$emit('update:modelValue', value);
    }
  },
  mounted() {
    this.getOptions().finally(() => this.$emit('initOver'));
    // this.handleChange(this.pollutionList[0].value);
  }
};
</script>
src/components/search-option/OptionLocation.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,164 @@
<template>
  <!-- <el-form-item :label="placeholder" :prop="prop"> -->
  <el-cascader
    :model-value="formatedValue"
    @change="handleChange"
    :options="locations"
    :placeholder="placeholder"
    :props="optionProps"
    :style="'width: ' + width + 'px'"
  />
  <!-- </el-form-item> -->
</template>
<script>
import { enumLocation } from '@/constant/location';
export default {
  props: {
    // æ˜¯å¦åœ¨é¦–选项处添加“全部”选项
    allOption: {
      type: Boolean,
      default: true
    },
    // æŸ¥è¯¢çš„行政级别,取值1,2,3,4, 5, 6
    level: {
      type: Number,
      default: 4
    },
    // ç»“果返回
    modelValue: Object,
    // æ˜¯å¦é»˜è®¤è¿”回初始选项
    initValue: {
      type: Boolean,
      default: true
    },
    // èƒ½å¦é€‰æ‹©ä»»æ„ä¸€çº§é€‰é¡¹
    checkStrictly: {
      type: Boolean,
      default: true
    },
    prop: {
      type: String,
      default: '_locations'
    },
    width: {
      type: Number,
      default: 220
    }
  },
  emits: ['update:modelValue'],
  data() {
    return {
      locations: enumLocation(this.allOption, this.level),
      optionProps: {
        checkStrictly: this.checkStrictly
      }
    };
  },
  computed: {
    placeholder() {
      const list = '省/市/区/镇/集/物'.split('/');
      const p = [];
      for (let i = 0; i < this.level; i++) {
        p.push(list[i]);
      }
      return p.join('/');
    },
    formatedValue() {
      return this.optionFormatReverse(this.modelValue);
    }
  },
  methods: {
    handleChange(value) {
      this.$emit('update:modelValue', this.optionFormat(value));
    },
    /**
     * åœ°åŒºé€‰é¡¹ç»“果格式化
     */
    optionFormat(val) {
      const res = {
        pCode: null,
        pName: null,
        cCode: null,
        cName: null,
        dCode: null,
        dName: null,
        tCode: null,
        tName: null,
        aCode: null,
        aName: null,
        mCode: null,
        mName: null
      };
      if (val.length > 0) {
        res.pCode = val[0][0];
        res.pName = val[0][1];
      }
      if (val.length > 1) {
        res.cCode = val[1][0];
        res.cName = val[1][1];
      }
      if (val.length > 2) {
        res.dCode = val[2][0];
        res.dName = val[2][1];
      }
      if (val.length > 3) {
        res.tCode = val[3][0];
        res.tName = val[3][1];
      }
      if (val.length > 4) {
        res.aCode = val[4][0];
        res.aName = val[4][1];
      }
      if (val.length > 5) {
        res.mCode = val[5][0];
        res.mName = val[5][1];
      }
      return res;
    },
    optionFormatReverse(val) {
      const res = [];
      if (val) {
        if (val.pName) {
          res.push([val.pCode, val.pName]);
        }
        if (val.cName) {
          res.push([val.cCode, val.cName]);
        }
        if (val.dName) {
          res.push([val.dCode, val.dName]);
        }
        if (val.tName) {
          res.push([val.tCode, val.tName]);
        }
        if (val.aName) {
          res.push([val.aCode, val.aName]);
        }
        if (val.mName) {
          res.push([val.mCode, val.mName]);
        }
      }
      return res;
    }
  },
  mounted() {
    if (this.initValue) {
      if (this.checkStrictly) {
        this.handleChange([this.locations[0].value]);
      } else {
        const f = (location) => {
          if (location.children && location.children.length > 0) {
            const r = f(location.children[0]);
            r.unshift(location.value);
            return r;
          } else {
            return [location.value];
          }
        };
        this.handleChange(f(this.locations[0]));
      }
    }
  }
};
</script>
src/components/search-option/OptionResponseLevel.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
<template>
  <el-select
    :model-value="modelValue"
    @update:model-value="handleChange"
    placeholder="响应级别"
    class="w-120"
  >
    <el-option
      v-for="(s, i) in responseLevelList"
      :key="i"
      :label="s.label"
      :value="s.value"
    />
  </el-select>
</template>
<script>
/**
 * çº¿ç´¢ä»»åŠ¡å“åº”çº§åˆ«é€‰é¡¹
 */
import { responseLevelList } from '@/constant/response-level';
export default {
  props: {
    modelValue: String
  },
  emits: ['update:modelValue', 'initOver'],
  data() {
    return {
      responseLevelList: responseLevelList()
    };
  },
  methods: {
    handleChange(value) {
      this.$emit('update:modelValue', value);
    }
  },
  mounted() {
    this.$emit('initOver');
    // this.handleChange(this.pollutionList[0].value);
  }
};
</script>
src/components/search-option/OptionTravelMode.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
<template>
  <el-select
    :model-value="modelValue"
    @update:model-value="handleChange"
    placeholder="出行方式"
    class="w-120"
  >
    <el-option
      v-for="(s, i) in travelModeList"
      :key="i"
      :label="s.label"
      :value="s.value"
    />
  </el-select>
</template>
<script>
/**
 * çº¿ç´¢ä»»åŠ¡å‡ºè¡Œæ–¹å¼é€‰é¡¹
 */
import { travelModeList } from '@/constant/travel-mode';
export default {
  props: {
    modelValue: String
  },
  emits: ['update:modelValue', 'initOver'],
  data() {
    return {
      travelModeList: travelModeList()
    };
  },
  methods: {
    handleChange(value) {
      this.$emit('update:modelValue', value);
    }
  },
  mounted() {
    this.$emit('initOver');
    // this.handleChange(this.pollutionList[0].value);
  }
};
</script>
src/constant/location.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,257 @@
/**
 * èŽ·å–è¡Œæ”¿åŒºåˆ’
 * @param {Boolean} allOption æ˜¯å¦åœ¨å¤´éƒ¨æ·»åŠ â€œå…¨éƒ¨â€é€‰é¡¹
 * @param {Number} level èŽ·å–çš„åˆ†ç±»æ·±åº¦ï¼ŒèŒƒå›´ 1 - 4
 * @returns
 */
function enumLocation(allOption = true, level = 4) {
  const l = _enumLocation();
  if (!allOption) {
    l.shift();
  }
  _deleteByLevel(l, level, 1);
  return l;
}
function _enumLocation() {
  return [
    {
      label: '全市',
      value: [null, '全市']
    },
    {
      label: '上海市',
      value: ['31', '上海市'],
      children: [
        {
          label: '上海市',
          value: ['3100', '上海市'],
          children: [
            {
              label: '金山区',
              value: ['310116', '金山区'],
              children: [
                { label: '张堰镇', value: ['310116103', '张堰镇'] },
                { label: '亭林镇', value: ['310116104', '亭林镇'] },
                { label: '吕巷镇', value: ['310116105', '吕巷镇'] },
                { label: '廊下镇', value: ['310116107', '廊下镇'] },
                { label: '高新区', value: ['310116503', '高新区'] },
                { label: '金山卫镇', value: ['310116109', '金山卫镇'] },
                { label: '漕泾镇', value: ['310116112', '漕泾镇'] },
                {
                  label: '山阳镇',
                  value: ['310116113', '山阳镇'],
                  children: [
                    {
                      label: '万达广场',
                      value: ['31011611301', '万达广场']
                    }
                  ]
                },
                { label: '石化街道', value: ['310116001', '石化街道'] },
                { label: '朱泾镇', value: ['310116101', '朱泾镇'] },
                { label: '枫泾镇', value: ['310116102', '枫泾镇'] },
                { label: '碳谷绿湾', value: ['9000', '碳谷绿湾'] }
              ]
            },
            {
              label: '徐汇区',
              value: ['310104', '徐汇区'],
              children: [
                {
                  label: '漕河泾新兴技术开发区',
                  value: ['310104501', '漕河泾新兴技术开发区']
                },
                { label: '湖南路街道', value: ['310104004', '湖南路街道'] },
                { label: '天平路街道', value: ['310104003', '天平路街道'] },
                { label: '虹梅路街道', value: ['310104012', '虹梅路街道'] },
                { label: '枫林路街道', value: ['310104008', '枫林路街道'] },
                { label: '斜土路街道', value: ['310104007', '斜土路街道'] },
                { label: '长桥街道', value: ['310104010', '长桥街道'] },
                {
                  label: '田林街道',
                  value: ['310104011', '田林街道'],
                  children: [
                    {
                      label: '田尚坊',
                      value: ['31010401101', '田尚坊']
                    }
                  ]
                },
                { label: '康健新村街道', value: ['310104013', '康健新村街道'] },
                {
                  label: '徐家汇街道',
                  value: ['310104014', '徐家汇街道'],
                  children: [
                    {
                      label: '天钥桥',
                      value: ['31010401401', '天钥桥']
                    }
                  ]
                },
                { label: '凌云路街道', value: ['310104015', '凌云路街道'] },
                { label: '龙华街道', value: ['310104016', '龙华街道'] },
                { label: '漕河泾街道', value: ['310104017', '漕河泾街道'] },
                { label: '华泾镇', value: ['310104103', '华泾镇'] }
              ]
            },
            {
              label: '静安区',
              value: ['310106', '静安区'],
              children: [
                {
                  label: '大宁路街道',
                  value: ['310106019', '大宁路街道'],
                  children: [
                    {
                      label: '久光中心',
                      value: ['31010601901', '久光中心']
                    }
                  ]
                },
                { label: '彭浦新村街道', value: ['310106020', '彭浦新村街道'] },
                { label: '临汾路街道', value: ['310106021', '临汾路街道'] },
                { label: '芷江西路街道', value: ['310106022', '芷江西路街道'] },
                {
                  label: '彭浦镇',
                  value: ['310106101', '彭浦镇'],
                  children: [
                    {
                      label: '大融城',
                      value: ['31010610101', '大融城']
                    }
                  ]
                },
                { label: '江宁路街道', value: ['310106006', '江宁路街道'] },
                { label: '石门二路街道', value: ['310106011', '石门二路街道'] },
                {
                  label: '南京西路街道',
                  value: ['310106012', '南京西路街道'],
                  children: [
                    {
                      label: 'X88',
                      value: ['31010601201', 'X88']
                    }
                  ]
                },
                { label: '静安寺街道', value: ['310106013', '静安寺街道'] },
                {
                  label: '曹家渡街道',
                  value: ['310106014', '曹家渡街道'],
                  children: [
                    {
                      label: '889',
                      value: ['31010601401', '889']
                    }
                  ]
                },
                { label: '天目西路街道', value: ['310106015', '天目西路街道'] },
                {
                  label: '北站街道',
                  value: ['310106016', '北站街道'],
                  children: [
                    {
                      label: '大悦城',
                      value: ['31010601601', '大悦城']
                    }
                  ]
                },
                { label: '宝山路街道', value: ['310106017', '宝山路街道'] },
                { label: '共和新路街道', value: ['310106018', '共和新路街道'] }
              ]
            },
            {
              label: '普陀区',
              value: ['310107', '普陀区'],
              children: [
                { label: '曹杨新村街道', value: ['310107005', '曹杨新村街道'] },
                { label: '万里街道', value: ['310107021', '万里街道'] },
                { label: '真如镇街道', value: ['310107022', '真如镇街道'] },
                { label: '长征镇', value: ['310107102', '长征镇'] },
                { label: '桃浦镇', value: ['310107103', '桃浦镇'] },
                { label: '石泉路街道', value: ['310107017', '石泉路街道'] },
                { label: '甘泉路街道', value: ['310107016', '甘泉路街道'] },
                { label: '长寿路街道', value: ['310107015', '长寿路街道'] },
                { label: '长风新村街道', value: ['310107014', '长风新村街道'] },
                { label: '宜川路街道', value: ['310107020', '宜川路街道'] }
              ]
            },
            {
              label: '闵行区',
              value: ['310112', '闵行区'],
              children: [
                { label: '江川路街道', value: ['310112001', '江川路街道'] },
                { label: '古美街道', value: ['310112006', '古美街道'] },
                { label: '新虹街道', value: ['310112008', '新虹街道'] },
                { label: '浦锦街道', value: ['310112009', '浦锦街道'] },
                { label: '莘庄镇', value: ['310112101', '莘庄镇'] },
                { label: '七宝镇', value: ['310112102', '七宝镇'] },
                { label: '颛桥镇', value: ['310112103', '颛桥镇'] },
                { label: '华漕镇', value: ['310112106', '华漕镇'] },
                { label: '虹桥镇', value: ['310112107', '虹桥镇'] },
                { label: '梅陇镇', value: ['310112108', '梅陇镇'] },
                { label: '吴泾镇', value: ['310112110', '吴泾镇'] },
                { label: '马桥镇', value: ['310112112', '马桥镇'] },
                { label: '浦江镇', value: ['310112114', '浦江镇'] },
                { label: '莘庄工业区', value: ['310112501', '莘庄工业区'] }
              ]
            },
            {
              label: '长宁区',
              value: ['310105', '长宁区'],
              children: [
                { label: '华阳路街道', value: ['310105001', '华阳路街道'] },
                { label: '江苏路街道', value: ['310105002', '江苏路街道'] },
                { label: '新华路街道', value: ['310105004', '新华路街道'] },
                { label: '周家桥街道', value: ['310105005', '周家桥街道'] },
                { label: '天山路街道', value: ['310105006', '天山路街道'] },
                { label: '仙霞新村街道', value: ['310105008', '仙霞新村街道'] },
                { label: '虹桥街道', value: ['310105009', '虹桥街道'] },
                { label: '程家桥街道', value: ['310105010', '程家桥街道'] },
                { label: '北新泾街道', value: ['310105011', '北新泾街道'] },
                { label: '新泾镇', value: ['310105102', '新泾镇'] }
              ]
            },
            {
              label: '宝山区',
              value: ['310113', '宝山区'],
              children: []
            },
            {
              label: '嘉定区',
              value: ['310114', '嘉定区'],
              children: []
            },
            {
              label: '浦东新区',
              value: ['310115', '浦东新区'],
              children: []
            }
          ]
        }
      ]
    }
  ];
}
// æŒ‰ç…§éœ€æ±‚的定位精度返回对应数据
function _deleteByLevel(locations, level, step) {
  if (step == level) {
    locations.forEach((l) => {
      if (l.children) {
        l.children = undefined;
      }
    });
    return;
  } else {
    step++;
    locations.forEach((l) => {
      if (l.children) {
        _deleteByLevel(l.children, level, step);
      }
    });
  }
}
export { enumLocation };
src/constant/response-level.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
function responseLevelList() {
  return [
    {
      label: '当日',
      value: 0
    },
    {
      label: '三天内',
      value: 1
    },
    {
      label: '一周内',
      value: 2
    },
    {
      label: '当月',
      value: 3
    },
  ];
}
function responseLevelName(type) {
  return responseLevelList().find((v) => v.value == type).label;
}
export { responseLevelList, responseLevelName };
src/constant/travel-mode.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
function travelModeList() {
  return [
    {
      label: '驾车',
      value: 0
    },
    {
      label: '公共交通',
      value: 1
    },
    {
      label: '步行',
      value: 2
    },
  ];
}
function travelModeName(type) {
  return travelModeList().find((v) => v.value == type).label;
}
export { travelModeList, travelModeName };
src/model/clueQuestion.js
@@ -1,8 +1,8 @@
import { imgUrl } from '@/api/index';
import { $clue } from '@/api/index';
function getClueQuestion(data) {
  data.cqFilePath = data.cqFilePath.split(';').map((val) => {
    return imgUrl + val;
    return $clue.imgUrl + val;
  });
  return data;
}
src/views/overlay-clue/ClueLayout.vue
@@ -8,6 +8,7 @@
        v-model:show="show"
        :clueData="selectedClue"
        @pushed="(e) => (selectedClue.cuploaded = e)"
        @onClueTaskChange="handleClueTaskChange"
      ></ClueReport>
    </el-col>
  </el-row>
@@ -28,6 +29,10 @@
  show.value = true;
  selectedClue.value = clue;
};
function handleClueTaskChange() {
  selectedClue.value.taskCount = 1;
}
</script>
<style scoped></style>
src/views/overlay-clue/list/ClueManage.vue
@@ -15,11 +15,6 @@
    </div>
    <el-scrollbar height="70vh" class="p-h-1">
      <ClueList :dataList="clueList" @itemSelected="selectClue">
        <!-- <template #extra>
          <el-button size="small" type="primary" @click="getClues">
            å‘布任务
          </el-button>
        </template> -->
      </ClueList>
    </el-scrollbar>
    <el-row justify="space-between" class="p-8">
@@ -42,7 +37,7 @@
import clueApi from '@/api/clue/clueApi';
import { onMapMounted } from '@/components/map/baseMap';
import moment from 'moment';
import { ref, onMounted } from 'vue';
import { ref, onMounted, reactive } from 'vue';
const emits = defineEmits('itemSelected');
src/views/overlay-clue/list/components/ClueList.vue
@@ -10,7 +10,7 @@
      >
        <div class="clue-item">
          <el-row justify="space-between">
            <el-col span="20">
            <el-col :span="18">
              <div class="flex gap-1">
                <div class="clue-num">{{ $nf(item.cid) }}</div>
                <el-text class="fy-h1" truncated>{{
@@ -18,11 +18,16 @@
                }}</el-text>
              </div>
            </el-col>
            <!-- <el-col :span="4">
            <el-col :span="6">
              <el-row justify="end">
                <slot name="extra"></slot>
                <el-tag
                  :type="item.taskCount > 0 ? 'info' : 'danger'"
                  effect="plain"
                >
                  {{ item.taskCount > 0 ? '已发布任务' : '未发布任务' }}
                </el-tag>
              </el-row>
            </el-col> -->
            </el-col>
          </el-row>
          <el-space>
            <el-row align="middle">
src/views/overlay-clue/report/ClueReport.vue
@@ -56,20 +56,31 @@
      </el-scrollbar>
    </div>
  </CloseButton>
  <ClueTaskEdit
    v-model="clueTaskDialog"
    :clue-task="clueTask"
    :clue-data="clueData"
    :create="isCreateMode"
    @on-submit="handelClueTaskEdit"
  ></ClueTaskEdit>
</template>
<script>
import ClueReportClue from './components/ClueReportClue.vue';
import ClueReportConclusion from './components/ClueReportConclusion.vue';
import ClueReportQuestion from './components/ClueReportQuestion.vue';
import ClueTaskEdit from '@/views/overlay-clue/task/ClueTaskEdit.vue';
import { useMessageBoxTip } from '@/composables/messageBox';
import clueApi from '@/api/clue/clueApi';
import clueTaskApi from '@/api/clue/clueTaskApi';
export default {
  components: {
    ClueReportClue,
    ClueReportConclusion,
    ClueReportQuestion
    ClueReportQuestion,
    ClueTaskEdit
  },
  props: {
    clueData: {
@@ -80,11 +91,20 @@
    },
    show: Boolean
  },
  emits: ['update:show', 'pushed'],
  emits: ['update:show', 'pushed', 'onClueTaskChange'],
  data() {
    return {
      clueTask: undefined
      clueTask: undefined,
      clueTaskDialog: false,
      isCreateMode: true
    };
  },
  watch: {
    clueData(nV, oV) {
      if (nV && nV != oV) {
        this.getClueTask();
      }
    }
  },
  methods: {
    closeEdit() {
@@ -104,8 +124,25 @@
        this.$emit('pushed', res);
      });
    },
    publishTask() {
    getClueTask() {
      clueTaskApi
        .fetchClueTask({ clueId: this.clueData.cid })
        .then((res) => {
          this.isCreateMode = res.data.length == 0;
          if (res.data.length > 0) {
            this.clueTask = res.data[0];
          } else {
            this.clueTask = undefined;
          }
        });
    },
    publishTask() {
      this.clueTaskDialog = true;
    },
    handelClueTaskEdit() {
      this.getClueTask();
      this.$emit('onClueTaskChange');
    }
  }
};
src/views/overlay-clue/task/ClueTaskEdit.vue
@@ -1,3 +1,288 @@
<template>
  <el-dialog
    style="pointer-events: auto"
    :model-value="modelValue"
    @update:modelValue="handleDialogChange"
    width="50%"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    destroy-on-close
  >
    <template #header>
      <span> {{ create ? '发布线索任务' : '更新线索任务' }}</span>
</template>
    <el-form
      label-width="120px"
      label-position="right"
      :rules="rules"
      :model="formObj"
      ref="formRef"
    >
      <el-form-item label="线索编号" prop="clueId">
        <el-input
          v-model="formObj.clueId"
          placeholder="请输入线索编号"
          class="w-200"
          :disabled="true"
        ></el-input>
      </el-form-item>
      <el-form-item label="任务时间" prop="taskTime">
        <el-date-picker
          v-model="formObj.taskTime"
          type="date"
          placeholder="选择日期"
        />
      </el-form-item>
      <el-form-item label="区县" prop="_location">
        <OptionLocation
          :level="3"
          :initValue="false"
          :checkStrictly="false"
          :allOption="false"
          v-model="formObj._location"
        ></OptionLocation>
      </el-form-item>
      <el-form-item label="响应级别" prop="responseLevel">
        <OptionResponseLevel
          v-model="formObj.responseLevel"
        ></OptionResponseLevel>
      </el-form-item>
      <el-form-item label="出行方式" prop="travelMode">
        <OptionTravelMode
          v-model="formObj.travelMode"
        ></OptionTravelMode>
      </el-form-item>
      <el-form-item label="无人机" prop="hasUav">
        <el-switch
          v-model="formObj.hasUav"
          inline-prompt
          active-text="有"
          inactive-text="无"
        />
      </el-form-item>
      <el-form-item label="执行人" prop="executorIds">
        <OptionExecutors
          v-model="formObj.executorIds"
        ></OptionExecutors>
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="onCancel">取消</el-button>
      <el-button
        :disabled="!edit"
        type="primary"
        :loading="loading"
        @click="onSubmit"
        >确定</el-button
      >
    </template>
  </el-dialog>
</template>
<script setup>
import { ref, reactive, watch, onMounted } from 'vue';
import { useFormConfirm } from '@/composables/formConfirm';
import clueTaskApi from '@/api/clue/clueTaskApi';
const props = defineProps({
  modelValue: Boolean,
  clueData: {
    type: Object,
    default: () => {
      return {};
    }
  },
  clueTask: Object,
  create: {
    type: Boolean,
    default: true
  }
});
const emits = defineEmits(['update:modelValue', 'onSubmit']);
function handleDialogChange(value) {
  emits('update:modelValue', value);
}
const { formObj, formRef, edit, onSubmit, onCancel, clear } =
  useFormConfirm({
    submit: {
      do: submit
    },
    cancel: {
      do: cancel
    }
  });
const loading = ref(false);
// è¡¨å•检查规则
const rules = reactive({
  clueId: [
    {
      required: true,
      message: '线索编号不能为空',
      trigger: 'blur'
    }
  ],
  taskTime: [
    {
      required: true,
      message: '线索时间不能为空',
      trigger: 'change'
    }
  ],
  _location: [
    {
      required: true,
      message: '区县不能为空',
      trigger: 'change'
    }
  ],
  responseLevel: [
    {
      required: true,
      message: '响应级别不能为空',
      trigger: 'change'
    }
  ],
  executorIds: [
    {
      required: true,
      message: '执行人不能为空',
      trigger: 'change'
    }
  ]
});
function submit() {
  const param = getParams();
  return props.create ? createClueTask(param) : updateClueTask(param);
}
function cancel() {
  emits('update:modelValue', false);
}
function createClueTask(param) {
  return clueTaskApi
    .createClueTask(param)
    .then(() => {
      emits('update:modelValue', false);
      emits('onSubmit');
      // clear();
    })
    .finally(() => {
      loading.value = false;
    });
}
function updateClueTask(param) {
  return clueTaskApi
    .updateClueTask(param)
    .then(() => {
      emits('update:modelValue', false);
      emits('onSubmit');
      // clear();
    })
    .finally(() => {
      loading.value = false;
    });
}
function getParams() {
  return {
    guid: formObj.value.guid,
    clueId: formObj.value.clueId,
    taskTime: formObj.value.taskTime,
    provinceCode: formObj.value._location.pCode,
    provinceName: formObj.value._location.pName,
    cityCode: formObj.value._location.cCode,
    cityName: formObj.value._location.cName,
    districtCode: formObj.value._location.dCode,
    districtName: formObj.value._location.dName,
    townCode: formObj.value._location.tCode,
    townName: formObj.value._location.tName,
    responseLevel: formObj.value.responseLevel,
    travelMode: formObj.value.travelMode,
    hasUav: formObj.value.hasUav,
    executorIds: formObj.value.executorIds.join(';')
  };
}
// watch(
//   () => props.clueData,
//   (nV, oV) => {
//     if (nV && nV != oV) {
//       formObj.value.clueId = nV.cid;
//     }
//   }
// );
// watch(
//   () => props.clueTask,
//   (nV, oV) => {
//     if (nV && nV != oV) {
//       formObj.value.guid = nV.guid;
//       formObj.value.clueId = nV.clueId;
//       formObj.value.taskTime = nV.taskTime;
//       formObj.value._location = {
//         pCode: nV.provinceCode,
//         pName: nV.provinceName,
//         cCode: nV.cityCode,
//         cName: nV.cityName,
//         dCode: nV.districtCode,
//         dName: nV.districtName,
//         tCode: nV.townCode,
//         tName: nV.townName
//       };
//       formObj.value.responseLevel = nV.responseLevel;
//       formObj.value.travelMode = nV.travelMode;
//       formObj.value.hasUav = nV.hasUav;
//       formObj.value.executorIds = nV.executorIds.split(';');
//     }
//   }
// );
watch(
  () => [props.modelValue, props.clueData, props.clueTask],
  (nV, oV) => {
    const [m, d, t] = nV;
    if (m) {
      formObj.value = {
        taskTime: new Date(),
        _location: {
          pCode: '31',
          pName: '上海市',
          cCode: '3100',
          cName: '上海市',
          dCode: '310106',
          dName: '静安区',
          tCode: undefined,
          tName: undefined
        }
      };
      if (d) {
        formObj.value.clueId = d.cid;
      }
      if (t) {
        formObj.value.guid = t.guid;
        formObj.value.clueId = t.clueId;
        formObj.value.taskTime = t.taskTime;
        formObj.value._location = {
          pCode: t.provinceCode,
          pName: t.provinceName,
          cCode: t.cityCode,
          cName: t.cityName,
          dCode: t.districtCode,
          dName: t.districtName,
          tCode: t.townCode,
          tName: t.townName
        };
        formObj.value.responseLevel = t.responseLevel;
        formObj.value.travelMode = t.travelMode;
        formObj.value.hasUav = t.hasUav;
        formObj.value.executorIds = t.executorIds.split(';');
      }
    }
  }
);
</script>