This commit is contained in:
zlwu committed 2026-08-22 09:08:05 +08:00
1 parent 833544def3
commit 16be6fa22d
204 files changed
+455089 -46491

No files matched your search

+27 -24
View File
@@ -185,6 +185,14 @@
<el-icon><CircleClose /></el-icon>标为无效
</el-button>
<ListExportDropdown
:export-fn="exportClue"
:get-filter-params="getClueExportFilterParams"
:has-selection="true"
:selected-ids="ids.map(String)"
:visible-columns="clueExportColumns"
filename="线索列表.xlsx"
/>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
@@ -504,6 +512,7 @@
import AssignForm from './assignForm.vue'
import InvalidForm from './invalidForm.vue'
import DetailDrawer from './detailDrawer.vue'
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
@@ -579,12 +588,29 @@
]
})
const clueExportColumns = computed(() => {
const cols = [...visibleColumns.value]
if (!cols.includes('company')) cols.unshift('company')
return cols
})
function getClueExportFilterParams() {
const params = buildQueryParams()
delete params.pageNum
delete params.pageSize
if (activeView.value === 'mine') params.viewMode = 'mine'
else if (activeView.value === 'toFollow') params.viewType = 'toFollow'
else if (activeView.value === 'today') params.viewType = 'todayNew'
else if (activeView.value === 'converted') params.viewType = 'converted'
return params
}
const importDialogVisible = ref(false)
const importFileList = ref<UploadFile[]>([])
const importLoading = ref(false)
const importUpdateSupport = ref(false)
const templateLoading = ref(false)
const exportLoading = ref(false)
const canConvertSelected = computed(() => {
if (selectedRows.value.length !== 1) return false
@@ -852,29 +878,6 @@
}
}
async function handleExport() {
exportLoading.value = true
try {
const params = buildQueryParams()
const res = await exportClue(params)
const blob =
res instanceof Blob
? res
: new Blob([res as BlobPart], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `线索列表_${new Date().getTime()}.xlsx`
link.click()
URL.revokeObjectURL(url)
} catch {
ElMessage.error('导出失败')
} finally {
exportLoading.value = false
}
}
watchDebounced(
() => ({ ...searchForm, dateRange: dateRange.value }),
@@ -130,9 +130,14 @@
>
<el-icon><Upload /></el-icon>上传扫描件
</el-button>
<el-button class="btn-ghost" :loading="exportLoading" @click="handleExport">
<el-icon><Download /></el-icon>导出
</el-button>
<ListExportDropdown
:export-fn="exportContract"
:get-filter-params="getContractExportFilterParams"
:has-selection="true"
:selected-ids="ids.map(String)"
:visible-columns="contractExportColumns"
filename="合同列表.xlsx"
/>
<ListToolbarEndActions
:loading="loading"
@@ -562,6 +567,7 @@ import {
openCrmAttachmentPreviewInNewTab
} from '@/utils/crm-attachment'
import ListToolbarDateShortcuts from '@/components/core/list-toolbar-date-shortcuts/index.vue'
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListColumnStatsRow from '@/components/business/list-column-stats-row/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
@@ -578,7 +584,6 @@ const isManageMode = computed(() => props.mode === 'manage')
const pageTitle = computed(() => (isManageMode.value ? '合同管理' : '合同审批'))
const userStore = useUserStore();
const exportLoading = ref(false);
/** 当前登录用户 ID(后端 current-user 返回 id,兼容历史 userId 字段) */
const resolveCurrentUserId = (): string => {
@@ -1108,29 +1113,26 @@ const handleView = (row?: ContractVO) => {
contractDetailRef.value?.open(targetRow.id);
};
/** 导出按钮操作 */
const handleExport = async () => {
exportLoading.value = true;
try {
const res = await exportContract({ ...queryParams.value });
const blob =
res instanceof Blob
? res
: new Blob([res as any], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `contract_${new Date().getTime()}.xlsx`;
link.click();
URL.revokeObjectURL(url);
} catch (error) {
console.error('导出失败:', error);
} finally {
exportLoading.value = false;
const contractExportColumns = computed(() => {
const cols = [...visibleColumns.value]
if (!cols.includes('contractNo')) cols.unshift('contractNo')
if (!cols.includes('contractName')) {
const idx = cols.indexOf('contractNo')
cols.splice(idx + 1, 0, 'contractName')
}
};
return cols
})
function getContractExportFilterParams() {
const params: Record<string, any> = { ...queryParams.value }
if (dateRange.value && dateRange.value.length === 2) {
params.signDateBegin = dateRange.value[0]
params.signDateEnd = dateRange.value[1]
}
delete params.pageNum
delete params.pageSize
return params
}
/** 提交审批(单条) */
const handleSubmit = (row?: ContractVO) => {
@@ -70,13 +70,14 @@
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button
class="btn-ghost"
:loading="exportLoading"
@click="handleExport"
>
<el-icon><Download /></el-icon>导出
</el-button>
<ListExportDropdown
:export-fn="exportApproval"
:get-filter-params="getApprovalExportFilterParams"
:has-selection="true"
:selected-ids="selectedRows.map((r) => String(r.id))"
:visible-columns="approvalExportColumns"
filename="合同审批记录.xlsx"
/>
<ListToolbarEndActions
:loading="loading"
@@ -266,6 +267,7 @@
</template>
<script setup lang="ts" name="ContractApproval">
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { ref, reactive, computed, onMounted, watch } from 'vue'
@@ -319,6 +321,24 @@
resetToDefault
} = usePersistedListColumns('crm_contract_approval_visible_columns', listColumnDefs)
const approvalExportColumns = computed(() => {
const cols = [...visibleColumns.value]
if (!cols.includes('contractNo')) cols.unshift('contractNo')
return cols
})
function getApprovalExportFilterParams() {
const params: Record<string, any> = { ...queryParams.value }
if (dateRange.value && dateRange.value.length === 2) {
params.beginTime = dateRange.value[0]
params.endTime = dateRange.value[1]
}
delete params.pageNum
delete params.pageSize
return params
}
const queryParams = ref<ApprovalQuery>({
pageNum: 1,
pageSize: 20,
@@ -356,7 +376,6 @@
const loading = ref(true)
const selectedRows = ref<ApprovalVO[]>([])
const submitLoading = ref(false)
const exportLoading = ref(false)
const total = ref(0)
const dateRange = ref<[string, string]>()
@@ -538,36 +557,6 @@
viewDialog.data = res.data
}
const handleExport = async () => {
if (dateRange.value && dateRange.value.length === 2) {
queryParams.value.beginTime = dateRange.value[0]
queryParams.value.endTime = dateRange.value[1]
} else {
queryParams.value.beginTime = undefined
queryParams.value.endTime = undefined
}
exportLoading.value = true
try {
const res = await exportApproval(queryParams.value)
const blob =
res instanceof Blob
? res
: new Blob([res as BlobPart], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `contract_approval_${new Date().getTime()}.xlsx`
link.click()
URL.revokeObjectURL(url)
} catch (error) {
console.error(error)
$modal.msgError('导出失败')
} finally {
exportLoading.value = false
}
}
onMounted(() => {
getList()
@@ -119,13 +119,14 @@
>
<el-icon><RefreshLeft /></el-icon>撤回
</el-button>
<el-button
class="btn-ghost"
:loading="exportLoading"
@click="handleExport"
>
<el-icon><Download /></el-icon>导出
</el-button>
<ListExportDropdown
:export-fn="exportChange"
:get-filter-params="getChangeExportFilterParams"
:has-selection="true"
:selected-ids="selectedRows.map((r) => String(r.id))"
:visible-columns="changeExportColumns"
filename="合同变更.xlsx"
/>
<ListToolbarEndActions
:loading="loading"
@@ -402,6 +403,7 @@
</template>
<script setup lang="ts" name="ContractChange">
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { ref, reactive, computed, onMounted } from 'vue'
@@ -472,6 +474,24 @@
resetToDefault
} = usePersistedListColumns('crm_contract_change_visible_columns', listColumnDefs)
const changeExportColumns = computed(() => {
const cols = [...visibleColumns.value]
if (!cols.includes('contractNo')) cols.unshift('contractNo')
return cols
})
function getChangeExportFilterParams() {
const params: Record<string, any> = { ...queryParams.value }
if (dateRange.value && dateRange.value.length === 2) {
params.beginTime = dateRange.value[0]
params.endTime = dateRange.value[1]
}
delete params.pageNum
delete params.pageSize
return params
}
const queryParams = ref<ChangeQuery>({
pageNum: 1,
pageSize: 20,
@@ -505,7 +525,6 @@
const selectedRows = ref<ChangeVO[]>([])
const loading = ref(true)
const submitLoading = ref(false)
const exportLoading = ref(false)
const total = ref(0)
const ids = ref<number[]>([])
const dateRange = ref<[string, string]>()
@@ -757,36 +776,6 @@
await refreshViewDialogIfOpen()
}
const handleExport = async () => {
if (dateRange.value && dateRange.value.length === 2) {
queryParams.value.beginTime = dateRange.value[0]
queryParams.value.endTime = dateRange.value[1]
} else {
queryParams.value.beginTime = undefined
queryParams.value.endTime = undefined
}
exportLoading.value = true
try {
const res = await exportChange(queryParams.value)
const blob =
res instanceof Blob
? res
: new Blob([res as BlobPart], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `contract_change_${new Date().getTime()}.xlsx`
link.click()
URL.revokeObjectURL(url)
} catch (error) {
console.error(error)
$modal.msgError('导出失败')
} finally {
exportLoading.value = false
}
}
const cancel = () => {
dialog.visible = false
@@ -75,13 +75,14 @@
>
<el-icon><Delete /></el-icon>删除
</el-button>
<el-button
class="btn-ghost"
:loading="exportLoading"
@click="handleExport"
>
<el-icon><Download /></el-icon>导出
</el-button>
<ListExportDropdown
:export-fn="exportTemplate"
:get-filter-params="getTemplateExportFilterParams"
:has-selection="true"
:selected-ids="selectedRows.map((r) => String(r.id))"
:visible-columns="templateExportColumns"
filename="合同模板.xlsx"
/>
<ListToolbarEndActions
:loading="loading"
@@ -332,6 +333,7 @@
</template>
<script setup lang="ts" name="ContractTemplate">
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { ref, reactive, computed, onMounted } from 'vue'
@@ -397,6 +399,20 @@
resetToDefault
} = usePersistedListColumns('crm_contract_template_visible_columns', listColumnDefs)
const templateExportColumns = computed(() => {
const cols = [...visibleColumns.value]
if (!cols.includes('templateName')) cols.unshift('templateName')
return cols
})
function getTemplateExportFilterParams() {
const params: Record<string, any> = { ...queryParams.value }
delete params.pageNum
delete params.pageSize
return params
}
const queryParams = ref<TemplateQuery>({
pageNum: 1,
pageSize: 20,
@@ -428,7 +444,6 @@
const selectedRows = ref<TemplateVO[]>([])
const loading = ref(true)
const submitLoading = ref(false)
const exportLoading = ref(false)
const total = ref(0)
const ids = ref<number[]>([])
@@ -618,29 +633,6 @@
}
}
const handleExport = async () => {
exportLoading.value = true
try {
const res = await exportTemplate(queryParams.value)
const blob =
res instanceof Blob
? res
: new Blob([res as BlobPart], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `contract_template_${new Date().getTime()}.xlsx`
link.click()
URL.revokeObjectURL(url)
} catch (error) {
console.error(error)
$modal.msgError('导出失败')
} finally {
exportLoading.value = false
}
}
const cancel = () => {
dialog.visible = false
+26 -18
View File
@@ -161,6 +161,14 @@
放入公海
</el-button>
<ListExportDropdown
:export-fn="fetchExportCustomer"
:get-filter-params="getCustomerExportFilterParams"
:has-selection="true"
:selected-ids="selectedRows.map((r) => String(r.id))"
:visible-columns="customerExportColumns"
filename="客户列表.xlsx"
/>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
@@ -681,6 +689,7 @@ import TransferForm from './TransferDialog.vue'
import IntoPoolForm from './IntoPoolDialog.vue'
import FollowForm from './FollowFormDialog.vue'
import DealForm from './DealDialog.vue'
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
@@ -780,6 +789,23 @@ const {
]
})
const customerExportColumns = computed(() => {
const cols = [...visibleColumns.value]
if (!cols.includes('customerName')) cols.unshift('customerName')
return cols
})
function getCustomerExportFilterParams() {
const params = buildQueryParams()
delete params.page
delete params.pageSize
if (activeView.value && activeView.value !== 'all') {
params.viewMode = activeView.value
}
return params
}
// 子组件引用
const customerFormRef = ref<InstanceType<typeof CustomerForm>>()
const customerDetailRef = ref<InstanceType<typeof CustomerDetail>>()
@@ -986,24 +1012,6 @@ async function handleDelete(row?: any) {
await getList()
}
/** 导出按钮操作 */
async function handleExport() {
try {
const params = buildQueryParams()
const res = await fetchExportCustomer(params)
const blob = new Blob([res as any], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
})
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `客户列表_${new Date().getTime()}.xlsx`
link.click()
URL.revokeObjectURL(url)
} catch {
ElMessage.error('导出失败')
}
}
watchDebounced(
() => ({ ...searchForm, dateRange: dateRange.value }),
@@ -92,6 +92,14 @@
手动执行
</el-button>
<ListExportDropdown
:export-fn="fetchExportPoolRules"
:get-filter-params="getPoolRuleExportFilterParams"
:has-selection="true"
:selected-ids="selectedRows.map((r) => String(r.id))"
:visible-columns="poolRuleExportColumns"
filename="公海规则.xlsx"
/>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
@@ -227,6 +235,7 @@
<script setup lang="ts">
import { watchDebounced } from '@vueuse/core'
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
@@ -247,6 +256,7 @@ import {
fetchDeletePoolRule,
fetchTogglePoolRule,
fetchExecutePoolRules,
fetchExportPoolRules,
} from '@/api/crm/pool'
import RuleDialog from './components/RuleDialog.vue'
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
@@ -276,6 +286,22 @@ const {
resetToDefault
} = usePersistedListColumns('crm_pool_rule_visible_columns', listColumnDefs)
const poolRuleExportColumns = computed(() => {
const cols = [...visibleColumns.value]
if (!cols.includes('poolName')) cols.unshift('poolName')
return cols
})
function getPoolRuleExportFilterParams() {
const quickKeyword = quickSearchKeyword.value?.trim()
return {
poolName: quickKeyword || searchForm.poolName || undefined,
ruleType: searchForm.ruleType || undefined,
status: searchForm.status || undefined
}
}
// 数据列表
const ruleList = ref<any[]>([])
const loading = ref(false)
@@ -71,6 +71,14 @@
分配客户
</el-button>
<ListExportDropdown
:export-fn="fetchExportPoolCustomers"
:get-filter-params="getPoolExportFilterParams"
:has-selection="true"
:selected-ids="ids"
:visible-columns="poolExportColumns"
filename="公海客户.xlsx"
/>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
@@ -536,7 +544,8 @@ import {
More,
} from '@element-plus/icons-vue'
import { useDictStore } from '@/store/modules/dict'
import { fetchPoolCustomerList } from '@/api/crm/pool'
import { fetchPoolCustomerList, fetchExportPoolCustomers } from '@/api/crm/pool'
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
@@ -623,6 +632,24 @@ const {
]
})
const poolExportColumns = computed(() => {
const cols = [...visibleColumns.value]
if (!cols.includes('customerName')) cols.unshift('customerName')
if (!cols.includes('customerNo')) cols.unshift('customerNo')
return cols
})
function getPoolExportFilterParams() {
const quickKeyword = quickSearchKeyword.value?.trim()
const filterKeyword =
searchForm.customerName?.trim() || searchForm.customerNo?.trim() || undefined
return {
keyword: quickKeyword || filterKeyword,
level: searchForm.level || undefined
}
}
const claimDialogRef = ref()
const assignDialogRef = ref()
const customerDetailRef = ref()
+9 -18
View File
@@ -135,6 +135,14 @@
<el-icon><User /></el-icon>批量分配
</el-button>
<ListExportDropdown
:export-fn="exportOpportunity"
:get-filter-params="getOpportunityExportFilterParams"
:has-selection="true"
:selected-ids="ids.map(String)"
:visible-columns="opportunityExportColumns"
filename="商机列表.xlsx"
/>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
@@ -587,6 +595,7 @@
import BatchUpdateStageDialog from './components/BatchUpdateStageDialog.vue'
import BatchUpdateDateDialog from './components/BatchUpdateDateDialog.vue'
import OpportunityDetailDrawer from './components/OpportunityDetailDrawer.vue'
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
@@ -594,7 +603,6 @@
const router = useRouter()
const selectedRows = ref<OpportunityVO[]>([])
const exportLoading = ref(false)
const assignLoading = ref(false)
// 列配置
@@ -841,23 +849,6 @@
await getList()
}
/** 导出按钮操作 */
const handleExport = async () => {
exportLoading.value = true
try {
const blob = await exportOpportunity({ ...queryParams.value })
const url = window.URL.createObjectURL(blob as Blob)
const link = document.createElement('a')
link.href = url
link.download = `商机数据_${Date.now()}.xlsx`
link.click()
window.URL.revokeObjectURL(url)
} catch {
ElMessage.error('导出失败')
} finally {
exportLoading.value = false
}
}
/** 变更阶段 */
const handleChangeStage = (row?: OpportunityVO) => {
+23 -24
View File
@@ -111,6 +111,14 @@
<el-icon><CircleClose /></el-icon>作废
</el-button>
<ListExportDropdown
:export-fn="exportQuote"
:get-filter-params="getQuoteExportFilterParams"
:has-selection="true"
:selected-ids="ids.map(String)"
:visible-columns="quoteExportColumns"
filename="报价单列表.xlsx"
/>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
@@ -867,6 +875,7 @@ import { fitRichTextTablesToMaxWidth } from '@/utils/tinymce-table-excel-resize'
import QuoteFormDialog from './components/QuoteFormDialog.vue'
import QuoteDetailDialog from './components/QuoteDetailDialog.vue'
import ListToolbarDateShortcuts from '@/components/core/list-toolbar-date-shortcuts/index.vue'
import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import {
@@ -1001,6 +1010,20 @@ const {
]
})
const quoteExportColumns = computed(() => {
const cols = [...visibleColumns.value]
if (!cols.includes('quoteNo')) cols.unshift('quoteNo')
return cols
})
function getQuoteExportFilterParams() {
const params = { ...buildListQueryParams() }
delete params.pageNum
delete params.pageSize
return params
}
type QuoteViewName = 'all' | 'submitted' | 'accepted'
interface TabFilterState {
@@ -1054,7 +1077,6 @@ const loading = ref(true);
const total = ref(0);
const expandLoadingMap = reactive<Record<string, boolean>>({});
const selectedRows = ref<QuoteVO[]>([]);
const exportLoading = ref(false);
const single = ref(true);
const multiple = ref(true);
const ids = ref<(string | number)[]>([]);
@@ -1463,29 +1485,6 @@ const handleQuickSearchClear = () => {
handleQuickSearch();
};
/** 导出按钮操作 */
const handleExport = async () => {
exportLoading.value = true;
try {
const res = await exportQuote(buildListQueryParams())
const blob =
res instanceof Blob
? res
: new Blob([res as any], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `quote_${new Date().getTime()}.xlsx`
link.click()
URL.revokeObjectURL(url)
} catch (error) {
console.error('导出失败:', error)
} finally {
exportLoading.value = false;
}
}
/** 报价分析 */
const handleStatistics = () => {