浏览代码

feat: ✨ 完成合同管理详情

@sun-chaoqun 2 年之前
父节点
当前提交
85efa8b781

+ 2 - 2
src/api/index.ts

@@ -1,6 +1,6 @@
 import axios from 'axios'
 import type { AxiosInstance, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
-import { ElMessage, ElLoading, ElNotification } from 'element-plus'
+import { ElMessage, ElNotification } from 'element-plus'
 import { ResultEnum, ResultData, ContentType } from './interface/index'
 import { GlobalStore } from '@/stores/index'
 import router from '@/router/index'
@@ -12,7 +12,7 @@ let loadingInstance: LoadingType = {}
 
 const config = {
   // 默认地址请求地址,可在 .env.*** 文件中修改
-  // baseURL: import.meta.env.VITE_BZD_ERP_APP_API as string,
+  baseURL: process.env.NODE_ENV ? '' : (import.meta.env.VITE_BZD_ERP_APP_API as string),
   // 设置超时时间(10s)
   timeout: ResultEnum.TIMEOUT as number,
   // 跨域时候允许携带凭证

+ 21 - 1
src/components/Drawer/index.vue

@@ -35,11 +35,17 @@ defineExpose({ closeDrawer, openDrawer })
       :size="size"
     >
       <template #header="{ close, titleId, titleClass }">
-        <slot name="header" :params="{ close, titleId, titleClass }"></slot>
+        <div class="header">
+          <slot name="header" :params="{ close, titleId, titleClass }"></slot>
+          <el-divider border-style="dashed" />
+        </div>
       </template>
       <div class="drawer__content">
         <slot></slot>
       </div>
+      <template #footer>
+        <slot name="footer"></slot>
+      </template>
     </el-drawer>
   </div>
 </template>
@@ -48,4 +54,18 @@ defineExpose({ closeDrawer, openDrawer })
 .drawer__content {
   height: 100%;
 }
+.header {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+:deep(.el-drawer__header) {
+  display: flex;
+  align-items: start;
+  margin-bottom: 0;
+}
+:deep(.el-drawer__footer) {
+  z-index: 1;
+  /* box-shadow: 0px -2px 5px #ccc; */
+}
 </style>

+ 55 - 0
src/hooks/useTablePublic.ts

@@ -1,3 +1,5 @@
+import type { FormInstance } from 'element-plus'
+
 export interface LeaveUserInfoIn {
   T_user_name: string
   T_dept: string
@@ -24,6 +26,21 @@ export interface OvertimeUserInfoIn {
   T_prove_img: string
 }
 
+export interface ContractFormType {
+  T_uuid: string
+  T_number: string
+  T_customer: string
+  T_product: any
+  T_money: string
+  T_date: string
+  T_remark: string
+  T_pdf: string
+  T_submit: string
+  T_recoveries: string
+  T_invoice: string
+  T_submit_name: string
+}
+
 export const Project_State = [
   { id: 1, name: '待审核' },
   { id: 2, name: '进行中' },
@@ -35,6 +52,38 @@ export const Project_F_State = [
   { id: 4, name: '已发绩效' }
 ]
 
+export const columns = [
+  { type: 'index', label: '序号', width: 80, align: 'center ' },
+  { label: '产品图片', prop: 'T_img', align: 'center ', name: 'T_img' },
+  { label: '产品名称', prop: 'T_name', align: 'center ' },
+  { label: '产品分类', prop: 'T_class_name', align: 'center ' },
+  { label: '产品型号', prop: 'T_model', align: 'center ' },
+  { label: '产品规格', prop: 'T_spec', align: 'center ' },
+  { label: '是否关联SN', prop: 'T_relation_sn', align: 'center ', width: 120, name: 'T_relation_sn' },
+  { label: '*数量', prop: 'count', align: 'center ', name: 'count' },
+  { prop: 'operation', label: '操作', width: 80, fixed: 'right' }
+]
+export const columnsRecoveries = [
+  { type: 'index', label: '序号', width: 80, align: 'center ' },
+  { label: '回款时间', prop: 'T_date', align: 'center ' },
+  { label: '回款金额', prop: 'T_money', align: 'center ' },
+  { prop: 'operation', label: '操作', width: 150, fixed: 'right' }
+]
+export const labelsRecoveries = {
+  date: '回款时间',
+  money: '回款金额'
+}
+export const columnsInvoice = [
+  { type: 'index', label: '序号', width: 80, align: 'center ' },
+  { label: '开票时间', prop: 'T_date', align: 'center ' },
+  { label: '开票金额', prop: 'T_money', align: 'center ' },
+  { prop: 'operation', label: '操作', width: 150, fixed: 'right' }
+]
+export const labelsInvoice = {
+  date: '开票时间',
+  money: '开票金额'
+}
+
 export function useTablePublic() {
   //判断是否相等,相同时改变背景颜色
   const tableRowClassName = (T_uuid: string, T_uuid2: string): any => {
@@ -52,7 +101,13 @@ export function useTablePublic() {
       return ''
     }
   }
+  const resetForm = (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    formEl.resetFields()
+  }
+
   return {
+    resetForm,
     tableRowClassName
   }
 }

+ 6 - 1
src/layouts/Header/Breadcrumb.vue

@@ -60,7 +60,12 @@ const onBreadcrumbClick = (item: any, index: number) => {
 <template>
   <div class="breadcrumb-box">
     <el-breadcrumb :separator-icon="ArrowRight">
-      <transition-group appear name="breadcrumb">
+      <!-- animate__slideInUp -->
+      <transition-group
+        appear
+        leave-active-class="animate__animated animate__slideInUp"
+        enter-active-class="animate__animated animate__slideInDown"
+      >
         <el-breadcrumb-item
           v-for="(item, index) in breadcrumbList"
           :key="item.path"

+ 1 - 1
src/views/salary/salary/relus.ts

@@ -2,7 +2,7 @@ import type { FormRules } from 'element-plus'
 
 export const floatReg = /^[-\+]?\d+(\.\d+)?$/
 
-const validate_float = () => {
+export const validate_float = () => {
   return (rule: any, value: any, callback: any) => {
     if (value === '') {
       callback(new Error('请输入金额'))

+ 0 - 1
src/views/storehouse/Classify.vue

@@ -148,7 +148,6 @@ const searchHandle = () => {
         <h4 :id="params.titleId" :class="params.titleClass">{{ isNew ? '添加' : '编辑' }} - 产品分类</h4>
       </template>
       <el-form ref="ruleFormRef" :model="form" :rules="rules">
-        <el-divider border-style="dashed" />
         <el-form-item label="产品分类名称:" :label-width="formLabelWidth" prop="name">
           <el-input v-model="form.name" type="text" autocomplete="off" placeholder="请输入产品分类名称" />
         </el-form-item>

+ 0 - 1
src/views/storehouse/InventoryStatistics.vue

@@ -308,7 +308,6 @@ const getSalaryParams = (row: any) => {
       <template #header="{ params }">
         <h4 :id="params.titleId" :class="params.titleClass">库存明细</h4>
       </template>
-      <el-divider />
       <TableBase
         border
         ref="TableDetailRef"

+ 0 - 1
src/views/storehouse/List.vue

@@ -142,7 +142,6 @@ const searchHandle = () => {
         <h4 :id="params.titleId" :class="params.titleClass">{{ isNew ? '添加' : '编辑' }} - 仓库名称</h4>
       </template>
       <el-form ref="ruleFormRef" :model="form" :rules="rules">
-        <el-divider border-style="dashed" />
         <el-form-item label="仓库名称:" :label-width="formLabelWidth" prop="name">
           <el-input v-model="form.name" type="text" autocomplete="off" placeholder="请输入仓库名称" />
         </el-form-item>

+ 0 - 1
src/views/storehouse/ProductionList.vue

@@ -212,7 +212,6 @@ onMounted(() => {
         <h4 :id="params.titleId" :class="params.titleClass">{{ isNew ? '添加' : '编辑' }} - 产品</h4>
       </template>
       <el-form ref="ruleFormRef" :model="form" :rules="rules">
-        <el-divider border-style="dashed" />
         <el-form-item label="产品名称:" class="m-b-6" :label-width="formLabelWidth" prop="T_name">
           <el-input v-model="form.T_name" type="text" autocomplete="off" placeholder="请输入产品名称" />
         </el-form-item>

+ 0 - 1
src/views/storehouse/inventory/InStorageForm.vue

@@ -218,7 +218,6 @@ defineExpose({
         <h4 :id="params.titleId" :class="params.titleClass">入库</h4>
       </template>
       <el-form ref="ruleFormRef" :model="form" :rules="rules">
-        <el-divider border-style="dashed" />
         <el-form-item label="入库单号:" :label-width="formLabelWidth" prop="T_number">
           <el-input v-model="form.T_number" type="text" :disabled="true" placeholder="系统自动生成" class="w-50" />
         </el-form-item>

+ 0 - 1
src/views/storehouse/outStock/OutStock.vue

@@ -159,7 +159,6 @@ const { options } = depotHooks()
         <h4 :id="params.titleId" :class="params.titleClass">发货管理</h4>
       </template>
       <el-form ref="ruleFormRef" :model="form" :rules="rules">
-        <el-divider border-style="dashed" />
         <el-form-item label="送货方式:" :label-width="formLabelWidth" prop="T_delivery_type">
           <el-select v-model="form.T_delivery_type" clearable placeholder="请选择送货方式~">
             <el-option v-for="item in delivery_type" :key="item.id" :label="item.name" :value="item.id" />

+ 73 - 29
src/views/storehouse/sales/ContractDetail.vue

@@ -23,15 +23,25 @@ interface InfoType {
   T_submit: string
   T_submit_name: string
   T_type: number
+  T_no_recoveries_money: number
+  T_no_invoice_money: number
 }
 
 const route = useRoute()
 const router = useRouter()
 const isSale = ref(false)
 const tableSnData = ref<any[]>([])
+const tableData = ref<any[]>([])
+const invoiceTableData = ref<any[]>([])
+const recoveriesTableData = ref<any[]>([])
 const info = ref<InfoType | undefined>()
 const globalStore = GlobalStore()
 const drawerSnRef = ref<InstanceType<typeof Drawer> | null>(null)
+const headerCellStyle = ref({
+  background: '#909399',
+  height: '50px',
+  color: '#fff'
+})
 
 const columns = [
   { type: 'index', label: '序号', width: 80, align: 'center ' },
@@ -46,6 +56,18 @@ const columns = [
   { prop: 'operation', label: '操作', width: 100, fixed: 'right', align: 'center ' }
 ]
 
+const columnsRecoveries = [
+  { type: 'index', label: '序号', width: 80, align: 'center ' },
+  { label: '回款时间', prop: 'T_date', align: 'center ' },
+  { label: '回款金额', prop: 'T_money', align: 'center ' }
+]
+
+const columnsInvoice = [
+  { type: 'index', label: '序号', width: 80, align: 'center ' },
+  { label: '开票时间', prop: 'T_date', align: 'center ' },
+  { label: '开票金额', prop: 'T_money', align: 'center ' }
+]
+
 const snColumns = [
   { type: 'index', label: '序号', width: 80, align: 'center ' },
   { label: 'SN', prop: 'sn', align: 'center ' }
@@ -55,7 +77,10 @@ const getStorehouseContractGet = async () => {
   const res: any = await Storehouse_Contract_Get({ User_tokey: globalStore.GET_User_tokey, T_number: route.params.id })
   if (res.Code === 200) {
     info.value = res.Data
-    tableData.value = res.Data.T_Product
+    const { T_Product, T_invoice, T_recoveries } = res.Data
+    tableData.value = T_Product
+    invoiceTableData.value = T_invoice
+    recoveriesTableData.value = T_recoveries
   }
 }
 const previewPdf = (str: string) => window.open(str)
@@ -81,7 +106,6 @@ const getState = (val: number, type: string) => {
       return type === 'T_State' ? '待审核' : '已全部出库'
   }
 }
-const tableData = ref<any[]>([])
 
 /**
  * 回调
@@ -130,25 +154,9 @@ onUnmounted(() => {
           >
         </el-row>
         <el-row>
-          <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2"> <span>合同类型</span></el-col>
-          <el-col :xs="11" :sm="9" :md="7" :lg="6" :xl="5"
-            ><span>{{ info?.T_type! === 1 ? '销售合同' : '验证合同' }}</span></el-col
-          >
-        </el-row>
-        <el-row>
           <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2"><span>产品明细</span></el-col>
           <el-col :span="21">
-            <el-table
-              :data="tableData"
-              style="width: 100%"
-              border
-              stripe
-              :header-cell-style="{
-                background: '#909399',
-                height: '50px',
-                color: '#fff'
-              }"
-            >
+            <el-table border stripe :data="tableData" style="width: 100%" :header-cell-style="headerCellStyle">
               <template v-for="item in columns" :key="item.prop">
                 <el-table-column v-bind="item" v-if="item.fixed !== 'right'">
                   <template #default="{ row }" v-if="item.prop === item.name">
@@ -191,12 +199,18 @@ onUnmounted(() => {
         <el-row>
           <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2"> <span>合同金额</span></el-col>
           <el-col :xs="11" :sm="9" :md="7" :lg="6" :xl="5">
-            <!-- <span>{{ info.T_money }}</span> -->
             <el-text type="danger">{{ info?.T_money! }}¥</el-text>
           </el-col>
         </el-row>
 
         <el-row>
+          <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2"> <span>项目</span></el-col>
+          <el-col :xs="11" :sm="9" :md="7" :lg="6" :xl="5">
+            <span>{{ info?.T_money }}</span>
+          </el-col>
+        </el-row>
+
+        <el-row>
           <!-- 1-未出库 2-已部分出库 3-已全部出库 -->
           <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2"><span>出库状态</span></el-col>
           <el-col :xs="11" :sm="9" :md="7" :lg="6" :xl="5"
@@ -225,6 +239,43 @@ onUnmounted(() => {
             <el-button v-else type="primary" @click="previewPdf(info?.T_pdf!)">查看附件</el-button></el-col
           >
         </el-row>
+
+        <el-row>
+          <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2"><span>回款明细</span></el-col>
+          <el-col :span="21">
+            <el-table
+              border
+              stripe
+              :data="recoveriesTableData"
+              style="width: 100%"
+              :header-cell-style="headerCellStyle"
+            >
+              <el-table-column v-bind="item" v-for="item in columnsRecoveries" :key="item.prop"></el-table-column>
+            </el-table>
+          </el-col>
+        </el-row>
+
+        <el-row>
+          <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2"><span>未回款金额</span></el-col>
+          <el-col :xs="11" :sm="9" :md="7" :lg="6" :xl="5">
+            <el-text type="danger">{{ info?.T_no_recoveries_money! }}¥</el-text>
+          </el-col>
+        </el-row>
+
+        <el-row>
+          <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2"><span>开票明细</span></el-col>
+          <el-col :span="21">
+            <el-table border stripe :data="invoiceTableData" style="width: 100%" :header-cell-style="headerCellStyle">
+              <el-table-column v-bind="item" v-for="item in columnsInvoice" :key="item.prop"></el-table-column>
+            </el-table>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="2"><span>未开票金额</span></el-col>
+          <el-col :xs="11" :sm="9" :md="7" :lg="6" :xl="5"
+            ><el-text type="danger">{{ info?.T_no_invoice_money! }}¥</el-text></el-col
+          >
+        </el-row>
       </div>
 
       <el-divider />
@@ -239,14 +290,7 @@ onUnmounted(() => {
       </div>
     </div>
     <Drawer ref="drawerSnRef" :handleClose="callbackSnDrawer" size="30%">
-      <el-table
-        :data="tableSnData"
-        style="width: 100%; height: 99%"
-        :header-cell-style="{
-          background: '#dedfe0',
-          height: '50px'
-        }"
-      >
+      <el-table :data="tableSnData" style="width: 100%; height: 99%" :header-cell-style="headerCellStyle">
         <template v-for="item in snColumns" :key="item">
           <el-table-column v-if="item.type === 'index'" v-bind="item" />
           <el-table-column v-if="item.prop" v-bind="item" />
@@ -272,7 +316,7 @@ onUnmounted(() => {
     }
     & .content {
       height: calc(100% - 72px - 25px - 40px);
-      overflow-y: scroll;
+      overflow-y: auto;
       .el-row {
         margin-bottom: 16px;
       }

+ 123 - 83
src/views/storehouse/sales/ContractForm.vue

@@ -1,31 +1,46 @@
 <script setup lang="ts">
-import { ref, reactive, nextTick } from 'vue'
-import Drawer from '@/components/Drawer/index.vue'
-import type { FormInstance, FormRules } from 'element-plus'
-import { Delete } from '@element-plus/icons-vue'
-import { GlobalStore } from '@/stores/index'
-import Upload from '@/components/Upload/index.vue'
 import {
+  columns,
+  ContractFormType,
+  useTablePublic,
+  columnsRecoveries,
+  labelsRecoveries,
+  columnsInvoice,
+  labelsInvoice
+} from '@/hooks/useTablePublic'
+import {
+  Storehouse_Contract_Get,
   Storehouse_Contract_Add,
   Storehouse_Contract_Edit,
-  Storehouse_Contract_Product_List,
   Storehouse_Contract_Gen_Number
 } from '@/api/storehouse/index'
 import { ElMessage } from 'element-plus'
+import TableDetail from './TableDetail.vue'
+import { GlobalStore } from '@/stores/index'
+import { ref, reactive, nextTick } from 'vue'
+import Drawer from '@/components/Drawer/index.vue'
+import { Delete } from '@element-plus/icons-vue'
+import Upload from '@/components/Upload/index.vue'
 import ImageCom from '@/components/Image/index.vue'
-import InStorageProduct from '@/views/storehouse/inventory/InStorageProduct.vue'
+import type { FormInstance, FormRules } from 'element-plus'
 import ContractUser from '@/views/storehouse/outStock/receiveUser.vue'
+import InStorageProduct from '@/views/storehouse/inventory/InStorageProduct.vue'
 
+const isNew = ref(true)
 const isProduct = ref(false)
+const tableData = ref<any[]>([])
 let selectProductData: any[] = []
-const isNew = ref(true)
 const globalStore = GlobalStore()
-const User_tokey = globalStore.GET_User_tokey
 const formLabelWidth = ref('120px')
+const { resetForm } = useTablePublic()
 const ruleFormRef = ref<FormInstance>()
+const contractNumberLoading = ref(false)
+const User_tokey = globalStore.GET_User_tokey
 const drawerRef = ref<InstanceType<typeof Drawer> | null>(null)
 const uploadRef = ref<InstanceType<typeof Upload> | null>(null)
 const receiveUserdialog = ref<InstanceType<typeof ContractUser> | null>(null)
+const RecoveriesRef = ref<InstanceType<typeof TableDetail> | null>(null)
+const InvoiceRef = ref<InstanceType<typeof TableDetail> | null>(null)
 
 const validate_T_product = (rule: any, value: any, callback: any) => {
   if (value === '') {
@@ -38,56 +53,37 @@ const validate_T_product = (rule: any, value: any, callback: any) => {
 }
 
 const rules = reactive<FormRules>({
-  T_number: [{ required: true, message: '请输入物联网卡号', trigger: 'blur' }],
+  T_number: [{ required: true, message: '请输入合同编号', trigger: 'blur' }],
   T_customer: [{ required: true, message: '请输入客户名称', trigger: 'blur' }],
   T_type: [{ required: true, message: '请选择合同类型', trigger: 'blur' }],
   T_product: [{ validator: validate_T_product, trigger: 'blur' }],
   T_money: [{ required: true, message: '请输入合同金额', trigger: 'blur' }],
-  T_date: [{ required: true, message: '请选择业务日期', trigger: 'blur' }]
+  T_date: [{ required: true, message: '请选择签订时间', trigger: 'blur' }],
+  T_submit: [{ required: true, message: '请选择合同负责人', trigger: 'change' }],
+  T_submit_name: [{ required: true, message: '请输入项目名称', trigger: 'blur' }]
 })
 
 const callbackDrawer = (done: () => void) => {
-  resetForm(ruleFormRef.value)
-  isNew.value = true
-  selectProductData = []
-  drawerProductRef.value?.clearSelection()
-  isProduct.value = false
+  closeCancle()
   done()
 }
 
-const resetForm = (formEl: FormInstance | undefined) => {
-  if (!formEl) return
-  tableData.value = []
-  formEl.resetFields()
-}
-
 // 父级方法
 const emit = defineEmits<{ (event: 'onTableList'): void }>()
 
-interface FormType {
-  T_uuid: string
-  T_number: string
-  T_customer: string
-  T_type: any
-  T_product: any
-  T_money: string
-  T_date: string
-  T_remark: string
-  T_pdf: string
-  T_submit: string
-}
-
-const form = reactive<FormType>({
+const form = reactive<ContractFormType>({
   T_uuid: '',
   T_number: '',
   T_customer: '',
-  T_type: '',
   T_product: '',
   T_money: '',
   T_date: '',
   T_remark: '',
   T_pdf: '',
-  T_submit: ''
+  T_submit: '',
+  T_recoveries: '',
+  T_invoice: '',
+  T_submit_name: ''
 })
 
 const openDrawer = (type: string, row?: any) => {
@@ -101,21 +97,23 @@ const openDrawer = (type: string, row?: any) => {
 }
 // edit data echo
 const editDataEcho = async (row: any) => {
+  form.T_uuid = row.T_submit
   form.T_pdf = row.T_pdf
   form.T_date = row.T_date
-  form.T_type = row.T_type
   form.T_money = row.T_money
-  form.T_remark = row.T_remark
+  form.T_submit = row.T_submit_name
   form.T_number = row.T_number
   form.T_customer = row.T_customer
 
-  const res: any = await Storehouse_Contract_Product_List({
+  const res: any = await Storehouse_Contract_Get({
     User_tokey,
     T_number: row.T_number
   })
   if (res.Code === 200) {
-    res.Data &&
-      (tableData.value = res.Data.map((item: any) => {
+    const { T_Product, T_invoice, T_recoveries, T_remark } = res.Data
+    form.T_remark = T_remark
+    T_Product &&
+      (tableData.value = T_Product.map((item: any) => {
         item.Id = item.T_product_id
         item.T_img = item.T_product_img
         item.T_name = item.T_product_name
@@ -127,6 +125,8 @@ const editDataEcho = async (row: any) => {
         return item
       }))
     selectProductData = tableData.value
+    T_invoice && InvoiceRef.value?.setMoneyDeatil(T_invoice)
+    T_recoveries && RecoveriesRef.value?.setMoneyDeatil(T_recoveries)
     blurHandle()
   }
 }
@@ -144,21 +144,35 @@ const deleteProduct = (row: any) => {
   drawerProductRef.value?.selectTableChange(row)
 }
 
+const getMontageStr = (arr: any[], value1: string, value2: string): string => {
+  let str = ''
+  arr.forEach(item => {
+    str += `${item[value1]},${item[value2]}|`
+  })
+  return str
+}
+
+const getFomrParams = () => {
+  const recoveriesData = RecoveriesRef.value?.getMoneyDeatil()
+  const invoiceData = InvoiceRef.value?.getMoneyDeatil()
+  if (recoveriesData?.length) form.T_recoveries = getMontageStr(recoveriesData, 'T_date', 'T_money')
+  if (invoiceData?.length) form.T_invoice = getMontageStr(invoiceData, 'T_date', 'T_money')
+  const product = getMontageStr(tableData.value, 'Id', 'count')
+  const params = {
+    ...form,
+    User_tokey,
+    T_submit: form.T_uuid,
+    T_product: product
+  }
+  return params
+}
+
 const AddContract = (formEl: FormInstance | undefined) => {
   if (!formEl) return
   formEl.validate(async valid => {
     if (valid) {
       let res: any = {}
-      let product = ''
-      tableData.value.forEach(item => {
-        product += `${item.Id},${item.count}|`
-      })
-      const params = {
-        ...form,
-        User_tokey,
-        T_submit: form.T_uuid,
-        T_product: product
-      }
+      const params = getFomrParams()
       if (isNew.value) {
         res = await Storehouse_Contract_Add(params)
       } else {
@@ -167,31 +181,14 @@ const AddContract = (formEl: FormInstance | undefined) => {
       if (res.Code === 200) {
         ElMessage.success(`${isNew.value ? '添加' : '修改'}合同成功!!`)
         nextTick(() => {
-          drawerRef.value?.closeDrawer()
+          closeCancle()
           emit('onTableList')
-          resetForm(ruleFormRef.value)
-          isNew.value = true
         })
       }
-    } else {
-      return false
-    }
+    } else false
   })
 }
 
-const tableData = ref<any[]>([])
-const columns = [
-  { type: 'index', label: '序号', width: 80, align: 'center ' },
-  { label: '产品图片', prop: 'T_img', align: 'center ', name: 'T_img' },
-  { label: '产品名称', prop: 'T_name', align: 'center ' },
-  { label: '产品分类', prop: 'T_class_name', align: 'center ' },
-  { label: '产品型号', prop: 'T_model', align: 'center ' },
-  { label: '产品规格', prop: 'T_spec', align: 'center ' },
-  { label: '是否关联SN', prop: 'T_relation_sn', align: 'center ', width: 120, name: 'T_relation_sn' },
-  { label: '*数量', prop: 'count', align: 'center ', name: 'count' },
-  { prop: 'operation', label: '操作', width: 80, fixed: 'right' }
-]
-
 const drawerProductRef = ref<InstanceType<typeof InStorageProduct> | null>(null)
 /**
  * 添加产品
@@ -218,9 +215,18 @@ const ProductselectionChange = (row: any) => {
  * 全选
  */
 const ProductSelectionAllChange = (selection: any[]) => (tableData.value = selection)
+/**
+ * 生成合同编号
+ */
 const getContractNumber = async () => {
-  const res: any = await Storehouse_Contract_Gen_Number({ User_tokey })
-  if (res.Code === 200) form.T_number = res.Data
+  contractNumberLoading.value = true
+  const res: any = await Storehouse_Contract_Gen_Number({ User_tokey }).catch(() => {
+    contractNumberLoading.value = false
+  })
+  if (res.Code === 200) {
+    contractNumberLoading.value = false
+    form.T_number = res.Data
+  }
 }
 /**
  * 合同负责人
@@ -230,6 +236,16 @@ const getReceiveInfo = ({ T_uuid, T_name }: { T_uuid: string; T_name: string })
   form.T_submit = T_name
   form.T_uuid = T_uuid
 }
+const closeCancle = () => {
+  resetForm(ruleFormRef.value)
+  isNew.value = isProduct.value = false
+  tableData.value = []
+  selectProductData = []
+  drawerProductRef.value?.clearSelection()
+  RecoveriesRef.value?.clearDetail()
+  InvoiceRef.value?.clearDetail()
+  drawerRef.value?.closeDrawer()
+}
 defineExpose({
   openDrawer
 })
@@ -242,11 +258,12 @@ defineExpose({
         <h4 :id="params.titleId" :class="params.titleClass">{{ isNew ? '添加' : '编辑' }} - 合同</h4>
       </template>
       <el-form ref="ruleFormRef" :model="form" :rules="rules">
-        <el-divider border-style="dashed" />
         <el-form-item label="合同编号:" :label-width="formLabelWidth" prop="T_number">
           <div>
             <el-input v-model="form.T_number" :disabled="!isNew" placeholder="请输入合同编号" class="w-50" />
-            <el-button type="primary" @click="getContractNumber">生成合同编号</el-button>
+            <el-button :loading="contractNumberLoading" :disabled="!isNew" type="primary" @click="getContractNumber"
+              >生成合同编号</el-button
+            >
           </div>
         </el-form-item>
         <el-form-item label="客户名称:" :label-width="formLabelWidth" prop="T_customer">
@@ -310,6 +327,9 @@ defineExpose({
         <el-form-item label="合同负责人:" :label-width="formLabelWidth" prop="T_submit">
           <el-input v-model="form.T_submit" placeholder="请选择合同负责人" class="w-50" @focus="selectApprover" />
         </el-form-item>
+        <el-form-item label="项目:" :label-width="formLabelWidth" prop="T_submit_name">
+          <el-input v-model="form.T_submit_name" placeholder="请输入项目名称" class="w-50" />
+        </el-form-item>
         <el-form-item label="合同备注:" :label-width="formLabelWidth" prop="T_remark">
           <el-input
             class="w-50"
@@ -334,15 +354,35 @@ defineExpose({
             <template #tip> 只能上传pdf格式文件!!</template>
           </Upload>
         </el-form-item>
-
-        <div class="btn">
-          <el-divider>
-            <el-button>取消</el-button>
+        <el-form-item label="回款明细:" :label-width="formLabelWidth" prop="T_recoveries">
+          <TableDetail ref="RecoveriesRef" :columns="columnsRecoveries" title="回款明细" :labels="labelsRecoveries">
+            <template #add="{ AddDetail }">
+              <el-button type="primary" @click="AddDetail">
+                <el-icon><Plus /></el-icon><span style="margin-left: 6px">添加</span>
+              </el-button>
+            </template>
+          </TableDetail>
+        </el-form-item>
+        <el-form-item label="开票明细:" :label-width="formLabelWidth" prop="T_invoice">
+          <TableDetail ref="InvoiceRef" :columns="columnsInvoice" title="开票明细" :labels="labelsInvoice">
+            <template #add="{ AddDetail }">
+              <el-button type="primary" @click="AddDetail">
+                <el-icon><Plus /></el-icon><span style="margin-left: 6px">添加</span>
+              </el-button>
+            </template>
+          </TableDetail>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div>
+          <el-divider border-style="dashed" />
+          <div class="btn">
+            <el-button @click="closeCancle">取消</el-button>
             <el-button v-if="isNew" color="#626aef" @click="AddContract(ruleFormRef)">提交</el-button>
             <el-button v-else color="#626aef" @click="AddContract(ruleFormRef)">修改</el-button>
-          </el-divider>
+          </div>
         </div>
-      </el-form>
+      </template>
     </Drawer>
     <ContractUser ref="receiveUserdialog" @onUserInfo="getReceiveInfo" title="合同负责人" />
     <InStorageProduct

+ 142 - 3
src/views/storehouse/sales/TableDetail.vue

@@ -1,6 +1,145 @@
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import { ref, reactive, nextTick } from 'vue'
+import { generateRandom } from '@/utils/common'
+import Drawer from '@/components/Drawer/index.vue'
+import { Delete, Edit } from '@element-plus/icons-vue'
+import { useTablePublic } from '@/hooks/useTablePublic'
+import type { FormInstance, FormRules } from 'element-plus'
+import { validate_float } from '@/views/salary/salary/relus'
+import { ColumnProps } from '@/components/TableBase/interface/index'
+
+const isNew = ref(true)
+const tableData = ref<any[]>([])
+const ruleFormRef = ref<FormInstance>()
+const drawerRef = ref<InstanceType<typeof Drawer> | null>(null)
+const { resetForm } = useTablePublic()
+const form = ref({
+  id: '',
+  T_date: '',
+  T_money: ''
+})
+const rules = reactive<FormRules>({
+  T_date: [{ required: true, message: '请输入物联网卡号', trigger: 'blur' }],
+  T_money: [{ required: true, validator: validate_float(), trigger: 'blur' }]
+})
+
+const callbackDrawer = (done: () => void) => {
+  done()
+  resetForm(ruleFormRef.value)
+}
+const AddMoneyDetailed = (type: string, row?: any) => {
+  isNew.value = type === 'new' ? true : false
+  if (type === 'edit') {
+    form.value = { ...row }
+  }
+  drawerRef.value?.openDrawer()
+}
+const AddMoney = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl.validate(async valid => {
+    if (valid) {
+      if (isNew.value) {
+        tableData.value.push({ ...form.value, id: generateRandom() })
+      } else {
+        const index = tableData.value.findIndex(item => item.id === form.value.id)
+        tableData.value[index] = { ...form.value }
+      }
+      nextTick(() => {
+        isNew.value = true
+        resetForm(ruleFormRef.value)
+        drawerRef.value?.closeDrawer()
+      })
+    }
+  })
+}
+const deleteMoneyDeatil = (row: any) => {
+  tableData.value = tableData.value.filter((item: any) => item.id !== row.id)
+}
+
+const props = defineProps<{ columns: ColumnProps[]; title: string; labels: any }>()
+const title = ref(props.title)
+
+const getMoneyDeatil = () => tableData.value
+const setMoneyDeatil = (dataArr: any[]) => {
+  tableData.value = dataArr.map((item: any) => {
+    item.id = generateRandom()
+    return item
+  })
+}
+const clearDetail = () => (tableData.value = [])
+defineExpose({
+  clearDetail,
+  getMoneyDeatil,
+  setMoneyDeatil
+})
+</script>
 <template>
-  <div></div>
+  <div class="table-detail">
+    <el-table
+      border
+      stripe
+      :data="tableData"
+      style="width: 100%"
+      :header-cell-style="{ background: '#dedfe0', height: '50px' }"
+    >
+      <template v-for="item in columns" :key="item.prop">
+        <el-table-column v-bind="item" v-if="item.fixed !== 'right'"></el-table-column>
+        <el-table-column v-bind="item" v-if="item.fixed === 'right'">
+          <template #default="{ row }">
+            <el-button link type="primary" size="small" :icon="Edit" @click="AddMoneyDetailed('edit', row)"
+              >编辑</el-button
+            >
+            <el-button link type="danger" size="small" :icon="Delete" @click="deleteMoneyDeatil(row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </template>
+      <template #append>
+        <slot name="add" :AddDetail="AddMoneyDetailed('new')"></slot>
+      </template>
+    </el-table>
+    <Drawer ref="drawerRef" :handleClose="callbackDrawer">
+      <template #header="{ params }">
+        <h4 :id="params.titleId" :class="params.titleClass">{{ isNew ? '添加' : '编辑' }} - {{ title }}</h4>
+      </template>
+      <el-form ref="ruleFormRef" :model="form" :rules="rules">
+        <el-form-item :label="labels['date']" label-width="100px" prop="T_date">
+          <el-date-picker
+            v-model="form.T_date"
+            class="my-date-picker"
+            style="width: 100%"
+            type="date"
+            :placeholder="labels['date']"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+          />
+        </el-form-item>
+        <el-form-item :label="labels['money']" label-width="100px" prop="T_money">
+          <el-input v-model="form.T_money" type="text" autocomplete="off" :placeholder="labels['money']" />
+        </el-form-item>
+        <el-form-item label-width="100px">
+          <div class="btn">
+            <el-button v-if="isNew" color="#626aef" @click="AddMoney(ruleFormRef)">添加</el-button>
+            <el-button v-else color="#626aef" @click="AddMoney(ruleFormRef)">修改</el-button>
+          </div>
+        </el-form-item>
+      </el-form>
+    </Drawer>
+  </div>
 </template>
 
-<style scoped></style>
+<style scoped lang="scss">
+.table-detail {
+  width: 100%;
+}
+.el-form-item {
+  margin-bottom: 18px;
+}
+.btn {
+  margin-top: 32px;
+  display: flex;
+  justify-content: center;
+  .el-button {
+    padding: 0 32px;
+  }
+}
+</style>