浏览代码

feat: 新创建订单通用逻辑添加

cuiyalong 3 月之前
父节点
当前提交
c5f0e4cce2
共有 38 个文件被更改,包括 1803 次插入423 次删除
  1. 29 2
      src/api/modules/index.js
  2. 1 0
      src/assets/css/_variables.scss
  3. 5 1
      src/components/uploadFile.vue
  4. 228 34
      src/store/order.js
  5. 5 2
      src/utils/globalFun.js
  6. 7 2
      src/views/create-order/components/ProductInfoCardList.vue
  7. 126 39
      src/views/create-order/components/contractInfoModule.vue
  8. 28 6
      src/views/create-order/components/create.vue
  9. 31 36
      src/views/create-order/components/otherInfoModule.vue
  10. 77 45
      src/views/create-order/components/paymentPlanModule.vue
  11. 169 97
      src/views/create-order/components/performanceBelongsModule.vue
  12. 6 0
      src/views/create-order/components/product-info-submodule/AccountNumbers.vue
  13. 1 0
      src/views/create-order/components/product-info-submodule/CheckboxGroup.vue
  14. 63 23
      src/views/create-order/components/product-info-submodule/ContractAmount.vue
  15. 2 2
      src/views/create-order/components/product-info-submodule/ElOnlineContractForm.vue
  16. 10 2
      src/views/create-order/components/product-info-submodule/ProductTypeSelector.vue
  17. 1 0
      src/views/create-order/components/product-info-submodule/RadioGroup.vue
  18. 19 9
      src/views/create-order/components/product-info-submodule/RelatedOrders.vue
  19. 8 3
      src/views/create-order/components/product-info-submodule/ServiceList.vue
  20. 1 1
      src/views/create-order/components/product-info-submodule/TextCard.vue
  21. 40 44
      src/views/create-order/components/product-info-submodule/ValidityPeriod.vue
  22. 1 2
      src/views/create-order/components/product-info-submodule/select-tree.vue
  23. 43 26
      src/views/create-order/components/productInfoModule.vue
  24. 214 0
      src/views/create-order/components/schema-form/params.js
  25. 5 0
      src/views/create-order/components/schema-form/params/base.js
  26. 40 0
      src/views/create-order/components/schema-form/params/common.js
  27. 63 0
      src/views/create-order/components/schema-form/params/svip.js
  28. 14 4
      src/views/create-order/components/schema-form/products/common.js
  29. 1 1
      src/views/create-order/components/schema-form/products/svip.js
  30. 79 25
      src/views/create-order/components/schema-form/schema-form.vue
  31. 2 3
      src/views/create-order/components/schema-form/schema.js
  32. 17 0
      src/views/create-order/data/interface.js
  33. 48 10
      src/views/create-order/data/options.js
  34. 42 0
      src/views/create-order/data/var.js
  35. 270 0
      src/views/create-order/hooks/checkRequired.js
  36. 72 3
      src/views/create-order/index.vue
  37. 18 0
      src/views/create-order/mixins/index.js
  38. 17 1
      src/views/create-order/ui/NumberInput.vue

+ 29 - 2
src/api/modules/index.js

@@ -23,15 +23,42 @@ export function ajaxGetUserService(data) {
   })
 }
 
-export function ajaxGetProductionList(query) {
+export function ajaxGetProductionList(params) {
   return request({
     url: '/jyOrderManager/product/list',
     method: 'get',
-    query,
+    params,
   })
 }
 
+/* 查询商品价格demo
+  data = {
+    "product_code":"cjdy003",
+    "recordPayType":1,
+    "phone":13027620004,
+    "filter":{
+        "buy_cycle":1,
+        "buy_type":3,
+        "give_cycle":1,
+        "give_type":2
+    }
+  }
+*/
+export function ajaxGetProductionPrice(data) {
+  return request({
+    url: '/jyOrderManager/product/getPrice',
+    method: 'post',
+    data,
+  })
+}
 
+export function ajaxCreateOrder(data) {
+  return request({
+    url: '/jyOrderManager/order/save',
+    method: 'post',
+    data,
+  })
+}
 
 export function getWorkDay(data) {
   return request({

+ 1 - 0
src/assets/css/_variables.scss

@@ -16,6 +16,7 @@ $gray_7: #5f5e64;
 $gray_8: #33323a; // VIP背景灰色
 $gray_89: #1b1a2a; // VIP背景灰色
 $gray_9: #171826;
+$gray_10: #1d1d1d;
 
 $main: #2abed1;
 $green: #00d086;

+ 5 - 1
src/components/uploadFile.vue

@@ -5,7 +5,7 @@
         <Upload 
             ref="uploadRef"
             :multiple="multipled"
-            action="/filemanage/upload"
+            :action="action"
             :before-upload="beforeImgFile"
             :on-success="handleSuccess"
             :on-exceeded-size="handleExceeded"
@@ -34,6 +34,10 @@
     export default {
         name: "uploadFile",
         props: {
+            action: {
+                type: String,
+                default: '/filemanage/upload'
+            },
             placeholdered: String,
             accepted: String,
             formated: Array,

+ 228 - 34
src/store/order.js

@@ -2,9 +2,13 @@ import {
   ajaxGetProductionList,
   ajaxGetUserService,
   ajaxGetSelectOptions,
+  ajaxGetProductionPrice,
+  ajaxCreateOrder,
 } from "../api/modules"
 import { findProductInThreeLevel } from '@/views/create-order/hooks'
+import { orderParams, createOrderParams } from '@/views/create-order/components/schema-form/params'
 import { ActivityProductName } from '@/views/create-order/data'
+import { checkRequired } from "@/views/create-order/hooks/checkRequired"
 import { getRandomString } from '@/utils/utils'
 import { cloneDeep } from "lodash"
 
@@ -17,6 +21,7 @@ class OrderProductCardItem {
       info: {},
       form: {},
       result: {},
+      standardPrice: '',
     }
   }
 
@@ -39,21 +44,68 @@ class OrderProductCardItem {
 }
 
 const defaultPageFormValue = {
-  buySubject: 1,
-  companyName: '',
-  username: '',
-  userTel: '',
-  accountTel: '13283800000',
+  // 基本信息
+  buySubject: 1, // 购买主体
+  companyName: '', // 公司名称
+  username: '', // 联系人名称
+  userTel: '', // 联系人电话
+  accountTel: '13283800000', // 权益开通电话
+  // 产品信息
+  channelCommission: '', // 渠道佣金
+  // 协议信息
+  agreeStatus: 1, // 协议状态
+  signUnit: 'h01',
+  orderMoney0Type: '1', // 0元订单类型
+  // 协议信息-文档协议
+  signCode: '', // 协议编号
+  signTime: '', // 协议签订时间
+  contract_archive_status: 0, // 协议归档状态
+  contract_archive_time: '', // 协议归档时间
+  contract_archive_num: '', // 协议归档份数
+  contract_file_url: '', // 归档协议文件地址
+
+  // 协议信息-电子协议
+  e_contract_type: 1, // 电子协议类型
+  e_contract_userA_type: 1, // 协议甲方类型
+  e_contract_userA_name: '', // 协议甲方
+  e_contract_userA_contacts_name: '', // 协议甲方联系人
+  e_contract_userA_contacts_tel: '', // 协议甲方联系方式
+  e_contract_userA_contacts_address: '', // 协议甲方联系地址
+  e_contract_userB_contacts_name: '', // 协议乙方联系人
+  e_contract_remark: '', // 协议备注
+  // 回款计划
+  paybackTimes: 1, // 回款次数
+  paymentDeadline: '', // 预计回款工作日
+  expectedPaymentDeadlineTime: '', // 预计回款日期
+  paybackTableData: [], // 回款计划表格
+  // 业绩归属
+  salePerson: [],
+  saleWay: [],
+  salePersonTableList: [],
+  // 其他信息
+  reservationPayWay: '', // 约定支付方式
+  orderChannel: 'd01', // 下单渠道
+  paymentAccountName: '', // 付款户名
+  orderRemark: '', // 订单备注
 }
 
 export default {
   namespaced: true,
   state: {
     schemaKey: Date.now(),
+    loading: {
+      productList: false,
+      userService: false,
+      price: false,
+      create: false,
+    },
     // 配置信息
     conf: {
       channel: [],
+      defaultSaleChannel: [],
+      currentUserEntId: '',
       deptTree: [],
+      orderChannel: [],
     },
     // 产品列表
     productList: [],
@@ -65,6 +117,18 @@ export default {
     },
   },
   getters: {
+    requestLoading(state) {
+      const loading = state.loading
+      let s = false
+      for (const key in loading) {
+        const item = loading[key]
+        if (item) {
+          s = true
+          break
+        }
+      }
+      return s
+    },
     userProductInfoList(state) {
       const { productInfoList } = state.orderInfo
       return productInfoList.map(card => {
@@ -119,11 +183,63 @@ export default {
     saleChannelOptions(state) {
       return cloneDeep(state.conf.channel)
     },
+    orderChannelOptions(state) {
+      return state.conf.orderChannel.map(c => {
+        return {
+          label: c.item_name,
+          value: c.item_code,
+        }
+      })
+    },
+    // 统计总金额
+    pageTotalMoney(state, getters) {
+      let hasContract = false
+      let contract = 0
+      let standard = 0
+      let rate = ''
+      getters.userProductInfoList.forEach(item => {
+        let hc = item.contractAmount?.contractMoney
+        let check = hc !== undefined && hc !== '' && hc !== null
+        hasContract = hasContract || check
+
+        let c = Number(item.contractAmount?.contractMoney || 0)
+        let s = Number(item.contractAmount?.standardMoney || 0)
+        contract += c
+        standard += s
+      })
+      if (standard === 0) {
+        rate = 0
+      } else {
+        rate = `${(contract / standard * 100).toFixed(2)}%`
+      }
+      return {
+        hasContract,
+        contract,
+        standard,
+        rate,
+      }
+    },
+    salesMoneyTotal(state, getters) {
+      // 销售业绩=合同金额-渠道佣金
+      return getters.pageTotalMoney.contract - state.pageForm.channelCommission
+    },
+    // 预计回款金额
+    exceptPaybackMoney(_, getters) {
+      return getters.salesMoneyTotal
+    },
+    // 是否是0元订单
+    orderMoney0Type(state, getters) {
+      const { hasContract, contract } = getters.pageTotalMoney
+      return contract === 0 && hasContract
+    }
   },
   mutations: {
     refreshSchema(state) {
       state.schemaKey = Date.now()
     },
+    setLoadingState(state, { key, value }) {
+      state.loading[key] = value
+    },
     setConfInfo(state, { key, value }) {
       state.conf[key] = value
     },
@@ -141,6 +257,12 @@ export default {
     resetPageForm(state, payload = cloneDeep(defaultPageFormValue)) {
       state.pageForm = payload
     },
+    // 修改数组对象中某个值的 mutation
+    updatePageFormArrayObjectValue(state, { arrayName, index, key, newValue }) {
+      if (state.pageForm[arrayName] && state.pageForm[arrayName][index]) {
+        state.pageForm[arrayName][index][key] = newValue;
+      }
+    },
     addOrderProductItem(state) {
       state.orderInfo.productInfoList.push(
         new OrderProductCardItem('产品')
@@ -167,12 +289,24 @@ export default {
   },
   actions: {
     // 获取产品备选项信息
-    async getProductList ({ dispatch }, payload) {
-      const { error_code: code, error_msg: msg, data } = await ajaxGetProductionList(payload)
-      if (code === 0 && data) {
-        dispatch('initProductListData', data)
-      } else {
-        console.log(msg)
+    async getProductList ({ commit, dispatch, state }, payload) {
+      const p = {
+        subject: state.pageForm.buySubject,
+        ...payload,
+      }
+
+      commit('setLoadingState', { key: 'productList', value: true })
+      try {
+        const { error_code: code, error_msg: msg, data } = await ajaxGetProductionList(p)
+        if (code === 0 && data) {
+          dispatch('initProductListData', data)
+        } else {
+          console.log(msg)
+        }
+      } catch (error) {
+        console.log(error)
+      } finally {
+        commit('setLoadingState', { key: 'productList', value: false })
       }
     },
     // 初始化产品备选项信息
@@ -200,40 +334,50 @@ export default {
         })
       }
       // 整理活动数据
-      const aList = activityList.map(act => {
-        act.label = act.name
-        act.value = act.code
-        act.activityMark = 1
-        // 找到code所在商品,在使用时,要固定产品类型为当前product_code,且不能更改
-        if (Array.isArray(act.products)) {
-          act.product_list = act.products.map(product => {
-            const productCode = product.product_code
-            // 从productList中找到对应产品
-            const productItem = findProductInThreeLevel(pList, pl => {
-              return pl.code === productCode
+      let aList = []
+      if (Array.isArray(activityList)) {
+        aList = activityList.map(act => {
+          act.label = act.name
+          act.value = act.code
+          act.activityMark = 1
+          // 找到code所在商品,在使用时,要固定产品类型为当前product_code,且不能更改
+          if (Array.isArray(act.products)) {
+            act.product_list = act.products.map(product => {
+              const productCode = product.product_code
+              // 从productList中找到对应产品
+              const productItem = findProductInThreeLevel(pList, pl => {
+                return pl.code === productCode
+              })
+              return productItem
             })
-            return productItem
-          })
-
-        }
-        return act
-      })
+          }
+          return act
+        })
+      }
 
       pList.push({
         label: ActivityProductName,
         value: ActivityProductName,
+        activityMark: 1,
         children: aList
       })
 
       commit('setProductList', pList)
     },
     // 检查该联系人手机号或公司名称是否存在目同产品类型且权益已开通或已过期的订单
-    async getUserService(_, payload = {}) {
-      const { error_code: code, error_msg: msg, data } = await ajaxGetUserService(payload)
-      if (code === 0 && data) {
-        return data
-      } else {
-        console.log(msg)
+    async getUserService({ commit }, payload = {}) {
+      commit('setLoadingState', { key: 'userService', value: true })
+      try {
+        const { error_code: code, error_msg: msg, data } = await ajaxGetUserService(payload)
+        commit('setLoadingState', { key: 'userService', value: false })
+        if (code === 0 && data) {
+          return data
+        } else {
+          console.log(msg)
+        }
+      } catch (error) {
+        commit('setLoadingState', { key: 'userService', value: false })
+        console.log(error)
       }
     },
     // 获取备选项
@@ -248,5 +392,55 @@ export default {
         console.log(msg)
       }
     },
+    async getPrice({ state, commit }, payload = {}) {
+      const { type, productInfo } = payload
+      const params = orderParams(type).priceParams({
+        pageForm: state.pageForm,
+        productInfo,
+        type,
+      })
+      if (!params) return
+      commit('setLoadingState', { key: 'price', value: true })
+      try {
+        const { error_code: code, error_msg: msg, data } = await ajaxGetProductionPrice(params)
+        if (code === 0) {
+          commit('setLoadingState', { key: 'price', value: false })
+          return data
+        } else {
+          console.log(msg)
+        }
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    async createOrder({ state, commit, getters }, payload = {}) {
+      // save 1-暂存 2-提交
+      const { save } = payload
+      const pass = checkRequired({
+        pageForm: state.pageForm,
+        productInfoList: state.orderInfo.productInfoList,
+      })
+      const params = createOrderParams({
+        pageForm: state.pageForm,
+        productInfoList: state.orderInfo.productInfoList,
+        pageTotalMoney: getters.pageTotalMoney,
+        orderMoney0Type: getters.orderMoney0Type,
+      })
+      if (!pass) return
+      if (!params) return
+      params.save = save
+      commit('setLoadingState', { key: 'create', value: true })
+      try {
+        const { error_code: code, error_msg: msg, data } = await ajaxCreateOrder(params)
+        if (code === 0) {
+          commit('setLoadingState', { key: 'create', value: false })
+          return data
+        } else {
+          console.log(msg)
+        }
+      } catch (error) {
+        console.log(error)
+      }
+    },
   },
 }

+ 5 - 2
src/utils/globalFun.js

@@ -99,10 +99,13 @@ export function formatPrice(s, n = -1, comma = false) {
  *     · 'yyyy年MM月dd日 HH时mm分ss秒 EEE'  --->  输出如2019年09月20日 18时20分23秒 星期二
  *  参考: https://www.cnblogs.com/mr-wuxiansheng/p/6296646.html
  */
-export function dateFormatter(date, fmt = 'yyyy-MM-dd HH:mm:ss') {
+export function dateFormatter(date = '', fmt = 'yyyy-MM-dd HH:mm:ss') {
   // 将传入的date转为时间对象
   if (!date) return ''
-  date = moment(date).valueOf()
+  // 处理ios不兼容'2022-6-6'类似的'-'问题
+  if (typeof data === 'string') {
+    date = date.replace(/-/g, '/')
+  }
   date = new Date(date)
   const o = {
     'y+': date.getFullYear(),

+ 7 - 2
src/views/create-order/components/ProductInfoCardList.vue

@@ -8,7 +8,7 @@
     >
       <template #actions>
         <el-button
-          v-if="productCardList.length > 1"
+          v-if="showProductDelete(product, index)"
           type="text"
           icon="el-icon-delete"
           @click="removeProduct(index)"
@@ -65,6 +65,7 @@ export default {
   },
   computed: {
     ...mapState({
+      pageForm: state => state.order.orderInfo.pageForm,
       productCardList: state => state.order.orderInfo.productInfoList
     }),
   },
@@ -74,7 +75,7 @@ export default {
   },
   methods: {
     getProductList() {
-      this.$store.dispatch('order/getProductList', { subject: 0 })
+      this.$store.dispatch('order/getProductList')
     },
     addProduct() {
       this.$store.commit('order/addOrderProductItem')
@@ -98,6 +99,10 @@ export default {
         this.$store.commit('order/removeOrderProductItem', index)
       }
     },
+    showProductDelete() {
+      // 第一个产品不展示删除按钮
+      return this.productCardList.length > 1
+    },
     isActivityProduct(index) {
       const t = this.productCardList[index]
       return t.activityCode

+ 126 - 39
src/views/create-order/components/contractInfoModule.vue

@@ -1,16 +1,21 @@
 <template>
   <!-- 签约信息 -->
-  <el-form ref="form" :model="form" :rules="rules" label-width="126px" class="order-contract-info-container">
+  <el-form ref="form" :model="pageForm" :rules="rules" label-width="126px" class="order-contract-info-container">
     <el-row :gutter="2">
       <el-col :span="12">
         <el-form-item label="协议状态" prop="agreeStatus" :required="required.agreeStatus">
-          <RadioGroup v-model="form.agreeStatus" :options="conf.agreeStatusOptions" />
+          <RadioGroup
+            :value="pageForm.agreeStatus"
+            @input="onChangeFormItem('agreeStatus', $event)"
+            :options="conf.agreeStatusOptions"
+          />
         </el-form-item>
       </el-col>
       <el-col :span="12">
         <el-form-item label="签约主体" prop="signUnit" v-if="showXieYi">
           <el-select
-            v-model="form.signUnit"
+            :value="pageForm.signUnit"
+            @input="onChangeFormItem('signUnit', $event)"
             size="medium"
             class="el-select-w100"
             placeholder="请选择签约主体">
@@ -29,7 +34,8 @@
         <el-col :span="12">
           <el-form-item label="协议签订时间" prop="signTime">
             <el-date-picker
-              v-model="form.signTime"
+              :value="pageForm.signTime"
+              @input="onChangeFormItem('signTime', $event)"
               value-format="timestamp"
               type="date"
               size="medium"
@@ -39,40 +45,104 @@
         </el-col>
         <el-col :span="12">
           <el-form-item label="协议编号" prop="signCode" v-if="showSingCode">
-            <el-input v-model="form.signCode" placeholder="请输入协议编号" size="medium" maxlength="40"></el-input>
+            <el-input
+              :value="pageForm.signCode"
+              @input="onChangeFormItem('signCode', $event)"
+              placeholder="请输入协议编号"
+              size="medium"
+              maxlength="40"
+            ></el-input>
           </el-form-item>
         </el-col>
       </el-row>
-      <!-- <el-row :gutter="2">
-        <el-col :span="24">
-          <el-form-item label="协议备注" prop="signRemark" v-if="showOnlineContractForm">
-            <el-input v-model="form.signRemark" placeholder="请输入协议备注" size="medium" maxlength="180"></el-input>
+      <el-row :gutter="2">
+        <el-col :span="12">
+          <el-form-item label="协议归档状态" prop="contract_archive_status">
+            <RadioGroup
+              :value="pageForm.contract_archive_status"
+              @input="onChangeFormItem('contract_archive_status', $event)"
+              :options="conf.signArchiveOptions"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="归档时间" required v-if="showArchiveSignModule">
+            <el-date-picker
+              :value="pageForm.contract_archive_time"
+              @input="onChangeFormItem('contract_archive_time', $event)"
+              value-format="timestamp"
+              type="date"
+              size="medium"
+              placeholder="请选择协议归档时间">
+            </el-date-picker>
           </el-form-item>
         </el-col>
       </el-row>
-      <el-row :gutter="2">
+      <el-row :gutter="2" v-if="showArchiveSignModule">
+        <el-col :span="12">
+          <el-form-item label="归档份数" prop="contract_archive_num">
+            <number-input
+              :value="pageForm.contract_archive_num"
+              @input="onChangeFormItem('contract_archive_num', $event)"
+              placeholder="请输入归档份数"
+              maxlength="2">
+            </number-input>
+          </el-form-item>
+        </el-col>
         <el-col :span="12">
-          <el-form-item label="电子协议类型" prop="signType" v-if="showOnlineContractForm">
-            <RadioGroup v-model="form.signType" :options="conf.eSignTypeOptions" />
+          <el-form-item label="归档协议" prop="contract_file_url">
+            <b-upload
+              ref="uploadRef"
+              @fileData="contractFile"
+              :placeholdered="'请上传归档协议'"
+              :accepted="'.doc,.pdf,.docx,.rar,.zip,.jpg,.png,.jpeg'"
+              :formated="['doc','pdf','docx','rar','zip','jpg','png','jpeg']"
+              :multipled="false"
+              @onRemove="onRemoveUpload"
+            ></b-upload>
           </el-form-item>
         </el-col>
-      </el-row> -->
+      </el-row>
 
       <!-- 电子协议表单 -->
-      <ElOnlineContractForm
+      <!-- <ElOnlineContractForm
         ref="onlineContractFormRef"
         v-if="showOnlineContractForm"
         :showMore="showMore && showOnlineContractForm"
         :buySubject="buySubject"
         :orderEntName="companyName"
-        :e_contract_type.sync="e_.contract_type"
+        :e_contract_type="e_.contract_type"
         :e_contract_userA_type.sync="e_.contract_userA_type"
         :e_contract_userA_name.sync="e_.contract_userA_name"
         :e_contract_userA_contacts_name.sync="e_.contract_userA_contacts_name"
         :e_contract_userA_contacts_tel.sync="e_.contract_userA_contacts_tel"
         :e_contract_userA_contacts_address.sync="e_.contract_userA_contacts_address"
         :e_contract_userB_contacts_name.sync="e_.contract_userB_contacts_name"
-        :e_contract_remark.sync="e_.contract_remark" />
+        :e_contract_remark.sync="e_.contract_remark" /> -->
+
+      <ElOnlineContractForm
+        ref="onlineContractFormRef"
+        v-if="showOnlineContractForm"
+        :showMore="showMore && showOnlineContractForm"
+        :buySubject="buySubject"
+        :orderEntName="companyName"
+        :e_contract_type="pageForm.e_contract_type"
+        @update:e_contract_type="onChangeFormItem('e_contract_type', $event)"
+        :e_contract_userA_type="pageForm.e_contract_userA_type"
+        @update:e_contract_userA_type="onChangeFormItem('e_contract_userA_type', $event)"
+        :e_contract_userA_name="pageForm.e_contract_userA_name"
+        @update:e_contract_userA_name="onChangeFormItem('e_contract_userA_name', $event)"
+        :e_contract_userA_contacts_name="pageForm.e_contract_userA_contacts_name"
+        @update:e_contract_userA_contacts_name="onChangeFormItem('e_contract_userA_contacts_name', $event)"
+        :e_contract_userA_contacts_tel="pageForm.e_contract_userA_contacts_tel"
+        @update:e_contract_userA_contacts_tel="onChangeFormItem('e_contract_userA_contacts_tel', $event)"
+        :e_contract_userA_contacts_address="pageForm.e_contract_userA_contacts_address"
+        @update:e_contract_userA_contacts_address="onChangeFormItem('e_contract_userA_contacts_address', $event)"
+        :e_contract_userB_contacts_name="pageForm.e_contract_userB_contacts_name"
+        @update:e_contract_userB_contacts_name="onChangeFormItem('e_contract_userB_contacts_name', $event)"
+        :e_contract_remark="pageForm.e_contract_remark"
+        @update:e_contract_remark="onChangeFormItem('e_contract_remark', $event)"
+      />
 
       <el-divider>
         <el-button
@@ -87,14 +157,19 @@
 <script>
 import RadioGroup from '@/views/create-order/components/product-info-submodule/RadioGroup'
 import ElOnlineContractForm from '@/views/create-order/components/product-info-submodule/ElOnlineContractForm'
-import { agreeStatusOptions, eSignTypeOptions, signUnitOptions } from '@/views/create-order/data/index.js'
+import NumberInput from '@/views/create-order/ui/NumberInput.vue'
+import bUpload from '@/components/uploadFile'
+import { agreeStatusOptions, eSignTypeOptions, signUnitOptions, signArchiveOptions } from '@/views/create-order/data/index.js'
 import { selectorVModelMixin } from '@/utils/mixins/selector-v-model'
-import { mapState, mapGetters } from 'vuex'
+import { mapGetters } from 'vuex'
+import { pageFormState } from '@/views/create-order/mixins'
 
 export default {
   name: 'ContractInfoModule',
-  mixins: [selectorVModelMixin],
+  mixins: [selectorVModelMixin, pageFormState],
   components: {
+    bUpload,
+    NumberInput,
     ElOnlineContractForm,
     RadioGroup
   },
@@ -104,36 +179,34 @@ export default {
         agreeStatusOptions,
         eSignTypeOptions,
         signUnitOptions,
+        signArchiveOptions,
       },
       required: {
         agreeStatus: true,
       },
       form: {
-        agreeStatus: '1',
-        signUnit: 'h01',
-        signTime: '',
-        signCode: '',
+        // agreeStatus: 1,
+        // signUnit: 'h01',
+        // signTime: '',
+        // signCode: '',
         signRemark: '',
         signType: '2',
       },
       showMoreXieYi: false,
       // 电子协议相关
       e_: {
-        contract_type: 1, // 电子协议类型
-        contract_userA_type: 1, // 协议甲方类型
-        contract_userA_name: '', // 协议甲方
-        contract_userA_contacts_name: '', // 协议甲方联系人
-        contract_userA_contacts_tel: '', // 协议甲方联系方式
-        contract_userA_contacts_address: '', // 协议甲方联系地址
-        contract_userB_contacts_name: '', // 协议乙方联系人
-        contract_remark: '', // 协议备注
+        // contract_type: 1, // 电子协议类型
+        // contract_userA_type: 1, // 协议甲方类型
+        // contract_userA_name: '', // 协议甲方
+        // contract_userA_contacts_name: '', // 协议甲方联系人
+        // contract_userA_contacts_tel: '', // 协议甲方联系方式
+        // contract_userA_contacts_address: '', // 协议甲方联系地址
+        // contract_userB_contacts_name: '', // 协议乙方联系人
+        // contract_remark: '', // 协议备注
       },
     }
   },
   computed: {
-    ...mapState({
-      pageForm: state => state.order.pageForm,
-    }),
     ...mapGetters('order', ['userProductInfoList']),
     buySubject() {
       return this.pageForm.buySubject
@@ -141,6 +214,9 @@ export default {
     companyName() {
       return this.pageForm.companyName
     },
+    showArchiveSignModule() {
+      return this.pageForm.contract_archive_status === 1
+    },
     requiredList() {
       return [
         {
@@ -172,7 +248,7 @@ export default {
       }
     },
     showXieYi() {
-      return this.form.agreeStatus === '1'
+      return this.pageForm.agreeStatus === 1
     },
     showMore() {
       return this.showMoreXieYi
@@ -183,9 +259,10 @@ export default {
     showSingCode() {
       return !this.showOnlineContractForm
     },
+    // 可以迁移到store中
     showOnlineContractForm() {
-      const paybackCompanyCheck = this.form.signUnit == 'h01' // 签约主体为:北京剑鱼信息技术有限公司/h01
-      const contractStatusCheck = this.form.agreeStatus == '1' // 协议状态为签协议
+      const paybackCompanyCheck = this.pageForm.signUnit == 'h01' // 签约主体为:北京剑鱼信息技术有限公司/h01
+      const contractStatusCheck = this.pageForm.agreeStatus === 1 // 协议状态为签协议
 
 
       // 4.支持线上生成电子协议:
@@ -194,7 +271,7 @@ export default {
       // (3)且销售策略为“售卖”的产品规格及购买主体有配置电子协议模板;
       // 销售策略为“售卖”的产品规格付费类型为“购买”、“续费”、“试用”。
 
-      return true
+      return false
       // 展示条件:
       // 1. 产品类型是超级订阅(且付费类型为“购买”、“续费”)才展示。
       // 2. 产品类型是“大会员”且会员套餐为“商机版2.0”、“专家版2.0”(且服务类型为“新购服务”、“延长服务”)
@@ -248,9 +325,19 @@ export default {
       if (!t) return
       Object.assign(this.form, t)
     },
+    contractFile(val) {
+      this.onChangeFormItem('contract_file_url', val)
+    },
+    onRemoveUpload () {
+      this.onChangeFormItem('contract_file_url', '')
+    },
+    onChangeFormItem(key, e) {
+      this.setPageFormData(key, e)
+    },
     initSignTime() {
       // 初始化协议签订时间为当天
-      this.form.signTime = Date.now()
+      // this.form.signTime = Date.now()
+      this.onChangeFormItem('signTime', Date.now())
     },
     showMoreForm() {
       this.showMoreXieYi = !this.showMoreXieYi

+ 28 - 6
src/views/create-order/components/create.vue

@@ -10,8 +10,8 @@
       <ContractInfoModule ref="moduleContractInfo" />
     </ModuleCard>
     <!-- 合同金额为0,则不展示回款计划模块 -->
-    <ModuleCard class="create-order-module" title="回款计划" v-if="totalMoney > 0">
-      <PaymentPlanModule ref="modulePaymentPlan" :totalMoney="totalMoney" :contractStatus="pageForm.agreeStatus" />
+    <ModuleCard class="create-order-module" title="回款计划" v-if="showOrderMoney0Type">
+      <PaymentPlanModule ref="modulePaymentPlan" :contractStatus="pageForm.agreeStatus" />
     </ModuleCard>
     <ModuleCard class="create-order-module" title="业绩归属">
       <PerformanceBelongsModule ref="modulePerformanceBelong" setDefaultUser />
@@ -30,7 +30,7 @@ import ProductInfoModule from './productInfoModule.vue'
 import PerformanceBelongsModule from './performanceBelongsModule.vue'
 import OtherInfoModule from './otherInfoModule.vue'
 import ContractInfoModule from './contractInfoModule.vue'
-import { mapState } from 'vuex'
+import { mapState, mapGetters } from 'vuex'
 
 export default {
   name: 'CreateOrderContent',
@@ -52,6 +52,10 @@ export default {
     ...mapState({
       pageForm: state => state.order.pageForm,
     }),
+    ...mapGetters('order', ['pageTotalMoney', 'orderMoney0Type']),
+    showOrderMoney0Type() {
+      return !this.orderMoney0Type
+    },
   },
   created() {
     this.$store.dispatch('order/getSelectOptions')
@@ -112,10 +116,28 @@ export default {
   .el-select-w100.el-select {
     width: 100%;
   }
+
+  .el-input__inner,
+  .el-textarea__inner {
+    color: $gray_10;
+    // &:focus {
+    //   outline: 0;
+    //   box-shadow: 0 0 0 2px rgba($main,.2);
+    // }
+  }
+
+  .form-item-container {
+    font-size: 14px;
+    line-height: 22px;
+    .form-item-label {
+      color: #686868;
+    }
+    .form-item-value {
+      color: $gray_10;
+    }
+  }
 }
-.create-order-content-container {
-  padding-top: 20px;
-}
+
 .create-order-module {
   font-size: 14px;
 }

+ 31 - 36
src/views/create-order/components/otherInfoModule.vue

@@ -1,11 +1,12 @@
 <template>
   <!-- 其他信息 -->
-  <el-form ref="form" :model="form" :rules="rules" label-width="126px" class="order-other-info-container">
+  <el-form ref="form" :model="pageForm" :rules="rules" label-width="126px" class="order-other-info-container">
     <el-row :gutter="2">
       <el-col :span="12">
-        <el-form-item label="约定支付方式" prop="reservationPayWay" required>
+        <el-form-item label="约定支付方式" required prop="reservationPayWay">
           <el-select
-            v-model="form.reservationPayWay"
+            :value="pageForm.reservationPayWay"
+            @input="setPageFormData('reservationPayWay', $event)"
             size="medium"
             placeholder="请选择约定支付方式">
             <el-option
@@ -20,12 +21,13 @@
       <el-col :span="12">
         <el-form-item label="下单渠道" prop="orderChannel" required>
           <el-select
-            v-model="form.orderChannel"
+            :value="pageForm.orderChannel"
+            @input="setPageFormData('orderChannel', $event)"
             class="el-select-w100"
             size="medium"
             placeholder="请选择下单渠道">
             <el-option
-              v-for="item in conf.orderChannelOptions"
+              v-for="item in orderChannelOptions"
               :key="item.value"
               :label="item.label"
               :value="item.value">
@@ -37,14 +39,26 @@
     <el-row :gutter="2">
       <el-col :span="24">
         <el-form-item label="付款户名" prop="paymentAccountName">
-          <el-input v-model="form.paymentAccountName" placeholder="个人打款为姓名,公司打款为公司名称,对公转账将根据付款户名自动关联回款信息" size="medium" maxlength="100"></el-input>
+          <el-input
+            :value="pageForm.paymentAccountName"
+            @input="setPageFormData('paymentAccountName', $event)"
+            placeholder="个人打款为姓名,公司打款为公司名称,对公转账将根据付款户名自动关联回款信息"
+            size="medium"
+            maxlength="100"
+          ></el-input>
         </el-form-item>
       </el-col>
     </el-row>
     <el-row :gutter="2">
       <el-col :span="24">
         <el-form-item label="订单备注" prop="remark">
-          <el-input v-model="form.remark" placeholder="请输入订单备注" size="medium" maxlength="100"></el-input>
+          <el-input
+            :value="pageForm.orderRemark"
+            @input="setPageFormData('orderRemark', $event)"
+            placeholder="请输入订单备注"
+            size="medium"
+            maxlength="100"
+          ></el-input>
         </el-form-item>
       </el-col>
     </el-row>
@@ -54,33 +68,31 @@
 <script>
 import { payWayOptions } from '@/views/create-order/data/index.js'
 import { selectorVModelMixin } from '@/utils/mixins/selector-v-model'
-import { mapState } from 'vuex'
+import { pageFormState } from '@/views/create-order/mixins'
+import { mapGetters } from 'vuex'
 
 export default {
   name: 'OtherInfoModule',
-  mixins: [selectorVModelMixin],
+  mixins: [selectorVModelMixin, pageFormState],
   data() {
     return {
       conf: {
         payWayOptions,
-        orderChannelOptions: [],
       },
       required: {
         reservationPayWay: true,
         orderChannel: true,
       },
       form: {
-        reservationPayWay: '',
-        orderChannel: 'd01',
-        paymentAccountName: '',
-        remark: '',
+        // reservationPayWay: '',
+        // orderChannel: 'd01',
+        // paymentAccountName: '',
+        // remark: '',
       }
     }
   },
   computed: {
-    ...mapState({
-      pageForm: state => state.order.pageForm,
-    }),
+    ...mapGetters('order', ['orderChannelOptions']),
     companyName() {
       return this.pageForm.companyName
     },
@@ -98,12 +110,10 @@ export default {
   },
   watch: {
     companyName(n) {
-      this.form.paymentAccountName = n
+      // this.form.paymentAccountName = n
+      this.setPageFormData('paymentAccountName', n)
     },
   },
-  created() {
-    this.getOrderChannel()
-  },
   methods: {
     validate() {
       return new Promise((resolve, reject) => {
@@ -123,21 +133,6 @@ export default {
       if (!t) return
       Object.assign(this.form, t)
     },
-    getOrderChannel() {
-      if (this.conf.orderChannelOptions.length > 0) {
-        return
-      }
-      this.$request('/order/getDictItem').data({ name: '下单渠道' }).success((res) => {
-        if (Array.isArray(res?.data?.data)) {
-          this.conf.orderChannelOptions = res.data.data.map(s => {
-            return {
-              label: s.item_name,
-              value: s.item_code,
-            }
-          })
-        }
-      }).post()
-    }
   }
 }
 </script>

+ 77 - 45
src/views/create-order/components/paymentPlanModule.vue

@@ -2,7 +2,11 @@
   <!-- 回款计划 -->
   <el-form ref="form" label-width="206px" class="payment-plan-container">
     <el-form-item class="payment-plan-line" label-width="156px" label="回款次数" :required="required.payback">
-      <number-input v-model.number="form.payback" @input="onPaybackChange" placeholder="请输入" maxlength="2">
+      <number-input
+        :value="pageForm.paybackTimes"
+        @input="onPaybackChange"
+        placeholder="请输入"
+        maxlength="2">
         <template #append>次</template>
       </number-input>
       <div class="payback-table-container" v-if="payBackTimesMoreThan1">
@@ -26,7 +30,9 @@
             label="预计回款时间">
             <template slot-scope="scope">
               <el-date-picker
-                v-model="paybackTableData[scope.$index].time"
+                :value="paybackTableData[scope.$index].time"
+                @input="onChangeTableLineData('time', scope.$index, $event)"
+                value-format="timestamp"
                 type="date"
                 placeholder="请选择日期">
               </el-date-picker>
@@ -39,10 +45,10 @@
             label="预计回款金额(元)">
             <template slot-scope="scope">
               <el-input
-                v-model.number="paybackTableData[scope.$index].money"
+                :value="paybackTableData[scope.$index].money"
+                @input="onPaybackSplitMoneyChange(scope, $event)"
                 type="number"
                 placeholder="请输入"
-                @input="onPaybackSplitMoneyChange(scope)"
                 size="medium"
                 :disabled="scope.$index === paybackTableData.length - 1"
               ></el-input>
@@ -52,7 +58,11 @@
       </div>
     </el-form-item>
     <el-form-item class="payment-plan-line" :label="paybackTimeLabel" :required="required.paymentDeadline" v-if="!payBackTimesMoreThan1">
-      <number-input v-model.number="form.paymentDeadline" @input="onPaymentDeadlineChange" placeholder="请输入" maxlength="3">
+      <number-input
+        :value="pageForm.paymentDeadline"
+        @input="onPaymentDeadlineChange"
+        placeholder="请输入"
+        maxlength="3">
         <template #append>个工作日回款,预计回款时间:<span style="color: #36a3f7">{{ expectedPaymentDeadlineTimeString }}</span></template>
       </number-input>
     </el-form-item>
@@ -67,28 +77,20 @@ import { selectorVModelMixin } from '@/utils/mixins/selector-v-model'
 import { agreeStatusOptions } from '@/views/create-order/data/index.js'
 import NumberInput from '@/views/create-order/ui/NumberInput.vue'
 import { getWorkDay } from '@/api/modules'
-
-class PayBackTableRow {
-  constructor(time = '', money = '') {
-    this.time = time
-    this.money = money
-  }
-}
+import { pageFormState } from '@/views/create-order/mixins'
+import { mapState, mapGetters } from 'vuex'
+import { PayBackTableRow } from '@/views/create-order/data/interface'
 
 export default {
   name: 'PaymentPlanModule',
-  mixins: [selectorVModelMixin],
+  mixins: [selectorVModelMixin, pageFormState],
   components: {
     NumberInput
   },
   props: {
-    totalMoney: {
-      type: [Number, String],
-      default: undefined
-    },
     contractStatus: {
-      type: String,
-      default: '0',
+      type: [String, Number],
+      default: 0,
       validator(v) {
         return agreeStatusOptions.map(a => a.value).includes(v)
       }
@@ -108,20 +110,26 @@ export default {
         paymentDeadline: true,
       },
       form: {
-        payback: '1',
-        paymentDeadline: '',
+        // payback: '1',
+        // paymentDeadline: '',
       },
-      paybackTableData: [],
+      // paybackTableData: [],
       expectedPaymentDeadlineTime: '',
     }
   },
   computed: {
+    ...mapState({
+      pageForm: state => state.order.pageForm,
+    }),
+    ...mapGetters('order', ['pageTotalMoney', 'exceptPaybackMoney']),
     payBackTimesMoreThan1() {
-      return this.form.payback > 1
+      return this.pageForm.paybackTimes > 1
+    },
+    paybackTableData() {
+      return this.pageForm.paybackTableData
     },
     exceptedPayBackMoney() {
-      // exceptPayBackMoney
-      return this.totalMoney
+      return this.exceptPaybackMoney
     },
     requiredList() {
       return [
@@ -140,11 +148,11 @@ export default {
       ].filter(f => f.required)
     },
     expectedPaymentDeadlineTimeString() {
-      return this.expectedPaymentDeadlineTime || '-'
+      return this.pageForm.expectedPaymentDeadlineTime || '-'
     },
     paybackTimeLabel() {
       return '自协议签订/订单创建之日起'
-      // return this.contractStatus === '1' ? '自协议签订之日起': '自订单创建之日起'
+      // return this.contractStatus === 1 ? '自协议签订之日起': '自订单创建之日起'
     }
   },
   methods: {
@@ -166,18 +174,21 @@ export default {
       if (!t) return
       Object.assign(this.form, t)
     },
-    onPaybackChange() {
+    onPaybackChange(i) {
       const max = this.conf.paybackMax
-      const i = this.form.payback
       if (i > max) {
-        this.form.payback = max
+        this.setPageFormData('paybackTimes', max)
         this.$message({
           message: `最大值为${max}`,
           type: 'warning'
         })
+      } else {
+        this.setPageFormData('paybackTimes', i)
       }
       if (i > 1) {
         this.initPaybackTableData()
+      } else {
+        this.clearPaybackTableData()
       }
     },
     calcLastColumnMoney() {
@@ -194,51 +205,58 @@ export default {
 
       return lastMoney
     },
-    onPaybackSplitMoneyChange(scope) {
+    onPaybackSplitMoneyChange(scope, e) {
       const index = scope.$index
-      if (this.exceptedPayBackMoney === undefined || this.exceptedPayBackMoney === null) {
+      this.onChangeTableLineData('money', index, e)
+      if (!this.pageTotalMoney.hasContract) {
+        this.onChangeTableLineData('money', index, '')
         return this.$message({
           message: '请先输入合同金额',
           type: 'warning'
         })
       }
       if (scope.row.money === 0 || scope.row.money === '0') {
-        scope.row.money = ''
+        this.onChangeTableLineData('money', index, '')
         return this.$message({
-          message: '回款金额不能为0',
+          message: '不可为0',
           type: 'warning'
         })
       }
       let lastMoney = this.calcLastColumnMoney()
       // 1.2 限制当前格子的金额
       if (lastMoney < 0) {
-        this.paybackTableData[index].money = ''
         this.$message({
-          message: '需小于合同金额',
+          message: '需小于应收金额',
           type: 'warning'
         })
+        this.onChangeTableLineData('money', index, '')
         lastMoney = this.calcLastColumnMoney()
       }
-      this.paybackTableData[this.paybackTableData.length - 1].money = lastMoney
+      this.onChangeTableLineData('money', this.paybackTableData.length - 1, lastMoney)
+    },
+    onPaymentDeadlineChange(e) {
+      this.setPageFormData('paymentDeadline', e)
+      this.onPaymentDeadlineChangeCallback()
     },
     // 回款计划,工作日变更
-    onPaymentDeadlineChange: debounce(function() {
+    onPaymentDeadlineChangeCallback: debounce(function() {
       this.expectedPaymentDeadlineTime = ''
 
       // 签协议:预计回款时间=协议签订时间+填写的工作日
       // 不签协议:预计回款时间=订单创建时间+填写的工作日
 
       // 回款工作日为0
-      const paymentDeadline = toNumber(this.form.paymentDeadline)
+      const paymentDeadline = toNumber(this.pageForm.paymentDeadline)
       if (paymentDeadline === 0) {
-        if (this.contractStatus === '1') {
+        if (this.contractStatus === 1) {
           this.expectedPaymentDeadlineTime  = dateFormatter(this.contractTime || new Date(), 'yyyy-MM-dd')
         }else{
           this.expectedPaymentDeadlineTime = dateFormatter(new Date(), 'yyyy-MM-dd')
         }
+        this.setPageFormData('expectedPaymentDeadlineTime', this.expectedPaymentDeadlineTime)
       } else {
         let start = ''
-        if (this.contractStatus === '1') { // 签协议
+        if (this.contractStatus === 1) { // 签协议
           start = dateFormatter(this.contractTime || new Date(), 'yyyy-MM-dd')
         } else { // 不签协议
           start = dateFormatter(new Date(), 'yyyy-MM-dd')
@@ -252,14 +270,19 @@ export default {
         }
         getWorkDay(params).then((res) => {
           if (res.data) {
-            this.expectedPaymentDeadlineTime = res.data
+            // this.expectedPaymentDeadlineTime = res.data
+            this.setPageFormData('expectedPaymentDeadlineTime', res.data)
           }
         })
       }
     }, 700),
+    clearPaybackTableData() {
+      this.setPageFormData('paybackTableData', [])
+    },
     initPaybackTableData() {
-      const arr = new Array(this.form.payback).fill(undefined).map(() => new PayBackTableRow())
-      this.paybackTableData = arr
+      const arr = new Array(this.pageForm.paybackTimes - 0).fill(undefined).map(() => new PayBackTableRow())
+      // this.paybackTableData = arr
+      this.setPageFormData('paybackTableData', arr)
     },
     getSummaries(param) {
       const { columns } = param
@@ -280,6 +303,15 @@ export default {
       });
       return sums;
     },
+    onChangeTableLineData(key, index, e) {
+      // this.paybackTableData[index].time = e
+      this.updatePageFormArrayObjectValue({
+        arrayName: 'paybackTableData',
+        index,
+        key,
+        newValue: e
+      })
+    },
   }
 }
 </script>
@@ -291,7 +323,7 @@ export default {
     .el-input-group__append {
       background: transparent;
       border: none;
-      color: #1d1d1d;
+      color: $gray_10;
     }
     .el-input-group--append {
       width: auto;

+ 169 - 97
src/views/create-order/components/performanceBelongsModule.vue

@@ -3,15 +3,18 @@
   <el-form ref="form" label-width="156px" class="performance-belongs-container">
     <el-form-item class="performance-belongs-line" label="销售人员" :required="required.salePerson">
       <SelectTree
-        v-model="form.salePerson"
         class="sales-person-select"
+        :value="pageForm.salePerson"
+        @input="setPageFormData('salePerson', $event)"
         @change="onSalePersonSelectChange"
         :options="salesDepList"
         placeholder="请选择销售人员">
       </SelectTree>
-      <div class="sale-person-list-container" v-if="form.salePerson.length > 1">
+      <div class="sale-person-list-container" v-if="pageForm.salePerson.length > 1">
         <el-table
-          :data="form.salePersonTableList"
+          :data="pageForm.salePersonTableList"
+          :summary-method="getSummaries"
+          show-summary
           stripe
           border>
           <el-table-column
@@ -24,13 +27,15 @@
             prop="money"
             header-align="center"
             align="center"
-            label="预计回款金额(元)">
+            label="销售业绩(元)">
             <template slot-scope="scope">
               <number-input
-                v-model.number="form.salePersonTableList[scope.$index].money"
+                :value="pageForm.salePersonTableList[scope.$index].money"
+                @input="onSalePersonSplitMoneyChange(scope, $event)"
+                :disabled="scope.$index === getLastEditLineIndex()"
                 type="number"
+                :decimal="2"
                 placeholder="请输入"
-                @input="onSalePersonSplitMoneyChange(scope)"
                 size="medium"
               ></number-input>
             </template>
@@ -41,16 +46,14 @@
             align="center"
             label="销售渠道">
             <template slot-scope="scope" >
-              <template v-if="scope.$index === form.salePersonTableList.length - 1">-</template>
-              <template v-else>
-                <el-cascader
-                  :options="saleChannelOptions"
-                  :props="conf.cascaderProps"
-                  v-model="form.salePersonTableList[scope.$index].saleWay"
-                  size="medium"
-                  placeholder="请选择销售渠道"
-                  clearable></el-cascader>
-              </template>
+              <el-cascader
+                :options="saleChannelOptions"
+                :props="conf.cascaderProps"
+                :value="pageForm.salePersonTableList[scope.$index].saleWay"
+                @input="onChangeTableLineData('saleWay', scope.$index, $event)"
+                size="medium"
+                placeholder="请选择销售渠道"
+                clearable></el-cascader>
             </template>
           </el-table-column>
         </el-table>
@@ -58,7 +61,8 @@
     </el-form-item>
     <el-form-item class="performance-belongs-line" label="销售渠道" v-if="onlyOneSale" :required="required.saleWay && onlyOneSale">
       <el-cascader
-        v-model="form.saleWay"
+        :value="pageForm.saleWay"
+        @input="setPageFormData('saleWay', $event)"
         :options="saleChannelOptions"
         :props="conf.cascaderProps"
         size="medium"
@@ -69,24 +73,15 @@
 </template>
 
 <script>
-import { debounce } from 'lodash'
-import { selectorVModelMixin } from '@/utils/mixins/selector-v-model'
 import NumberInput from '@/views/create-order/ui/NumberInput.vue'
 import SelectTree from '@/views/create-order/components/product-info-submodule/select-tree'
 import { mapState, mapGetters } from 'vuex'
-
-class SalePersonTableRow {
-  constructor(name = '', id = '', money = '', saleWay = '') {
-    this.name = name
-    this.id = id
-    this.money = money
-    this.saleWay = saleWay
-  }
-}
+import { pageFormState } from '@/views/create-order/mixins'
+import { SalePersonTableRow } from '@/views/create-order/data/interface'
 
 export default {
   name: 'PerformanceBelongsModule',
-  mixins: [selectorVModelMixin],
+  mixins: [pageFormState],
   components: {
     NumberInput,
     SelectTree,
@@ -116,31 +111,39 @@ export default {
         saleWay: false,
       },
       form: {
-        salePerson: [],
-        saleWay: [],
-        salePersonTableList: [],
+        // salePerson: [],
+        // saleWay: [],
+        // salePersonTableList: [],
       },
     }
   },
   computed: {
-    ...mapGetters([
-      'getAdminUser',
-    ]),
+    ...mapState({
+      currentUserId: state => state.order.conf.currentUserEntId,
+      defaultSaleChannel: state => state.order.conf.defaultSaleChannel,
+    }),
     ...mapGetters('order', [
       'depTreeList',
       'saleChannelOptions',
+      'pageTotalMoney',
+      'salesMoneyTotal',
     ]),
     salesDepList() {
       return this.depTreeList
     },
     onlyOneSale() {
-      return this.form.salePerson.length === 1
+      return this.pageForm.salePerson.length === 1
     },
     salePersonDetailInfo() {
-      return this.form.salePerson.map(fs => {
+      return this.pageForm.salePerson.map(fs => {
         return this.deepestNodesArr.find(r => r.value === fs)
       })
     },
+    salesMoneyTotalNum() {
+      // 销售业绩=合同金额-渠道佣金
+      // return this.pageTotalMoney.contract - this.pageForm.channelCommission
+      return this.salesMoneyTotal
+    },
     deepestNodesArr() {
       return this.findDeepestNodes(this.salesDepList)
     },
@@ -162,14 +165,23 @@ export default {
     },
   },
   watch: {
-    'form.salePerson': function() {
+    'pageForm.salePerson': function() {
       this.initSaleWayDefaultValue()
+    },
+    currentUserId: {
+      immediate: true,
+      handler() {
+        this.setDefaultUserFn()
+      }
     }
   },
+  created() {
+    window.t = this
+  },
   methods: {
     validate() {
       return new Promise((resolve, reject) => {
-        const { salePerson, saleWay } = this.form
+        const { salePerson, saleWay } = this.pageForm
         const pass = salePerson.length > 0 && saleWay.length > 0
         if (pass) {
           resolve()
@@ -178,12 +190,12 @@ export default {
         }
       })
     },
-    getState() {
-      return Object.assign({}, this.form)
-    },
-    setState(t) {
-      if (!t) return
-      Object.assign(this.form, t)
+    setDefaultUserFn() {
+      if (this.setDefaultUser) {
+        if (this.currentUserId) {
+          this.setPageFormData('salePerson', [this.currentUserId])
+        }
+      }
     },
     findDeepestNodes(treeData = []) {
       let deepestNodes = []
@@ -203,21 +215,9 @@ export default {
 
       return deepestNodes
     },
-    getSaleWayOption() {
-      if (this.conf.saleWayOptions.length > 0) return
-      this.loading.saleWay = true
-      const r = this.$store.dispatch('order/getSelectOptions')
-      this.$request('/order/getSalesChannelItem').data({ name: '销售渠道' }).success((res) => {
-        this.conf.saleWayOptions = res.data?.data || []
-      }).complete(() => {
-        
-      }).post()
-
-      this.loading.saleWay = false
-    },
     getSelectSalePersonDetailList() {
       // 按照选择的顺序取
-      const { salePerson } = this.form
+      const { salePerson } = this.pageForm
       const arr = []
       salePerson.forEach(id => {
         const target = this.deepestNodesArr.find(opt => opt.value === id)
@@ -233,16 +233,16 @@ export default {
     initSalePersonList() {
       const selectDetailList = this.getSelectSalePersonDetailList()
       const arr = selectDetailList.map(item => {
-        return new SalePersonTableRow(item.label, item.value)
+        const saleWayKey = this.getDefaultSaleWayKey(item)
+        return new SalePersonTableRow(item.label, item.value, '', saleWayKey)
       })
-      arr.push(new SalePersonTableRow('合计'))
-      this.form.salePersonTableList = arr
+      this.setPageFormData('salePersonTableList', arr)
     },
     calcSalePersonLastColumnMoney() {
       // 1. 计算最后一个格子的金额
       let eNum = 0
-      this.form.salePersonTableList.forEach((p, index) => {
-        if (index <= this.form.salePersonTableList.length - 2) {
+      this.pageForm.salePersonTableList.forEach((p, index) => {
+        if (index <= this.getLastEditLineIndex()) {
           eNum += Number(p.money)
         }
       })
@@ -251,52 +251,124 @@ export default {
     compareDepName(a = '', b = '') {
       return a.includes(b) || b.includes(a)
     },
+    getSummaries(param) {
+      const { columns } = param
+      const sums = [];
+      columns.forEach((column, index) => {
+        if (index === 0) {
+          sums[index] = '合计';
+          return;
+        }
+        if (index === 1) {
+          sums[index] = this.salesMoneyTotalNum || '-';
+          return
+        }
+        if (index === 2) {
+          sums[index] = '-';
+          return
+        }
+      });
+      return sums;
+    },
     initSaleWayDefaultValue() {
-      const { salePerson } = this.form
+      const { salePerson } = this.pageForm
       if (salePerson.length === 1) {
         const depInfo = this.salePersonDetailInfo[0]
-        const saleWayKey = this.getDefaultSaleWayKey(depInfo.parentLabel)
-        this.form.saleWay = saleWayKey
+        const saleWayKey = this.getDefaultSaleWayKey(depInfo)
+        // this.form.saleWay = saleWayKey
+        if (saleWayKey) {
+          this.setPageFormData('saleWay', saleWayKey)
+        }
       } else {
         this.salePersonDetailInfo.forEach((depInfo, index) => {
-          const saleWayKey = this.getDefaultSaleWayKey(depInfo.parentLabel)
-          this.form.salePersonTableList[index].saleWay = saleWayKey
+          const saleWayKey = this.getDefaultSaleWayKey(depInfo)
+          // this.form.salePersonTableList[index].saleWay = saleWayKey
+          if (saleWayKey) {
+            this.onChangeTableLineData('saleWay', index, saleWayKey)
+          }
         })
       }
     },
-    getDefaultSaleWayKey(name) {
-      let parentLevel = null
-      let childLevel = null
-      const arr = []
-      parentLevel = this.saleChannelOptions.find(first => {
-        if (Array.isArray(first.children)) {
-          const target = first.children.find(s => {
-            const res = this.compareDepName(s.item_name, name)
-            if (res) {
-              childLevel = s
-              return s
-            }
-          })
-          if (target) {
-            return first
-          }
+    getSaleWayIdWith2(id) {
+      const prop = this.conf.cascaderProps.value
+      const target = this.saleChannelOptions.find(channel => {
+        if (Array.isArray(channel.children)) {
+          return channel.children.find(c => c[prop] === id)
         }
+        console.log(channel.children)
       })
-      if (parentLevel && childLevel) {
-        arr.push(parentLevel.item_code)
-        arr.push(childLevel.item_code)
+      if (target) {
+        return target[prop]
+      }
+    },
+    getDefaultSaleWayKey(depInfo) {
+      const deptId = depInfo.deptId
+      if (this.defaultSaleChannel) {
+        const defaultSaleWayId = this.defaultSaleChannel[deptId]
+        if (defaultSaleWayId) {
+          const saleWayKey2 = this.getSaleWayIdWith2(defaultSaleWayId)
+          if (saleWayKey2) {
+            const saleWayKey = [saleWayKey2, defaultSaleWayId]
+            return saleWayKey
+          }
+        }
       }
-      return arr || []
     },
-    onSalePersonSplitMoneyChange: debounce(function(scope) {
-      if (scope.$index === this.form.salePersonTableList.length - 1) {
-        // 最后一行,不做处理
-        return
+    calcLastColumnMoney() {
+      // 1. 计算最后一个格子的金额
+      // 1.1 先计算除了最后一个的总和
+      let eNum = 0
+      this.pageForm.salePersonTableList.forEach((p, index) => {
+        if (index <= this.pageForm.salePersonTableList.length - 2) {
+          eNum += Number(p.money)
+        }
+      })
+      
+      const lastMoney = this.salesMoneyTotalNum - eNum
+
+      return lastMoney
+    },
+    getLastEditLineIndex() {
+      return this.pageForm.salePersonTableList.length - 1
+    },
+    onSalePersonSplitMoneyChange(scope, e) {
+      const index = scope.$index
+      this.onChangeTableLineData('money', index, e)
+      if (!this.pageTotalMoney.hasContract) {
+        this.onChangeTableLineData('money', index, '')
+        return this.$message({
+          message: '请先输入合同金额',
+          type: 'warning'
+        })
       }
-      // 不是最后一行,计算最后一行的合计
-      const total = this.calcSalePersonLastColumnMoney()
-      this.form.salePersonTableList[this.form.salePersonTableList.length - 1].money = total
-    }, 200),
+      if (scope.row.money === 0 || scope.row.money === '0') {
+        this.onChangeTableLineData('money', index, '')
+        return this.$message({
+          message: '不可为0',
+          type: 'warning'
+        })
+      }
+
+      let lastMoney = this.calcLastColumnMoney()
+      // 1.2 限制当前格子的金额
+      if (lastMoney < 0) {
+        this.$message({
+          message: '合计值需小于应收金额',
+          type: 'warning'
+        })
+        this.onChangeTableLineData('money', index, '')
+        lastMoney = this.calcLastColumnMoney()
+      }
+      this.onChangeTableLineData('money', this.getLastEditLineIndex(), lastMoney)
+    },
+    onChangeTableLineData(key, index, e) {
+      this.updatePageFormArrayObjectValue({
+        arrayName: 'salePersonTableList',
+        index,
+        key,
+        newValue: e
+      })
+    },
   }
 }
 </script>
@@ -308,7 +380,7 @@ export default {
     .el-input-group__append {
       background: transparent;
       border: none;
-      color: #1d1d1d;
+      color: $gray_10;
     }
     .el-input-group--append {
       width: auto;

+ 6 - 0
src/views/create-order/components/product-info-submodule/AccountNumbers.vue

@@ -5,6 +5,7 @@
       v-model.number="payCount"
       @input="onInputChange"
       class="number-input"
+      :disabled="disabled"
       :placeholder="placeholder"
       maxlength="3"
     ></number-input>
@@ -14,6 +15,7 @@
       <number-input
         v-model.number="freeCount"
         @input="onInputChange"
+        :disabled="disabled"
         class="number-input"
         :placeholder="placeholder"
         maxlength="3"
@@ -31,6 +33,10 @@ export default {
     NumberInput
   },
   props: {
+    disabled: {
+      type: Boolean,
+      default: false
+    },
     showFreeCount: {
       type: Boolean,
       default: false

+ 1 - 0
src/views/create-order/components/product-info-submodule/CheckboxGroup.vue

@@ -58,6 +58,7 @@ export default {
 ::v-deep {
   .el-radio {
     line-height: 36px;
+    color: $gray_10;
   }
 }
 </style>

+ 63 - 23
src/views/create-order/components/product-info-submodule/ContractAmount.vue

@@ -2,7 +2,7 @@
   <div class="contract-amount-container">
     <template v-if="showContractMoney">
       <number-input
-        :value="value"
+        v-model="contractMoney"
         @input="onInputChange"
         :decimal="2"
         class="number-input number-input-long"
@@ -12,9 +12,14 @@
     </template>
     <span class="sub-info-container">
       <span class="sub-info-item no-amount-money" v-if="noStandardMoney">该产品暂无法计算标准售价</span>
-      <span class="sub-info-item" v-else>标准售价:{{ formatMoney(standardMoney) }}</span>
-
-      <span class="sub-info-item">折扣率:{{ formatMoney(discountRate) }}</span>
+      <span class="sub-info-item form-item-container" v-else>
+        <span class="form-item-label">标准售价:</span>
+        <span class="form-item-value">{{ formatMoney(standardMoney) }}</span>
+      </span>
+      <span class="sub-info-item form-item-container">
+        <span class="form-item-label">折扣率:</span>
+        <span class="form-item-value">{{ discountRate }}</span>
+      </span>
     </span>
   </div>
 </template>
@@ -27,32 +32,67 @@ export default {
   name: 'ContractAmount',
   props: {
     value: {
-      type: [String, Number],
-      default: '',
+      type: [Object, Array]
     },
     showContractMoney: {
       type: Boolean,
-      default: false
-    },
-    noStandardMoney: {
-      type: Boolean,
-      default: false
-    },
-    standardMoney: {
-      type: [String, Number],
-      default: '11111',
-    },
-    discountRate: {
-      type: [String, Number],
-      default: '',
+      default: true
     },
   },
   components: {
     NumberInput
   },
+  data() {
+    return {
+      contractMoney: '',
+      standardMoney: '',
+    }
+  },
+  model: {
+    prop: 'value',
+    event: 'change'
+  },
+  computed: {
+    discountRate() {
+      if (this.contractMoney >= 0 && this.standardMoney) {
+        return `${(this.contractMoney / this.standardMoney * 100).toFixed(2)}%`
+      } else {
+        return '-'
+      }
+    },
+    noStandardMoney() {
+      return this.standardMoney === -1
+    }
+  },
+  watch: {
+    value: {
+      immediate: true,
+      handler(n) {
+        this.setState(n)
+      }
+    }
+  },
   methods: {
-    onInputChange(e) {
-      this.$emit('input', e)
+    getState() {
+      return {
+        contractMoney: this.contractMoney,
+        standardMoney: this.standardMoney,
+      }
+    },
+    setState(e) {
+      if (!e) {
+        return
+      }
+      const { contractMoney, standardMoney } = e
+      this.contractMoney = contractMoney
+      this.standardMoney = standardMoney
+    },
+    onChange() {
+      const e = this.getState()
+      this.$emit('change', e)
+    },
+    onInputChange() {
+      this.onChange()
     },
     located(val) {
       return currencyFormat(val)
@@ -76,12 +116,12 @@ export default {
     width: 128px;
   }
   .number-input-long {
-    width: 300px;
+    width: 260px;
   }
 
   .sub-info-container {
     .sub-info-item {
-      margin-left: 80px;
+      margin-left: 60px;
     }
   }
 

+ 2 - 2
src/views/create-order/components/product-info-submodule/ElOnlineContractForm.vue

@@ -9,6 +9,7 @@
             maxlength="180"
             type="textarea"
             placeholder="请输入协议备注"
+            show-word-limit
             @input="inputChange($event, 'e_contract_remark')"
             :autosize="{minRows: 2,maxRows: 5}"
             :disabled="hasDisabled && disabledFiled && disabledFiled.indexOf('e_contract_remark') > -1"
@@ -54,7 +55,7 @@
 
     <el-row :gutter="2" v-show="showMore">
       <el-col :span="12">
-        <el-form-item label="甲方联系人" prop="e_contract_userA_contacts_name" required :show-message="false">
+        <el-form-item label="甲方联系人" required :show-message="false">
           <el-input
             :value="e_contract_userA_contacts_name"
             @input="inputChange($event, 'e_contract_userA_contacts_name')"
@@ -71,7 +72,6 @@
             @input="inputChange($event, 'e_contract_userA_contacts_tel')"
             placeholder="请输入甲方联系方式"
             size="medium"
-            show-word-limit
             maxlength="40"
           ></el-input>
         </el-form-item>

+ 10 - 2
src/views/create-order/components/product-info-submodule/ProductTypeSelector.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="product-type-selector-container">
-    <el-form-item label="选择产品" required>
+    <el-form-item label="选择产品" required :error="productTypeError">
       <el-cascader
         :options="productTypeOptions"
         class="el-cascader-w100"
@@ -31,6 +31,7 @@
 import { productTypeOptions2 } from '@/views/create-order/data/index.js'
 import { ActivityProductName } from '@/views/create-order/data'
 import { cloneDeep } from 'lodash'
+import { productKeyMap } from '@/views/create-order/data'
 
 
 export default {
@@ -92,7 +93,14 @@ export default {
       } else {
         return false
       }
-    }
+    },
+    productTypeError() {
+      if (this.productType1.includes(productKeyMap.dhy)) {
+        return '注:购买主体为个人,主账号与子账号额度共享'
+      } else {
+        return ''
+      }
+    },
   },
   methods: {
     onInput() {

+ 1 - 0
src/views/create-order/components/product-info-submodule/RadioGroup.vue

@@ -65,6 +65,7 @@ export default {
 ::v-deep {
   .el-radio {
     line-height: 36px;
+    color: $gray_10;
     &:focus:not(.is-focus):not(:active):not(.is-disabled) {
       .el-radio__inner {
         box-shadow: none;

+ 19 - 9
src/views/create-order/components/product-info-submodule/RelatedOrders.vue

@@ -1,6 +1,6 @@
 <template>
   <!-- 关联订单 -->
-  <div class="related-orders-container">
+  <div class="related-orders-container" :class="{ 'user-disabled': disabled }">
     <div class="relate-order-select">
       <el-select
         :value="linkedIdText"
@@ -42,6 +42,10 @@ export default {
       type: Boolean,
       default: false
     },
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
     options: {
       type: Array,
       default() {
@@ -198,6 +202,7 @@ export default {
       this.dialogTableVisible = f
     },
     handleSelectClick() {
+      if (this.disabled) return
       if (this.tableData.length > 0) {
         this.showTableDialog(true)
       } else {
@@ -240,18 +245,23 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-::v-deep {
-  .el-input.is-disabled {
-    .el-input__inner {
-      background-color: #fff;
-      border-color: #dcdfe6;
-      color: inherit;
-      cursor: pointer;
-      pointer-events: auto;
+.related-orders-container {
+  &:not(.user-disabled) {
+    ::v-deep {
+      .el-input.is-disabled {
+        .el-input__inner {
+          background-color: #fff;
+          border-color: #dcdfe6;
+          color: inherit;
+          cursor: pointer;
+          pointer-events: auto;
+        }
+      }
     }
   }
 }
 
+
 .relate-order-select {
   position: relative;
   display: inline-block;

+ 8 - 3
src/views/create-order/components/product-info-submodule/ServiceList.vue

@@ -7,7 +7,7 @@
           <div class="service-list-item-header">
             <el-checkbox
               v-model="service.checked"
-              :disabled="service.disabled"
+              :disabled="service.disabled || disabled"
               :indeterminate="service.indeterminate"
               @change="handleCheckAllChange($event, service)"
             >{{ service.label }}</el-checkbox>
@@ -15,12 +15,13 @@
           <div class="service-list-item-content">
             <template v-if="Array.isArray(service.children) && service.children.length > 0">
               <el-checkbox-group v-model="service.childrenValue" @change="handleChildrenCheckedChange($event, service)">
-                <el-checkbox v-for="sc in service.children" :label="sc.value" :key="sc.value">
+                <el-checkbox v-for="sc in service.children" :label="sc.value" :key="sc.value" :disabled="sc.disabled || disabled">
                   {{ sc.label }}
                   <el-select
                     v-if="Array.isArray(sc.options) && sc.options.length"
                     v-model="sc.checked"
                     class="el-select-w120"
+                    :disabled="disabled"
                     size="mini"
                     :placeholder="'0' + service.optionsUnit">
                     <el-option
@@ -56,6 +57,10 @@ export default {
       type: String,
       default: ''
     },
+    disabled: {
+      type: Boolean,
+      default: false,
+    }
   },
   data() {
     return {
@@ -289,7 +294,7 @@ export default {
   }
 }
 .service-list-container {
-  color: #1d1d1d;
+  color: $gray_10;
   font-size: 14px;
   .service-list-content {
     padding: 8px;

+ 1 - 1
src/views/create-order/components/product-info-submodule/TextCard.vue

@@ -18,7 +18,7 @@ export default {
 
 <style lang="scss" scoped>
 .text-card-container {
-  color: #1d1d1d;
+  color: $gray_10;
   font-size: 14px;
   ::v-deep {
     .text-color-main {

+ 40 - 44
src/views/create-order/components/product-info-submodule/ValidityPeriod.vue

@@ -10,19 +10,23 @@
           <number-input
             v-model.number="pay.count"
             @input="onNumberInputChange('pay')"
+            :readonly="disabled"
+            :min="1"
             class="number-input"
             placeholder="请输入付费周期"
             maxlength="3"
           ></number-input>
           <el-select
             v-model="pay.unit"
-            :disabled="payUnitDisabled"
+            :disabled="payUnitDisabled || disabled"
+            size="medium"
             @change="onUnitChange('pay')"
             class="unit-select">
             <el-option
               v-for="item in payUnitOptions"
               size="medium"
               :key="item.value"
+              value-key="label"
               :label="item.label"
               :value="item.value">
             </el-option>
@@ -35,18 +39,21 @@
             v-model.number="free.count"
             @input="onNumberInputChange('free')"
             class="number-input"
+            :readonly="disabled"
             placeholder="请输入付费周期"
             maxlength="3"
           ></number-input>
           <el-select
             v-model="free.unit"
-            :disabled="freeUnitDisabled"
+            :disabled="freeUnitDisabled || disabled"
+            size="medium"
             @change="onUnitChange('free')"
             class="unit-select">
             <el-option
               v-for="item in freeUnitOptions"
               size="medium"
               :key="item.value"
+              value-key="label"
               :label="item.label"
               :value="item.value">
             </el-option>
@@ -54,8 +61,8 @@
           <span>合计:<span class="highlight-text">{{ totalCountText }}</span></span>
         </template>
       </template>
-      <span class="action-container" v-if="showOpenOnPaymentDay">
-        <el-checkbox v-model="openOnPaymentDay" @change="onNumberInputChange" size="medium">全额回款当日开通</el-checkbox>
+      <span class="action-container" v-if="showPaybackOpenServerDay">
+        <el-checkbox v-model="paybackOpenServer" :disabled="disabled" @change="onNumberInputChange" size="medium">全额回款当日开通</el-checkbox>
       </span>
     </div>
     <div class="validity-period-line validity-period-line-2" v-if="showButtonTip">
@@ -68,6 +75,7 @@
 import NumberInput from '@/views/create-order/ui/NumberInput.vue'
 import { selectorVModelMixin } from '@/utils/mixins/selector-v-model'
 import { cloneDeep } from 'lodash'
+import { dateTimeUnitOptions } from '@/views/create-order/data/index.js'
 
 export default {
   name: 'ValidityPeriod',
@@ -76,12 +84,16 @@ export default {
     NumberInput
   },
   props: {
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
     showPayUnit: {
       type: Array,
       default() {
         return [
-          // '月',
-          // '日',
+          // 1,
+          // 2,
         ]
       }
     },
@@ -89,8 +101,8 @@ export default {
       type: Array,
       default() {
         return [
-          // '月',
-          // '日',
+          // 1,
+          // 2,
         ]
       }
     },
@@ -106,7 +118,7 @@ export default {
       type: Boolean,
       default: false,
     },
-    showOpenOnPaymentDay: {
+    showPaybackOpenServerDay: {
       type: Boolean,
       default: true,
     },
@@ -125,45 +137,22 @@ export default {
     amountText: {
       type: String,
       default: '',
-    }
+    },
   },
   data() {
     return {
       conf: {
-        unitOptions: [
-          {
-            label: '日',
-            value: '日',
-            textValue: '天',
-            rank: 1,
-          },
-          {
-            label: '月',
-            value: '月',
-            textValue: '个月',
-            rank: 3,
-          },
-          {
-            label: '季',
-            value: '季',
-            textValue: '季',
-            rank: 6,
-          },
-          {
-            label: '年',
-            value: '年',
-            rank: 9,
-          },
-        ]
+        // 1天 2月 3年 4季度
+        unitOptions: dateTimeUnitOptions
       },
-      openOnPaymentDay: false,
+      paybackOpenServer: false,
       pay: {
         count: '',
-        unit: '月',
+        unit: 2,
       },
       free: {
         count: '',
-        unit: '日',
+        unit: 1,
       }
     }
   },
@@ -180,9 +169,9 @@ export default {
     },
     totalCountText() {
       const payCount = Number(this.pay.count)
-      const payUnit = this.pay.unit
+      const payUnit = this.findUnitItem(this.pay.unit)?.label
       const freeCount = Number(this.free.count)
-      const freeUnit = this.free.unit
+      const freeUnit = this.findUnitItem(this.free.unit)?.label
 
       const payUnitIndex = this.unitTextArr.indexOf(payUnit)
       const freeUnitIndex = this.unitTextArr.indexOf(freeUnit)
@@ -223,6 +212,12 @@ export default {
         return this.conf.unitOptions
       }
     },
+    findUnitItem(value) {
+      return this.conf.unitOptions.find(item => item.value === value)
+    },
+    // onChange() {
+    //   this.onNumberInputChange()
+    // },
     onNumberInputChange() {
       const payload = this.getState()
       this.$emit('change', payload)
@@ -234,8 +229,8 @@ export default {
     },
     setState(e = {}) {
       if (!e) return
-      const { openOnPaymentDay, payCount, payUnit, freeCount, freeUnit } = e
-      this.openOnPaymentDay = !!openOnPaymentDay
+      const { paybackOpenServer, payCount, payUnit, freeCount, freeUnit } = e
+      this.paybackOpenServer = !!paybackOpenServer
       if (payCount) {
         this.pay.count = payCount
       }
@@ -252,11 +247,11 @@ export default {
     getState() {
       if (this.amountText) {
         return {
-          openOnPaymentDay: this.openOnPaymentDay,
+          paybackOpenServer: this.paybackOpenServer,
         }
       } else {
         const params = {
-          openOnPaymentDay: this.openOnPaymentDay,
+          paybackOpenServer: this.paybackOpenServer,
           payCount: this.pay.count,
           payUnit: this.pay.unit,
           freeCount: this.free.count,
@@ -271,6 +266,7 @@ export default {
 
 <style lang="scss" scoped>
 .validity-period-container {
+  color: $gray_10;
   .number-input {
     margin: 0 8px;
     width: 128px;

+ 1 - 2
src/views/create-order/components/product-info-submodule/select-tree.vue

@@ -4,7 +4,7 @@
       :value="tagLabel"
       @input="onSelectValueInput"
       :multiple="multiple"
-      :popper-append-to-body="false"
+      :popper-append-to-body="true"
       size="medium"
       :placeholder="placeholder">
       <template #empty>
@@ -148,7 +148,6 @@ export default {
     },
   },
   methods: {
-
     onInput(e) {
       this.$emit('input', e)
     },

+ 43 - 26
src/views/create-order/components/productInfoModule.vue

@@ -7,24 +7,25 @@
     <div class="product-info-footer">
       <el-form ref="form" :model="form" :rules="rules" label-width="126px" class="order-base-info-container">
         <div class="desc-detail-info-list">
-          <div class="desc-detail-info-item">
-            <div class="desc-label">合同金额合计</div>
-            <div class="desc-value">{{ located(form.totalContractMoney) }}</div>
+          <div class="desc-detail-info-item form-item-container">
+            <div class="desc-label form-item-label">合同金额合计</div>
+            <div class="desc-value form-item-value">{{ formatMoney(pageTotalMoney.contract) }}</div>
           </div>
-          <div class="desc-detail-info-item">
-            <div class="desc-label">标准售价合计</div>
-            <div class="desc-value">{{ located(form.totalStandardMoney) }}</div>
+          <div class="desc-detail-info-item form-item-container">
+            <div class="desc-label form-item-label">标准售价合计</div>
+            <div class="desc-value form-item-value">{{ formatMoney(pageTotalMoney.standard) }}</div>
           </div>
-          <div class="desc-detail-info-item">
-            <div class="desc-label">折扣率</div>
-            <div class="desc-value">{{ form.rate }}</div>
+          <div class="desc-detail-info-item form-item-container">
+            <div class="desc-label form-item-label">折扣率</div>
+            <div class="desc-value form-item-value">{{ pageTotalMoney.rate }}</div>
           </div>
         </div>
         <el-row :gutter="2">
           <el-col :span="12">
             <el-form-item label="渠道佣金" prop="channelCommission" :required="required.channelCommission">
               <number-input
-                v-model="form.channelCommission"
+                :value="channelCommission"
+                @input="onChannelCommissionChange"
                 placeholder="请填写合同金额"
                 :decimal="2"
               >
@@ -33,9 +34,15 @@
             </el-form-item>
           </el-col>
         </el-row>
-        <el-form-item label="0元订单类型" prop="orderMoney0Type" :required="required.orderMoney0Type">
+        <el-form-item
+          label="0元订单类型"
+          prop="orderMoney0Type"
+          v-if="orderMoney0TypeShow"
+          :required="required.orderMoney0Type"
+        >
           <RadioGroup
-            v-model="form.orderMoney0Type"
+            v-model="pageForm.orderMoney0Type"
+            @input="setPageFormData('orderMoney0Type', $event)"
             :options="conf.orderMoney0TypeOptions"
           />
         </el-form-item>
@@ -45,16 +52,17 @@
 </template>
 
 <script>
-import { selectorVModelMixin } from '@/utils/mixins/selector-v-model'
 import ProductInfoCardList from './ProductInfoCardList.vue'
 import NumberInput from '@/views/create-order/ui/NumberInput.vue'
 import { orderMoney0TypeOptions } from '@/views/create-order/data/index.js'
 import { currencyFormat } from '@/utils/str'
 import RadioGroup from '@/views/create-order/components/product-info-submodule/RadioGroup'
+import { mapState, mapGetters } from 'vuex'
+import { pageFormState } from '@/views/create-order/mixins'
 
 export default {
   name: 'ProductInfoModule',
-  mixins: [selectorVModelMixin],
+  mixins: [pageFormState],
   components: {
     RadioGroup,
     ProductInfoCardList,
@@ -70,16 +78,17 @@ export default {
         orderMoney0Type: true,
       },
       form: {
-        totalContractMoney: '3121231',
-        totalStandardMoney: '4513131',
-        rate: '111%',
-
         channelCommission: '0',
         orderMoney0Type: '1',
       },
     }
   },
   computed: {
+    ...mapState({
+      pageForm: state => state.order.pageForm,
+      channelCommission: state => state.order.pageForm.channelCommission
+    }),
+    ...mapGetters('order', ['pageTotalMoney', 'orderMoney0Type']),
     rules() {
       const channelCommissionRequired = this.required.channelCommission
 
@@ -89,14 +98,22 @@ export default {
         ]
       }
     },
-  },
-  created() {
-    this.initDefaultProductType()
+    orderMoney0TypeShow() {
+      return this.orderMoney0Type
+    },
   },
   methods: {
     located(val) {
       return currencyFormat(val)
     },
+    formatMoney(val) {
+      val = val - 0
+      if (val && !isNaN(val)) {
+        return this.located(val)
+      } else {
+        return '-'
+      }
+    },
     validate() {
       return new Promise((resolve, reject) => {
         this.$refs.form.validate((valid) => {
@@ -111,9 +128,9 @@ export default {
     async validateChildren() {
       const card = this.$refs.productionInfoCardList
     },
-    getState() {},
-    setState() {},
-    initDefaultProductType() {},
+    onChannelCommissionChange(e) {
+      this.setPageFormData('channelCommission', e)
+    },
   }
 }
 </script>
@@ -123,7 +140,7 @@ export default {
   .el-input-group__append {
     background: transparent;
     border: none;
-    color: #1d1d1d;
+    color: $gray_10;
   }
 }
 .desc-detail-info-list,
@@ -135,7 +152,7 @@ export default {
   margin-bottom: 12px;
 }
 .desc-detail-info-item {
-  margin-right: 24px;
+  margin-right: 30px;
   .desc-label {
     color: #686868;
   }

+ 214 - 0
src/views/create-order/components/schema-form/params.js

@@ -0,0 +1,214 @@
+
+
+import { schemaKeyMap, productKeyMap } from '@/views/create-order/data'
+import { SvipParamsGroup } from './params/svip'
+import { dateFormatter } from '@/utils/globalFun'
+// import { SalePersonTableRow, PayBackTableRow } from '@/views/create-order/data/interface'
+
+// 查询价格接口参数
+export function orderParams (type) {
+  if (type === productKeyMap.cjdy) {
+    return new SvipParamsGroup(type)
+  } else if (type === productKeyMap.dhy) {
+    return console.log(123)
+  }
+}
+
+
+// 获取回款计划
+function getReturnPlant(pageForm, contractMoney) {
+  let planList = undefined
+  if (Array.isArray(pageForm.paybackTableData) && pageForm.paybackTableData.length > 0) {
+    planList = pageForm.paybackTableData.map((p, index) => {
+      return {
+        code: index + 1,
+        time: p.time ? dateFormatter(p.time, 'yyyy-MM-dd') : '-',
+        money: p.money - 0 || 0
+      }
+    })
+    if (planList) {
+      planList.push({
+        code: '合计',
+        time: '-',
+        money: contractMoney
+      })
+    }
+  }
+  return {
+    day_num: pageForm.paymentDeadline - 0,
+    expect_time: pageForm.expectedPaymentDeadlineTime,
+    plant_list: planList,
+  }
+}
+function getSaleMoney(pageForm, contractMoney) {
+  let arr = []
+  const salePerson = pageForm.salePerson
+  const salePersonTableList = pageForm.salePersonTableList
+  if (Array.isArray(salePerson)) {
+    if (salePerson.length === 1) {
+      arr = salePerson.map(s => {
+        return {
+          id: s,
+          money: contractMoney - 0,
+          channel: pageForm.saleWay[1]
+        }
+      })
+    } else if (salePerson.length > 1) {
+      arr = salePersonTableList.filter(r => r.id).map(r => {
+        return {
+          id: r.id,
+          money: r.money - 0,
+          channel: r.saleWay[1]
+        }
+      })
+    }
+  }
+  return arr
+}
+/* 创建订单参数整理
+{
+  "order_status": 0,
+  "orderStatus": 0,
+  "buySubject": 1,
+  "companyName": "Example Corp",
+  "paybackCompany": "Acme Inc",
+  "personName": "John Doe",
+  "personPhone": "1234567890",
+  "userPhone": "1234567890",
+  "emptyPhone": 0,
+  "orderCode": "ORD12345",
+  "productArr": [
+    {
+      "id": 123456789,
+      "product_code": "VIP_PRO",
+      "activity_code": "SUMMER2023",
+      "order_code": "ORD12345",
+      "phone": "1234567890",
+      "amount": 99900,
+      "original_price": 129900,
+      "tactics": 3,
+      "reqSubject": 2,
+      "reqCompanyName": "Example Corp",
+      "product_type": "subscription",
+      "service_type": 1,
+      "linked_orderId": 123456788,
+      "paybackOpenServer": true,
+      "filter": {
+        "buyAccountCount": 5,
+        "giftAccountCount": 2,
+        "cutAccount": 1,
+        "buy_cycle": 12,
+        "buy_type": 2,
+        "give_cycle": 3,
+        "give_type": 2,
+        "finalAccountCount": 6,
+        "finalAreaCount": 3,
+        "supplement": "Additional support package"
+      }
+    }
+  ],
+  "contractMoney": 10000,
+  "order_money": 9500,
+  "channelCommission": 500,
+  "zeroOrderType": "none",
+  "contract": {
+    "contractId": "CT123",
+    "signDate": "2023-01-01"
+  },
+  "returnPlant": {
+    "planId": "RP123",
+    "amount": 10000,
+    "dueDate": "2023-06-01"
+  },
+  "saleMoney": [
+    {
+      "salesId": "S100",
+      "amount": 5000,
+      "date": "2023-01-15"
+    }
+  ],
+  "actPrice": {
+    "VIP_PRO": 89900
+  },
+  "payType": "credit",
+  "orderChannel": "web",
+  "paymentUser": "Jane Smith",
+  "orderRemark": "Urgent order",
+  "save": 2
+}
+*/
+export function createOrderParams(payload = {}) {
+  const { pageForm, productInfoList, pageTotalMoney, orderMoney0Type } = payload
+
+  const returnPlant = getReturnPlant(pageForm, pageTotalMoney.contract)
+  const saleMoney = getSaleMoney(pageForm, pageTotalMoney.contract)
+
+  let productArr = []
+  if (Array.isArray(productInfoList) && productInfoList.length > 0) {
+    productArr = productInfoList.map(product => {
+      const productType = product.productCardInfo.result?.productCode
+      if (productType) {
+        const params = orderParams(productType).getProductArrParams({
+          type: productType,
+          pageForm,
+          productInfo: product,
+        })
+        return params
+      }
+    })
+    productArr = productArr.filter(r => !!r)
+  }
+
+  const contract = {
+    contractId: '', // 归档协议编号,在创建订单期间不会上传。在订单详情处上传
+    contractCode: pageForm.signCode, // 线下协议编号
+    
+    contractStatus: pageForm.agreeStatus - 0, // 协议状态
+    e_contract_type: pageForm.e_contract_type, // 电子协议类型:  1有电子章  2无电子章
+    e_contract_userA_type: pageForm.e_contract_userA_type, // 协议甲方类型: 1个人  2企业
+    e_contract_userA_name: pageForm.e_contract_userA_name, // 协议甲方
+    e_contract_userA_contacts_name: pageForm.e_contract_userA_contacts_name, // 协议甲方联系人
+    e_contract_userA_contacts_tel: pageForm.e_contract_userA_contacts_tel, // 协议甲方联系方式
+    e_contract_userA_contacts_address: pageForm.e_contract_userA_contacts_address, // 协议甲方联系地址
+    e_contract_userB_contacts_name: pageForm.e_contract_userB_contacts_name, // 协议乙方联系人
+    e_contract_remark: pageForm.e_contract_remark, // 协议备注
+
+    contract_archive_status: pageForm.contract_archive_status, // 协议归档状态 1 已归档 0 未归档	
+    contractTime: pageForm.signTime ? dateFormatter(pageForm.signTime, 'yyyy-MM-dd') : '', // 协议签订时间
+    contract_file_url: pageForm.contract_file_url, // 上传合同地址
+    contract_archive_time: pageForm.contract_archive_time, // 协议归档时间
+    contract_archive_num: pageForm.contract_archive_num - 0, // 协议归档份数
+  }
+  
+  const p = {
+    order_status: pageForm.orderStatus, // 订单状态  0 未完成 1 已完成(审核通过后可以修改为已完成)
+    orderCode: pageForm.orderCode, // 订单编号
+    buySubject: pageForm.buySubject,
+    companyName: pageForm.companyName, // 公司名称
+    paybackCompany: pageForm.signUnit, // 签约主体
+    personName: pageForm.userTel, // 联系人手机号
+    personPhone: pageForm.userTel, // 联系人手机号
+    userPhone: pageForm.accountTel, // 开通服务手机号
+    contractMoney: pageTotalMoney.contract, // 合同金额总计
+    order_money: pageTotalMoney.standard, // 标准售价总计
+    channelCommission: pageForm.channelCommission - 0, // 佣金
+    zeroOrderType: orderMoney0Type ? pageForm.orderMoney0Type : 'none', // 0元订单类型
+    // emptyPhone: 0, // 无手机号
+    orderRemark: pageForm.orderRemark, // 订单备注
+    paymentUser: pageForm.paymentAccountName, // 付款户名
+    orderChannel: pageForm.orderChannel, // 下单渠道
+    payType: pageForm.reservationPayWay, // 支付方式
+    returnPlant,
+    productArr,
+    saleMoney,
+    contract,
+    // actPrice: {
+    //   VIP_PRO: 89900,
+    // },
+  }
+
+  console.log(p, pageForm, productInfoList)
+
+  return p
+}
+

+ 5 - 0
src/views/create-order/components/schema-form/params/base.js

@@ -0,0 +1,5 @@
+export class Parameters {
+  priceParams() {}
+
+  getProductArrParams() {}
+}

+ 40 - 0
src/views/create-order/components/schema-form/params/common.js

@@ -0,0 +1,40 @@
+
+// Id            int64  `json:"id"`                   //订单详情表id修改时需要此参数
+// ProductCode   string `json:"product_code"`         //产品code
+// ActivityCode  string `json:"activity_code"`        //活动code
+// OrderCode     string `json:"order_code,omitempty"` //订单编号
+// Phone         string `json:"phone,omitempty"`      //开通手机号
+// Amount        int64  `json:"amount"`               //实际价格
+// OriginalPrice int64  `json:"original_price"`       //原价
+// Tactics int `json:"tactics"` //策略 1仅购买 2仅赠送 3购买+赠送
+
+// ServiceType       int            ` json:"service_type,omitempty"`     //付费类型 1:购买 2:续费 3:升级 4:试用
+// LinkedOrderId     int64          `json:"linked_orderId,omitempty"`    //关联订单明细id
+// PaybackOpenServer bool           `json:"paybackOpenServer,omitempty"` //是否全额回款开通
+import { schemaKeyMap } from '@/views/create-order/data'
+
+export function getCommonParams(pageForm, productInfo) {
+  const productForm = productInfo?.productCardInfo?.form || {}
+  const serviceType = productForm[schemaKeyMap.payment]
+  const linkedOrderId = productForm[schemaKeyMap.linkedOrderId]
+  const paybackOpenServer = productForm.validityPeriod?.paybackOpenServer
+
+  const productCode = productForm[schemaKeyMap.specification]
+  const activityCode = productInfo?.productCardInfo?.result?.activityCode
+  const tactics = productForm.saleGifts
+  const standardMoney = productForm.contractAmount?.standardMoney
+  const contractMoney = productForm.contractAmount?.contractMoney
+
+  return {
+    product_code: productCode,
+    activity_code: activityCode,
+    phone: pageForm.accountTel,
+    amount: contractMoney,
+    original_price: standardMoney,
+    tactics,
+    service_type: serviceType,
+    serviceType, // 兼容大会参数
+    linked_orderId: linkedOrderId,
+    paybackOpenServer,
+  }
+}

+ 63 - 0
src/views/create-order/components/schema-form/params/svip.js

@@ -0,0 +1,63 @@
+import { Parameters } from './base'
+import { getCommonParams } from "./common";
+import { schemaKeyMap, productTypeMap } from '@/views/create-order/data'
+
+export class SvipParamsGroup extends Parameters {
+
+  constructor(type) {
+    super()
+    this.type = type
+  }
+  // 价格接口参数
+  priceParams({ pageForm, productInfo, type } = {}) {
+    // 参数为productArr中的某一项
+    const params = this.getProductArrParams({ pageForm, productInfo, type })
+  
+    if (!params.phone) return
+    if (!params.productType) return
+    if (!params.product_code) return
+    if (!params.filter?.buy_cycle) return
+    if (!params.filter?.buy_type) return
+  
+    return params
+  }
+
+  // 接口文档:http://172.31.31.204:4001/project/346/interface/api/3856
+  // 计算价格的方法
+  // BuyAccountCount  int `json:"buyAccountCount"`  //购买数量(仅购买主体是企业,个人版不需要)
+  // GiftAccountCount int `json:"giftAccountCount"` //赠送数量(仅购买主体是企业,个人版不需要)
+  // BuyCycle         int `json:"buy_cycle"`        //购买周期
+  // BuyType          int `json:"buy_type"`         //购买周期 类型 1天 2月 3年 4季度
+  // GiftCycle        int `json:"give_cycle"`       //赠送周期
+  // GiftType         int `json:"give_type"`        //赠送周期 类型 1天 2月 3年 4季度
+  // Supplement       string `json:"supplement" doc:"补充服务"`
+  getProductArrParams({ pageForm, productInfo, type }) {
+    const commonParams = getCommonParams(pageForm, productInfo)
+      const productForm = productInfo?.productCardInfo?.form || {}
+      const p = {
+        productType: productTypeMap[type],
+        ...commonParams,
+      }
+
+      const filter = {}
+      if (pageForm.buySubject === 2) {
+        // 企业版
+        filter.buyAccountCount = productForm.subAccountNumbers?.payCount
+        filter.giftAccountCount = productForm.subAccountNumbers?.freeCount
+      }
+      filter.buy_cycle = productForm.validityPeriod?.payCount
+      filter.buy_type = productForm.validityPeriod?.payUnit
+      filter.give_cycle = productForm.validityPeriod?.freeCount || 0
+      filter.give_type = productForm.validityPeriod?.freeUnit
+      // filter.supplement = productForm.supplement
+
+      p.filter = filter
+
+      return p
+  }
+}
+
+
+
+
+

+ 14 - 4
src/views/create-order/components/schema-form/products/common.js

@@ -147,6 +147,12 @@ export function createPaymentTypeSchema(lim, state) {
 
 // 通用关联订单规则生成
 export function createRelatedOrdersSchema() {
+  // const options = upgradeContentOptions
+  // const validateArr = options.filter(r => !r.hide)
+  // const key = schemaKeyMap.upgradeContent
+  // const defaultValue = state[key] || []
+
+
   return createSchemaItem({
     label: '关联订单',
     key: 'relatedOrders',
@@ -227,6 +233,7 @@ export function createValidityPeriodSchema() {
     component: ValidityPeriod,
     required: true,
     showMessage: false,
+    defaultValue: {},
     props: {
       amountText: '',
     }
@@ -234,16 +241,19 @@ export function createValidityPeriodSchema() {
 }
 
 // 合同金额
-export function createContractAmountSchema() {
+export function createContractAmountSchema({ state }) {
+  const key = 'contractAmount'
+  const defaultValue = state[key] || {}
   return createSchemaItem({
     label: '合同金额',
-    key: 'contractAmount',
+    key,
     className: 'contract-amount',
     component: ContractAmount,
     required: true,
+    defaultValue,
     props: {
-      showSubInfo: true,
-    }
+      showContractMoney: true,
+    },
   })
 }
 

+ 1 - 1
src/views/create-order/components/schema-form/products/svip.js

@@ -57,7 +57,7 @@ export function createSvipSchemaList(conf) {
     // 有效周期
     createValidityPeriodSchema(),
     // 合同金额
-    createContractAmountSchema(),
+    createContractAmountSchema({ info, state }),
   )
   return schema
 }

+ 79 - 25
src/views/create-order/components/schema-form/schema-form.vue

@@ -14,7 +14,23 @@
 import SchemaFormRenderer from '@/components/common/form-schema-renderer.vue'
 import { createSchema } from './schema'
 import { mapState, mapMutations } from 'vuex'
-import { schemaKeyMap } from '@/views/create-order/data'
+import { schemaKeyMap, productKeyMap } from '@/views/create-order/data'
+import { cloneDeep, debounce } from 'lodash'
+
+// 浅对比两个对象,找出值不相同的字段的 key 并以数组形式返回
+function findShallowDifferentKeys(obj1, obj2) {
+  const differentKeys = [];
+  if (typeof obj1!== 'object' || obj1 === null || typeof obj2!== 'object' || obj2 === null) {
+    return differentKeys;
+  }
+  const allKeys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
+  allKeys.forEach(key => {
+    if (obj1[key]!== obj2[key]) {
+      differentKeys.push(key);
+    }
+  });
+  return differentKeys;
+}
 
 export default {
   name: 'SchemaForm',
@@ -24,7 +40,7 @@ export default {
   props: {
     productType: {
       type: String,
-      default: 'cjdy'
+      default: productKeyMap.cjdy,
     },
     productInfo: {
       type: Object,
@@ -60,6 +76,8 @@ export default {
       ],
       rules: {},
       selectedRelatedOrder: {},
+      cacheValue: {}, // oldValue
+      unRefreshSchemaWhenFieldsChanged: ['contractAmount', 'linkedOrderId'],
     }
   },
   computed: {
@@ -97,7 +115,11 @@ export default {
     async validate() {
       return this.$refs.schemaForm.validate()
     },
+    cacheBeforeValue() {
+      Object.assign(this.cacheValue, this.value)
+    },
     onInput(e) {
+      this.cacheBeforeValue()
       this.$emit('input', e)
       this.afterInput() // 用于动态修改用户限制选中项
     },
@@ -131,7 +153,7 @@ export default {
         }
       } else if (buySubject === 2 && ent) {
         if (accountTel && companyName) {
-          this.getUserService()
+          await this.getUserService()
         }
       }
       this.afterInput()
@@ -162,20 +184,43 @@ export default {
       }
       return r
     },
+    getChangedFields() {
+      return findShallowDifferentKeys(this.value, this.cacheValue)
+    },
+    needChangeSchema() {
+      const changedFieldsArr = this.getChangedFields()
+      const changedFields = changedFieldsArr.join(',')
+      if (!changedFields) return true
+      return !this.unRefreshSchemaWhenFieldsChanged.includes(changedFields)
+    },
     getSchemaItemWithKey(key) {
       return this.schema.find(item => item.key === key)
     },
     // 处理并分发各个产品卡片操作的限制
-    afterInput() {
+    afterInput: debounce(function() {
+      // 修改某些字段时候才会更新schema
+      const needChange = this.needChangeSchema()
+      if (!needChange) return
       this.$nextTick(() => {
-        // this.changeSchemaCommon()
-        if (this.productType === 'cjdy') {
+        // 注意:此处函数不允许异步动态修改props。会造成递归循环调用
+        if (this.productType === productKeyMap.cjdy) {
           this.groupCjdySchema()
-        } else if (this.productInfo === 'dhy') {
+        } else if (this.productType === productKeyMap.dhy) {
           this.changeDhySchema()
         }
+        this.checkSchemaRule()
       })
-      console.log(this.thisProductInfoListItem, this.schema)
+    }, 10),
+    getPriceRequest: debounce(async function getPrice(p) {
+      this.$store.dispatch('order/getPrice', p).then(r => {
+        // 刷新xx组件价格
+        const key = schemaKeyMap.contractAmount
+        this.refreshValue({ [key]: Object.assign({}, this.value[key], { standardMoney: r }) })
+      })
+    }, 600),
+    // 根据schema的显示隐藏,检查schemaRule是否需要过滤
+    checkSchemaRule() {
+
     },
     // 工具:动态修改选中可用的类型
     utilChangeAvailablePayment() {
@@ -250,7 +295,7 @@ export default {
           // 1.1 个人: serviceArrMap不能位空,且,vipExist为true。此时可以续费(升级),否则不可以续费(升级)
           this.utilCheckAndEnabledUpgradeRenew()
         } else if (buySubject === 2) {
-          // 1.2 企业: 选择关联订单后再次查询。然后做1.1相判断
+          // 1.2 企业: 选择关联订单后再次查询。然后做1.1相判断
           // 此处判断在关联订单选择完成后判断
         }
       }
@@ -262,6 +307,7 @@ export default {
       const relatedOrders = this.getSchemaItemWithKey('relatedOrders')
       if (Array.isArray(this.thisProductInfoListItem?.productUserService?.serviceArrMap)) {
         const opt = this.thisProductInfoListItem?.productUserService?.serviceArrMap || []
+        // 表格源数据,传入后还需要再次整理
         relatedOrders.props.options = opt.map(t => {
           return {
             ...t,
@@ -344,7 +390,7 @@ export default {
       }
       main.props.text = `付费${mainAccountCount}个&nbsp;&nbsp;合计:<span class="text-color-main">${allTotal}</span>个`
     },
-    // 服务周期
+    // 有效周期
     commonChangeValidityPeriod() {
       const { saleGifts, [schemaKeyMap.payment]: payment } = this.value
       const m = this.getSchemaItemWithKey('validityPeriod')
@@ -358,19 +404,19 @@ export default {
           //  1 付费:文本框,非必填,仅可输入1和正整数,单位可选:月(初始值)、天
           //  2 赠送:文本框,非必填,仅可输入0和正整数,单位同付费,不支持选择。
           //  3  合计:付费+赠送,初始值为-,单位同付费,如若为0,则提示“请输入有效周期”;
-          this.$set(m.props, 'showPayUnit', ['月', '日'])
+          this.$set(m.props, 'showPayUnit', [1, 2])
           this.$set(m.props, 'freeUnitDisabled', true)
           this.$set(m.props, 'showPayInput', true)
           this.$set(m.props, 'sameUnit', true)
           if (payment === 1 || payment === 4) {
             // 购买、试用
             // do something...
-            this.$set(m.props, 'showOpenOnPaymentDay', true)
+            this.$set(m.props, 'showPaybackOpenServerDay', true)
             this.$set(m.props, 'showButtonTip', false)
           } else if (payment === 2) {
             // 续费
             // 不展示自动开通模块
-            this.$set(m.props, 'showOpenOnPaymentDay', false)
+            this.$set(m.props, 'showPaybackOpenServerDay', false)
             // 显示底部提示
             this.$set(m.props, 'showButtonTip', true)
           } else {
@@ -379,19 +425,19 @@ export default {
           }
         } else if (saleGifts === 2) {
           // 赠送
-          this.$set(m.props, 'showPayUnit', ['月', '日'])
+          this.$set(m.props, 'showPayUnit', [1, 2])
           this.$set(m.props, 'freeUnitDisabled', true)
           this.$set(m.props, 'showPayInput', false)
           // this.$set(m.props, 'sameUnit', true)
           if (payment === 1 || payment === 4) {
             // 购买、试用
             // do something...
-            this.$set(m.props, 'showOpenOnPaymentDay', true)
+            this.$set(m.props, 'showPaybackOpenServerDay', true)
             this.$set(m.props, 'showButtonTip', false)
           } else if (payment === 2) {
             // 续费
             // 不展示自动开通模块
-            this.$set(m.props, 'showOpenOnPaymentDay', false)
+            this.$set(m.props, 'showPaybackOpenServerDay', false)
             // 显示底部提示
             this.$set(m.props, 'showButtonTip', true)
           } else {
@@ -401,17 +447,16 @@ export default {
         }
       } else if (this.utilCheckIsSourcePack()) {
         // 【产品属性为资源包】且【该产品类型支持系统自动开通权限】
-        // 并且仅展示自动开通
+        // 仅展示自动开通
         // m.show = false // 有自动开通权限【展示】
         this.$set(m.props, 'showPayInput', false)
         this.$set(m.props, 'showFreeInput', false)
-        this.$set(m.props, 'showOpenOnPaymentDay', true)
+        this.$set(m.props, 'showPaybackOpenServerDay', true)
         this.$set(m.props, 'showButtonTip', false)
       } else {
         m.show = false
       }
     },
-    // 合同金额
     commonChangeContractAmount() {
       const { saleGifts } = this.value
       const m = this.getSchemaItemWithKey('contractAmount')
@@ -424,12 +469,16 @@ export default {
         this.$set(m.props, 'showContractMoney', false)
       }
 
-      // const price = this.getPrice()
-      // this.$set(m.props, 'noStandardMoney', price === -1)
-      // this.$set(m.props, 'standardMoney', price)
-
-      // const rate = discountRate()
-      // this.$set(m.props, 'discountRate', rate)
+      if (this.utilCheckIsVipService()) {
+        const p = {
+          productInfo: cloneDeep(this.thisProductInfoListItem),
+          type: this.productType,
+          productIndex: this.index,
+        }
+        this.getPriceRequest(p)
+      } else {
+        // 无法计算商品价格
+      }
     },
     // 组合以及定制:超级订阅规则
     groupCjdySchema() {
@@ -449,6 +498,11 @@ export default {
       // 合同金额
       this.commonChangeContractAmount()
     },
+    // 组合以及定制:大会员规则
+    changeDhySchema() {
+      // const m = this.getSchemaItemWithKey('productType')
+      // console.log(m)
+    },
     commonChangeSpecificationSchema() {
       const { buySubject } = this.pageForm
       const { [schemaKeyMap.payment]: payment } = this.value

+ 2 - 3
src/views/create-order/components/schema-form/schema.js

@@ -1,4 +1,5 @@
 import { createSvipSchemaList } from './products/svip'
+import { schemaKeyMap, productKeyMap } from '@/views/create-order/data'
 
 // 生成规则
 export function createSchemaListWithType(conf = {}) {
@@ -8,7 +9,7 @@ export function createSchemaListWithType(conf = {}) {
     console.warn('未传入type')
     return schemaList
   }
-  if (type === 'cjdy') {
+  if (type === productKeyMap.cjdy) {
     schemaList = createSvipSchemaList(conf)
   }
   return schemaList
@@ -54,14 +55,12 @@ export function createSchema(conf = {}) {
   const type = conf.type || ''
   const info = conf.info || {}
   const state = conf.state || {}
-  const index = conf.index || '0'
   const beforeAreaChange = conf.beforeAreaChange || undefined
   const schemaList = createSchemaListWithType({
     beforeAreaChange,
     type,
     info,
     state,
-    index,
   })
   const defaultValue = calcDefaultValueWithSchema(schemaList)
   const rules = calcRulesWithSchema(schemaList)

+ 17 - 0
src/views/create-order/data/interface.js

@@ -0,0 +1,17 @@
+// 回款表格行数据结构
+export class PayBackTableRow {
+  constructor(time = '', money = '') {
+    this.time = time
+    this.money = money
+  }
+}
+
+// 业绩表格行数据结构
+export class SalePersonTableRow {
+  constructor(name = '', id = '', money = '', saleWay = '') {
+    this.name = name
+    this.id = id
+    this.money = money
+    this.saleWay = saleWay
+  }
+}

+ 48 - 10
src/views/create-order/data/options.js

@@ -26,11 +26,11 @@ export const upgradeContentOptions = [
 export const agreeStatusOptions = [
   {
     label: '签协议',
-    value: '1'
+    value: 1
   },
   {
     label: '不签协议',
-    value: '0'
+    value: 0
   },
 ]
 
@@ -46,6 +46,18 @@ export const eSignTypeOptions = [
   }
 ]
 
+// 协议归档状态
+export const signArchiveOptions = [
+  {
+    label: '未归档',
+    value: 0
+  },
+  {
+    label: '已归档',
+    value: 1
+  }
+]
+
 // 产品类型
 export const productTypeOptions = [
   {
@@ -203,23 +215,49 @@ export const chargeStatusOptions = [
   },
 ]
 
-// 单位
+// 旧单位,废弃
+// export const oldDateTimeUnitOptions = [
+//   {
+//     label: '年',
+//     value: '1'
+//   },
+//   {
+//     label: '月',
+//     value: '2'
+//   },
+//   {
+//     label: '天',
+//     value: '3'
+//   },
+//   {
+//     label: '季',
+//     value: '4'
+//   },
+// ]
+
 export const dateTimeUnitOptions = [
   {
-    label: '年',
-    value: '1'
+    label: '日',
+    value: 1,
+    textValue: '天',
+    rank: 1,
   },
   {
     label: '月',
-    value: '2'
+    value: 2,
+    textValue: '个月',
+    rank: 3, // 排序
   },
   {
-    label: '天',
-    value: '3'
+    label: '季',
+    value: 3,
+    textValue: '季',
+    rank: 6,
   },
   {
-    label: '季',
-    value: '4'
+    label: '年',
+    value: 4,
+    rank: 9,
   },
 ]
 

+ 42 - 0
src/views/create-order/data/var.js

@@ -1,7 +1,49 @@
 export const ActivityProductName = '活动产品'
 
+export const productKeyMap = {
+  cjdy: 'cjdy', // 超级订阅
+  bigmember: 'dyh', // 大会员
+}
+
+export const productTypeMap = {
+  cjdy: 'VIP订阅', // 超级订阅
+  dhy: '大会员',
+  sfdyb: '省份订阅包',
+  rmgl: '人脉管理',
+  ygcg: '阳光采购',
+  yzcgxzb: '业主采购分析报告下载包',
+  zbqyxzb: '企业中标分析报告下载包',
+  scfxxzb: '市场分析定制报告下载包',
+  cgdwhxb: '采购单位画像包',
+  fjxzb: '附件下载包',
+  qysjgl: '企业商机管理',
+  sjllb: '数据流量包',
+  lssj: '历史数据',
+  jghsj: '结构化数据',
+  xyt: '医械通',
+  wyszhyx: '物业数字化营销',
+  yyszb: '运营商专版',
+  xyxcx: '行业小程序',
+  saxyrz: '3A信用认证',
+  iostxrz: 'ISO体系认证',
+  gg: '广告',
+  zbbtk: '中标必听课',
+  jyb: '剑鱼币',
+  jywkhy: '剑鱼文库会员',
+  ds: '打赏',
+  sjbg: '数据报告',
+  sjwn: '数据文件',
+  qym: '权益码',
+  bszz: '标书制作',
+  xskcpx: '线下课程培训',
+  kcfx: '课程分销',
+  xwlp: '实物礼品',     
+}
+
 export const schemaKeyMap = {
   payment: 'paymentType',
   upgradeContent: 'upgradeContent',
   specification: 'productSpecification',
+  linkedOrderId: 'linkedOrderId',
+  contractAmount: 'contractAmount',
 }

+ 270 - 0
src/views/create-order/hooks/checkRequired.js

@@ -0,0 +1,270 @@
+import { Message, Notification } from 'element-ui'
+import { phoneRegExp } from '@/utils/data'
+import { schemaKeyMap, productKeyMap } from '@/views/create-order/data'
+
+// 消息提示
+// Message({
+//   message: 'xxxx',
+//   type: 'warning', // success/warning/info/error
+//   showClose: false,
+// })
+
+// 通知
+// Notification({
+//   title: '标题',
+//   message: 'xxxxxx',
+//   type: 'warning', // success/warning/info/error
+// })
+function showToast(message, type = 'warning') {
+  return Message({
+    message,
+    type,
+  })
+}
+function emptyValue(v) {
+  return v === null || v === undefined || v === ''
+}
+
+
+function checkSvipRequired(pageForm, product) {
+  const res = {
+    p: true,
+    msg: ''
+  }
+  const form = product.productCardInfo?.form || {}
+  // const info = product.productCardInfo?.info || {}
+
+  // 关联订单-续费升级必填
+  if (form.paymentType === 2 || form.paymentType === 3) {
+    if (!form.relatedOrders) {
+      return Object.assign(res, {
+        p: false,
+        msg: '请选择关联订单'
+      })
+    }
+  }
+
+  // 产品规格
+  if (!form.productSpecification) {
+    return Object.assign(res, {
+      p: false,
+      msg: '请选择产品规格'
+    })
+  }
+
+  if (form.validityPeriod) {
+    if (form.validityPeriod.payCount <= 0) {
+      return Object.assign(res, {
+        p: false,
+        msg: '请输入有效周期'
+      })
+    }
+  } else {
+    return Object.assign(res, {
+      p: false,
+      msg: '请输入有效周期'
+    })
+  }
+
+  if (form.contractAmount) {
+    const m = form.contractAmount.contractMoney
+    if (emptyValue(m)) {
+      return Object.assign(res, {
+        p: false,
+        msg: '请输入合同金额'
+      })
+    }
+  } else {
+    return Object.assign(res, {
+      p: false,
+      msg: '请输入合同金额'
+    })
+  }
+  return res
+}
+
+function checkProductRequired({ pageForm, product } = {}) {
+  const defaultRes = {
+    p: false,
+    msg: '请选择产品类型'
+  }
+  const productType = product.productCardInfo.result?.productCode
+  if (productType === productKeyMap.cjdy) {
+    return checkSvipRequired(pageForm,  product)
+  }
+  return defaultRes
+}
+
+// 检查是否能提交
+export async function checkRequired({ pageForm, productInfoList } = {}) {
+  const p = pageForm
+  const pInfoList = productInfoList
+
+  // 基本信息校验
+  if (!p.buySubject) {
+    showToast('请选择购买主体')
+    return false
+  }
+  if (p.buySubject === 2) {
+    if (!p.companyName) {
+      showToast('请输入公司名称')
+      return false
+    }
+  }
+  if (p.userTel) {
+    if (!phoneRegExp.test(p.userTel)) {
+      showToast('联系人手机号格式不正确')
+      return false
+    }
+  } else {
+    showToast('请输入联系人手机号')
+    return false
+  }
+  if (p.accountTel) {
+    if (!phoneRegExp.test(p.accountTel)) {
+      showToast('开通权益手机号格式不正确')
+      return false
+    }
+  } else {
+    showToast('请输入开通权益手机号')
+    return false
+  }
+
+  // 产品信息校验
+  if (pInfoList.length > 0) {
+    // 根据rules规则判断,是否全部通过
+    let pass = true
+    let message = ''
+    for (let i = 0; i < pInfoList.length; i++) {
+      const product = pInfoList[i]
+      const { msg, p } = checkProductRequired({ pageForm, product })
+      if (!p) {
+        pass = p
+        message = msg
+        break
+      }
+    }
+    if (!pass && message) {
+      showToast(message)
+      return false
+    }
+  } else {
+    showToast('请选择产品类型', 'error')
+    return false
+  }
+
+  // if(getters.orderMoney0Type && !pageForm.orderMoney0Type){
+  //   showToast('请选择0元订单类型')
+  //   return
+  // }
+
+  // 协议信息校验
+  // 区分是否要签电子协议(从store中取值)
+  const needESign = false // showOnlineContractForm 
+  if (needESign) {
+    // 如购买主体或甲方类型为“企业”,则公司名称必填
+    const entMark = pageForm.buySubject === 2 || pageForm.e_contract_userA_type === 2
+    if (entMark && !pageForm.companyName) {
+      showToast('公司名称必填')
+      return
+    }
+    if (!pageForm.e_contract_type) {
+      showToast('电子协议类型必填')
+      return
+    }
+    // if (!pageForm.e_contract_userA_type) {
+    //   showToast('协议甲方类型必填')
+    //   return
+    // }
+    // if (!pageForm.e_contract_userA_name) {
+    //   showToast('协议甲方必填')
+    //   return
+    // }
+    // if (!pageForm.e_contract_userA_contacts_name) {
+    //   showToast('协议甲方联系人必填')
+    //   return
+    // }
+    // if (!pageForm.e_contract_userA_contacts_tel) {
+    //   showToast('协议甲方联系方式必填')
+    //   return
+    // }
+    // if (!pageForm.e_contract_userA_contacts_address) {
+    //   showToast('协议甲方联系地址必填')
+    //   return
+    // }
+    if (!pageForm.e_contract_userB_contacts_name) {
+      showToast('协议乙方联系人必填')
+      return
+    }
+  } else {
+    if (pageForm.contract_archive_status === 1) {
+      // 已归档
+      if (!pageForm.contract_archive_time) {
+        showToast('请选择归档时间')
+        return false
+      }
+    }
+  }
+
+  // 回款计划校验
+  if (!pageForm.paybackTimes) {
+    showToast('请输入回款次数')
+    return false
+  }
+  if (pageForm.paybackNum > 1 && !pageForm.paybackTableData.every(item => item.time && item.money)) {
+    showToast('请填写完整回款计划')
+    return
+  }
+  if (pageForm.paybackNum <= 1 && !pageForm.expectedPaymentDeadlineTime) {
+    showToast('请输入回款计划工作日')
+    return
+  }
+
+  // 业绩归属校验
+  if (Array.isArray(pageForm.salePerson)) {
+    if (pageForm.salePerson.length <= 0) {
+      showToast('请选择销售人员')
+      return
+    } else if (pageForm.salePerson.length === 1) {
+      // 一个销售人员
+      if (Array.isArray(pageForm.saleWay)) {
+        if (pageForm.saleWay.length <= 0) {
+          showToast('请选择销售渠道')
+          return
+        }
+      } else {
+        showToast('请选择销售渠道')
+        return
+      }
+    } else {
+      // 一个以上的销售人员
+      if (Array.isArray(pageForm.salePersonTableList)) {
+        const p = pageForm.salePersonTableList.every(item => item.money && item.saleWay)
+        if (!p) {
+          const p1 = pageForm.salePersonTableList.every(item => item.money)
+          if (!p1) {
+            showToast('请输入销售业绩')
+            return
+          }
+          const p2 = pageForm.salePersonTableList.every(item => Array.isArray(item.saleWay) && item.saleWay.length > 0)
+          if (!p2) {
+            showToast('请输入销售渠道')
+            return
+          }
+        }
+      }
+    }
+  } else {
+    showToast('请选择销售人员')
+    return
+  }
+
+
+  // 其他信息校验
+  if (!pageForm.reservationPayWay) {
+    showToast('请选择约定支付方式')
+    return
+  }
+
+  return true
+}

+ 72 - 3
src/views/create-order/index.vue

@@ -1,14 +1,22 @@
 <template>
   <div class="page-container">
-    <h1>创建订单</h1>
-    <div class="create-order-container">
+    <div class="page-container-header">创建订单</div>
+    <div class="page-container-content create-order-container">
       <CreateOrder />
     </div>
+    <div class="page-container-footer fixed-bottom">
+      <div class="button-group">
+        <el-button size="medium" class="submit-button submit" type="primary" :loading="loading.create" @click="submit">提交</el-button>
+        <el-button size="medium" class="submit-button stash" type="primary" plain :loading="loading.create" @click="stash">暂存</el-button>
+        <el-button size="medium" class="submit-button cancel" plain @click="cancel">取消</el-button>
+      </div>
+    </div>
   </div>
 </template>
 
 <script>
 import CreateOrder from './components/create.vue'
+import { mapActions, mapState } from 'vuex'
 
 export default {
   name: 'CreateOrderIndex',
@@ -16,13 +24,74 @@ export default {
     CreateOrder
   },
   created() {},
+  computed: {
+    ...mapState({
+      loading: state => state.order.loading
+    })
+  },
+  methods: {
+    ...mapActions('order', ['createOrder']),
+    async doSave(save) {
+      const id = await this.createOrder({ save })
+      if (id) {
+        this.$toast('订单创建成功')
+      }
+    },
+    submit() {
+      // 提交
+      this.doSave(2)
+    },
+    stash() {
+      // 暂存
+      this.doSave(1)
+    },
+    cancel() {},
+  }
 }
 </script>
 
 <style lang="scss" scoped>
 .page-container {
   background-color: #fff;
-  padding: 8px;
+  padding: 8px 0;
   border-radius: 8px;
 }
+.page-container-header,
+.page-container-content,
+.page-container-footer {
+  padding: 0 8px;
+}
+
+.page-container-header {
+  padding: 20px 0;
+  font-size: 18px;
+  line-height: 28px;
+  color: #1d1d1d;
+  text-align: center;
+}
+
+.page-container-footer {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 8px;
+}
+.button-group {
+  display: inline-block;
+  .submit-button {
+    width: 132px;
+    &.stash {
+      background: #fff;
+      &:hover {
+        color: $main;
+      }
+    }
+  }
+}
+.fixed-bottom {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
 </style>

+ 18 - 0
src/views/create-order/mixins/index.js

@@ -0,0 +1,18 @@
+import { mapState, mapMutations } from 'vuex'
+
+export const pageFormState = {
+  computed: {
+    ...mapState({
+      pageForm: state => state.order.pageForm
+    })
+  },
+  methods: {
+    ...mapMutations('order', [
+      'setPageForm',
+      'updatePageFormArrayObjectValue',
+    ]),
+    setPageFormData(key, e) {
+      this.setPageForm({ key, data: e })
+    }
+  }
+}

+ 17 - 1
src/views/create-order/ui/NumberInput.vue

@@ -5,8 +5,9 @@
     @input="handleInput"
     :placeholder="placeholder"
     type="text"
+    :readonly="readonly"
+    :disabled="disabled"
     size="medium"
-    min="0"
     :maxlength="maxlength"
   >
     <template #prepend>
@@ -26,6 +27,18 @@ export default {
       type: [Number, String],
       default: ''
     },
+    readonly: {
+      type: Boolean,
+      default: false,
+    },
+    min: {
+      type: Number,
+      default: 0
+    },
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
     // 保留n位小数
     decimal: {
       type: Number,
@@ -46,6 +59,9 @@ export default {
       let newVal = ''
       if (this.decimal <= 0) {
         newVal = this.formatNumber(val)
+        if (newVal < this.min) {
+          newVal = ''
+        }
       } else {
         newVal = this.formatFloat(val, this.decimal)
       }