收款计划

pull/853/head
wersd 2025-06-29 09:39:05 +08:00
parent 0fb01cd478
commit 05f5087306
22 changed files with 969 additions and 358 deletions

View File

@ -5,14 +5,18 @@ export interface ContractVO {
id: number
name: string
no: string
customerId: number[]
customer?:{
customerId: number
customerName?: string
otherCustomerId: number[]
otherCustomer?:{
id: number
name: string
}
businessId: number
businessName: string
deptId: number
deptName?: string
contactLastTime: Date
ownerUserId: number
ownerUserName?: string
@ -38,6 +42,8 @@ export interface ContractVO {
creator: string
creatorName: string
updateTime?: Date
picUrls?: string[]
fileUrls?: string[]
products?: [
{
id: number

View File

@ -4,6 +4,7 @@ export interface ReceivableVO {
id: number
no: string
planId?: number
period?: number // 期数
customerId?: number
customerName?: string
contractId?: number

View File

@ -11,6 +11,8 @@ export interface ReceivablePlanVO {
remindTime: Date
customerId: number
customerName?: string
deptId: number
deptName?: string
contractId?: number
contractNo?: string
ownerUserId: number

View File

@ -10,6 +10,7 @@ export interface DeptVO {
phone: string
email: string
createTime: Date
type: number
}
// 查询部门(精简)列表
@ -17,6 +18,11 @@ export const getSimpleDeptList = async (): Promise<DeptVO[]> => {
return await request.get({ url: '/system/dept/simple-list' })
}
// 查询部门(精简)列表
export const querySimpleDeptList = async () => {
return await request.get({ url: '/system/dept/query-list?type=2'})
}
// 查询部门列表
export const getDeptPage = async (params: PageParam) => {
return await request.get({ url: '/system/dept/list', params })

View File

@ -0,0 +1,147 @@
<template>
<div>
<div v-if="loading" class="loading-container">
<el-skeleton :rows="5" animated />
</div>
<div v-else class="attachment-container">
<!-- 图片展示部分 -->
<div class="section" v-if="picUrls?.length">
<h3 class="section-title">图片</h3>
<div class="image-list">
<div
v-for="(url, index) in picUrls"
:key="index"
class="image-item"
>
<el-image
:src="url"
:preview-src-list="picUrls"
:initial-index="index"
fit="cover"
class="thumbnail"
>
<template #error>
<div class="image-error">
<span>加载失败</span>
</div>
</template>
</el-image>
</div>
</div>
</div>
<!-- 文件展示部分 -->
<div class="section" v-if="fileUrls?.length">
<h3 class="section-title">文件</h3>
<div class="file-list">
<div
v-for="(url, index) in fileUrls"
:key="index"
class="file-item"
>
<el-icon class="file-icon"><Document /></el-icon>
<div class="file-info">
<a
:href="url"
target="_blank"
class="file-name"
@click.prevent="downloadFile(url)"
>
{{ extractFileName(url) }}
</a>
<!-- <div class="file-actions">
<el-button
type="primary"
link
@click="downloadFile(url)"
>
下载
</el-button>
<el-button
type="info"
link
@click="previewFile(url)"
>
预览
</el-button>
</div> -->
</div>
</div>
</div>
</div>
<!-- 空状态显示 -->
<el-empty
v-if="!picUrls?.length && !fileUrls?.length"
description="暂无附件数据"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ElMessage, ElLoading } from 'element-plus'
import { ref } from 'vue'
import { Document } from '@element-plus/icons-vue'
const props = defineProps<{
loading: boolean
picUrls: string[]
fileUrls: string[]
}>()
// URL
const extractFileName = (url: string) => {
try {
const path = new URL(url).pathname
return decodeURIComponent(path.substring(path.lastIndexOf('/') + 1))
} catch {
return decodeURIComponent(url.substring(url.lastIndexOf('/') + 1))
}
}
//
const downloadFile = (url: string) => {
const loadingInstance = ElLoading.service({
lock: true,
text: '文件下载中...',
background: 'rgba(0, 0, 0, 0.7)'
})
try {
const link = document.createElement('a')
link.href = url
link.download = extractFileName(url)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
ElMessage.success('文件下载开始')
} catch (error: any) {
ElMessage.error('下载失败: ' + error.message)
} finally {
setTimeout(() => {
loadingInstance.close()
}, 500)
}
}
//
const previewFile = (url: string) => {
if (url.match(/\.(png|jpg|jpeg|gif|webp)$/i)) {
window.open(url, '_blank')
}
}
</script>
<style scoped>
.attachment-container { padding: 16px; }
.section { margin-bottom: 24px; }
.section-title { font-weight: bold; margin-bottom: 8px; }
.image-list { display: flex; flex-wrap: wrap; gap: 12px; }
.image-item { width: 120px; height: 120px; }
.thumbnail { width: 100%; height: 100%; object-fit: cover; }
.file-list { display: flex; flex-direction: column; gap: 12px; }
.file-item { display: flex; align-items: center; }
.file-icon { margin-right: 8px; }
.file-info { flex: 1; }
.file-name { color: #409EFF; text-decoration: underline; cursor: pointer; }
.file-actions { margin-left: 16px; }
.image-error { color: #f56c6c; text-align: center; }
</style>

View File

@ -129,6 +129,7 @@ export enum DICT_TYPE {
SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status',
SYSTEM_NOTIFY_TEMPLATE_TYPE = 'system_notify_template_type',
SYSTEM_SOCIAL_TYPE = 'system_social_type',
SYSTEM_DEPT_TYPE = 'system_dept_type',
// ========== INFRA 模块 ==========
INFRA_BOOLEAN_STRING = 'infra_boolean_string',

View File

@ -14,7 +14,12 @@
</el-form-item>
</el-col> -->
<el-col :span="8">
<el-form-item label="客户签约方 " prop="customerId">
<el-form-item label="合同名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入合同名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="主客户签约方 " prop="customerId">
<el-select
v-model="formData.customerId"
placeholder="请选择客户签约方 "
@ -23,6 +28,26 @@
:multiple-limit="3"
filterable
clearable
>
<el-option
v-for="item in customerList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="其他客户签约方 " prop="otherCustomerId">
<el-select
v-model="formData.otherCustomerId"
placeholder="请选择客户签约方 "
class="w-1/1"
@change="handleCustomerChange"
:multiple-limit="2"
filterable
clearable
multiple
>
<el-option
@ -35,7 +60,7 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="公司签约方主体" prop="deptId">
<el-form-item label="公司签约方" prop="deptId">
<el-select
v-model="formData.deptId"
class="w-1/1"
@ -49,27 +74,6 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="合同名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入合同名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="负责人" prop="ownerUserId">
<el-select
v-model="formData.ownerUserId"
:disabled="formType !== 'create'"
class="w-1/1"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
@ -147,7 +151,7 @@
</el-row>
<el-row>
<el-col :span="8">
<el-col>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
</el-form-item>
@ -167,7 +171,7 @@
</ContentWrap>
<el-row>
<el-col :span="8">
<el-form-item label="产品总金额" prop="totalProductPrice">
<el-form-item label="合同总金额" prop="totalProductPrice">
<el-input
disabled
v-model="formData.totalProductPrice"
@ -175,7 +179,7 @@
/>
</el-form-item>
</el-col>
<el-col :span="8">
<!-- <el-col :span="8">
<el-form-item label="整单折扣(%" prop="discountPercent">
<el-input-number
v-model="formData.discountPercent"
@ -196,8 +200,14 @@
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
</el-col> -->
</el-row>
<el-form-item label="附加图片" prop="picUrls">
<UploadImgs v-model="formData.picUrls" :fileType="['image/jpeg', 'image/png']" />
</el-form-item>
<el-form-item label="附件" prop="fileUrls">
<UploadFile v-model="formData.fileUrls" :fileType="['pdf']"/>
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"></el-button>
@ -228,7 +238,8 @@ const formData = ref({
id: undefined,
no: undefined,
name: undefined,
customerId: [],
customerId: undefined,
otherCustomerId: [],
contractStatus: undefined,
contractType: undefined,
contractAttribute: undefined,
@ -243,16 +254,34 @@ const formData = ref({
discountPercent: 0,
totalProductPrice: undefined,
remark: undefined,
products: []
products: [],
picUrls: [],
fileUrls: [],
})
const validateEndTime = (rule: any, value: any, callback: any) => {
if (!value || !formData.value.startTime) {
callback()
return
}
if (value < formData.value.startTime) {
callback(new Error('结束时间不能早于开始时间'))
} else {
callback()
}
}
const formRules = reactive({
name: [{ required: true, message: '合同名称不能为空', trigger: 'blur' }],
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
customerId: [{ required: true, message: '主签约方不能为空', trigger: 'blur' }],
orderDate: [{ required: true, message: '下单日期不能为空', trigger: 'blur' }],
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }],
contractStatus: [{ required: true, message: '合同状态不能为空', trigger: 'blur' }],
contractType: [{ required: true, message: '合同类型不能为空', trigger: 'blur' }],
contractAttribute: [{ required: true, message: '合同属性不能为空', trigger: 'blur' }]
contractAttribute: [{ required: true, message: '合同属性不能为空', trigger: 'blur' }],
deptId: [{ required: true, message: '公司签约方不能为空', trigger: 'blur' }],
startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
endTime: [
{ required: true, message: '结束时间不能为空', trigger: 'blur' },
{ validator: validateEndTime, trigger: 'blur' }
],
})
const formRef = ref() // Ref
const userOptions = ref<UserApi.UserVO[]>([]) //
@ -305,13 +334,13 @@ const open = async (type: string, id?: number) => {
//
userOptions.value = await UserApi.getSimpleUserList()
//
if (formType.value === 'create') {
formData.value.ownerUserId = useUserStore().getUser.id
}
// if (formType.value === 'create') {
// formData.value.ownerUserId = useUserStore().getUser.id
// }
//
contactList.value = await ContactApi.getSimpleContactList()
//
deptList.value = await DeptApi.getSimpleDeptList()
deptList.value = await DeptApi.querySimpleDeptList()
}
defineExpose({ open }) // open
@ -322,6 +351,19 @@ const submitForm = async () => {
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
if (!formData.value.products || formData.value.products.length === 0) {
message.error('产品清单不能为空')
return
}
//
const hasPic = formData.value.picUrls && formData.value.picUrls.length > 0
const hasFile = formData.value.fileUrls && formData.value.fileUrls.length > 0
if (!hasPic && !hasFile) {
message.error('附加图片和附件不能同时为空')
return
}
//
formLoading.value = true
productFormRef.value.validate()
@ -348,7 +390,8 @@ const resetForm = () => {
id: undefined,
no: undefined,
name: undefined,
customerId: [],
customerId: undefined,
otherCustomerId: [],
contractStatus: undefined,
contractType: undefined,
contractAttribute: undefined,
@ -363,7 +406,9 @@ const resetForm = () => {
discountPercent: 0,
totalProductPrice: undefined,
remark: undefined,
products: []
products: [],
picUrls: [],
fileUrls: [],
}
formRef.value?.resetFields()
}

View File

@ -12,48 +12,33 @@
<el-table-column label="序号" type="index" align="center" width="60" />
<el-table-column label="产品名称" min-width="180">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productId`" :rules="formRules.productId" class="mb-0px!">
<el-select
v-model="row.productId"
clearable
filterable
@change="onChangeProduct($event, row)"
placeholder="请选择产品"
>
<el-form-item :prop="`${$index}.productName`" :rules="formRules.productName" class="mb-0px!">
<el-input
v-model="row.productName"
placeholder="请输入产品名称"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="单位" min-width="80">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productUnit`" :rules="formRules.productUnit" class="mb-0px!">
<el-select v-model="row.productUnit" placeholder="请选择单位">
<el-option
v-for="item in productList"
:key="item.id"
:label="item.name"
:value="item.id"
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_UNIT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="条码" min-width="150">
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled v-model="row.productNo" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="单位" min-width="80">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
</template>
</el-table-column>
<el-table-column label="价格(元)" min-width="120">
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled v-model="row.productPrice" :formatter="erpPriceInputFormatter" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="售价(元)" fixed="right" min-width="140">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.contractPrice`" class="mb-0px!">
<el-form-item :prop="`${$index}.productPrice`" :rules="formRules.productPrice" class="mb-0px!">
<el-input-number
v-model="row.contractPrice"
v-model="row.productPrice"
controls-position="right"
:min="0.001"
:precision="2"
@ -96,27 +81,39 @@
<script setup lang="ts">
import * as ProductApi from '@/api/crm/product'
import { erpPriceInputFormatter, erpPriceMultiply } from '@/utils'
import { DICT_TYPE } from '@/utils/dict'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
const props = defineProps<{
products: undefined
disabled: false
products: any[]
disabled: boolean
}>()
const formLoading = ref(false) //
const formData = ref([])
//
interface ProductRow {
id?: string | number
productName?: string
productUnit?: string
productPrice?: number
count?: number
totalPrice?: number
}
const formData = ref<ProductRow[]>([])
const formRules = reactive({
productId: [{ required: true, message: '产品不能为空', trigger: 'blur' }],
contractPrice: [{ required: true, message: '合同价格不能为空', trigger: 'blur' }],
productName: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
productUnit: [{ required: true, message: '单位不能为空', trigger: 'blur' }],
productPrice: [{ required: true, message: '价格不能为空', trigger: 'blur' }],
count: [{ required: true, message: '产品数量不能为空', trigger: 'blur' }]
})
const formRef = ref([]) // Ref
const productList = ref<ProductApi.ProductVO[]>([]) //
/** 初始化设置产品项 */
watch(
() => props.products,
async (val) => {
formData.value = val
(val) => {
formData.value = val || []
},
{ immediate: true }
)
@ -130,8 +127,8 @@ watch(
}
//
val.forEach((item) => {
if (item.contractPrice != null && item.count != null) {
item.totalPrice = erpPriceMultiply(item.contractPrice, item.count)
if (item.productPrice != null && item.count != null) {
item.totalPrice = erpPriceMultiply(item.productPrice, item.count)
} else {
item.totalPrice = undefined
}
@ -142,14 +139,13 @@ watch(
/** 新增按钮操作 */
const handleAdd = () => {
const row = {
const row: ProductRow = {
id: undefined,
productId: undefined,
productName: undefined,
productUnit: undefined, //
productNo: undefined, //
productPrice: undefined, //
contractPrice: undefined,
count: 1
count: 1,
totalPrice: undefined
}
formData.value.push(row)
}
@ -159,17 +155,6 @@ const handleDelete = (index: number) => {
formData.value.splice(index, 1)
}
/** 处理产品变更 */
const onChangeProduct = (productId, row) => {
const product = productList.value.find((item) => item.id === productId)
if (product) {
row.productUnit = product.unit
row.productNo = product.no
row.productPrice = product.price
row.contractPrice = product.price
}
}
/** 表单校验 */
const validate = () => {
return formRef.value.validate()
@ -178,6 +163,7 @@ defineExpose({ validate })
/** 初始化 */
onMounted(async () => {
productList.value = await ProductApi.getProductSimpleList()
// productList
// productList.value = await ProductApi.getProductSimpleList()
})
</script>

View File

@ -20,23 +20,22 @@
<!-- <el-descriptions-item label="合同编号">{{ contract.no }}</el-descriptions-item> -->
<el-descriptions-item label="合同名称">{{ contract.name }}</el-descriptions-item>
<el-descriptions-item label="客户签约方">
<div>
<template v-if="Array.isArray(contract.customer)">
<span v-for="(c, idx) in contract.customer" :key="c.id">
{{ c.name }}<span v-if="idx < contract.customer.length - 1"><br /> </span>
{{ contract.customerName || '' }}
</el-descriptions-item>
<el-descriptions-item label="其他签约方">
<div>
<template v-if="Array.isArray(contract.otherCustomer)">
<span v-for="(c, idx) in contract.otherCustomer" :key="c.id">
{{ c.name }}<span v-if="idx < contract.otherCustomer.length - 1"><br /> </span>
</span>
</template>
<template v-else>
{{ contract.customer?.name || '' }}
{{ contract.otherCustomer?.name || '' }}
</template>
</div>
</el-descriptions-item>
<el-descriptions-item label="责任人">
{{ contract.ownerUserName }}
</el-descriptions-item>
<el-descriptions-item label="合同金额(元)">
{{ erpPriceInputFormatter(contract.totalPrice) }}
<el-descriptions-item label="公司签约方">
{{ contract.deptName }}
</el-descriptions-item>
<el-descriptions-item label="签订时间">
{{ formatDate(contract.orderDate, 'YYYY-MM-DD') }}
@ -48,7 +47,13 @@
{{ formatDate(contract.endTime, 'YYYY-MM-DD') }}
</el-descriptions-item>
<el-descriptions-item label="合同状态">
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="contract.auditStatus" />
<dict-tag :type="DICT_TYPE.CRM_CONTRACT_STATUS" :value="contract.contractStatus" />
</el-descriptions-item>
<el-descriptions-item label="合同类型">
<dict-tag :type="DICT_TYPE.CRM_CONTRACT_TYPE" :value="contract.contractType" />
</el-descriptions-item>
<el-descriptions-item label="合同属性">
<dict-tag :type="DICT_TYPE.CRM_CONTRACT_ATTRIBUTE" :value="contract.contractAttribute" />
</el-descriptions-item>
<el-descriptions-item label="合同金额(元)">
{{ erpPriceInputFormatter(contract.totalPrice) }}

View File

@ -3,7 +3,7 @@
<el-table :data="contract.products" :stripe="true" :show-overflow-tooltip="true">
<el-table-column
align="center"
label="产品名称"
label="产品或服务"
fixed="left"
prop="productName"
min-width="160"
@ -12,7 +12,6 @@
{{ scope.row.productName }}
</template>
</el-table-column>
<el-table-column label="产品条码" align="center" prop="productNo" min-width="120" />
<el-table-column align="center" label="产品单位" prop="productUnit" min-width="160">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
@ -25,13 +24,13 @@
min-width="140"
:formatter="erpPriceTableColumnFormatter"
/>
<el-table-column
<!-- <el-table-column
label="合同价格(元)"
align="center"
prop="contractPrice"
min-width="140"
:formatter="erpPriceTableColumnFormatter"
/>
/> -->
<el-table-column
align="center"
label="数量"
@ -48,10 +47,10 @@
/>
</el-table>
<el-row class="mt-10px" justify="end">
<el-col :span="3"> 整单折扣{{ erpPriceInputFormatter(contract.discountPercent) }}% </el-col>
<el-col :span="4">
产品总金额{{ erpPriceInputFormatter(contract.totalProductPrice) }}
</el-col>
<!-- <el-col :span="3"> 整单折扣{{ erpPriceInputFormatter(contract.discountPercent) }}% </el-col> -->
<!-- <el-col :span="4">
产品总金额{{ erpPriceInputFormatter(contract.) }}
</el-col> -->
</el-row>
</ContentWrap>
</template>

View File

@ -1,37 +1,35 @@
<!-- 合同详情页面组件-->
<template>
<ContractDetailsHeader v-loading="loading" :contract="contract">
<el-button v-if="permissionListRef?.validateWrite" @click="openForm('update', contract.id)">
<el-button v-hasPermi="['crm:contract:update']" @click="openForm('update', contract.id)">
编辑
</el-button>
<el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transferContract">
<!-- <el-button v-hasPermi="['crm:contract:update']" type="primary" @click="transferContract">
转移
</el-button>
</el-button> -->
</ContractDetailsHeader>
<el-col>
<el-tabs>
<el-tab-pane label="产品">
<el-tab-pane label="合同清单">
<ContractProductList :contract="contract" />
</el-tab-pane>
<el-tab-pane label="付款">
<el-tab-pane label="付款计划">
<ReceivablePlanList
:contract-id="contract.id!"
:customer-id="Array.isArray(contract.customerId) ? contract.customerId[0] : contract.customerId"
:customer-id="contract.customerId"
@create-receivable="createReceivable"
/>
<ReceivableList
ref="receivableListRef"
:contract-id="contract.id!"
:customer-id="Array.isArray(contract.customerId) ? contract.customerId[0] : contract.customerId"
:customer-id="contract.customerId"
/>
</el-tab-pane>
<el-tab-pane label="团队成员">
<PermissionList
ref="permissionListRef"
:biz-id="contract.id!"
:biz-type="BizTypeEnum.CRM_CONTRACT"
:show-action="true"
@quit-team="close"
<el-tab-pane label="合同明细">
<AttachmentTab
:loading="loading"
:pic-urls="contract.picUrls"
:file-urls="contract.fileUrls"
/>
</el-tab-pane>
<el-tab-pane label="操作日志">
@ -57,6 +55,7 @@ import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
import PermissionList from '@/views/crm/permission/components/PermissionList.vue'
import ReceivableList from '@/views/crm/receivable/components/ReceivableList.vue'
import ReceivablePlanList from '@/views/crm/receivable/plan/components/ReceivablePlanList.vue'
import AttachmentTab from '@/components/AttachmentFile/index.vue'
defineOptions({ name: 'CrmContractDetail' })
const props = defineProps<{ id?: number }>()

View File

@ -28,13 +28,13 @@
placeholder="请输入合同名称"
@keyup.enter="handleQuery"
/>
<el-form-item label="客户" prop="customerId">
<el-form-item label="客户签约方" prop="customerId">
<el-select
v-model="queryParams.customerId"
class="!w-240px"
clearable
lable-key="name"
placeholder="请选择客户"
placeholder="请选择客户签约方"
value-key="id"
@keyup.enter="handleQuery"
>
@ -76,28 +76,27 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true" :row-class-name="tableRowClassName">
<!-- <el-table-column align="center" fixed="left" label="合同编号" prop="no" width="180" /> -->
<el-table-column align="center" fixed="left" label="合同名称" prop="name" width="160">
<el-table-column align="center" fixed="left" label="合同名称" prop="name" width="300">
<template #default="scope">
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
{{ scope.row.name }}
</el-link>
</template>
</el-table-column>
<el-table-column align="center" label="客户签约方" prop="customer.id" width="350">
<el-table-column align="center" label="客户签约方" prop="deptId" width="300">
<template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openCustomerDetail(scope.row.customer[0].id)"
@click="openCustomerDetail(scope.row.customerId)"
>
{{ scope.row.customer[0].name }}
{{ scope.row.customerName }}
</el-link>
</template>
</el-table-column>
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="160" />
<!-- <el-table-column
align="center"
label="合同金额(元)"
@ -119,13 +118,13 @@
width="160"
:formatter="dateFormatter2"
/>
<el-table-column
<!-- <el-table-column
align="center"
label="已付款金额(万元)"
prop="totalReceivablePrice"
width="160"
:formatter="erpPriceTableColumnFormatter"
/>
/> -->
<!-- <el-table-column
align="center"
label="未付款金额(元)"
@ -137,14 +136,14 @@
{{ erpPriceInputFormatter(scope.row.totalPrice - scope.row.totalReceivablePrice) }}
</template>
</el-table-column> -->
<el-table-column align="center" label="责任人" prop="ownerUserName" width="160" />
<!-- <el-table-column align="center" label="责任人" prop="ownerUserName" width="160" /> -->
<el-table-column align="center" fixed="right" label="合同状态" prop="contractStatus" width="160">
<el-table-column align="center" label="合同状态" prop="contractStatus" width="160">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_CONTRACT_STATUS" :value="scope.row.contractStatus" />
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="合同类型" prop="contractType" width="160">
<el-table-column align="center" label="合同类型" prop="contractType" width="160">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_CONTRACT_TYPE" :value="scope.row.contractType" />
</template>
@ -160,7 +159,7 @@
>
编辑
</el-button>
<el-button
<!-- <el-button
v-if="scope.row.auditStatus === 0"
v-hasPermi="['crm:contract:update']"
link
@ -168,8 +167,8 @@
@click="handleSubmit(scope.row)"
>
提交审核
</el-button>
<el-button
</el-button> -->
<!-- <el-button
v-else
link
v-hasPermi="['crm:contract:update']"
@ -177,7 +176,7 @@
@click="handleProcessDetail(scope.row)"
>
查看审批
</el-button>
</el-button> -->
<el-button
v-hasPermi="['crm:contract:delete']"
link
@ -329,4 +328,45 @@ onMounted(async () => {
await getList()
customerList.value = await CustomerApi.getCustomerSimpleList()
})
//
const tableRowClassName = ({ row }: { row: any }) => {
if (!row.endTime) return '';
const endTime = new Date(row.endTime);
const today = new Date();
if(row.contractAttribute === 2 ) {
//
return '';
}
//
const diffTime = today.getTime() - endTime.getTime();
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
console.log('diffDays', diffDays, endTime);
if (diffDays <= 90) { // 3
return 'expiring-soon-row';
} else if (diffDays <= 180) { // 6
return 'expiring-row';
}
return '';
};
</script>
<style>
/* 添加行样式 */
.el-table .expiring-row td {
background-color: #fffcde !important; /* 黄色背景 */
}
.el-table .expiring-soon-row td {
background-color: #ffe8e8 !important; /* 红色背景 */
}
/* 如果需要,可以添加悬停效果 */
.el-table .expiring-row:hover>td,
.el-table .expiring-soon-row:hover>td {
opacity: 0.9;
}
</style>

View File

@ -63,7 +63,7 @@
<el-option
v-for="data in contractList"
:key="data.id"
:disabled="data.auditStatus !== 20"
:label="data.name"
:value="data.id!"
/>

View File

@ -1,39 +1,45 @@
<template>
<!-- 操作栏 -->
<el-row justify="end">
<el-button @click="openForm('create')">
<el-row>
<text class="text-xs font-bold">
实付明细
</text>
<!-- <el-button @click="openForm('create')">
<Icon class="mr-5px" icon="icon-park:income-one" />
创建付款
</el-button>
</el-button> -->
</el-row>
<!-- 列表 -->
<ContentWrap class="mt-10px">
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column align="center" label="付款编号" prop="no" />
<el-table-column align="center" label="客户" prop="customerName" />
<el-table-column align="center" label="合同" prop="contract.no" />
<el-table-column
:formatter="dateFormatter2"
align="center"
label="付款日期"
label="实际付款日期"
prop="returnTime"
width="150px"
min-width="150px"
/>
<el-table-column align="center" label="付款方式" prop="returnType" width="130px">
<!-- <el-table-column align="center" label="付款编号" prop="no" /> -->
<el-table-column align="center" label="收款单位" prop="customerName" min-width="300px" />
<el-table-column align="center" label="付款单位" prop="deptName" min-width="300px"/>
<el-table-column align="center" label="对应计划期数" prop="period" min-width="150px" />
<!-- <el-table-column align="center" label="合同" prop="contract.no" /> -->
<el-table-column
align="center"
label="实付金额(元)"
prop="price"
:formatter="erpPriceTableColumnFormatter"
min-width="150px"
/>
<el-table-column align="center" label="付款方式" prop="returnType">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE" :value="scope.row.returnType" />
</template>
</el-table-column>
<el-table-column
align="center"
label="付款金额(元)"
prop="price"
:formatter="erpPriceTableColumnFormatter"
/>
<el-table-column align="center" label="负责人" prop="ownerUserName" />
<el-table-column align="center" label="备注" prop="remark" />
<el-table-column align="center" fixed="right" label="操作" width="130px">
<!-- <el-table-column align="center" label="负责人" prop="ownerUserName" /> -->
<!-- <el-table-column align="center" label="备注" prop="remark" /> -->
<!-- <el-table-column align="center" fixed="right" label="操作" width="130px">
<template #default="scope">
<el-button
v-hasPermi="['crm:receivable:update']"
@ -52,7 +58,7 @@
删除
</el-button>
</template>
</el-table-column>
</el-table-column> -->
</el-table>
<!-- 分页 -->
<Pagination

View File

@ -9,22 +9,24 @@
>
<el-row>
<el-col :span="12">
<el-form-item label="款期数" prop="period">
<el-form-item label="款期数" prop="period">
<el-input v-model="formData.period" disabled placeholder="保存时自动生成" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="负责人" prop="ownerUserId">
<el-form-item label="付款单位">
<el-select
v-model="formData.ownerUserId"
:disabled="formType !== 'create'"
v-model="formData.contractId"
disabled
class="w-1/1"
filterable
placeholder="请选择付款单位"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
v-for="data in contractList"
:key="data.id"
:label="data.deptName"
:value="data.id!"
/>
</el-select>
</el-form-item>
@ -32,14 +34,13 @@
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="客户名称" prop="customerId">
<el-form-item label="收款单位" prop="customerId">
<el-select
v-model="formData.customerId"
:disabled="formType !== 'create'"
disabled
class="w-1/1"
filterable
placeholder="请选择客户"
@change="handleCustomerChange"
placeholder="请选择收款单位"
>
<el-option
v-for="item in customerList"
@ -54,7 +55,7 @@
<el-form-item label="合同名称" prop="contractId">
<el-select
v-model="formData.contractId"
:disabled="formType !== 'create' || !formData.customerId"
disabled
class="w-1/1"
filterable
placeholder="请选择合同"
@ -71,14 +72,14 @@
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="计划付款金额" prop="price">
<el-form-item label="计划金额(元)" prop="price">
<el-input-number
v-model="formData.price"
:min="0.01"
:precision="2"
class="!w-100%"
controls-position="right"
placeholder="请输入计划付款金额"
placeholder="请输入计划金额"
/>
</el-form-item>
</el-col>
@ -95,7 +96,7 @@
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<!-- <el-col :span="12">
<el-form-item label="提前几天提醒" prop="remindDays">
<el-input-number
v-model="formData.remindDays"
@ -116,10 +117,10 @@
/>
</el-select>
</el-form-item>
</el-col>
</el-col> -->
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
<el-form-item label="用途" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入用途" type="textarea" />
</el-form-item>
</el-col>
</el-row>
@ -150,9 +151,7 @@ const formData = ref<ReceivablePlanApi.ReceivablePlanVO>({} as ReceivablePlanApi
const formRules = reactive({
price: [{ required: true, message: '计划付款金额不能为空', trigger: 'blur' }],
returnTime: [{ required: true, message: '计划付款日期不能为空', trigger: 'blur' }],
customerId: [{ required: true, message: '客户编号不能为空', trigger: 'blur' }],
contractId: [{ required: true, message: '合同编号不能为空', trigger: 'blur' }],
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }]
remark: [{ required: true, message: '用途不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref
const customerList = ref<CustomerApi.CustomerVO[]>([]) //
@ -181,9 +180,9 @@ const open = async (type: string, id?: number, customerId?: number, contractId?:
//
customerList.value = await CustomerApi.getCustomerSimpleList()
//
if (formType.value === 'create') {
formData.value.ownerUserId = useUserStore().getUser.id
}
// if (formType.value === 'create') {
// formData.value.ownerUserId = useUserStore().getUser.id
// }
// customerId contractId
if (customerId) {
formData.value.customerId = customerId
@ -231,10 +230,13 @@ const resetForm = () => {
const handleCustomerChange = async (customerId: number) => {
//
formData.value.contractId = undefined
formData.value.deptName = undefined
//
if (customerId) {
contractList.value = []
contractList.value = await ContractApi.getContractSimpleList(customerId)
}
}
</script>

View File

@ -1,45 +1,51 @@
<template>
<!-- 操作栏 -->
<el-row justify="end">
<el-button @click="openForm('create', undefined)">
<Icon class="mr-5px" icon="icon-park:income" />
创建付款计划
</el-button>
<el-row align="middle" class="mb-2">
<el-col :span="12">
<span class="text-xs font-bold">付款计划</span>
</el-col>
<el-col :span="12" class="text-right">
<el-button @click="openForm('create', undefined)">
<Icon class="mr-5px" icon="icon-park:income" />
创建付款计划
</el-button>
</el-col>
</el-row>
<!-- 列表 -->
<ContentWrap class="mt-10px">
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column align="center" label="客户名称" prop="customerName" width="150px" />
<el-table-column align="center" label="合同编号" prop="contractNo" width="200px" />
<el-table-column align="center" label="期数" prop="period" />
<el-table-column
align="center"
label="计划付款(元)"
prop="price"
width="120"
:formatter="erpPriceTableColumnFormatter"
/>
<el-table-column
:formatter="dateFormatter2"
align="center"
label="计划付款日期"
prop="returnTime"
width="180px"
min-width="150px"
/>
<el-table-column align="center" label="提前几天提醒" prop="remindDays" width="150" />
<el-table-column align="center" label="收款单位" prop="customerName" min-width="300px" />
<el-table-column align="center" label="付款单位" prop="deptName" min-width="300px"/>
<el-table-column align="center" label="期数" prop="period" min-width="150px"/>
<el-table-column
align="center"
label="计划付款金额(元)"
prop="price"
min-width="180"
:formatter="erpPriceTableColumnFormatter"
/>
<!-- <el-table-column align="center" label="提前几天提醒" prop="remindDays" width="150" />
<el-table-column
:formatter="dateFormatter2"
align="center"
label="提醒日期"
prop="remindTime"
width="180px"
/>
<el-table-column label="负责人" prop="ownerUserName" width="120" />
<el-table-column align="center" label="备注" prop="remark" />
/> -->
<!-- <el-table-column label="负责人" prop="ownerUserName" width="120" /> -->
<el-table-column align="center" label="用途" prop="remark" min-width="300px"/>
<el-table-column align="center" fixed="right" label="操作" width="200px">
<template #default="scope">
<el-button
<!-- <el-button
v-hasPermi="['crm:receivable:create']"
link
type="primary"
@ -47,7 +53,7 @@
:disabled="scope.row.receivableId"
>
创建付款
</el-button>
</el-button> -->
<el-button
v-hasPermi="['crm:receivable-plan:update']"
link

View File

@ -44,7 +44,7 @@
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button
<!-- <el-button
v-hasPermi="['crm:receivable-plan:create']"
plain
type="primary"
@ -52,7 +52,7 @@
>
<Icon class="mr-5px" icon="ep:plus" />
新增
</el-button>
</el-button> -->
<el-button
v-hasPermi="['crm:receivable-plan:export']"
:loading="exportLoading"
@ -69,12 +69,15 @@
<!-- 列表 -->
<ContentWrap>
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane label="我负责的" name="1" />
<el-tab-pane label="下属负责的" name="3" />
</el-tabs>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column align="center" fixed="left" label="客户名称" prop="customerName" width="150">
<el-table-column
:formatter="dateFormatter2"
align="center"
label="计划付款日期"
prop="returnTime"
width="180px"
/>
<el-table-column align="center" label="收款单位名称" prop="customerName" width="150">
<template #default="scope">
<el-link
:underline="false"
@ -85,7 +88,17 @@
</el-link>
</template>
</el-table-column>
<el-table-column align="center" label="合同编号" prop="contractNo" width="200px" />
<el-table-column align="center" label="合同名称" prop="contractName" width="200px" >
<template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openContractDetail(scope.row.contractId)"
>
{{ scope.row.contractName }}
</el-link>
</template>
</el-table-column>
<el-table-column align="center" label="期数" prop="period">
<template #default="scope">
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
@ -101,30 +114,8 @@
:formatter="erpPriceTableColumnFormatter"
/>
<el-table-column
:formatter="dateFormatter2"
align="center"
label="计划付款日期"
prop="returnTime"
width="180px"
/>
<el-table-column align="center" label="提前几天提醒" prop="remindDays" width="150" />
<el-table-column
align="center"
label="提醒日期"
prop="remindTime"
width="180px"
:formatter="dateFormatter2"
/>
<el-table-column align="center" label="付款方式" prop="returnType" width="130px">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE" :value="scope.row.returnType" />
</template>
</el-table-column>
<el-table-column align="center" label="备注" prop="remark" />
<el-table-column label="负责人" prop="ownerUserName" width="120" />
<el-table-column
align="center"
label="实际付款金额(元)"
label="已付款金额(元)"
prop="receivable.price"
width="160"
>
@ -135,16 +126,16 @@
<el-text v-else>{{ erpPriceInputFormatter(0) }}</el-text>
</template>
</el-table-column>
<el-table-column
<!-- <el-table-column
align="center"
label="实际付款日期"
prop="receivable.returnTime"
width="180px"
:formatter="dateFormatter2"
/>
/> -->
<el-table-column
align="center"
label="实际付款金额(元)"
label="付款金额(元)"
prop="receivable.price"
width="160"
>
@ -155,21 +146,6 @@
<el-text v-else>{{ erpPriceInputFormatter(scope.row.price) }}</el-text>
</template>
</el-table-column>
<el-table-column
:formatter="dateFormatter"
align="center"
label="更新时间"
prop="updateTime"
width="180px"
/>
<el-table-column
:formatter="dateFormatter"
align="center"
label="创建时间"
prop="createTime"
width="180px"
/>
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
<el-table-column align="center" fixed="right" label="操作" width="180px">
<template #default="scope">
<el-button
@ -245,11 +221,6 @@ const exportLoading = ref(false) // 导出的加载中
const activeName = ref('1') // tab
const customerList = ref<CustomerApi.CustomerVO[]>([]) //
/** tab 切换 */
const handleTabClick = (tab: TabsPaneContext) => {
queryParams.sceneType = tab.paneName
handleQuery()
}
/** 查询列表 */
const getList = async () => {
@ -325,6 +296,10 @@ const openDetail = (id: number) => {
const openCustomerDetail = (id: number) => {
push({ name: 'CrmCustomerDetail', params: { id } })
}
/** 打开合同详情 */
const openContractDetail = (id: number) => {
push({ name: 'CrmContractDetail', params: { id } })
}
/** 初始化 **/
onMounted(async () => {

View File

@ -0,0 +1,162 @@
<template>
<Dialog v-model="dialogVisible" :title="dialogTitle">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="110px"
>
<el-row>
<el-col :span="12">
<el-form-item label="付款单位">
<el-select
v-model="formData.contractId"
class="w-1/1"
filterable
placeholder="请选择付款单位"
>
<el-option
v-for="data in deptList"
:key="data.id"
:label="data.name"
:value="data.id!"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="收款单位" prop="customerName">
<el-input
v-model="formData.customerName"
placeholder="请输入收款单位"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="计划金额(元)" prop="price">
<el-input-number
v-model="formData.price"
:min="0.01"
:precision="2"
class="!w-100%"
controls-position="right"
placeholder="请输入计划金额"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计划付款日期" prop="returnTime">
<el-date-picker
v-model="formData.returnTime"
placeholder="选择计划付款日期"
type="date"
value-format="x"
class="!w-100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="用途" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入用途" type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
import * as UserApi from '@/api/system/user'
import * as CustomerApi from '@/api/crm/customer'
import * as ContractApi from '@/api/crm/contract'
import { useUserStore } from '@/store/modules/user'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { cloneDeep } from 'lodash-es'
import * as DeptApi from '@/api/system/dept'
import { de } from 'element-plus/es/locale'
const { t } = useI18n() //
const message = useMessage() //
const userOptions = ref<UserApi.UserVO[]>([]) //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref<ReceivablePlanApi.ReceivablePlanVO>({} as ReceivablePlanApi.ReceivablePlanVO)
const formRules = reactive({
price: [{ required: true, message: '计划付款金额不能为空', trigger: 'blur' }],
returnTime: [{ required: true, message: '计划付款日期不能为空', trigger: 'blur' }],
remark: [{ required: true, message: '用途不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref
const deptList = ref<DeptApi.DeptVO[]>([])
/** 打开弹窗 */
const open = async (type: string, id?: number, customerId?: number, contractId?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
const data = await ReceivablePlanApi.getReceivablePlan(id)
formData.value = cloneDeep(data)
formData.value.contractId = data?.contractId
} finally {
formLoading.value = false
}
}
//
userOptions.value = await UserApi.getSimpleUserList()
//
deptList.value = await DeptApi.querySimpleDeptList()
//
// if (formType.value === 'create') {
// formData.value.ownerUserId = useUserStore().getUser.id
// }
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
try {
const data = formData.value as unknown as ReceivablePlanApi.ReceivablePlanVO
if (formType.value === 'create') {
await ReceivablePlanApi.createReceivablePlan(data)
message.success(t('common.createSuccess'))
} else {
await ReceivablePlanApi.updateReceivablePlan(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {} as ReceivablePlanApi.ReceivablePlanVO
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,276 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="68px"
>
<el-form-item label="客户名称" prop="customerId">
<el-select
v-model="queryParams.customerId"
class="!w-240px"
placeholder="请选择客户"
@keyup.enter="handleQuery"
>
<el-option
v-for="item in customerList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="合同编号" prop="contractNo">
<el-input
v-model="queryParams.contractNo"
class="!w-240px"
clearable
placeholder="请输入合同编号"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button
v-hasPermi="['crm:receivable-plan:create']"
plain
type="primary"
@click="openForm('create')"
>
<Icon class="mr-5px" icon="ep:plus" />
新增
</el-button>
<el-button
v-hasPermi="['crm:receivable-plan:export']"
:loading="exportLoading"
plain
type="success"
@click="handleExport"
>
<Icon class="mr-5px" icon="ep:download" />
导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column
:formatter="dateFormatter2"
align="center"
label="计划付款日期"
prop="returnTime"
width="180px"
/>
<el-table-column align="center" label="付款单位" prop="deptName" min-width="300">
<template #default="scope">
{{ scope.row.deptName }}
</template>
</el-table-column>
<el-table-column align="center" label="收款单位" prop="customerName" min-width="300">
<template #default="scope">
{{ scope.row.customerName }}
</template>
</el-table-column>
<el-table-column
align="center"
label="计划付款金额(元)"
prop="price"
width="160"
:formatter="erpPriceTableColumnFormatter"
/>
<el-table-column
align="center"
label="已付款金额(元)"
prop="receivable.price"
width="160"
>
<template #default="scope">
<el-text v-if="scope.row.receivable">
{{ erpPriceInputFormatter(scope.row.receivable.price) }}
</el-text>
<el-text v-else>{{ erpPriceInputFormatter(0) }}</el-text>
</template>
</el-table-column>
<!-- <el-table-column
align="center"
label="实际付款日期"
prop="receivable.returnTime"
width="180px"
:formatter="dateFormatter2"
/> -->
<el-table-column
align="center"
label="应付款金额(元)"
prop="receivable.price"
width="160"
>
<template #default="scope">
<el-text v-if="scope.row.receivable">
{{ erpPriceInputFormatter(scope.row.price - scope.row.receivable.price) }}
</el-text>
<el-text v-else>{{ erpPriceInputFormatter(scope.row.price) }}</el-text>
</template>
</el-table-column>
<el-table-column align="center" label="创建人" prop="creatorName" width="150">
<template #default="scope">
{{ scope.row.creatorName }}
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="180px">
<template #default="scope">
<el-button
v-hasPermi="['crm:receivable-plan:update']"
link
type="primary"
@click="openForm('update', scope.row.id)"
>
编辑
</el-button>
<el-button
v-hasPermi="['crm:receivable-plan:delete']"
link
type="danger"
@click="handleDelete(scope.row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<RequestForm ref="formRef" @success="getList" />
</template>
<script lang="ts" setup>
import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
import download from '@/utils/download'
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
import * as CustomerApi from '@/api/crm/customer'
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
import { TabsPaneContext } from 'element-plus'
import RequestForm from '@/views/crm/receivable/request/RequestForm.vue'
defineOptions({ name: 'ExpenseRequest' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const total = ref(0) //
const list = ref([]) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
sceneType: '1', // activeName
customerId: undefined,
contractNo: undefined
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
const activeName = ref('1') // tab
const customerList = ref<CustomerApi.CustomerVO[]>([]) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await ReceivablePlanApi.getReceivablePlanPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await ReceivablePlanApi.deleteReceivablePlan(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await ReceivablePlanApi.exportReceivablePlan(queryParams)
download.excel(data, '付款计划.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 打开详情 */
const { push } = useRouter()
const openDetail = (id: number) => {
push({ name: 'CrmReceivablePlanDetail', params: { id } })
}
/** 打开客户详情 */
const openCustomerDetail = (id: number) => {
push({ name: 'CrmCustomerDetail', params: { id } })
}
/** 初始化 **/
onMounted(async () => {
await getList()
//
customerList.value = await CustomerApi.getCustomerSimpleList()
})
</script>

View File

@ -22,83 +22,11 @@
<!-- 附件标签页 -->
<el-tab-pane label="附件">
<div v-if="loading" class="loading-container">
<el-skeleton :rows="5" animated />
</div>
<div v-else class="attachment-container">
<!-- 图片展示部分 -->
<div class="section" v-if="stationSite.picUrls?.length">
<h3 class="section-title">图片</h3>
<div class="image-list">
<div
v-for="(url, index) in stationSite.picUrls"
:key="index"
class="image-item"
>
<el-image
:src="url"
:preview-src-list="stationSite.picUrls"
:initial-index="index"
fit="cover"
class="thumbnail"
>
<template #error>
<div class="image-error">
<span>加载失败</span>
</div>
</template>
</el-image>
</div>
</div>
</div>
<!-- 文件展示部分 -->
<div class="section" v-if="stationSite.fileUrls?.length">
<h3 class="section-title">文件</h3>
<div class="file-list">
<div
v-for="(url, index) in stationSite.fileUrls"
:key="index"
class="file-item"
>
<el-icon class="file-icon"><Document /></el-icon>
<div class="file-info">
<a
:href="url"
target="_blank"
class="file-name"
@click.prevent="downloadFile(url)"
>
{{ extractFileName(url) }}
</a>
<div class="file-actions">
<el-button
type="primary"
link
@click="downloadFile(url)"
>
下载
</el-button>
<el-button
type="info"
link
@click="previewFile(url)"
>
预览
</el-button>
</div>
</div>
</div>
</div>
</div>
<!-- 空状态显示 -->
<el-empty
v-if="!stationSite.picUrls?.length && !stationSite.fileUrls?.length"
description="暂无附件数据"
/>
</div>
<AttachmentTab
:loading="loading"
:pic-urls="stationSite.picUrls"
:file-urls="stationSite.fileUrls"
/>
</el-tab-pane>
<!-- <el-tabs>
<el-tab-pane label="操作日志">
@ -128,6 +56,7 @@ import FollowUpList from '@/views/crm/followup/components/FollowUpList.vue'
import StationSiteForm from '../StationSiteForm.vue'
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
import { ElMessage, ElLoading } from 'element-plus'
import AttachmentTab from '@/components/AttachmentFile/index.vue'
const route = useRoute()
const stationSiteId = ref<number>()

View File

@ -24,7 +24,17 @@
<el-form-item label="显示排序" prop="sort">
<el-input-number v-model="formData.sort" :min="0" controls-position="right" />
</el-form-item>
<el-form-item label="负责人" prop="leaderUserId">
<el-form-item label="部门类型" prop="type">
<el-select v-model="formData.type" clearable placeholder="请选择类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_DEPT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<!-- <el-form-item label="负责人" prop="leaderUserId">
<el-select v-model="formData.leaderUserId" clearable placeholder="请输入负责人">
<el-option
v-for="item in userList"
@ -33,14 +43,14 @@
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form-item> -->
<el-form-item label="联系电话" prop="phone">
<el-input v-model="formData.phone" maxlength="11" placeholder="请输入联系电话" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" maxlength="50" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-form-item label="状态" prop="status" width="300px">
<el-select v-model="formData.status" clearable placeholder="请选择状态">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
@ -83,13 +93,15 @@ const formData = ref({
leaderUserId: undefined,
phone: undefined,
email: undefined,
status: CommonStatusEnum.ENABLE
status: CommonStatusEnum.ENABLE,
type: undefined
})
const formRules = reactive<FormRules>({
parentId: [{ required: true, message: '上级部门不能为空', trigger: 'blur' }],
name: [{ required: true, message: '部门名称不能为空', trigger: 'blur' }],
sort: [{ required: true, message: '显示排序不能为空', trigger: 'blur' }],
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
type: [{ required: true, message: '部门类型不能为空', trigger: ['blur', 'change'] }],
phone: [
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
],

View File

@ -59,25 +59,31 @@
:default-expand-all="isExpandAll"
v-if="refreshTable"
>
<el-table-column prop="name" label="部门名称" />
<el-table-column prop="name" label="部门名称" width="500px" />
<!--
<el-table-column prop="leader" label="负责人">
<template #default="scope">
{{ userList.find((user) => user.id === scope.row.leaderUserId)?.nickname }}
</template>
</el-table-column>
</el-table-column> -->
<el-table-column prop="sort" label="排序" />
<el-table-column prop="status" label="状态">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column
<el-table-column label="部门类型" align="center" prop="type" width="150px">
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_DEPT_TYPE" :value="scope.row.type" width="300px"/>
</template>
</el-table-column>
<!-- <el-table-column
label="创建时间"
align="center"
prop="createTime"
width="180"
:formatter="dateFormatter"
/>
/> -->
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button