Sfoglia il codice sorgente

Merge branch 'master' of http://47.100.37.243:10191/wutt/manHourHousekeeper

Guo1B0 11 mesi fa
parent
commit
34ef0551f5
28 ha cambiato i file con 855 aggiunte e 172 eliminazioni
  1. 133 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/components/detailcompinents/relatedTasks.vue
  2. 50 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/components/detailcompinents/type.d.ts
  3. 5 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/api.ts
  4. 5 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/api.ts
  5. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/deteleTables.vue
  6. 209 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/stageSetting.vue
  7. 78 33
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/detail/index.vue
  8. 23 12
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue
  9. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/type.d.ts
  10. 19 10
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/api.ts
  11. 45 20
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/index.vue
  12. 3 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/constant.ts
  13. 80 7
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/information.vue
  14. 85 51
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/relatedTasks.vue
  15. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/index.vue
  16. 63 10
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/index.vue
  17. 5 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/type.d.ts
  18. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/BusinessOpportunityMapper.java
  19. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/BusinessOpportunityService.java
  20. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/BusinessOpportunityServiceImpl.java
  21. 17 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SalesOrderServiceImpl.java
  22. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SysFormServiceImpl.java
  23. 1 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java
  24. 16 9
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  25. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  26. 1 2
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml
  27. 3 2
      fhKeeper/formulahousekeeper/timesheet/src/components/vueMultipleDept.vue
  28. 2 1
      fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue

+ 133 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/components/detailcompinents/relatedTasks.vue

@@ -0,0 +1,133 @@
+<template>
+    <div class="relatedTasks pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">相关任务</div>
+            <div>
+                <el-button type="primary" @click="newTask()">新建任务</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-3">
+            <el-table :data="relatedTaskstable" border style="width: 100%;height: 100%;">
+                <el-table-column prop="taskName" label="任务名称">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large" @click="toTask()">{{
+                            scope.row.taskName
+                        }}</el-button>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="priorityStr" label="优先级" width="130" />
+                <el-table-column prop="statusStr" label="状态" width="130" />
+                <el-table-column prop="executorNamesStr" label="执行人" width="130" />
+                <el-table-column prop="startTimes" label="开始时间" width="130" />
+                <el-table-column prop="endTimes" label="截至时间" width="130" />
+            </el-table>
+        </div>
+    </div>
+
+    <!-- 新建任务 -->
+    <TaskModal :visible="allVisible.taskModalVisible" :edit-form="taskModalForm" :save-loading="taskLoading"
+        @close="closeTaskModal" @submit="submitForm" :title="'新建任务'" :disabled-list="props.disabledList" />
+</template>
+<script lang="ts" setup>
+import { PRIORITY } from '../TaskModal/api';
+import { STATUS } from '../../pages/tasks/api';
+import { formatDate } from '../../utils/times';
+import { createTaskFromType } from '../../utils/tools';
+import { ElNotification, NotificationParamsTyped } from 'element-plus'
+import { ref, reactive, onMounted, watchEffect } from 'vue'
+import { useRouter } from "vue-router";
+import TaskModal from '../TaskModal/index.vue'
+import { createTask } from '../TaskModal/taskFunction';
+import { ITEM_RENDER_EVT } from 'element-plus/es/components/virtual-list/src/defaults';
+import { Props, Emits } from './type';
+
+const props = defineProps<Props>()
+const emits = defineEmits<Emits>();
+
+type saveLoadingType = "1" | "2" | "3" | "4"; //1是没有保存, 2是正在保存, 3是保存成功, 4是保存失败
+
+const relatedTaskstable = ref([])
+const information = ref<any>({})
+const router = useRouter()
+const taskLoading = ref<saveLoadingType>('1')
+const taskModalForm = ref({}) // 任务弹窗表单
+const allVisible = reactive({
+    taskModalVisible: false
+})
+
+function submitForm(submitData: any, isClose: boolean) { // 任务提交
+    taskLoading.value = '2'
+    createTask(submitData, isClose).then((res) => {
+        const { saveLoading, isClose } = res
+        taskLoading.value = saveLoading
+        allVisible.taskModalVisible = isClose
+        globalPopup('新建成功', 'success')
+        emits('refreshData')
+    }).catch((err) => {
+        const { saveLoading, isClose, message } = err
+        taskLoading.value = saveLoading
+        allVisible.taskModalVisible = isClose
+        globalPopup(message, 'error')
+    })
+}
+
+function globalPopup(str: string, type: string) {
+    notificationTiop({
+      message: str || '成功',
+      type: type,
+      title: "提示",
+      duration: 2000
+    })
+}
+
+const notificationTiop = (options: NotificationParamsTyped) => {
+  ElNotification(options)
+}
+
+function newTask() {
+    const { id } = information.value
+    taskModalForm.value = { ...createTaskFromType(props.formTaskType), [props.filed as any]: id, }
+    allVisible.taskModalVisible = true
+}
+
+function toTask() {
+    router.push({
+        path: `/tasks`
+    })
+}
+
+function closeTaskModal() {
+    allVisible.taskModalVisible = false
+}
+
+// 接收参数赋值
+function receiveAssignment(item: any) {
+    console.log(item, '<==== 过来的值')
+    information.value = item.information
+    const dataVal = item.data
+    for (let i in dataVal) {
+        dataVal[i].executorNamesStr = (dataVal[i].executorNames || []).join(','),
+            dataVal[i].startTimes = dataVal[i].startDate ? formatDate(new Date(dataVal[i].startDate)) : '',
+            dataVal[i].endTimes = dataVal[i].endDate ? formatDate(new Date(dataVal[i].endDate)) : '',
+            dataVal[i].priorityStr = PRIORITY.find(item => item.value == dataVal[i].priority)?.label || '',
+            dataVal[i].statusStr = STATUS.find(item => item.value == dataVal[i].status)?.label || ''
+    }
+    relatedTaskstable.value = dataVal
+}
+
+watchEffect(() => {
+    receiveAssignment(props)
+});
+
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.relatedTasks {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 50 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/components/detailcompinents/type.d.ts

@@ -0,0 +1,50 @@
+type TASK_VALUE_TYPE = 0 | 1 | 2 | 3;
+type tableTypeFiled = {
+    id: number,
+    status: number,
+    priority: number,
+    executorNames: Array,
+    startDate: string,
+    endDate: string,
+}
+export interface Props {
+    /**
+     * data: 任务 Table 数据
+     */
+    data: tableTypeFiled[];
+    /**
+     * information: 详情数据
+     */
+    information: any;
+    /**
+     * disabledList:新建任务需要禁用的字段(参考 TaskModal 文件)
+     */
+    disabledList?: (
+        | "taskName"
+        | "priority"
+        | "taskType"
+        | "customId"
+        | "businessOpportunityId"
+        | "orderId"
+        | "clueId"
+        | "contactsId"
+        | "executorId"
+        | "startDate"
+        | "endDate"
+    )[];
+    /** 
+     * formTaskType: 任务类型  0是客户, 1是商机, 2是销售订单 ,3是线索
+     */
+    formTaskType: TASK_VALUE_TYPE,
+    /**
+     * 详情数据中需要关联的任务类型字段
+     */
+    filed?: 'customId' | 'businessOpportunityId' | 'orderId' | 'clueId' 
+}
+
+export interface Emits {
+    /**
+     *  新建任务后触发的更新数据事件
+     */
+    (event: "refreshData"): void;
+}

+ 5 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/api.ts

@@ -2,3 +2,8 @@ export const SENDVCODE = "/user/sendVcode";     //发送验证码
 export const REGISTER = "/user/insertCompany";  //注册
 export const LOGIN = "/user/loginAdmin";        //登录
 export const IMPORTTIMELIST = "/sys-form/getExportTemplate" // 下载模板
+
+export const SEX: sexTYpe[] = [
+    { label: "男", value: '1' },
+    { label: "女", value: '0' },
+];

+ 5 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/api.ts

@@ -10,6 +10,11 @@ export const BUSINESSDETELE = `/business-opportunity/delete`
 export const BATCHTRANSFER = `/business-opportunity/claim`
 export const BUSIESS_DETELELIST = `/business-opportunity/deleterList`
 export const BUSIESS_ROWBACK = `/business-opportunity/rollBack`
+export const BUSIESS_PERDETELE = `/business-opportunity/deleterDelete`
+export const BUSIESS_GETSATE = `/business-opportunity/getStage`
+export const BUSIESS_SAVESAIE = `/business-opportunity/saveStage`
+export const BUSIESS_ALL = `/business-opportunity/getAll`
+export const BUSIESS_INFO = `/business-opportunity/getInfo`
 
 
 export const stageStatus = [

+ 2 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/deteleTables.vue

@@ -45,7 +45,7 @@
 <script lang="ts" setup>
 import { post } from '@/utils/request';
 import { ref, reactive, onMounted, watchEffect, watch, inject } from 'vue'
-import { BUSIESS_DETELELIST, BUSIESS_ROWBACK, tableColumn } from '../api';
+import { BUSIESS_DETELELIST, BUSIESS_PERDETELE, BUSIESS_ROWBACK, tableColumn } from '../api';
 import { ElTable } from 'element-plus';
 import { confirmAction } from '@/utils/tools';
 import { formatDate } from '@/utils/times';
@@ -90,7 +90,7 @@ function batchOperation(type: operationType) {
 
 function businessOperationItem(value: string | number, label: string, type: operationType, batch: boolean = false) {
     confirmAction(`确定${batch ? '批量' : ''}${type}【${label}】商机吗?`).then(() => {
-        let url = type == '恢复' ? BUSIESS_ROWBACK : ''
+        let url = type == '恢复' ? BUSIESS_ROWBACK : BUSIESS_PERDETELE
         post(url, { ids: value }).then(res => {
             if (res.code != 'ok') {
                 globalPopup?.showError(res.msg)

+ 209 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/stageSetting.vue

@@ -0,0 +1,209 @@
+<template>
+    <div>
+        <el-dialog v-model="stageVisible" width="1000" :before-close="beForeCancel" :show-close="false" top="10vh">
+            <template #header="{ close, titleId, titleClass }">
+                <div class="flex justify-between items-center border-b pb-3 dialog-header">
+                    <h4 :id="titleId">阶段设置</h4>
+                    <div>
+                        <el-button type="primary" @click="addStage(false)">新增</el-button>
+                        <el-button type="primary" @click="saveState()" v-loading="allLoading.saveLoading">保存</el-button>
+                        <el-button @click="cancel()">取消</el-button>
+                    </div>
+                </div>
+            </template>
+            <div class="h-[60vh] flex flex-col">
+                <div class="flex-1 w-full overflow-hidden">
+                    <el-table :data="stageTableList" border v-loading="allLoading.tableLoading"
+                        style="width: 100%;height: 100%;">
+                        <el-table-column :prop="'seq'" :label="'顺序'" width="100">
+                            <template #default="scope">
+                                {{ scope.$index + 1 }}
+                            </template>
+                        </el-table-column>
+                        <el-table-column :prop="'name'" :label="'阶段名称'"></el-table-column>
+                        <el-table-column :prop="'plan'" :label="'进度'" width="100"></el-table-column>
+                        <el-table-column label="操作" fixed="right" width="200">
+                            <template #default="scope">
+                                <el-button link type="primary" size="large" @click="addStage(scope.row)">编辑</el-button>
+                                <el-button link type="danger" size="large" @click="deteStage(+scope.$index)">删除</el-button>
+                                <el-button link type="primary" size="large" @click="moveStage(+scope.$index, 'up')"
+                                    v-if="scope.$index != 0">上移</el-button>
+                                <el-button link type="primary" size="large" @click="moveStage(+scope.$index, 'down')"
+                                    v-if="scope.$index < stageTableList.length - 1">下移</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                </div>
+            </div>
+
+            <!-- 内层弹窗 -->
+            <el-dialog width="700px" v-model="allVisible.editVisible" append-to-body :show-close="false">
+                <template #header="{ close, titleId, titleClass }">
+                    <div class="flex justify-between items-center border-b pb-3 dialog-header">
+                        <h4 :id="titleId">添加阶段</h4>
+                        <div>
+                            <el-button type="primary" @click="editState(true)">保存并新增</el-button>
+                            <el-button type="primary" @click="editState(false)">保存</el-button>
+                            <el-button @click="allVisible.editVisible = false">取消</el-button>
+                        </div>
+                    </div>
+                </template>
+                <div class="h-[120px] flex flex-col pt-5">
+                    <div class="flex flex-row w-full items-center">
+                        <div class="w-[100px] mr-2 text-right">阶段名称:</div>
+                        <div class="flex-1">
+                            <el-input v-model="stageForm.name" placeholder="请输入阶段名称" clearable></el-input>
+                        </div>
+                    </div>
+                    <div class="flex flex-row w-full items-center pt-3">
+                        <div class="w-[100px] mr-2 text-right">进度:</div>
+                        <div class="flex-1">
+                            <el-input-number v-model="stageForm.plan" controls-position="right" :min="0"
+                                :max="100"></el-input-number>
+                            <span class="inline-block ml-2">%</span>
+                        </div>
+                    </div>
+                </div>
+            </el-dialog>
+        </el-dialog>
+    </div>
+</template>
+<script lang="ts" setup>
+import { post } from '@/utils/request';
+import { ref, reactive, onMounted, watch, inject } from 'vue'
+import { BUSIESS_GETSATE, BUSIESS_SAVESAIE } from '../api';
+import { List } from 'echarts';
+
+type moveStageType = 'up' | 'down';
+type stageFormType = {
+    name: string,
+    plan: number,
+    seq: number,
+    id?: number,
+    isFinish?: number
+}
+
+const emits = defineEmits(['closeVisible']);
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const stageVisible = ref(false)
+const stageTableList = ref<stageFormType[]>([])
+const allLoading = reactive({
+    editLoading: false,
+    saveLoading: false,
+    tableLoading: false
+})
+const allVisible = reactive({
+    editVisible: false,
+})
+const stageForm = reactive<stageFormType>({
+    name: '',
+    plan: 0,
+    seq: 0,
+})
+
+const props = defineProps<{
+    visibles: boolean
+}>()
+
+watch(() => props.visibles, (newVal) => {
+    stageVisible.value = newVal
+    if (newVal) {
+        getTableList()
+    }
+})
+
+function saveState() {
+    let newList = JSON.parse(JSON.stringify(stageTableList.value))
+    let data = newList.map((item: stageFormType, index: number) => {
+        return {
+            ...item,
+            seq: index
+        }
+    })
+    allLoading.saveLoading = true
+    post(BUSIESS_SAVESAIE, {list: JSON.stringify(data)}).then(() => {
+        globalPopup?.showSuccess('保存成功')
+        cancel()
+    }).finally(() => {
+        allLoading.saveLoading = false
+    })
+}
+
+function editState(flag: boolean) {
+    if (!stageForm.name) {
+        globalPopup?.showWarning('请输入阶段名称')
+        return
+    }
+    stageTableList.value.push({ ...stageForm })
+    if (flag) {
+        resetStage()
+    }
+    allVisible.editVisible = flag
+}
+
+function addStage(item: any) {
+    const row = JSON.parse(JSON.stringify(item))
+    if (!item) {
+        resetStage()
+    } else {
+        Object.assign(stageForm, {
+            name: row.name,
+            plan: row.plan,
+            seq: row.seq
+        })
+    }
+    allVisible.editVisible = true
+}
+
+function resetStage() {
+    let newData = JSON.parse(JSON.stringify(stageTableList.value))
+    let maxnum = newData.sort((a: any, b: any) => { return b.seq - a.seq; })[0];
+    Object.assign(stageForm, {
+        name: '',
+        plan: 0,
+        seq: +maxnum + 1
+    })
+}
+
+function moveStage(index: number, stageType: moveStageType) {
+    const tableList = stageTableList.value
+    if (stageType == 'up') {
+        stageTableList.value = tableList.slice(0, index - 1).concat(tableList[index], tableList[index - 1], tableList.slice(index + 1))
+    }
+
+    if (stageType == 'down') {
+        stageTableList.value = tableList.slice(0, index).concat(tableList[index + 1], tableList[index], tableList.slice(index + 2))
+    }
+}
+
+function deteStage(index: number) {
+    stageTableList.value.splice(index, 1)
+}
+
+function getTableList() {
+    allLoading.tableLoading = true
+    post(BUSIESS_GETSATE, {}).then((res) => {
+        const { data } = res
+        let newData = data || []
+        stageTableList.value = newData.sort(function (a: any, b: any) {
+            return a.seq - b.seq;
+        });
+    }).finally(() => {
+        allLoading.tableLoading = false
+    })
+}
+
+function cancel() {
+    emits('closeVisible', 'stageSetVisible')
+}
+
+function beForeCancel(done: () => void) {
+    emits('closeVisible', 'stageSetVisible')
+    done()
+}
+
+onMounted(() => {
+});
+
+</script>
+<style scoped lang="scss"></style>

+ 78 - 33
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/detail/index.vue

@@ -1,29 +1,29 @@
 <template>
   <div class="h-full flex p-3 flex-col businessDetail">
-    <div class="w-full bg-white p-2 mb-2 shadow-md rounded-md flex items-center">
+    <div class="w-full bg-white p-2 mb-2 shadow-md rounded-md flex items-center" v-loading="allLoading.skeletonLoading">
       <div class="icon mr-4">
         <el-link :underline="false" @click="backPath()">
           <el-icon class="el-icon--right"><icon-view /></el-icon> 返回商机列表
         </el-link>
       </div>
       <div class="mr-8">
-        <el-select v-model="value" placeholder="请选择" style="width: 150px">
+        <el-select v-model="optionVal" placeholder="请选择" style="width: 150px" filterable @change="getDetail()">
           <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
         </el-select>
       </div>
       <div class="flex-1 flex h-full justify-end overflow-auto scroll-bar-hide cursor-pointer" @wheel="handleScroll">
         <div :class="`${index === 0 ? 'startStep' : 'nextStep'} relative rounded-md flex items-center backGray pl-6 pr-6`"
-          v-for="(item, index) in 2" :key="index">
-          <div class="pr-3 text-nowrap">验证客户</div>
-          <div class="text-nowrap">30%</div>
+          v-for="(item, index) in stageList" :key="index">
+          <div class="pr-3 text-nowrap">{{ item.name }}</div>
+          <div class="text-nowrap">{{ item.plan }}</div>
         </div>
       </div>
       <div class="relative rounded-md flex items-center itemPing backGray endStep item pl-6 pr-6 mr-4">
         <el-select v-model="stageStatusVal" placeholder="结束" style="width: 100px" class="selectClas">
-          <el-option v-for="item in stageStatus" :key="item.id" :label="item.name" :value="item.id" />
+          <el-option v-for="(item, index) in stageListOption" :key="index" :label="item.label" :value="item.value" />
         </el-select>
       </div>
-      <div class="bg-[#0052CC] rounded-md text itemPing pl-2 pr-2 flex items-center">
+      <div class="bg-[#0052CC] rounded-md text itemPing pl-2 pr-2 flex items-center aloneText">
         <el-link :underline="false">推进至阶段【验证客户】</el-link>
       </div>
     </div>
@@ -40,7 +40,7 @@
 
       <div class="w-full h-auto flex justify-between mt-2">
         <div class="bg-white shadow-md rounded-md" style="width: 65%;">
-          <RelatedTasks />
+          <DetailCompinents :data="detailCompinentsData" :information="businessInfo" :formTaskType="1" :filed="'businessOpportunityId'" :disabledList="['taskType', 'businessOpportunityId']" @refreshData="getDetail" />
         </div>
         <div class="bg-white ml-2 shadow-md rounded-md flex-1">
           <OperationRecord />
@@ -61,39 +61,46 @@ import { ref, reactive, onMounted, inject } from "vue";
 import type { FormInstance, FormRules } from 'element-plus'
 import { Edit, ArrowLeft as IconView } from '@element-plus/icons-vue'
 import { backPath } from '@/utils/tools'
-import { stageStatus } from '../api'
+import { useRoute } from "vue-router";
+import { BUSIESS_ALL, BUSIESS_GETSATE, BUSIESS_INFO } from '../api'
 
 import Information from '../component/information.vue'
 import Attachment from '../component/attachment.vue'
-import RelatedTasks from '../component/relatedTasks.vue';
+// import RelatedTasks from '../component/relatedTasks.vue';
+import DetailCompinents from '@/components/detailcompinents/relatedTasks.vue'
 import OperationRecord from '../component/operationRecord.vue';
 import Products from '../component/products.vue';
+import { post } from "@/utils/request";
+import { number } from "echarts";
 
-const value = ref('')
+type stageListType = {
+  name: string,
+  plan: string
+}
+
+const route = useRoute()
+const detailCompinentsData = ref([])
+const optionVal = ref<any>('')
 const stageStatusVal = ref('')
-const options = [
-  {
-    value: 'Option1',
-    label: 'Option1',
-  },
-  {
-    value: 'Option2',
-    label: 'Option2',
-  },
-  {
-    value: 'Option3',
-    label: 'Option3',
-  },
-  {
-    value: 'Option4',
-    label: 'Option4',
-  },
-  {
-    value: 'Option5',
-    label: 'Option5',
-  },
-]
+const options = ref<optionType[]>([])
+const allLoading = reactive({
+  skeletonLoading: false
+})
+
+const businessInfo = ref({})
+const stageListOption = ref<optionType[]>([])
+const stageList = ref<stageListType[]>([])
+
 
+function getDetail() {
+  allLoading.skeletonLoading
+  post(BUSIESS_INFO, { id: optionVal.value }).then(({ data }) => {
+    businessInfo.value = (data || []);
+    detailCompinentsData.value = data.taskList || []
+  }).finally(() => {
+    allLoading.skeletonLoading
+  })
+}
 
 function handleScroll(event: any) { // 滚表横向滚动
   if (event.deltaY) {
@@ -102,6 +109,31 @@ function handleScroll(event: any) { // 滚表横向滚动
     element.scrollLeft += event.deltaY;
   }
 }
+
+function getOptionAll() {
+  post(BUSIESS_ALL, {}).then((res) => {
+    const { data } = res
+    options.value = (data || []).map((item: any) => ({
+      value: item.id + '',
+      label: item.name
+    }))
+  })
+}
+
+function getSatge() {
+  post(BUSIESS_GETSATE, {}).then(({ data }) => {
+    stageList.value = (data || []).sort((a: any, b: any) => { return a.seq - b.seq; }).filter((item: any) => !item.isFinish).map((item: any) => ({ name: item.name, plan: item.plan }))
+    stageListOption.value = (data || []).sort((a: any, b: any) => { return a.seq - b.seq; }).filter((item: any) => item.isFinish).map((item: any) => ({ label: item.name, value: item.id }))
+  })
+}
+
+onMounted(() => {
+  const { id } = route.query
+  optionVal.value = id
+  getSatge()
+  getOptionAll()
+  getDetail()
+})
 </script>
   
 <style lang="scss" scoped>
@@ -159,5 +191,18 @@ function handleScroll(event: any) { // 滚表横向滚动
     padding-top: 4px;
     padding-bottom: 4px;
   }
+
+  .aloneText {
+    padding-top: 9px;
+    padding-bottom: 9px;
+  }
+}
+</style>
+<style lang="scss">
+.businessDetail {
+  .el-select__wrapper {
+    background-color: #F4F5F7;
+    box-shadow: none !important;
+  }
 }
 </style>

+ 23 - 12
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue

@@ -51,9 +51,9 @@
           <el-button type="primary" @click="showVisible('batchTransferVisible')"
             :disabled="batchTableData.length <= 0">批量转移</el-button>
           <el-button type="primary" @click="batchDeteleItem()" :disabled="batchTableData.length <= 0">批量删除</el-button>
-          <el-button type="primary">阶段设置</el-button>
+          <el-button type="primary" @click="showVisible('stageSetVisible')">阶段设置</el-button>
           <el-button type="primary" @click="showVisible('deteleBusinessVisible')">回收站</el-button>
-          <el-button type="primary" @click="allVisible.importVisible = true">导入</el-button>
+          <el-button type="primary" @click="showVisible('importVisible')">导入</el-button>
           <el-button type="primary" @click="exportBusinessTableList()" :loading="allLoading.exoprtLoading">导出</el-button>
         </div>
         <div class="flex-1 w-full overflow-hidden">
@@ -155,6 +155,9 @@
 
     <!-- 回收站 -->
     <DeteleBusiness :visibles="allVisible.deteleBusinessVisible" @closeVisible="closeVisible" />
+
+    <!-- 阶段设置 -->
+    <StageSetting :visibles="allVisible.stageSetVisible" @closeVisible="closeVisible" />
   </div>
 </template>
 
@@ -162,7 +165,7 @@
 import { ref, reactive, onMounted, inject } from "vue";
 import type { ElTable, FormInstance, FormRules, UploadRequestOptions } from 'element-plus'
 import { useRouter, useRoute } from "vue-router";
-import { GETSYSFILED, MOD, GETPERSONNEL, GETGENERATEFOEM, GETBUSINESSLIST, UPDATEINSET, BUSINESSDETELE, BATCHTRANSFER, MODURL, tableColumn } from './api'
+import { GETSYSFILED, MOD, GETPERSONNEL, GETGENERATEFOEM, GETBUSINESSLIST, UPDATEINSET, BUSINESSDETELE, BATCHTRANSFER, MODURL, tableColumn, BUSIESS_GETSATE } from './api'
 import { GETTABLELIST } from '@/pages/product/api'
 import { post, get, uploadFile } from "@/utils/request";
 import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, createTaskFromType, formatDate, confirmAction, downloadTemplate, downloadFile } from '@/utils/tools'
@@ -172,6 +175,7 @@ import { GenerateForm } from '@zmjs/form-design';
 import RelatedProducts from './component/relatedProducts.vue'
 import TaskModal from '@/components/TaskModal/index.vue'
 import DeteleBusiness from './component/deteleTables.vue'
+import StageSetting from './component/stageSetting.vue'
 
 const route = useRoute()
 const router = useRouter()
@@ -199,6 +203,7 @@ const allVisible = reactive({
   taskModalVisible: false,
   batchTransferVisible: false,
   deteleBusinessVisible: false,
+  stageSetVisible: false,
   importVisible: false
 })
 const allText = reactive({
@@ -389,15 +394,21 @@ function resetForm() {
 }
 
 async function getSystemField() {
-  const systemField = getAllListByCode(['商机阶段'])
-  for (let i in systemField) {
-    const { data } = await get(`${GETSYSFILED}?code=${systemField[i]}`)
-    for (let key of Object.keys(fixedData)) {
-      if (systemField[i] == key) {
-        Object.assign(fixedData, { [key]: data })
-      }
-    }
-  }
+  // const systemField = getAllListByCode(['商机阶段'])
+  // for (let i in systemField) {
+  //   const { data } = await get(`${GETSYSFILED}?code=${systemField[i]}`)
+  //   for (let key of Object.keys(fixedData)) {
+  //     if (systemField[i] == key) {
+  //       Object.assign(fixedData, { [key]: data })
+  //     }
+  //   }
+  // }
+
+  const row = await post(BUSIESS_GETSATE, {})
+  fixedData.BusinessStage = (row.data || []).map((item: any) => {
+    const { name, id, seq } = item
+    return { name, id, seq }
+  }).sort(function (a: any, b: any) {return a.seq - b.seq;});
 
   const { data } = await post(GETPERSONNEL, {})
   fixedData.Personnel = data.map((item: any) => {

+ 2 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/type.d.ts

@@ -14,8 +14,8 @@ interface businessOpportunityFormType {
 
 interface fixedDataInterface {
   id: string | number;
-  companyId: string | number;
-  code: string;
+  companyId?: string | number;
+  code?: string;
   name: string;
   seq: string | number;
 }

+ 19 - 10
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/api.ts

@@ -1,11 +1,15 @@
+import { SEX } from '../api.ts'
 export const MOD = 'contacts'
+export const URL = '/contacts'
 
 export const GETSYSFILED = "/sys-dict/getListByCode";
 export const GETPERSONNEL = "/user/getSimpleActiveUserList";
-export const GETGENERATEFOEM = `sys-form/getListByCode${MOD}`
+export const GETGENERATEFOEM = `sys-form/getListByCode/${MOD}`
+
+export const URL_PAGECONTACTS = `${URL}/pageContacts`
 
 export const actionButtons: any[] = [
-    { text: '批量转移' },
+    { text: '新建' },
     { text: '批量删除' },
     { text: '导入' },
     { text: '导出' },
@@ -13,12 +17,17 @@ export const actionButtons: any[] = [
 
 export const tableColumns: TableColumn[] = [
     { prop: 'name', label: '联系人', event: 'toDetali', width: '150' },
-    { prop: 'mobile', label: '客户名称', width: '150' },
-    { prop: 'email', label: '电话号码', width: '200' },
-    { prop: 'wechat', label: '邮箱', width: '200' },
+    { prop: 'customName', label: '客户名称', width: '150' },
+    { prop: 'phone', label: '电话号码', width: '200' },
+    { prop: 'email', label: '邮箱', width: '200' },
     { prop: 'position', label: '职务', width: '100' },
-    { prop: 'company', label: '性别', width: '100' },
-    { prop: 'companya', label: '负责人', width: '100' },
-    { prop: 'companyb', label: '创建人', width: '100' },
-    { prop: 'companyc', label: '创建时间', width: '200' },
-]
+    { prop: 'sex', label: '性别', width: '100', event: 'getSex' },
+    { prop: 'ownerName', label: '负责人', width: '100' },
+    { prop: 'creatorName', label: '创建人', width: '100' },
+    { prop: 'createTime', label: '创建时间', width: '200' },
+]
+
+export const getSex = (val: number) => {
+    let sexItem = SEX.filter((item: sexTYpe) => item.value == val)
+    return sexItem.length > 0 ? sexItem[0].label : ''
+}

+ 45 - 20
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/index.vue

@@ -6,13 +6,19 @@
           <!-- 筛选条件 -->
           <el-form :model="filterForm" label-width="70px" style="max-width: 600px">
             <el-form-item v-for="(item, index) in filterItems" :key="index" :label="item.label">
-              <el-input v-if="item.type === 'input'" v-model="filterForm[item.key as keyof FilterForm]" clearable placeholder="请输入"></el-input>
+              <el-input v-if="item.type === 'input'" v-model="filterForm[item.key as keyof FilterForm]" clearable
+                placeholder="请输入"></el-input>
               <el-select v-else v-model="filterForm[item.key as keyof FilterForm]" placeholder="请选择" clearable>
                 <el-option v-for="option in item.options" :key="option.id" :label="option.name" :value="option.id" />
               </el-select>
             </el-form-item>
           </el-form>
         </div>
+        <div class="w-full flex p-3 shadow-[0_-3px_5px_0px_rgba(0,0,0,0.2)]">
+          <El-button class="w-full" @click="resetForm()" :loading="allLoading.formTableLading">重置</El-Button>
+          <El-button type="primary" class="w-full" :loading="allLoading.formTableLading"
+            @click="getContactPerson()">搜索</El-Button>
+        </div>
       </div>
     </div>
     <div class="flex-1 p-5 overflow-auto">
@@ -25,10 +31,15 @@
           <!-- 表格 -->
           <el-table ref="clueTableRef" :data="formTable" border v-loading="allLoading.formTableLading"
             style="width: 100%;height: 100%;">
-            <el-table-column v-for="(column, index) in tableColumns" :key="index" :prop="column.prop" :label="column.label" :width="column.width">
+            <el-table-column v-for="(column, index) in tableColumns" :key="index" :prop="column.prop"
+              :label="column.label" :width="column.width">
               <template #default="scope">
                 <template v-if="column.event === 'toDetali'">
-                  <el-button link type="primary" size="large" @click="toDetali(scope.row)">{{ scope.row.name }}</el-button>
+                  <el-button link type="primary" size="large" @click="toDetali(scope.row)">{{ scope.row.name
+                  }}</el-button>
+                </template>
+                <template v-if="column.event === 'getSex'">
+                  {{ getSex(scope.row.sex) }}
                 </template>
               </template>
             </el-table-column>
@@ -43,8 +54,7 @@
         </div>
         <div class="flex justify-end pt-3">
           <!-- 分页 -->
-          <el-pagination layout="total, prev, pager, next, sizes" :total="formTablePaging.total"
-            :hide-on-single-page="true" />
+          <el-pagination layout="total, prev, pager, next, sizes" :total="tableTotal" :hide-on-single-page="true" />
         </div>
       </div>
     </div>
@@ -55,7 +65,7 @@
 import { ref, reactive, onMounted, inject } from "vue";
 import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate } from '@/utils/tools'
 import { post, get } from "@/utils/request";
-import { actionButtons, tableColumns, GETSYSFILED, GETPERSONNEL, GETGENERATEFOEM, MOD } from "./api";
+import { actionButtons, tableColumns, GETSYSFILED, GETPERSONNEL, GETGENERATEFOEM, MOD, URL_PAGECONTACTS, getSex } from "./api";
 import { useRouter, useRoute } from "vue-router";
 
 const router = useRouter()
@@ -68,28 +78,26 @@ const filterForm = reactive<FilterForm>({ // 筛选条件
   createId: '',
   email: '',
 });
+const formTablePaging = reactive({ // 分页条件
+  pageIndex: 1,
+  pageSize: 10,
+})
+const tableTotal = ref(0)
 const selectData = reactive({ // 下拉数据
   Personnel: [] as personnelInterface[],
   Customer: [] as any[],
 })
 const filterItems = ref<FilterItem[]>([
-    { label: '联系人', key: 'contactPerson', type: 'input' },
-    { label: '客户名称', key: 'customerId', type: 'select', options: selectData.Customer },
-    { label: '电话号码', key: 'phoneNumber', type: 'input' },
-    { label: '邮箱', key: 'email', type: 'input' },
-    { label: '负责人', key: 'responsibleId', type: 'select', options: selectData.Personnel },
-    { label: '创建人', key: 'createId', type: 'select', options: selectData.Personnel },
+  { label: '联系人', key: 'contactPerson', type: 'input' },
+  { label: '客户名称', key: 'customerId', type: 'select', options: selectData.Customer },
+  { label: '电话号码', key: 'phoneNumber', type: 'input' },
+  { label: '邮箱', key: 'email', type: 'input' },
+  { label: '负责人', key: 'responsibleId', type: 'select', options: selectData.Personnel },
+  { label: '创建人', key: 'createId', type: 'select', options: selectData.Personnel },
 ])
-const formTablePaging = reactive({ // 分页条件
-  currentPage: 1,
-  pageSize: 10,
-  total: 0,
-})
 const generateFormData = ref([]) // 自定义表单数据
 
-const formTable = ref([
-  {name: '联系人', mobile: '13311112222', email: '123@qq.com', responsible: '张三', createId: '张三'}
-]) // 表格数据
+const formTable = ref([]) // 表格数据
 const allLoading = reactive({ // 按钮加载 Loading
   formTableLading: false,
 })
@@ -104,6 +112,22 @@ function toDetali(row: any) {
   })
 }
 
+function getContactPerson() {
+  allLoading.formTableLading = true
+  const formVal = getFromValue(filterForm)
+  post(URL_PAGECONTACTS, { ...formVal, ...formTablePaging }).then((res) => {
+    const { records, total } =  res.data
+    formTable.value = records
+    tableTotal.value = total
+  }).finally(() => {
+    allLoading.formTableLading = false
+  })
+}
+
+function resetForm() {
+  console.log('重置联系人');
+}
+
 async function getSystemField() {
   const systemField = getAllListByCode([])
   for (let i in systemField) {
@@ -143,6 +167,7 @@ function setFilterItems() {
 
 onMounted(() => {
   getSystemField()
+  getContactPerson()
 })
 </script>
 

+ 3 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/constant.ts

@@ -1,8 +1,10 @@
 export const MOD = '/thread'
+export const IMPORMOD = 'Thread'
 export const prefix = '/clue'
 export const GETSYSFILED = '/sys-dict/getListByCode'
 export const GETPERSONNEL = '/user/getSimpleActiveUserList'
 export const GETTEMPLATE = `/sys-form/getListByCode${MOD}`
+export const GETTEMPLATETWO = `/sys-form/getListByCode/business`
 export const GETTABLE = `${prefix}/listClue`
 export const GETDETAIL = `${prefix}/getDetail`
 export const UNDATECLAIM = `${prefix}/claim`
@@ -14,4 +16,4 @@ export const REFIENAME = `${prefix}/reFileName`
 export const UPLOADFILE = `${prefix}/uploadFile`
 export const DEYELWCLUE = `${prefix}/listDeleterClue`
 export const DETERDETELE = `${prefix}/deleterDelete`
-export const ROLLBACK = `${prefix}/rollback`
+export const ROLLBACK = `${prefix}/rollback`

+ 80 - 7
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/information.vue

@@ -3,7 +3,7 @@
         <div class="flex justify-between">
             <div class="title">基本信息</div>
             <div>
-                <el-button type="primary">转为商机</el-button>
+                <el-button type="primary" @click="showVisible('newBusinessisible')">转为商机</el-button>
                 <el-button type="primary" @click="claimClues()" v-if="!information.inchargerName">认领</el-button>
                 <el-button type="primary" @click="showVisible('clueDialogVisible')" v-else>转移</el-button>
                 <el-button type="primary" @click="editClue(information)">编辑</el-button>
@@ -104,16 +104,39 @@
                 <div class="pl-3 text-[#e94a4a]">转移后,将看不到此线索</div>
             </div>
         </el-dialog>
+
+        <!-- 转成商机 -->
+        <el-dialog v-model="dialogVisible.newBusinessisible" width="1000" :show-close="false" top="10vh">
+            <template #header="{ close, titleId, titleClass }">
+                <div class="flex justify-between items-center border-b pb-3 dialog-header">
+                    <h4 :id="titleId">{{ allText.businessisText }}</h4>
+                    <div>
+                        <el-button type="primary" @click="transferBusiness()"
+                            :loading="allLoading.businessSaveLading">转为商机</el-button>
+                        <el-button @click="closeVisible('newBusinessisible')">取消</el-button>
+                    </div>
+                </div>
+            </template>
+            <div class="h-[60vh] overflow-y-auto scroll-bar pt-3">
+                <GenerateForm ref="generateFormDataRef" :data="generateFormData" :value="generateFormVal" />
+                <div>相关产品</div>
+                <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList" />
+            </div>
+        </el-dialog>
     </div>
 </template>
 <script lang="ts" setup>
 import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
 import { GenerateForm } from '@zmjs/form-design';
-import { formatDate, confirmAction } from '@/utils/tools'
-import { GETTEMPLATE, UNDATEFORM, GETPERSONNEL, UNDATECLAIM } from '../../constant'
+import { formatDate, confirmAction, backPath } from '@/utils/tools'
+import { GETTEMPLATE, UNDATEFORM, GETPERSONNEL, UNDATECLAIM, GETTEMPLATETWO } from '../../constant'
 import { get, post } from '@/utils/request'
 import { useStore } from '@/store/index'
 import { ElMessageBox } from 'element-plus';
+import RelatedProducts from '@/pages/business/component/relatedProducts.vue'
+import { all } from 'axios';
+import { formatDateTime } from '@/utils/times';
+import { UPDATEINSET } from '@/pages/business/api';
 
 interface personnelInterface {
     id: string | number,
@@ -134,25 +157,59 @@ const generateFormKey = ref(1)
 const allLoading = reactive({
     generateFormLading: false,
     saveBtnLoading: false,
-    clueLoading: false
+    clueLoading: false,
+    businessSaveLading: false,
 })
 const dialogVisible = reactive({
     editClueDialogVisible: false,
-    clueDialogVisible: false
+    clueDialogVisible: false,
+    newBusinessisible: false
 })
 const allText = reactive({
     editClueText: '新建线索',
-    clueText: '认领线索'
+    clueText: '认领线索',
+    businessisText: '转为商机'
 })
 const generateForm: any = ref(null) // 模板
 const clueTemplate = ref({
     list: [],
     config: {}
 }) // 线索模板
+const generateFormData = ref({ // 商机模板
+    config: {},
+    list: []
+})
+const generateFormDataRef = ref<typeof GenerateForm>() // 商机模板dom
+const relatedProductsRef = ref<typeof RelatedProducts>()
+const productTableList = ref([])
+const generateFormVal = ref<any>({})
 const editForm = ref({}) // 编辑表单
 const transferValue = ref('') // 转移/认领 id
 const transferOptions = ref<personnelInterface[]>([]) // 转移人员列表
 
+// 转为商机
+function transferBusiness() {
+    generateFormDataRef.value?.getData().then((res: any) => {
+        let productTableListData = relatedProductsRef?.value?.returnData()
+        let newForm = {
+            ...res,
+            expectedTransactionDate: res.expectedTransactionDate ? formatDateTime(new Date(res.expectedTransactionDate)) : '',
+            businessItemProductList: productTableListData ? JSON.stringify(productTableListData) : []
+        }
+        allLoading.businessSaveLading = true
+        post(UPDATEINSET, { ...newForm }).then((_res) => {
+            dialogVisible.newBusinessisible = false
+            globalPopup?.showSuccess('操作成功')
+            backPath()
+        }).finally(() => {
+            allLoading.businessSaveLading = false
+        })
+    }).catch((_err: any) => {
+        console.log(_err)
+        globalPopup?.showError('请填写完整')
+    })
+}
+
 // 转移/认领 线索
 function transferClues() {
     const ids = information.value?.id
@@ -229,8 +286,21 @@ function receiveAssignment(item: any) {
 function showVisible(filed: keyof typeof dialogVisible) {
     if (filed == 'clueDialogVisible') {
         transferValue.value = ''
+        allText.clueText = '转移线索'
+    } else {
+        allText.clueText = '认领线索'
     }
-    dialogVisible[filed] = true
+
+    if (filed == 'newBusinessisible') {
+        generateFormVal.value.name = information.value.clueName
+    }
+    setTimeout(() => {
+        dialogVisible[filed] = true
+    }, 100)
+}
+
+function closeVisible(filed: keyof typeof dialogVisible) {
+    dialogVisible[filed] = false
 }
 
 async function getSystemField() {
@@ -252,6 +322,9 @@ onMounted(async () => {
     receiveAssignment(props)
     const res = await get(GETTEMPLATE)
     clueTemplate.value = JSON.parse(res.data[0].config)
+    const data = await get(GETTEMPLATETWO)
+    let newData = JSON.parse(data.data[0].config)
+    generateFormData.value = newData
     getSystemField()
 });
 </script>

+ 85 - 51
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/relatedTasks.vue

@@ -3,73 +3,107 @@
         <div class="flex justify-between">
             <div class="title">相关任务</div>
             <div>
-                <el-button type="primary">新建任务</el-button>
+                <el-button type="primary" @click="newTask()">新建任务</el-button>
             </div>
         </div>
         <div class="flex-1 overflow-auto pt-3">
             <el-table :data="relatedTaskstable" border style="width: 100%;height: 100%;">
                 <el-table-column prop="taskName" label="任务名称">
                     <template #default="scope">
-                        <el-button link type="primary" size="large">{{
+                        <el-button link type="primary" size="large" @click="toTask()">{{
                             scope.row.taskName
                         }}</el-button>
                     </template>
                 </el-table-column>
-                <el-table-column prop="priority" label="优先级" width="130" />
-                <el-table-column prop="status" label="状态" width="130" />
-                <el-table-column prop="executor" label="执行人" width="130" />
-                <el-table-column prop="startTime" label="开始时间" width="130" />
-                <el-table-column prop="endTime" label="截至时间" width="130" />
+                <el-table-column prop="priorityStr" label="优先级" width="130" />
+                <el-table-column prop="statusStr" label="状态" width="130" />
+                <el-table-column prop="executorNamesStr" label="执行人" width="130" />
+                <el-table-column prop="startTimes" label="开始时间" width="130" />
+                <el-table-column prop="endTimes" label="截至时间" width="130" />
             </el-table>
         </div>
     </div>
+
+    <!-- 新建任务 -->
+    <TaskModal :visible="allVisible.taskModalVisible" :edit-form="taskModalForm" :save-loading="taskLoading"
+        @close="closeTaskModal" @submit="submitForm" :title="'新建任务'" :disabled-list="['taskType', 'clueId']" />
 </template>
 <script lang="ts" setup>
-import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+import { PRIORITY } from '@/components/TaskModal/api';
+import { STATUS } from '@/pages/tasks/api';
+import { formatDate } from '@/utils/times';
+import { createTaskFromType } from '@/utils/tools';
+import { ref, reactive, onMounted, watchEffect, inject } from 'vue'
+import { useRouter } from "vue-router";
+import TaskModal from '@/components/TaskModal/index.vue'
+import { createTask } from '@/components/TaskModal/taskFunction';
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const emits = defineEmits(['refreshData']);
+
+const props = defineProps<{
+    data: any,
+    information: any
+}>()
+
+const relatedTaskstable = ref([])
+const information = ref<any>({})
+const router = useRouter()
+const taskLoading = ref<saveLoadingType>('1')
+const taskModalForm = ref({}) // 任务弹窗表单
+const allVisible = reactive({
+    taskModalVisible: false
+})
+
+function submitForm(submitData: any, isClose: boolean) { // 任务提交
+    taskLoading.value = '2'
+    createTask(submitData, isClose).then((res) => {
+        const { saveLoading, isClose } = res
+        taskLoading.value = saveLoading
+        allVisible.taskModalVisible = isClose
+        globalPopup?.showSuccess('新增成功')
+        emits('refreshData')
+    }).catch((err) => {
+        const { saveLoading, isClose, message } = err
+        taskLoading.value = saveLoading
+        allVisible.taskModalVisible = isClose
+        globalPopup?.showError(message)
+    })
+}
+
+function newTask() {
+    const { id } = information.value
+    taskModalForm.value = { ...createTaskFromType(3), clueId: id, }
+    allVisible.taskModalVisible = true
+}
+
+function toTask() {
+    router.push({
+        path: `/tasks`
+    })
+}
+
+function closeTaskModal() {
+    allVisible.taskModalVisible = false
+}
+
+// 接收参数赋值
+function receiveAssignment(item: any) {
+    information.value = item.information
+    const dataVal = item.data
+    for (let i in dataVal) {
+        dataVal[i].executorNamesStr = (dataVal[i].executorNames || []).join(','),
+            dataVal[i].startTimes = dataVal[i].startDate ? formatDate(new Date(dataVal[i].startDate)) : '',
+            dataVal[i].endTimes = dataVal[i].endDate ? formatDate(new Date(dataVal[i].endDate)) : '',
+            dataVal[i].priorityStr = PRIORITY.find(item => item.value == dataVal[i].priority)?.label || '',
+            dataVal[i].statusStr = STATUS.find(item => item.value == dataVal[i].status)?.label || ''
+    }
+    relatedTaskstable.value = dataVal
+}
+
+watchEffect(() => {
+    receiveAssignment(props)
+});
 
-const relatedTaskstable = ref([{
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}])
 // 生命周期钩子
 onMounted(() => {
 });

+ 2 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/index.vue

@@ -10,7 +10,7 @@
     </div>
     <div class="layout pl-3 pr-3 pb-3">
       <div class="bg-white w-2/3 shadow-md rounded-md">
-        <RelatedTasks :data="relatedTasks" :information="information" @refreshData="refreshData"></RelatedTasks>
+        <DetailCompinents :data="relatedTasks" :information="information" :formTaskType="3" :disabledList="['taskType', 'clueId']" :filed="'clueId'" @refreshData="refreshData"></DetailCompinents>
       </div>
       <div class="bg-white w-1/3 ml-3 shadow-md rounded-md">
         <OperationRecord :data="operationRecord" :information="information" @refreshData="refreshData"></OperationRecord>
@@ -22,7 +22,7 @@
 <script lang="ts" setup>
 import Information from './components/information.vue'
 import Attachment from './components/attachment.vue'
-import RelatedTasks from './components/relatedTasks.vue';
+import DetailCompinents from '@/components/detailcompinents/relatedTasks.vue'
 import OperationRecord from './components/operationRecord.vue';
 import { ref, reactive, onMounted, inject } from "vue";
 import { post, get } from "@/utils/request";

+ 63 - 10
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/index.vue

@@ -56,7 +56,7 @@
           <el-button type="primary" @click="batchTransfer()">批量转移</el-button>
           <el-button type="primary" @click="batchDeletes()">批量删除</el-button>
           <el-button type="primary" @click="showDeteleClue(true)">回收站</el-button>
-          <el-button type="primary">导入</el-button>
+          <el-button type="primary" @click="dialogVisible.importVisible = true">导入</el-button>
           <el-button type="primary">导出</el-button>
         </div>
         <div class="flex-1 w-full overflow-hidden">
@@ -83,8 +83,7 @@
                 <el-button link type="primary" size="large" @click="editClue(scope.row)">编辑</el-button>
                 <!-- <el-button link type="primary" size="large"
                   @click="dialogVisible.taskModalVisible = true">新建任务</el-button> -->
-                <el-button link type="primary" size="large"
-                  @click="newTask(scope.row)">新建任务</el-button>
+                <el-button link type="primary" size="large" @click="newTask(scope.row)">新建任务</el-button>
                 <el-button link type="danger" size="large" @click.prevent="deleteRow(scope.row)">删除</el-button>
               </template>
             </el-table-column>
@@ -140,21 +139,45 @@
 
     <DeteleTables :visibles="dialogVisible.deteleClueDialogVisible" @showDeteleClue="showDeteleClue" />
 
-    <TaskModal :visible="dialogVisible.taskModalVisible" :edit-form="taskModalForm" :save-loading="'1'"
+    <TaskModal :visible="dialogVisible.taskModalVisible" :edit-form="taskModalForm" :save-loading="taskLoading"
       @close="closeTaskModal" @submit="submitForm" :title="'新建任务'" :disabled-list="['taskType', 'clueId']" />
+
+    <!-- 导入线索 -->
+    <el-dialog v-model="dialogVisible.importVisible" width="680" :show-close="false" top="10vh">
+      <template #header="{ close, titleId, titleClass }">
+        <div class="flex justify-between items-center border-b pb-3 dialog-header">
+          <h4 :id="titleId">导入线索</h4>
+          <div class="flex">
+            <el-upload class="upload-demo mr-3" :limit="1" :show-file-list="false" accept=".xlsx"
+              :http-request="importProducts">
+              <el-button type="primary" :loading="allLoading.importLoading">导入</el-button>
+            </el-upload>
+            <el-button @click="dialogVisible.importVisible = false">取消</el-button>
+          </div>
+        </div>
+      </template>
+      <div class="p-8">
+        <div class="ml-4 mr-4">
+          <div class="flex items-center">1、点击下载 <el-link type="primary"
+              @click="downloadTemplate(IMPORMOD, '线索导入模板.xlsx')">线索导入模板.xlsx</el-link></div>
+          <div class="mt-4">2、填写excel文件、线索名称、线索来源必填</div>
+        </div>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script lang="ts" setup>
 import { ref, reactive, onMounted, inject } from "vue";
-import { GETSYSFILED, MOD, GETPERSONNEL, GETTABLE, GETTEMPLATE, GETDETAIL, UNDATECLAIM, UNDATEFORM, DELTEROW } from './constant'
-import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate, createTaskFromType, confirmAction } from '@/utils/tools'
-import { FormInstance, FormRules, ElMessageBox, ElTable } from 'element-plus'
-import { post, get } from "@/utils/request";
+import { GETSYSFILED, MOD, IMPORMOD, GETPERSONNEL, GETTABLE, GETTEMPLATE, GETDETAIL, UNDATECLAIM, UNDATEFORM, DELTEROW } from './constant'
+import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate, createTaskFromType, confirmAction, downloadTemplate } from '@/utils/tools'
+import { FormInstance, FormRules, ElMessageBox, ElTable, UploadRequestOptions } from 'element-plus'
+import { post, get, uploadFile } from "@/utils/request";
 import { useRouter, useRoute } from "vue-router";
 import { GenerateForm } from '@zmjs/form-design';
 import TaskModal from '@/components/TaskModal/index.vue'
 import DeteleTables from "./deteleTables.vue";
+import { createTask } from "@/components/TaskModal/taskFunction";
 
 // 定义类型
 interface fixedDataInterface {
@@ -190,16 +213,19 @@ const filterCriteriaForm = reactive<filterCriteriaFormType>({ // 筛选条件for
   pageFrom: 10
 })
 const generateFormKey = ref(1)
+const taskLoading = ref<saveLoadingType>('1')
 const allLoading = reactive({
   clueTableLading: false,
   generateFormLading: false,
   clueLoading: false,
+  importLoading: false,
 })
 const dialogVisible = reactive({
   editClueDialogVisible: false,
   taskModalVisible: false,
   clueDialogVisible: false,
-  deteleClueDialogVisible: false
+  deteleClueDialogVisible: false,
+  importVisible: false
 })
 const allText = reactive({
   editClueText: '新建线索',
@@ -272,7 +298,18 @@ function closeTaskModal() {
 }
 
 function submitForm(submitData: any, isClose: boolean) {
-  console.log(submitData, isClose)
+  taskLoading.value = '2'
+  createTask(submitData, isClose).then((res) => {
+    const { saveLoading, isClose } = res
+    taskLoading.value = saveLoading
+    dialogVisible.taskModalVisible = isClose
+    globalPopup?.showSuccess('新增成功')
+  }).catch((err) => {
+    const { saveLoading, isClose, message } = err
+    taskLoading.value = saveLoading
+    dialogVisible.taskModalVisible = isClose
+    globalPopup?.showError(message)
+  })
 }
 
 function editClue(item: any) {
@@ -327,6 +364,22 @@ function deleteRow(row: any) {
   })
 }
 
+async function importProducts(param: UploadRequestOptions) {
+  allLoading.importLoading = true
+  const formData = new FormData();
+  formData.append('multipartFile', param.file)
+  const res = await uploadFile('导入接口', formData).finally(() => {
+    allLoading.importLoading = false
+  })
+  allLoading.importLoading = false
+  if (res.code == 'ok') {
+    globalPopup?.showSuccess('导入成功' || '')
+    
+    return
+  }
+  globalPopup?.showError(res.msg || '')
+}
+
 function batchTransfer() {
   const data = clueTableRef.value && clueTableRef.value.getSelectionRows()
   if (!data.length) {

+ 5 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/type.d.ts

@@ -28,4 +28,8 @@ type REPEAT_VALUE_TYPE = 0 | 1 | 2 | 3 | 4; //0是每天, 1是每周, 2是每月
 
 type componentType = "success" | "info" | "warning" | "error"
 
-type TaskResponse = { saveLoading: saveLoadingType, isClose: boolean, message?: string }
+type TaskResponse = { saveLoading: saveLoadingType, isClose: boolean, message?: string }
+
+type optionType = { value: number | string, label: string | number }
+
+type sexTYpe = { value: number | string, label: string }

+ 1 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/BusinessOpportunityMapper.java

@@ -33,5 +33,5 @@ public interface BusinessOpportunityMapper extends BaseMapper<BusinessOpportunit
 
     Map<String, Object> getDataSummary(Integer companyId, String startDate, String endDate, String userId,@Param("list") List<String> targetUserIds);
 
-    Map<String, Object> getDataStage(Integer companyId, String startDate, String endDate, String userId,@Param("list") List<String> targetUserIds);
+    List<Map<String, Object>> getDataStage(Integer companyId, String startDate, String endDate, String userId,@Param("list") List<String> targetUserIds);
 }

+ 1 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/BusinessOpportunityService.java

@@ -57,7 +57,7 @@ public interface BusinessOpportunityService extends IService<BusinessOpportunity
 
     Map<String, Object> getDataSummary(Integer companyId, String startDate, String endDate, String userId,List<String> targetUserIds);
 
-    Map<String, Object> getDataStage(Integer companyId, String startDate, String endDate, String userId,List<String> targetUserIds);
+    List<Map<String, Object>> getDataStage(Integer companyId, String startDate, String endDate, String userId,List<String> targetUserIds);
 
     void deleterDelete(List<Integer> ids);
 }

+ 1 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/BusinessOpportunityServiceImpl.java

@@ -371,7 +371,7 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
     }
 
     @Override
-    public Map<String, Object> getDataStage(Integer companyId, String startDate, String endDate, String userId, List<String> targetUserIds) {
+    public List<Map<String, Object>> getDataStage(Integer companyId, String startDate, String endDate, String userId, List<String> targetUserIds) {
         return bOMapper.getDataStage(companyId,startDate,endDate,userId,targetUserIds);
     }
 

+ 17 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SalesOrderServiceImpl.java

@@ -566,13 +566,21 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
         int customCount = customService.count(customLambdaQueryWrapper);
         int contactsCount = contactsService.count(contactsLambdaQueryWrapper);
         int businessOpportunityCount = businessOpportunityService.count(businessOpportunityLambdaQueryWrapper);
+        List<BusinessOpportunity> businessOpportunityList = businessOpportunityService.list(businessOpportunityLambdaQueryWrapper);
+        double businessOpportunityPrice = businessOpportunityList.stream().filter(b->!StringUtils.isEmpty(b.getAmountOfMoney())).mapToDouble(b ->Double.valueOf(b.getAmountOfMoney())).sum();
         Integer salesOrderCount = salesOrderMapper.selectCount(salesOrderLambdaQueryWrapper);
+        List<SalesOrder> salesOrders = salesOrderMapper.selectList(salesOrderLambdaQueryWrapper);
+        double salesOrdersPrice = salesOrders.stream().mapToDouble(s -> s.getPrice().doubleValue()).sum();
         Integer clueCount = clueMapper.selectCount(clueLambdaQueryWrapper);
 
         int customCount1 = customService.count(customLambdaQueryWrapper1);
         int contactsCount1 = contactsService.count(contactsLambdaQueryWrapper1);
         int businessOpportunityCount1 = businessOpportunityService.count(businessOpportunityLambdaQueryWrapper1);
+        List<BusinessOpportunity> businessOpportunityList1 = businessOpportunityService.list(businessOpportunityLambdaQueryWrapper1);
+        double businessOpportunityPrice1 = businessOpportunityList1.stream().filter(b->!StringUtils.isEmpty(b.getAmountOfMoney())).mapToDouble(b -> Double.valueOf(b.getAmountOfMoney())).sum();
         Integer salesOrderCount1 = salesOrderMapper.selectCount(salesOrderLambdaQueryWrapper1);
+        List<SalesOrder> salesOrders1 = salesOrderMapper.selectList(salesOrderLambdaQueryWrapper1);
+        double salesOrdersPrice1 = salesOrders1.stream().mapToDouble(s -> s.getPrice().doubleValue()).sum();
         Integer clueCount1 = clueMapper.selectCount(clueLambdaQueryWrapper1);
         Map<String,Object> customMap=new HashMap<>();
         customMap.put("customCount",customCount);
@@ -586,10 +594,18 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
         businessOpportunityMap.put("businessOpportunityCount",businessOpportunityCount);
         businessOpportunityMap.put("businessOpportunityPromote",getPromote(businessOpportunityCount,businessOpportunityCount1));
         resultMap.put("businessOpportunity",businessOpportunityMap);
+        Map<String,Object> businessOpportunityPriceMap=new HashMap<>();
+        businessOpportunityPriceMap.put("businessOpportunityPrice",businessOpportunityPrice);
+        businessOpportunityPriceMap.put("businessOpportunityPromote",getPromote((int)businessOpportunityPrice,(int)businessOpportunityPrice1));
+        resultMap.put("businessOpportunityPrice",businessOpportunityPriceMap);
         Map<String,Object> salesOrderMap=new HashMap<>();
         salesOrderMap.put("salesOrderCount",salesOrderCount);
         salesOrderMap.put("salesOrderPromote",getPromote(salesOrderCount,salesOrderCount1));
         resultMap.put("salesOrder",salesOrderMap);
+        Map<String,Object> salesOrderPriceMap=new HashMap<>();
+        salesOrderPriceMap.put("salesOrdersPrice",salesOrdersPrice);
+        salesOrderPriceMap.put("salesOrderPricePromote",getPromote((int)salesOrdersPrice,(int)salesOrdersPrice1));
+        resultMap.put("salesOrdersPrice",salesOrderPriceMap);
         Map<String,Object> clueMap=new HashMap<>();
         clueMap.put("clueCount",clueCount);
         clueMap.put("cluePromote",getPromote(clueCount,clueCount1));
@@ -747,7 +763,7 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
                     targetUserIds=targetUserIds2;
             }
         }
-        Map<String,Object> dataMap=businessOpportunityService.getDataStage(companyId,startDate,endDate,userId,targetUserIds);
+        List<Map<String,Object>> dataMap=businessOpportunityService.getDataStage(companyId,startDate,endDate,userId,targetUserIds);
         Map<String,Object> resultMap=new HashMap<>();
         resultMap.put("dataMap",dataMap);
         msg.setData(resultMap);

+ 1 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SysFormServiceImpl.java

@@ -78,7 +78,7 @@ public class SysFormServiceImpl extends ServiceImpl<SysFormMapper, SysForm> impl
         allList.add(heads);
         String title;
         switch (code){
-            case "Clue":title = company.getCompanyName()+"_线索导入模板";
+            case "Thread":title = company.getCompanyName()+"_线索导入模板";
             break;
             case "Custom":title = company.getCompanyName()+"_客户导入模板";
                 break;

+ 1 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java

@@ -1193,6 +1193,7 @@ public class ProjectController {
 
     //todo 同步项目相关数据
     @RequestMapping("/synchronizationProject")
+    @LimitRequest(count = 1,time = 1000)
     public HttpRespMsg synchronizationProject(@RequestBody String dataJson){
         return projectService.synchronizationProject(dataJson);
     }

+ 16 - 9
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java

@@ -245,6 +245,8 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
     private CompanyDingdingMapper companyDingdingMapper;
     @Resource
     private CompanyDingdingService companyDingdingService;
+    @Resource
+    private TaskExecutorService taskExecutorService;
 
     @Value(value = "${upload.path}")
     private String path;
@@ -13794,18 +13796,21 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
             resultList=projectMapper.projectExpendProcessList(startDate,endDate,projectId,categoryId,userId,companyId,null,start,size);
             total=projectMapper.projectExpendProcessListCount(startDate,endDate,projectId,categoryId,userId,companyId,null);
         }
+        List<Integer> projectList = resultList.stream().map(i -> (Integer) i.get("projectId")).distinct().collect(Collectors.toList());
+        projectList.add(-1);
+        List<TaskExecutor> taskExecutorList = taskExecutorService.list(new LambdaQueryWrapper<TaskExecutor>().in(TaskExecutor::getProjectId, projectList));
+        List<String> executorIds = taskExecutorList.stream().map(TaskExecutor::getExecutorId).distinct().collect(Collectors.toList());
+        executorIds.add("-1");
+        List<Report> reportList = reportMapper.selectList(new LambdaQueryWrapper<Report>().eq(Report::getCompanyId,companyId).eq(Report::getState,1).in(Report::getProjectId, projectList).in(Report::getCreatorId, executorIds).between(Report::getCreateDate, startDate, endDate));
         for (Map<String, Object> map : resultList) {
-            if(StringUtils.isEmpty(String.valueOf(map.get("executorString")))){
-                continue;
-            }
-            String executorString = String.valueOf(map.get("executorString"));
-            List<String> list = ListUtil.convertLongIdsArrayToList(executorString);
             List<Map<String,Object>> itemList=new ArrayList<>();
-            for (String str : list) {
-                String[] split = str.split("\\|");
+            List<TaskExecutor> executors = taskExecutorList.stream().filter(t -> t.getProjectId().equals((Integer) map.get("projectId"))).collect(Collectors.toList());
+            for (TaskExecutor taskExecutor : executors) {
+                List<Report> reports = reportList.stream().filter(r -> r.getCreatorId().equals(taskExecutor.getExecutorId())&&r.getProjectId().equals((Integer)map.get("projectId"))).collect(Collectors.toList());
+                double sum = reports.stream().mapToDouble(Report::getWorkingTime).sum();
                 Map<String,Object> item=new HashMap<>();
-                item.put("userName",split[0]);
-                item.put("progress",split[1]);
+                item.put("userName",taskExecutor.getExecutorName());
+                item.put("progress",sum);
                 itemList.add(item);
             }
             Map<String, List<Map<String, Object>>> listMapGroup = itemList.stream().collect(Collectors.groupingBy(i -> String.valueOf(i.get("userName"))));
@@ -13823,8 +13828,10 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
         }
         //处理项目下的负责组长
         List<Integer> needProjectIds = resultList.stream().map(m -> Integer.valueOf(String.valueOf(m.get("projectId")))).collect(Collectors.toList());
+        needProjectIds.add(-1);
         List<Participation> participationList = participationMapper.selectList(new LambdaQueryWrapper<Participation>().in(Participation::getProjectId, needProjectIds));
         List<String> needUserIds = participationList.stream().map(m -> String.valueOf(m.getUserId())).collect(Collectors.toList());
+        needUserIds.add("-1");
         List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().in(User::getId, needUserIds));
         resultList.forEach(r->{
             List<Participation> targetParticipationList = participationList.stream().filter(p -> p.getProjectId().equals(Integer.valueOf(String.valueOf(r.get("projectId"))))).collect(Collectors.toList());

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -5582,11 +5582,11 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 if(needCorpWxTranslate){
                     item.add("$userName="+(map.get("corpwxUserId")==null?"":map.get("corpwxUserId"))+"$");
                     item.add(departmentService.exportWxDepartment(dept,departments));
-                    item.add(manager.isPresent()?manager.get().getName():"");
+                    item.add(manager.isPresent()?("$userName="+manager.get().getName()+"$"):"");
                 }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
                     item.add("$userName="+(map.get("name")==null?"":map.get("name"))+"$");
                     item.add(departmentService.exportDdDepartment(dept,departments));
-                    item.add(manager.isPresent()?manager.get().getName():"");
+                    item.add(manager.isPresent()?("$userName="+manager.get().getName()+"$"):"");
                 }else  {
                     item.add((String) map.get("name"));
                     item.add(departmentService.getSupDepartment(dept,departments));

+ 1 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml

@@ -1918,8 +1918,7 @@
     <select id="projectExpendProcessList" resultType="java.util.Map">
         select p.id AS projectId,p.project_name as projectName,DATE_FORMAT(p.`plan_start_date`,'%Y-%m-%d') AS planStartDate,DATE_FORMAT(p.`plan_end_date`,'%Y-%m-%d') AS planEndDate,pc.name as categoryName,p.project_code as projectCode,IFNULL(SUM(te.plan_hours),0) as planHour,
         IFNULL((select SUM(working_time) from report where project_id=p.id and create_date BETWEEN #{startDate} AND #{endDate} and state=1),0) as realHour, IFNULL((select SUM(cost) from report where project_id=p.id and create_date BETWEEN #{startDate} AND #{endDate} and state=1),0) as realCost,
-        IFNULL(IFNULL(SUM(te.plan_hours),0)-IFNULL((select SUM(working_time) from report where project_id=p.id and create_date BETWEEN #{startDate} AND #{endDate} and state=1),0),0) as residueHour,
-        GROUP_CONCAT(CONCAT_WS('|',te.executor_name,IFNULL((select SUM(working_time) from report where creator_id=te.executor_id and project_id=p.id and create_date BETWEEN #{startDate} AND #{endDate} and state=1),0))) as executorString
+        IFNULL(IFNULL(SUM(te.plan_hours),0)-IFNULL((select SUM(working_time) from report where project_id=p.id and create_date BETWEEN #{startDate} AND #{endDate} and state=1),0),0) as residueHour
         from task_executor te
         left join  user u on u.id=te.executor_id
         left join department d on u.department_id=d.department_id

+ 3 - 2
fhKeeper/formulahousekeeper/timesheet/src/components/vueMultipleDept.vue

@@ -4,8 +4,9 @@
             <div @click.stop="showVisible(true)">
                 <div class="bionicClassText" v-if="selectedDept.length <= 0">全部部门</div>
                 <div v-else>
-                    <el-tag @click.stop="" type="info" size="small" closable @close="deteleItem(0)">{{ selectedDeptLabel[0]
-                    }}</el-tag>
+                    <el-tag @click.stop="" type="info" size="small" closable @close="deteleItem(0)">
+                        <TranslationOpenDataText type='departmentName' :openid='selectedDeptLabel[0]'></TranslationOpenDataText>
+                    </el-tag>
                     <el-tag @click.stop="" type="info" size="small" v-if="selectedDeptLabel.length > 1">+ {{
                         selectedDeptLabel.length - 1 }}</el-tag>
                 </div>

+ 2 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue

@@ -3628,6 +3628,7 @@ export default {
         this.getConsumptionSchedule()
       }
       if(this.ins == 25) {
+        this.page=1
         this.getConsumptionScheduleTwo()
       }
       if(this.ins == 26) {
@@ -3962,7 +3963,7 @@ export default {
       let dataList = data || []
       dataList.push({id: 0, name: '未分类'})
       this.projectSortList = dataList
-      this.projectSortListTwo = dataList.filter((item) => item.name == '报价项目' || item.name == '售后工程项目' || item.name == '售后报价项目'||item.name == '工程项目')
+      this.projectSortListTwo = dataList.filter((item) => item.name != '报价项目' &&  item.name != '售后报价项目' && item.name != '研发项目' && item.name != '未分类')
       this.projectSortId = dataList[0].id
       this.projectSortName = dataList[0].name
     },