| | |
| | | for (let index = 0; index < 200; index++) { |
| | | result.push({ |
| | | id: `${index}`, |
| | | color: Random.color(), |
| | | cluesName: '@ctitle()', |
| | | formId: `@integer(100000, 999999)`, |
| | | archiveTime: '@datetime', |
| | |
| | | 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>; |
| | | }) |
| | | } |
| | |
| | | function handlePreviewDelete(url: string) { |
| | | emit('preview-delete', url); |
| | | } |
| | | |
| | | // 暴露openUploadModal 供父组件使用 |
| | | defineExpose({ |
| | | openUploadModal, |
| | | }); |
| | | </script> |
| | |
| | | <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"> |
| | |
| | | 添加跟进 |
| | | </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 |
| | |
| | | } |
| | | |
| | | // 动态列表加载 |
| | | 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 = { |
| | |
| | | }); |
| | | }; |
| | | |
| | | const pagination = { |
| | | onChange: (page: number) => { |
| | | console.log(page); |
| | | }, |
| | | pageSize: 3, |
| | | }; |
| | | |
| | | const openChange = (status: boolean) => { |
| | | Logger.log('openChange', status); |
| | |
| | | background-color: @component-background; |
| | | } |
| | | .scroll-wrap{ |
| | | height: calc(100vh - 290px); |
| | | height: calc(100vh - 310px); |
| | | } |
| | | //:deep(.scrollbar__wrap) { |
| | | // padding: 10px !important; |
| | | //} |
| | | } |
| | | </style> |
| | |
| | | @register="registerDrawer" |
| | | :maskClosable="false" |
| | | :keyboard="false" |
| | | width="500px" |
| | | width="550px" |
| | | > |
| | | <template #title> |
| | | <div class="text-right"> |
| | |
| | | <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>--> |
| | | <!--<!– <template v-for="item in projectList" :key="item.title"></template>–>--> |
| | | <!-- </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'); |
| | |
| | | }); |
| | | }; |
| | | |
| | | 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, |
| | |
| | | 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, |
| | |
| | | // showIndexColumn: false, // 是否显示序号列 |
| | | // useSearchForm: false, // 使用搜索表单 |
| | | // clickToRowSelect: false, |
| | | // formConfig: getFormConfig(), // 搜索表单配置 |
| | | pagination: { |
| | | // pageSize: 20, |
| | | pageSizeOptions: ['10', '20', '50', '100'], |
| | |
| | | }, |
| | | }); |
| | | |
| | | |
| | | |
| | | 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> |
| | |
| | | <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> |
| | | |
| | |
| | | </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'; |
| | |
| | | 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 = () => { |
| | |
| | | }); |
| | | }; |
| | | |
| | | // 获取子组件的引用 |
| | | 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 [ |
| | |
| | | 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, |
| | |
| | | // 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> |
| | |
| | | <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 /> |
| | |
| | | 添加跟进 |
| | | </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" |
| | |
| | | </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; |
| | |
| | | }; |
| | | // 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() { |
| | |
| | | } |
| | | |
| | | // 动态列表加载 |
| | | 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, |
| | |
| | | closeWrapLoading(); |
| | | } |
| | | } |
| | | |
| | | |
| | | </script> |
| | | <style lang="less"> |
| | | .clues-tab-dynamic { |
| | | .customer-tab-dynamic { |
| | | .ant-select-arrow{ |
| | | color: #000; |
| | | } |
| | |
| | | <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>--> |
| | |
| | | <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 = () => { |
| | |
| | | }); |
| | | }; |
| | | |
| | | 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> |
| | |
| | | background-color: @component-background; |
| | | } |
| | | .scroll-wrap{ |
| | | height: calc(100vh - 290px); |
| | | height: calc(100vh - 310px); |
| | | } |
| | | :deep(.scrollbar__wrap) { |
| | | padding: 5px !important; |
| | | } |
| | | } |
| | | </style> |