Sanakey
6 天以前 cb165187ddcf5d9cfd8aad97a2868d0343b14bd9
feat:完善客户Drawer详情
13个文件已修改
1018 ■■■■■ 已修改文件
mock/clues/clues-table.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/NewSchedule/src/scheduleFormData.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Upload/src/BasicUpload.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/clues/components/Drawer.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/clues/components/drawer/Dynamic.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/clues/components/drawer/Schedule.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/clues/components/drawer/index.vue 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customer/components/Drawer.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customer/components/drawer/Business.vue 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customer/components/drawer/Document.vue 233 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customer/components/drawer/Dynamic.vue 257 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customer/components/drawer/Tips.vue 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customer/components/drawer/index.vue 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mock/clues/clues-table.ts
@@ -15,6 +15,7 @@
  for (let index = 0; index < 200; index++) {
    result.push({
      id: `${index}`,
      color: Random.color(),
      cluesName: '@ctitle()',
      formId: `@integer(100000, 999999)`,
      archiveTime: '@datetime',
src/components/NewSchedule/src/scheduleFormData.tsx
@@ -102,7 +102,7 @@
              colors.map((item) => {
                return <RadioButton class="mr-5px" value={item.type_id} onChange={onChange}
                                    style={{backgroundColor: item.color,borderColor:item.color}}>
                  <span class={ model[field] === item.type_id ?'':'c-white select-none op0'}>✓</span>
                  <span class={'c-white' + (model[field] === item.type_id ?'':' select-none op0')}>✓</span>
                </RadioButton>;
              })
            }
src/components/Upload/src/BasicUpload.vue
@@ -162,4 +162,9 @@
  function handlePreviewDelete(url: string) {
    emit('preview-delete', url);
  }
  // 暴露openUploadModal 供父组件使用
  defineExpose({
    openUploadModal,
  });
</script>
src/views/clues/components/Drawer.vue
@@ -1,11 +1,12 @@
<template>
  <BasicDrawer
    class="basic-drawer-1"
    v-bind="$attrs"
    title="Basic Drawer"
    @register="registerDrawer"
    :maskClosable="false"
    :keyboard="false"
    width="500px"
    width="550px"
  >
    <template #title>
      <div class="text-right">
src/views/clues/components/drawer/Dynamic.vue
@@ -8,7 +8,7 @@
        添加跟进
      </a-button>
    </div>
    <div ref="wrapEl">
    <div ref="loadingEl">
      <div class="mb-20px flex justify-between flex-items-center">
        <span class="font-bold">共 {{totalData}} 条</span>
        <TreeSelect
@@ -120,16 +120,18 @@
  }
  // 动态列表加载
  const wrapEl = ref<ElRef>(null);
  const loadingEl = ref<ElRef>(null);
  const [openWrapLoading, closeWrapLoading] = useLoading({
    target: wrapEl,
    target: loadingEl,
    props: {
      tip: '加载中...',
      absolute: true,
    },
  });
  // 动态列表数据
  getCluesDynamicData();
  setTimeout(()=>{
    getCluesDynamicData();
  },50)
  async function getCluesDynamicData(){
    openWrapLoading();
    let params = {
src/views/clues/components/drawer/Schedule.vue
@@ -92,12 +92,6 @@
  });
};
const pagination = {
  onChange: (page: number) => {
    console.log(page);
  },
  pageSize: 3,
};
const openChange = (status: boolean) => {
  Logger.log('openChange', status);
src/views/clues/components/drawer/index.vue
@@ -150,7 +150,10 @@
      background-color: @component-background;
    }
    .scroll-wrap{
      height: calc(100vh - 290px);
      height: calc(100vh - 310px);
    }
    //:deep(.scrollbar__wrap)   {
    //  padding: 10px !important;
    //}
  }
</style>
src/views/customer/components/Drawer.vue
@@ -5,7 +5,7 @@
    @register="registerDrawer"
    :maskClosable="false"
    :keyboard="false"
    width="500px"
    width="550px"
  >
    <template #title>
      <div class="text-right">
src/views/customer/components/drawer/Business.vue
@@ -1,88 +1,74 @@
<template>
  <Row class="mb-10px">
    <Col span="12">
      <Dropdown :trigger="['click']" arrow @openChange="openChange">
        <span class="cursor-pointer" @click.prevent>
          {{ dropDownTitle }}
          <BasicArrow :expand="false" down/>
        </span>
        <template #overlay>
          <Menu @click="onMenuItemClick">
            <MenuItem key="0">
              <span>未完成</span>
            </MenuItem>
            <MenuItem key="1">
              <span>已完成</span>
            </MenuItem>
          </Menu>
  <a-collapse :class="prefixCls" v-model:activeKey="activeKey" ghost>
    <a-collapse-panel key="1" header="商机">
      <a-row :gutter="[12,12]" class="pb-20px">
        <template v-for="item in cardList" :key="item.title">
          <a-col :span="8">
            <a-card :class="`${prefixCls}-card`">
              <div :class="`${prefixCls}-card-title`">
<!--                <BasicTitle  :helpMessage="item.tips"></BasicTitle>-->
                {{ item.title }}
                <span v-if="item?.tips?.length>0" ><BasicHelp :text="item.tips" placement="top" /></span>
              </div>
              <div :class="`${prefixCls}__card-detail font-bold`">
                {{ item.detail || '--' }}
              </div>
            </a-card>
          </a-col>
        </template>
      </Dropdown>
    </Col>
    <Col span="12" :style="'text-align:right'">
      <Tooltip title="新建日程">
        <PlusSquareOutlined class="cursor-pointer" style="fontSize:24px;color: #aaa"
                            @click="openScheduleModal"/>
      </Tooltip>
      </a-row>
    </Col>
  </Row>
  <BasicTable
    @register="registerTable"
    @row-click="handleScheduleRowClick"
  >
    <template #bodyCell="{ column, record }">
      <template v-if="column.key === 'cluesName'">
        <div>
          <Avatar :size="16" :style="'background-color:'+ record.color"></Avatar>
          <span class="ml-5px">
            {{ record.cluesName }}
          </span>
        </div>
      <BasicTable
        @register="registerTable"
      >
        <template #bodyCell="{ column, record, index }">
          <template v-if="column.key === 'cluesName'">
            <div>
              <a class="ml-5px ">
                {{ record.cluesName }}
              </a>
            </div>
          </template>
          <template v-if="column.key === 'status'">
            <a-tag color="green">
              {{ record.status }}
            </a-tag>
          </template>
          <template v-if="column.key === 'endTime'">
            <span class="c-red">
              {{ record.endTime }}
            </span>
          </template>
        </template>
      </BasicTable>
      <template #extra >
        <PlusCircleOutlined @click="addBusinessForm" />
      </template>
    </template>
  </BasicTable>
    </a-collapse-panel>
  </a-collapse>
  <ScheduleDetailModal @register="registerScheduleModal"></ScheduleDetailModal>
  <div>
    <template v-for="item in projectList" :key="item.title">
      <Row class="cursor-pointer" :gutter="[22,26]" align="middle">
        <Col span="12">
          <Avatar :size="16" :style="'background-color:'+ item.color"></Avatar>
          <span class="ml-5px">{{ item.title }}</span>
        </Col>
        <Col span="12" :style="'text-align:right'">
          <span>{{ item.content }}</span>
        </Col>
      </Row>
    </template>
  </div>
  <!--  <List :class="prefixCls" item-layout="horizontal" :data-source="projectList" :grid="{ column: 1, gutter: 12}" :pagination="pagination" >-->
  <!--    <template #renderItem="{ item }">-->
  <!--      <ListItem>-->
  <!--        -->
  <!--      </ListItem>-->
  <!--    </template>-->
  <!--&lt;!&ndash;    <template v-for="item in projectList" :key="item.title"></template>&ndash;&gt;-->
  <!--  </List>-->
</template>
<script lang="ts" setup>
import {List, Avatar, Card, Row, Col, Dropdown, Menu, Tooltip, Tag} from 'ant-design-vue';
import {projectList} from './data';
import {PlusSquareOutlined} from '@ant-design/icons-vue';
import {PlusSquareOutlined,FileImageFilled,PlusCircleOutlined} from '@ant-design/icons-vue';
import {BasicArrow} from "@/components/Basic";
import {ref} from 'vue';
import ScheduleDetailModal from './ScheduleDetail.vue';
const ListItem = List.Item;
const MenuItem = Menu.Item;
import { BasicHelp } from '@/components/Basic';
import EventBus from '@/utils/eventBus';
import {BasicTable, ColumnChangeParam, TableAction, useTable} from "@/components/Table";
import {cluesListApi} from "@/api/clues/table";
import {getEditCellColumns, getFormConfig} from "@/views/clues/components/tableData";
import {useModal} from "@/components/Modal";
import {Row} from "ant-design-vue";
import {applicationList} from "@/views/demo/page/account/center/data";
import Business from "@/router/routes/modules/business";
import {cluesDynamicApi} from "@/api/clues/dynamic";
const [registerScheduleModal, {openModal, setModalProps}] = useModal();
const openScheduleModal = () => {
  Logger.log('点击openScheduleModal');
@@ -92,22 +78,86 @@
  });
};
const pagination = {
  onChange: (page: number) => {
    console.log(page);
  },
  pageSize: 3,
};
const activeKey = ref(['1']);
function addBusinessForm(event) {
  Logger.log('addBusinessForm', event);
  event.stopPropagation();
}
const openChange = (status: boolean) => {
  Logger.log('openChange', status);
};
const dropDownTitle = ref('未完成');
const onMenuItemClick = (e: any) => {
  let titles = ['未完成', '已完成'];
  Logger.log('onMenuItemClick', e);
  dropDownTitle.value = titles[e.key];
};
let cardList = ref([
  {
    title: '赢单商机金额(CNY)',
    detail: '0.00',
  },
  {
    title: '赢单商机均价(CNY)',
    detail: '0.00',
  },
  {
    title: '首次赢单日期',
    tips: '所有赢单商机中最早的结束日期',
    detail: '',
  },
  {
    title: '最近赢单日期',
    tips: '所有赢单商机中最近的结束日期',
    detail: '',
  },
  {
    title: '进行中的商机数',
    tips: '「进行中」是指除赢单&输单之外的商机阶段',
    detail: '10',
  },
  {
    title: '赢单商机数',
    detail: '0',
  },
  {
    title: '赢单率',
    detail: '0%',
  },
  {
    title: '平均销售周期',
    detail: '0小时',
  },
  {
    title: '已生效回款单数',
    detail: '0',
  },
  {
    title: '已生效回款金额',
    detail: '0',
  },
]);
// 商机总览列表数据
setTimeout(() => {
  // getCustomerCardData();
}, 50);
async function getCustomerCardData(){
  let params = {
    page: 1,
    pageSize: 10,
  };
  try {
    let res = await cluesDynamicApi(params)
    Logger.log('cluesDynamicApi...',res);
    cardList.value = res.items;
  } catch (e) {
    Logger.error(e);
  } finally {
  }
}
// const pagination = {
//   onChange: (page: number) => {
//     console.log(page);
//   },
//   pageSize: 3,
// };
const [
  registerTable,
@@ -136,16 +186,48 @@
  showIndexColumn: false,
  columns: [
    {
      // title: '',
      title: '商机编号',
      // defaultHidden: true,
      dataIndex: 'id',
      width: 70
    },
    {
      title: '商机名称',
      // defaultHidden: true,
      dataIndex: 'cluesName',
      width: 200
    },
    {
      // title: '',
      dataIndex: 'archiveTime',
      width: 200,
      title: '当前阶段',
      dataIndex: 'status',
      width: 120,
    },
    {
      title: '销售金额',
      dataIndex: 'formId',
      width: 80,
    },
    {
      title: '结束日期',
      dataIndex: 'endTime',
      width: 100,
    },
    {
      title: '已生效回款金额',
      dataIndex: 'formId',
      width: 80,
    },
    {
      title: '未回款金额',
      dataIndex: 'formId',
      width: 80,
    },
    {
      title: '负责人',
      dataIndex: 'name',
      width: 80,
    },
  ],
  // defSort: {
  //   pageNo: 1,
@@ -158,7 +240,6 @@
  // showIndexColumn: false, // 是否显示序号列
  // useSearchForm: false, // 使用搜索表单
  // clickToRowSelect: false,
  // formConfig: getFormConfig(), // 搜索表单配置
  pagination: {
    // pageSize: 20,
    pageSizeOptions: ['10', '20', '50', '100'],
@@ -167,18 +248,27 @@
  },
});
function handleScheduleRowClick(e) {
  Logger.log('handleScheduleRowClick', e);
  openModal(true, {
    // record,
    // isUpdate: true,
  });
const prefixCls = 'customer-modal-business';
</script>
<style lang="less" scoped>
.customer-modal-business{
  .icon-color{
    color: #1890ff;
  }
  :deep(&-btn) .ant-space{
    display: none;
  }
  :deep(.ant-collapse-content-box), :deep(.onbus-basic-table){
    padding: 0;
  }
  :deep(&-card) .ant-card-body{
    padding: 10px;
    background-color: #f9f9f9;
  }
  :deep(&-card-title) .onbus-basic-title{
    padding: 0;
  }
}
const prefixCls = 'account-center-project';
</script>
<style lang="less" >
</style>
src/views/customer/components/drawer/Document.vue
@@ -1,21 +1,60 @@
<template>
  <a-collapse v-model:activeKey="activeKey" ghost>
  <a-collapse class="customer-modal-document" v-model:activeKey="activeKey" ghost>
    <a-collapse-panel key="1" header="文档">
      <BasicTable
        @register="registerTable"
        @row-click="handleScheduleRowClick"
      >
        <template #bodyCell="{ column, record }">
        <template #bodyCell="{ column, record, index }">
          <template v-if="column.key === 'cluesName'">
            <div>
              <a-avatar :size="16" :style="'background-color:'+ record.color"></a-avatar>
              <span class="ml-5px">
            {{ record.cluesName }}
          </span>
              <FileImageFilled class="icon-color font-size-20px" />
              <a class="ml-5px c-black" data-index="{{index}}" @click="setImgVisible(record.id)">
                {{ record.cluesName }}
              </a>
              <a-image
                :style="{ display: 'none' }"
                :preview="{
                  visible: index == curImgIndex,
                  onVisibleChange: setImgVisible,
                }"
                :src="record.avatar"
              />
            </div>
          </template>
          <template v-else-if="column.key === 'action'">
            <TableAction
              stopButtonPropagation
              :dropDownActions="[
              {
                label: '发送邮件',
                onClick: handleWriteEmail.bind(null, record),
              },
              {
                label: '下载附件',
                onClick: handleDownloadFiles.bind(null, record),
              },
              {
                label: '删除文件',
                onClick: handleDeleteFiles.bind(null, record),
              },
            ]"
            />
          </template>
        </template>
      </BasicTable>
      <template #extra >
        <PlusCircleOutlined @click="addUploadFile" />
        <BasicUpload
          ref="uploadRef"
          :maxSize="20"
          :maxNumber="10"
          @change="handleUploadFileChange"
          :api="uploadApi"
          class="customer-modal-document-btn"
          :accept="['image/*']"
        />
      </template>
    </a-collapse-panel>
  </a-collapse>
@@ -24,7 +63,7 @@
</template>
<script lang="ts" setup>
import {projectList} from './data';
import {PlusSquareOutlined} from '@ant-design/icons-vue';
import {PlusSquareOutlined,FileImageFilled,PlusCircleOutlined} from '@ant-design/icons-vue';
import {BasicArrow} from "@/components/Basic";
import {ref} from 'vue';
import ScheduleDetailModal from './ScheduleDetail.vue';
@@ -34,6 +73,9 @@
import {cluesListApi} from "@/api/clues/table";
import {getEditCellColumns, getFormConfig} from "@/views/clues/components/tableData";
import {useModal} from "@/components/Modal";
import {uploadApi} from "@/api/sys/upload";
import {BasicUpload} from "@/components/Upload";
import {useMessage} from "@/hooks/web/useMessage";
const [registerScheduleModal, {openModal, setModalProps}] = useModal();
const openScheduleModal = () => {
@@ -44,23 +86,39 @@
  });
};
// 获取子组件的引用
const uploadRef = ref();
function addUploadFile(event) {
    Logger.log('addUploadFile', event);
    event.stopPropagation();
    if (uploadRef.value) {
      Logger.log('uploadRef', uploadRef.value);
      uploadRef.value.openUploadModal();
    }
}
// const addUploadFile = (event: MouseEvent) => {
//   Logger.log('addUploadFile', event);
//   // If you don't want click extra trigger collapse, you can prevent this:
//   event.stopPropagation();
//
// };
const { createMessage } = useMessage();
function handleUploadFileChange(list: string[]) {
  createMessage.success(`已上传文件${JSON.stringify(list)}`);
}
const activeKey = ref(['1']);
const pagination = {
  onChange: (page: number) => {
    console.log(page);
  },
  pageSize: 3,
};
const openChange = (status: boolean) => {
  Logger.log('openChange', status);
};
const dropDownTitle = ref('未完成');
const onMenuItemClick = (e: any) => {
  let titles = ['未完成', '已完成'];
  Logger.log('onMenuItemClick', e);
  dropDownTitle.value = titles[e.key];
const curImgIndex = ref<boolean>(-1);
const setImgVisible = (value): void => {
  Logger.log('setImgVisible', value);
  if (value === false) {
    curImgIndex.value = -1;
  } else {
    curImgIndex.value = value;
  }
  // imgVisible.value = value;
};
const [
@@ -90,16 +148,32 @@
  showIndexColumn: false,
  columns: [
    {
      // title: '',
      title: '文件名称',
      // defaultHidden: true,
      dataIndex: 'cluesName',
      width: 200
    },
    {
      // title: '',
      title: '关联类型',
      dataIndex: 'statusId',
      width: 200,
    },
    {
      title: '文件大小',
      dataIndex: 'formId',
      width: 80,
    },
    {
      title: '添加人',
      dataIndex: 'name',
      width: 80,
    },
    {
      title: '上传日期',
      dataIndex: 'archiveTime',
      width: 200,
    },
  ],
  // defSort: {
  //   pageNo: 1,
@@ -112,27 +186,118 @@
  // showIndexColumn: false, // 是否显示序号列
  // useSearchForm: false, // 使用搜索表单
  // clickToRowSelect: false,
  // formConfig: getFormConfig(), // 搜索表单配置
  pagination: {
    // pageSize: 20,
    pageSizeOptions: ['10', '20', '50', '100'],
    defaultPageSize: 20,
    // showSizeChanger: true,
  },
  actionColumn: {
    width: 50,
    title: '操作',
    dataIndex: 'action',
    fixed: 'right',
  },
  useSearchForm: true, // 使用搜索表单
  formConfig: {
    // labelWidth: 100,
    // layout: 'horizontal',
    rowProps: {
      // justify: 'start',
      gutter: 12,
    },
    showActionButtonGroup: false,
    // showResetButton: false,
    // showSubmitButton: false,
    schemas: [
      {
        field: `field1`,
        label: ``,
        component: 'Input',
        componentProps: {
          placeholder: '搜索文档名称',
        },
        colProps: {
          span:8,
        },
      },
      {
        field: 'recurringDates',
        label: '',
        component: 'Select',
        colProps: {
          span:8,
        },
        componentProps: {
          options: [
            {
              label: '全部类型',
              value: '1',
            },
            {
              label: '邮件附件',
              value: '2',
            },
            {
              label: '手动上传',
              value: '3',
            },
            {
              label: '销售订单已导单据',
              value: '4',
            },
            {
              label: '报价单已导单据',
              value: '5',
            },
          ],
        },
        defaultValue: '1'
      },
      {
        field: '[startDate, endDate]',
        label: '',
        component: 'RangePicker',
        componentProps: {
          style: {width: '100%'},
          format: 'YYYY-MM-DD',
          placeholder: ['开始日期、时间', '结束日期、时间'],
          // showTime: {format: 'HH:mm:ss'},
        },
        colProps: {
          span:8,
        },
      },
    ],
  },
});
function handleScheduleRowClick(e) {
  Logger.log('handleScheduleRowClick', e);
  openModal(true, {
    // record,
    // isUpdate: true,
  });
function handleWriteEmail(record: Recordable) {
  Logger.log('点击了写邮件', record);
}
function handleDownloadFiles(record: Recordable) {
  Logger.log('点击了下载文件', record);
}
function handleDeleteFiles(record: Recordable) {
  Logger.log('点击了删除文件', record);
}
const prefixCls = 'account-center-project';
</script>
<style lang="less" >
<style lang="less" scoped>
.customer-modal-document{
  .icon-color{
    color: #1890ff;
  }
  :deep(&-btn) .ant-space{
    display: none;
  }
  :deep(.ant-collapse-content-box), :deep(.onbus-basic-table) {
    padding: 0;
  }
}
</style>
src/views/customer/components/drawer/Dynamic.vue
@@ -1,6 +1,60 @@
<template>
  <div :class="prefixCls" >
    <div class="mb-20px">
      <div class="mb-10px flex justify-between flex-items-center">
        <div class="relative">
          <a-dropdown :trigger="['click']">
            <a-tooltip placement="top">
              <template #title>
                <span>切换商机</span>
              </template>
              <Icon
                icon="iconamoon:swap"
                class="cursor-pointer"
                @click=""
                :size="16"
              />
            </a-tooltip>
            <template #overlay>
              <a-menu
                v-model:selectedKeys="selectedBusinessTitleKeys"
                style="width: 220px"
                @click="handleBusinessTitleClick"
                :items="businessTitleArray"
              >
              </a-menu>
            </template>
          </a-dropdown>
          <span class="font-bold ml-5px">{{ businessTitle }}</span>
        </div>
        <a-dropdown :trigger="['click']">
          <MoreOutlined class="cursor-pointer mr-10px"/>
          <template #overlay>
            <a-menu>
              <a-menu-item key="0" @click="editBusiness">
                <span >编辑商机信息</span>
              </a-menu-item>
              <a-menu-item key="1" @click="addBusiness">
                <span>新建商机</span>
              </a-menu-item>
              <a-menu-item key="2" @click="removeBusiness">删除商机</a-menu-item>
            </a-menu>
          </template>
        </a-dropdown>
      </div>
      <a-row :gutter="12">
        <a-col :span="10" class="font-size-13px">
          销售金额 <span class="c-gray-4">CNY 0.00</span>
        </a-col>
        <a-col :span="10" class="font-size-13px">
          结束日期 <span class="c-red-5">2024-10-30</span>
        </a-col>
        <a-col :span="4" class="font-size-13px">
          备注 <span class="c-gray-4">--</span>
        </a-col>
      </a-row>
    </div>
    <div class="mb-20px">
      <a-button type="primary" shape="round" block @click="openFollowUpModal">
        <template #icon>
          <PlusCircleOutlined />
@@ -8,50 +62,86 @@
        添加跟进
      </a-button>
    </div>
    <div ref="wrapEl">
      <div class="mb-20px flex justify-between flex-items-center">
        <span class="font-bold">共 {{totalData}} 条</span>
        <TreeSelect
          v-model:value="currentDynamicType"
          :dropdownStyle="{color: 'red'}"
          style="width: 120px"
          placeholder="Please select"
          :allow-clear="false"
          :bordered="false"
          tree-default-expand-all
          :tree-data="dynamicTypeTree"
          @change="handleDynamicTypeChange"
        >
        </TreeSelect>
    <div class="mb-20px">
      <ScheduleDetailModal @register="registerScheduleModal"></ScheduleDetailModal>
      <div class="mb-10px flex justify-between flex-items-center">
        <span class="font-bold">计划日程</span>
        <a-button shape="round" @click="openScheduleModal">添加日程</a-button>
      </div>
      <Timeline >
        <TimelineItem v-for="item in dynamicList" :key="item.id">
          <Row>
            <Col span="16" class="c-gray-4">
      <div>
        <template v-for="(item,index) in scheduleList" :key="item.id">
          <a-row class="cursor-pointer mb-10px" :gutter="12" @click="handleScheduleRowClick" :data-index="index">
            <a-col span="10" >
              <a-avatar class="mb-3px" :size="10" :style="'background-color:'+ item.color" ></a-avatar>
              <span class="ml-5px c-blue">{{ item.beginTime }}</span>
            </a-col>
            <a-col span="14">
              <span>{{ item.cluesName }}</span>
            </a-col>
          </a-row>
        </template>
      </div>
    </div>
    <div ref="loadingEl">
      <div class="mb-20px flex justify-between flex-items-center">
        <span class="font-bold">历史动态</span>
<!--        <a-tree-select-->
<!--          v-model:value="currentDynamicType"-->
<!--          :dropdownStyle="{color: 'red'}"-->
<!--          style="width: 120px"-->
<!--          placeholder="Please select"-->
<!--          :allow-clear="false"-->
<!--          :bordered="false"-->
<!--          tree-default-expand-all-->
<!--          :tree-data="dynamicTypeTree"-->
<!--          @change="handleDynamicTypeChange"-->
<!--        >-->
<!--        </a-tree-select>-->
      </div>
      <div class="mb-20px">
        <a-radio-group size="default" v-model:value="currentDynamicType">
          <a-radio-button value="0">全部({{totalData}})</a-radio-button>
          <a-radio-button value="1">跟进记录({{totalData}})</a-radio-button>
          <a-radio-button value="2">往来邮件({{totalData}})</a-radio-button>
          <a-radio-button value="3">聊天记录({{totalData}})</a-radio-button>
          <a-radio-button value="4">其他({{totalData}})</a-radio-button>
        </a-radio-group>
<!--        <a-button type="primary">全部({{totalData}})</a-button>-->
<!--        <a-button type="primary">跟进记录({{totalData}})</a-button>-->
<!--        <a-button type="primary">往来邮件({{totalData}})</a-button>-->
<!--        <a-button type="primary">聊天记录({{totalData}})</a-button>-->
<!--        <a-button type="primary">其他({{totalData}})</a-button>-->
      </div>
      <a-timeline >
        <a-timeline-item v-for="item in dynamicList" :key="item.id">
          <a-row>
            <a-col span="16" class="c-gray-4">
              {{item.cluesName}}
            </Col>
            <Col :offset="2" span="6" class="c-gray-4">
            </a-col>
            <a-col :offset="2" span="6" class="c-gray-4">
              {{item.privateTime}}
            </Col>
          </Row>
            </a-col>
          </a-row>
          <div>
            {{item.failStatusName}}
          </div>
          <div>
            <ImagePreviewGroup>
            <a-image-preview-group>
              <template v-for="(img,index) in item.imageList" :key="index">
                <Image :width="100" class="pr-10px" :src="img" />
                <a-image :width="100" class="pr-10px" :src="img" />
              </template>
            </ImagePreviewGroup>
            </a-image-preview-group>
          </div>
        </TimelineItem>
      </Timeline>
        </a-timeline-item>
      </a-timeline>
    </div>
<!--    show-size-changer-->
<!--    show-quick-jumper-->
<!--    :show-total="total => `共 ${total} 条数据`"-->
    <Pagination
    <a-pagination
      class="text-right"
      v-model:current="currentPage"
      v-model:page-size="pageSize"
@@ -64,24 +154,42 @@
  </div>
</template>
<script lang="ts" setup>
  import { Timeline,Row,Col,ImagePreviewGroup,Image,Pagination,TreeSelect } from 'ant-design-vue';
import {
  Timeline,
  Row,
  Col,
  ImagePreviewGroup,
  Image,
  Pagination,
  TreeSelect,
} from 'ant-design-vue';
  import Icon from '@/components/Icon/Icon.vue';
  import { applicationList } from './data';
  import {cluesDynamicApi} from "@/api/clues/dynamic";
  import {ref,unref,getCurrentInstance} from "vue";
  import {useLoading} from "@/components/Loading";
  import {PlusCircleOutlined} from "@ant-design/icons-vue";
  import {PlusCircleOutlined, PlusOutlined, SettingOutlined} from "@ant-design/icons-vue";
  import {treeOptionsListApi} from "@/api/demo/tree";
  import EventBus from "@/utils/eventBus";
  const TimelineItem = Timeline.Item;
  import {MoreOutlined} from "@ant-design/icons-vue";
  import {BasicHelp} from "@/components/Basic";
  import {log} from "vxe-table";
  import {useModal} from "@/components/Modal";
  import ScheduleDetailModal from "@/views/clues/components/drawer/ScheduleDetail.vue";
  import {projectList} from "@/views/clues/components/drawer/data";
  import {cluesListApi} from "@/api/clues/table";
  // const TimelineItem = Timeline.Item;
  const prefixCls = 'clues-tab-dynamic';
  const prefixCls = 'customer-tab-dynamic';
  let scheduleList = ref([] as any[]);
  let dynamicList = ref([] as any[]);
  let totalData = ref(0);
  let currentPage = ref(1);
  let pageSize = ref(10);
  const handlePageChange = (current: number, size: number) => {
    currentPage.value = current;
@@ -100,8 +208,75 @@
  };
  // let _this = getCurrentInstance();
  let businessTitle = ref('新商机');
  let selectedBusinessTitleKeys = ref('1');
  let businessTitleArray = ref([
    {
      key: '1',
      label: '新商机',
      title: '新商机',
    },
    {
      key: '2',
      label: '订单确认',
      title: '订单确认',
    },
    {
      key: '3',
      label: '有意向',
      title: '有意向',
    },
    {
      key: '4',
      label: '测试新的机会',
      title: '测试新的机会',
    },
  ]);
  function handleBusinessTitleClick(e){
    Logger.log('handleBusinessTitleClick',e);
    selectedBusinessTitleKeys.value = e?.key;
    businessTitle.value = e?.item?.title;
  }
  // 日程相关
  const [registerScheduleModal, {openModal, setModalProps}] = useModal();
  const openScheduleModal = () => {
    Logger.log('点击openScheduleModal');
    EventBus.emit('openScheduleModal', {
      title: '新建任务',
      content: '新建任务内容'
    });
  };
  function handleScheduleRowClick(e) {
    let index = e?.currentTarget?.dataset?.index;
    Logger.log('handleScheduleRowClick', scheduleList.value[index]);
    openModal(true, {
      // record,
      // isUpdate: true,
    });
  }
  getCustomerScheduleList();
  async function getCustomerScheduleList(){
    let params = {
      page: 1,
      pageSize: 10,
    };
    try {
      let res = await cluesListApi(params)
      Logger.log('cluesListApi...',res);
      // console.log(_this);
      // _this.$set(dynamicList,res.items)
      scheduleList.value = res.items;
    } catch (e) {
      Logger.error(e);
    } finally {
    }
  }
  // 动态类型treeData
  let currentDynamicType = ref('0-0');
  let currentDynamicType = ref('0');
  let dynamicTypeTree = ref([]);
  getTreeData();
  async function getTreeData() {
@@ -120,17 +295,19 @@
  }
  // 动态列表加载
  const wrapEl = ref<ElRef>(null);
  const loadingEl = ref<ElRef>(null);
  const [openWrapLoading, closeWrapLoading] = useLoading({
    target: wrapEl,
    target: loadingEl,
    props: {
      tip: '加载中...',
      absolute: true,
    },
  });
  // 动态列表数据
  getCluesDynamicData();
  async function getCluesDynamicData(){
  setTimeout(()=>{
    getCustomerDynamicList();
  },50)
  async function getCustomerDynamicList(){
    openWrapLoading();
    let params = {
      page: currentPage.value,
@@ -153,9 +330,11 @@
      closeWrapLoading();
    }
  }
</script>
<style lang="less">
  .clues-tab-dynamic {
  .customer-tab-dynamic {
    .ant-select-arrow{
      color: #000;
    }
src/views/customer/components/drawer/Tips.vue
@@ -1,61 +1,29 @@
<template>
  <Row class="mb-10px">
    <Col span="12">
      <Dropdown :trigger="['click']" arrow @openChange="openChange">
        <span class="cursor-pointer" @click.prevent>
          {{ dropDownTitle }}
          <BasicArrow :expand="false" down/>
        </span>
        <template #overlay>
          <Menu @click="onMenuItemClick">
            <MenuItem key="0">
              <span>未完成</span>
            </MenuItem>
            <MenuItem key="1">
              <span>已完成</span>
            </MenuItem>
          </Menu>
        </template>
      </Dropdown>
    </Col>
    <Col span="12" :style="'text-align:right'">
      <Tooltip title="新建日程">
        <PlusSquareOutlined class="cursor-pointer" style="fontSize:24px;color: #aaa"
                            @click="openScheduleModal"/>
      </Tooltip>
  <div ref="loadingEl" class="bg-gray-50 p-10px">
    <a-divider>
      <span class="c-gray font-size-12px">以下是历史记录</span>
    </a-divider>
    <a-card class="mb-15px" style="width: auto" v-for="item in cardList" :key="item.id">
      <p class="tips-title">
        <AimOutlined class="c-amber-5" />
        商机"<a class="">{{item.cluesName}}</a>"已过了结单日期
      </p>
      <p class="tips-content font-size-12px">
        "<a class="">{{item.cluesName}}</a>"已过了结单日期,但仍未结束(赢单/输单),请尽快跟进
      </p>
      <p class="tips-time">
    </Col>
  </Row>
  <BasicTable
    @register="registerTable"
    @row-click="handleScheduleRowClick"
  >
    <template #bodyCell="{ column, record }">
      <template v-if="column.key === 'cluesName'">
        <div>
          <Avatar :size="16" :style="'background-color:'+ record.color"></Avatar>
          <span class="ml-5px">
            {{ record.cluesName }}
          </span>
        </div>
      </template>
    </template>
  </BasicTable>
  <ScheduleDetailModal @register="registerScheduleModal"></ScheduleDetailModal>
  <div>
    <template v-for="item in projectList" :key="item.title">
      <Row class="cursor-pointer" :gutter="[22,26]" align="middle">
        <Col span="12">
          <Avatar :size="16" :style="'background-color:'+ item.color"></Avatar>
          <span class="ml-5px">{{ item.title }}</span>
        </Col>
        <Col span="12" :style="'text-align:right'">
          <span>{{ item.content }}</span>
        </Col>
      </Row>
    </template>
      </p>
      <a-flex justify="space-between">
        <span class="c-gray font-size-12px">{{item.privateTime}}</span>
        <a-button class="font-size-12px" type="link">编辑商机</a-button>
      </a-flex>
    </a-card>
    <a-divider>
      <span class="c-gray font-size-12px">已显示全部内容</span>
    </a-divider>
  </div>
  <!--  <List :class="prefixCls" item-layout="horizontal" :data-source="projectList" :grid="{ column: 1, gutter: 12}" :pagination="pagination" >-->
  <!--    <template #renderItem="{ item }">-->
  <!--      <ListItem>-->
@@ -69,19 +37,18 @@
<script lang="ts" setup>
import {List, Avatar, Card, Row, Col, Dropdown, Menu, Tooltip, Tag} from 'ant-design-vue';
import {projectList} from './data';
import {PlusSquareOutlined} from '@ant-design/icons-vue';
import {AimOutlined} from '@ant-design/icons-vue';
import {BasicArrow} from "@/components/Basic";
import {ref} from 'vue';
import {onMounted, ref} from 'vue';
import ScheduleDetailModal from './ScheduleDetail.vue';
const ListItem = List.Item;
const MenuItem = Menu.Item;
import EventBus from '@/utils/eventBus';
import {BasicTable, ColumnChangeParam, TableAction, useTable} from "@/components/Table";
import {cluesListApi} from "@/api/clues/table";
import {cluesDynamicApi} from "@/api/clues/dynamic";
import {getEditCellColumns, getFormConfig} from "@/views/clues/components/tableData";
import {useModal} from "@/components/Modal";
import {useLoading} from "@/components/Loading";
const [registerScheduleModal, {openModal, setModalProps}] = useModal();
const openScheduleModal = () => {
@@ -92,93 +59,45 @@
  });
};
const pagination = {
  onChange: (page: number) => {
    console.log(page);
  },
  pageSize: 3,
};
const openChange = (status: boolean) => {
  Logger.log('openChange', status);
};
const dropDownTitle = ref('未完成');
const onMenuItemClick = (e: any) => {
  let titles = ['未完成', '已完成'];
  Logger.log('onMenuItemClick', e);
  dropDownTitle.value = titles[e.key];
};
const [
  registerTable,
  {
    // setLoading,
    // setProps,
    // getColumns,
    // getDataSource,
    // getRawDataSource,
    // reload,
    // getPaginationRef,
    // setPagination,
    // getSelectRows,
    // getSelectRowKeys,
    // setSelectedRowKeys,
    // clearSelectedRowKeys,
  },
] = useTable({
  canResize: true,
  api: cluesListApi,
  // beforeFetch: (params) => {
  //   console.log('beforeFetch', params);
  //   params.pageNo = params.page;
  //   // return Promise.resolve(params);
  // },
  showIndexColumn: false,
  columns: [
    {
      // title: '',
      // defaultHidden: true,
      dataIndex: 'cluesName',
      width: 200
    },
    {
      // title: '',
      dataIndex: 'archiveTime',
      width: 200,
    },
  ],
  // defSort: {
  //   pageNo: 1,
  //   pageSize: 20,
  //   field: 'name',
  //   order: 'ascend',
  // },
  rowKey: 'id',
  // showTableSetting: false,
  // showIndexColumn: false, // 是否显示序号列
  // useSearchForm: false, // 使用搜索表单
  // clickToRowSelect: false,
  // formConfig: getFormConfig(), // 搜索表单配置
  pagination: {
    // pageSize: 20,
    pageSizeOptions: ['10', '20', '50', '100'],
    defaultPageSize: 20,
    // showSizeChanger: true,
let cardList = ref([] as any[]);
// tips Loading
const loadingEl = ref<ElRef>(null);
const [openWrapLoading, closeWrapLoading] = useLoading({
  target: loadingEl,
  props: {
    tip: '加载中...',
    absolute: true,
  },
});
// tips列表数据
// 获取客户卡片数据
setTimeout(() => {
  getCustomerCardData();
}, 50);
async function getCustomerCardData(){
  openWrapLoading();
  let params = {
    page: 1,
    pageSize: 10,
  };
  try {
    // cluesDynamicApi(params).then(res => {
    //
    // })
    let res = await cluesDynamicApi(params)
    Logger.log('cluesDynamicApi...',res);
    // console.log(_this);
    // _this.$set(dynamicList,res.items)
    cardList.value = res.items;
function handleScheduleRowClick(e) {
  Logger.log('handleScheduleRowClick', e);
  openModal(true, {
    // record,
    // isUpdate: true,
  });
  } catch (e) {
    Logger.error(e);
  } finally {
    closeWrapLoading();
  }
}
const prefixCls = 'account-center-project';
</script>
<style lang="less" >
<style lang="less" scoped>
</style>
src/views/customer/components/drawer/index.vue
@@ -154,7 +154,10 @@
      background-color: @component-background;
    }
    .scroll-wrap{
      height: calc(100vh - 290px);
      height: calc(100vh - 310px);
    }
    :deep(.scrollbar__wrap)   {
      padding: 5px !important;
    }
  }
</style>