SelectOrderDetailCard.vue 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111
  1. <template>
  2. <div class="order-detail-card-container">
  3. <InfoCard title="基本信息" v-if="false">
  4. <div class="order-detail-card-content">
  5. <div class="order-detail-card-item" v-for="(item, index) in basicInfoItems" :key="index">
  6. {{ item.label }}:{{ getFilteredValue(orderData[item.key], item.filter) || '-' }}
  7. </div>
  8. </div>
  9. </InfoCard>
  10. <InfoCard title="产品信息">
  11. <div class="order-detail-card-content" v-if="false">
  12. <div class="order-detail-card-item" v-for="(item, index) in productInfoTotalItems" :key="index">
  13. <div v-if="item.key === 'pure_amount'">
  14. {{ item.label }}:¥{{ formatNumber(orderData[item.key]) || '-' }}
  15. <span class="red-chong" v-if="setRedPunchDisplay('合同金额')">(红冲过)</span>
  16. </div>
  17. <div v-else-if="item.key === 'final_price_total'">
  18. {{ item.label }}:¥{{ orderData[item.key] || '-' }}
  19. <span class="red-chong" v-if="setRedPunchDisplay('合同金额')">(红冲过)</span>
  20. </div>
  21. <div v-else-if="item.key === 'commission'">
  22. {{ item.label }}:¥{{ formatNumber(orderData[item.key]) || '0.00' }}
  23. <span class="red-chong" v-if="setRedPunchDisplay('销售费用')">(红冲过)</span>
  24. </div>
  25. <div v-else-if="item.key === 'rate_total'">
  26. {{ item.label }}:<span :class="{'red-chong': orderData[item.key] === '无法计算'}">{{ orderData[item.key] || '-' }}</span>
  27. <span class="red-chong" v-if="setRedPunchDisplay('折扣率')">(红冲过)</span>
  28. </div>
  29. <div v-else-if="item.key === 'zero_type'">
  30. <span v-if="shouldRenderItem(item)">
  31. {{ item.label }}:{{ getFilteredValue(orderData[item.key], item.filter) || '-' }}
  32. </span>
  33. </div>
  34. <div v-else>{{ item.label }}:{{ getFilteredValue(orderData[item.key], item.filter ) || '-' }}</div>
  35. </div>
  36. </div>
  37. <div class="order-detail-product-list">
  38. <div
  39. v-for="(product, index) in productData"
  40. :key="product.id"
  41. >
  42. <ProductCard
  43. style="margin-bottom: 20px"
  44. :title="setProductTitle(product, index)"
  45. :subtitle="product.auto !== 1 ? '该产品暂不支持系统自动开通权限,请联系运维开通': ''"
  46. >
  47. <div class="order-detail-product-content">
  48. <div class="grouped-items">
  49. <div
  50. v-for="(item, index) in productInfoItems"
  51. :key="index"
  52. :class="`item-span-${item.span}`">
  53. <div
  54. class="order-detail-card-item"
  55. v-if="shouldRenderItem(item, product)"
  56. >
  57. <div style="flex-direction: column;" v-if="item.key === 'linkedOrder'" class="linkedOrder">
  58. <div>{{ item.label }}:</div>
  59. <RelatedOrderTable class="detail-order-table" v-if="product.linkedOrder && Object.keys(product.linkedOrder).length" :table-data="product.options">
  60. </RelatedOrderTable>
  61. <span v-else>-</span>
  62. </div>
  63. <div v-else-if="item.key === 'subAccountCount'">
  64. <span v-html="getValidityPeriodHtml(product, item)"></span>
  65. </div>
  66. <div v-else-if="item.key === 'mainAccountCount'">
  67. <span v-html="getValidityPeriodHtml(product, item)"></span>
  68. </div>
  69. <div v-else-if="item.key ==='data_count'">
  70. <span v-html="getValidityPeriodHtml(product, item)"></span>
  71. </div>
  72. <div v-else-if="item.key ==='productName'">
  73. <span v-html="getValidityPeriodHtml(product, item)"></span>
  74. </div>
  75. <div v-else-if="item.key ==='rate'">
  76. {{ item.label }}:
  77. <span :class="{'no_open_root': product.original_price === 0}">{{ getFilteredValue(product[item.key], item.filter) || '-' }}</span>
  78. <span class="red-chong" v-if="product.isRed">(红冲过)</span>
  79. </div>
  80. <div v-else-if="item.key === 'final_price'">
  81. {{ item.label }}:¥{{ getFilteredValue(product[item.key], item.filter) }}
  82. <span class="red-chong" v-if="product.isRed">(红冲过)</span>
  83. </div>
  84. <div v-else-if="item.key === 'original_price'">
  85. {{ item.label }}:
  86. <span v-if="!product.original_price && product.product_type !== '阳光采购'" class="no_open_root">该产品暂无法计算标准售价</span>
  87. <span v-else>¥{{ getFilteredValue(product[item.key], item.filter) || '-' }}</span>
  88. </div>
  89. <div v-else-if="item.key === 'validity_period'">
  90. <span v-html="getValidityPeriodHtml(product, item)"></span>
  91. </div>
  92. <div v-else-if="item.key === 'supServiceIds'">
  93. {{ item.label }}:{{ getFilteredValue(product[item.key], item.filter) || '-' }}
  94. </div>
  95. <div v-else-if="item.key === 'service_starttime'">
  96. {{ item.label }}:
  97. <span :class="{'no_open_root':!product.is_service_open}" >{{ product.is_service_open? (product.service_starttime || '-') : '暂未开通' }}</span>
  98. </div>
  99. <div v-else-if="item.key === 'service_endtime'">
  100. {{ item.label }}:
  101. <span :class="{'no_open_root': !product.is_service_open}" >{{ product.is_service_open ? (product.service_endtime || '-') : '暂未开通' }}</span>
  102. </div>
  103. <div v-else-if="item.key ==='phone'">
  104. {{ item.label }}:{{ orderData.user_phone || '-' }}
  105. </div>
  106. <div v-else-if="item.key === 'giftCount'">
  107. {{ item.label }}:{{ product.filter.num || '-' }}个
  108. </div>
  109. <div v-else-if="item.key === 'sale_final_price'">
  110. {{ item.label }}:¥{{ divided(product.filter.num, 100) }}
  111. </div>
  112. <div v-else>{{ item.label }}:{{ getFilteredValue(product[item.key], item.filter) || '-' }}</div>
  113. </div>
  114. </div>
  115. </div>
  116. </div>
  117. <div class="order-detail-card-item" style="margin-left: 22px;">
  118. 是否{{formType}}:
  119. <el-radio-group v-model="product.canSelect">
  120. <el-radio :label="true">{{formType}}</el-radio>
  121. <el-radio :label="false">暂不操作</el-radio>
  122. </el-radio-group>
  123. </div>
  124. </ProductCard>
  125. <div v-if="formType === '退款'">
  126. <el-form v-show="product.canSelect" :disabled="!product.canSelect" ref="pForm" class="product-form" :model="product.form" :rules="FormRules" label-width="80px">
  127. <el-form-item label="退款金额" prop="money">
  128. <el-input v-model="product.form.money" @blur="formatInputMoney(product.form)" placeholder="请输入退款金额"></el-input>
  129. </el-form-item>
  130. <el-form-item label="退款原因" prop="reason">
  131. <el-select v-model="product.form.reason" placeholder="请选择退款原因">
  132. <el-option v-for="t in refundType" :key="t.v" :label="t.v" :value="t.v"></el-option>
  133. </el-select>
  134. </el-form-item>
  135. <el-form-item label="备注" prop="desc">
  136. <el-input type="textarea" maxlength="200" v-model="product.form.desc"></el-input>
  137. </el-form-item>
  138. </el-form>
  139. </div>
  140. <div v-if="formType === '红冲'">
  141. <el-form v-show="product.canSelect" :disabled="!product.canSelect" ref="pForm" class="product-form" :model="product.form" :rules="FormRules2" label-width="80px">
  142. <el-form-item label="合同金额" prop="money">
  143. <el-input v-model="product.form.money" @blur="formatInputMoney(product.form)" placeholder="请输入合同金额"></el-input>
  144. </el-form-item>
  145. <!-- <el-form-item label="佣金" prop="commission">-->
  146. <!-- <el-input v-model="product.form.commission" @blur="formatInputMoney(product.form, 'commission')" placeholder="请输入佣金金额"></el-input>-->
  147. <!-- </el-form-item>-->
  148. <el-form-item label="修正原因" prop="reason">
  149. <el-select v-model="product.form.reason" placeholder="请选择修正原因">
  150. <el-option v-for="t in correctionType" :key="t.v" :label="t.v" :value="t.v"></el-option>
  151. </el-select>
  152. </el-form-item>
  153. <el-form-item label="" prop="desc" v-if="product.form.reason === '其他'">
  154. <el-input type="textarea" maxlength="200" v-model="product.form.desc" placeholder="请输入修正原因"></el-input>
  155. </el-form-item>
  156. </el-form>
  157. </div>
  158. </div>
  159. </div>
  160. </InfoCard>
  161. <InfoCard title="其他信息" v-if="false">
  162. <div class="order-detail-card-content">
  163. <div class="order-detail-card-item" v-for="(item, index) in otherInfoItems" :key="index">
  164. {{ item.label }}:{{ getFilteredValue(orderData[item.key], item.filter) || '-' }}
  165. </div>
  166. </div>
  167. </InfoCard>
  168. <ServiceList v-show="false" :buySubject="orderData.buy_subject" ref="serviceListRef"></ServiceList>
  169. </div>
  170. </template>
  171. <script>
  172. import InfoCard from '../../ui/InfoCard.vue';
  173. import ProductCard from '../../ui/ProductCard.vue';
  174. import {buySubjectOptions, paymentTypeOptions} from '../../data/options.js';
  175. import { div, calcDiscountRate, roundToTwoDecimals } from '@/utils/number/';
  176. import TableCard from '../../ui/TableCard.vue';
  177. import ServiceList from "@/views/create-order/components/product-info-submodule/ServiceList.vue";
  178. import {showActivityCardRateModule} from "@/views/create-order/hooks/utils";
  179. import RelatedOrderTable from '../product-info-submodule/RelatedOrderTable.vue';
  180. import { dateFormatter } from '@/utils/globalFun'
  181. import { maskPhone } from '@/utils/str/index'
  182. import { findPaymentType } from '@/views/create-order/hooks/format'
  183. export default {
  184. name: 'SelectOrderDetailCard',
  185. components: {
  186. ServiceList,
  187. InfoCard,
  188. ProductCard,
  189. RelatedOrderTable,
  190. TableCard
  191. },
  192. props: {
  193. orderDetail: {
  194. type: Object,
  195. default: () => {}
  196. },
  197. formType: {
  198. type: String,
  199. default: () => ''
  200. }
  201. },
  202. data() {
  203. return {
  204. basicInfoItems: [
  205. { label: '创建人', key: 'create_person' },
  206. { label: '创建时间', key: 'create_time' },
  207. { label: '最近更新人', key: 'last_update_person' },
  208. { label: '最近更新时间', key: 'autoUpdate' },
  209. { label: '订单审核状态', key: 'audit_status', filter: 'orderCoursed' },
  210. { label: '订单状态', key: 'order_status', filter: 'orderStatus' }
  211. ],
  212. otherInfoItems: [
  213. { label: '约定支付方式', key: 'pay_way' },
  214. { label: '下单渠道', key: 'order_channel_new' },
  215. { label: '付款户名', key: 'payment_user'},
  216. { label: '订单备注', key: 'remark' },
  217. ],
  218. productInfoTotalItems: [
  219. { label: '合同金额合计', key: 'final_price_total' },
  220. { label: '标准售价合计', key: 'original_price_total'},
  221. { label: '折扣率', key: 'rate_total' },
  222. { label: '销售费用', key: 'commission'},
  223. { label: '净合同金额合计', key: 'pure_amount'},
  224. { label: '0元订单类型', key: 'zero_type', condition: () => this.orderData.final_price_total === '0.00' },
  225. ],
  226. productInfoItems: [
  227. { label: '活动产品', key: 'activityName', span: 1, condition: (product) => product.activityName },
  228. { label: '付费类型', key: 'service_type', filter: 'orderServiceType', span: 3},
  229. { label: '升级内容', key: 'supServiceIds', span: 3, condition: (product) => product.service_type === 3 },
  230. { label: '产品规格', key: 'productName', span: 3},
  231. { label: '报告份数', key: 'reportNumber', span: 1, condition: (product) => this.reportType.includes(product.product_type)},
  232. { label: '剑鱼币个数', key: 'coinCount', span: 1, condition: (product) => product.product_type && product.product_type === '剑鱼币'},
  233. { label: '购买产品', key: 'buyProductName', span: 1, condition: (product) => product.product_type && product.product_type === '结构化数据'},
  234. { label: '服务列表', key: 'bigServiceNamesPro', span: 1, condition: (product) => product.productName && product.productName.includes('自定义') && product.product_code === 'dyh001'},
  235. { label: '数据条数', key: 'data_count', span: 1, condition: (product) => product.data_count && product.data_count !== '-'},
  236. { label: '有效周期', key: 'validity_period', span: 1},
  237. { label: '合同金额', key: 'final_price', span: 3},
  238. { label: '标准售价', key: 'original_price', span: 3},
  239. { label: '折扣率', key: 'rate', span: 3},
  240. { label: '子账号数量', key: 'subAccountCount', span: 1, condition: (product) => product.filter.buyAccountCount || product.filter.giftAccountCount },
  241. { label: '主账号数量', key: 'mainAccountCount', span: 1 },
  242. { label: '关联订单', key: 'linkedOrder', span: 1},
  243. { label: '邮箱地址', key: 'email', span: 1, condition: (product) => product.product_type === '数据报告' },
  244. { label: '赠品数量', key: 'giftCount', span: 1, condition: (product) => {
  245. return product.tactics === '2' && product.filter.num && product.product_type !== '销售费用'
  246. } },
  247. { label: '销售费用', key: 'sale_final_price', span: 1, condition: (product) => {
  248. return product.tactics === '2' && product.product_type === '销售费用'
  249. }},
  250. { label: '补充说明', key: 'supExplanation', span: 1, condition: (product) => product.supExplanation && product.supExplanation !== '-' },
  251. { label: '开通权益手机号', key: 'phone', span: 3},
  252. { label: '服务开始时间', key: 'service_starttime', span: 3},
  253. { label: '服务结束时间', key: 'service_endtime', span: 3}
  254. ],
  255. orderData: {},
  256. productData: [],
  257. FormRules: {
  258. reason: [
  259. { required: true, message: '请选择退款原因', trigger: 'blur' },
  260. ],
  261. money: [
  262. { required: true, message: '请输入退款金额', trigger: 'blur' },
  263. ],
  264. },
  265. FormRules2: {
  266. reason: [
  267. { required: true, message: '请选择红冲原因', trigger: 'blur' },
  268. ],
  269. money: [
  270. { required: true, message: '请输入合同金额', trigger: 'blur' },
  271. ],
  272. },
  273. refundType: [
  274. { v: '个人垫付退款', n: '个人垫付退款' },
  275. { v: '产品购买错误', n: '产品购买错误' },
  276. { v: '客户不满意', n: '客户不满意' },
  277. { v: '活动免单', n: '活动免单' },
  278. { v: '测试', n: '测试' },
  279. { v: '其他', n: '其他' }
  280. ],
  281. correctionType: [
  282. { v: '录入错误', n: '录入错误' },
  283. { v: '客户不再支付', n: '客户不再支付' },
  284. { v: '其他', n: '其他' }
  285. ],
  286. linkOrderColumns: [
  287. {
  288. prop: 'name',
  289. label: '产品类型及规格',
  290. width: 154,
  291. render (row) {
  292. if(row.name === 'VIP订阅') {
  293. return '超级订阅'
  294. } else {
  295. return row.name ? row.name : '-'
  296. }
  297. }
  298. },
  299. {
  300. prop: 'empowerCount',
  301. label: '账号数量',
  302. width: 80
  303. },
  304. {
  305. prop: 'serviceEndTime',
  306. label: '到期时间',
  307. width: 102
  308. },
  309. {
  310. prop: 'buySubject',
  311. label: '购买主体',
  312. width: 80,
  313. render (row) {
  314. return buySubjectOptions.find(item => item.value === row.buySubject)?.label || '-'
  315. }
  316. },
  317. {
  318. prop:'order_code',
  319. label: '订单编号',
  320. width: 154
  321. },
  322. {
  323. prop:'service_type',
  324. label: '付费类型',
  325. width: 80
  326. },
  327. {
  328. prop:'create_time',
  329. label: '创建时间',
  330. width: 102
  331. }
  332. ],
  333. parsedFilter: {}
  334. }
  335. },
  336. watch: {
  337. orderDetail: {
  338. handler(newVal) {
  339. this.beforeInit(newVal);
  340. },
  341. deep: true,
  342. immediate: true
  343. }
  344. },
  345. computed: {
  346. reportType() {
  347. return ['数据报告', '业主采购分析报告下载包', '企业中标分析报告下载包', '市场分析定制报告下载包', '采购单位画像包', '附件下载包', '投标企业信用报告']
  348. }
  349. },
  350. mounted() {
  351. this.beforeInit();
  352. },
  353. methods: {
  354. formatInputMoney (f, key = 'money') {
  355. f[key] = this.formatNumber((f[key] || 0) * 100)
  356. },
  357. async getProductForm () {
  358. let vCount = 0
  359. let vCountSuccess = 0
  360. for (let i = 0; i < this.$refs.pForm.length; i++) {
  361. const v = this.$refs.pForm[i]
  362. if (!v.disabled) {
  363. vCount++
  364. await v.validate().then(() => {
  365. vCountSuccess++
  366. })
  367. } else {
  368. v.resetFields()
  369. }
  370. }
  371. if (vCount === vCountSuccess && vCountSuccess >= 1) {
  372. return this.productData.filter(v => v.canSelect)
  373. } else {
  374. return []
  375. }
  376. },
  377. initItemForm () {
  378. let result = {}
  379. switch (this.formType) {
  380. case '退款': {
  381. result = {
  382. money: '',
  383. reason: '',
  384. desc: ''
  385. }
  386. break
  387. }
  388. case '红冲': {
  389. result = {
  390. money: '',
  391. reason: '',
  392. commission: '',
  393. desc: ''
  394. }
  395. break
  396. }
  397. }
  398. return result
  399. },
  400. beforeInit() {
  401. this.$nextTick(() => {
  402. this.init();
  403. })
  404. },
  405. async init() {
  406. this.orderData = this.orderDetail?.orderData || {};
  407. let productData = this.orderDetail?.productData || [];
  408. const { _productArr } = this.orderDetail || {}
  409. if(productData.length > 0) {
  410. // 使用 Promise.all 来并行处理每个 product 的异步操作
  411. productData = await Promise.all(
  412. productData.map(async (product, index) => {
  413. product.canSelect = index === 0
  414. product.form = this.initItemForm()
  415. try {
  416. const parsedFilter = this.parseProductFilter(product);
  417. const serviceIds = parsedFilter.serviceIds || [];
  418. const supServiceIdsOrigin = parsedFilter.supServiceIds || [];
  419. const bigServiceNames = product.bigServiceNames || '';
  420. // 等待异步方法完成
  421. const supServiceIds = this.buildSupServiceIds(parsedFilter || []);
  422. const bigServiceNamesPro = product.product_type === '大会员' ? await this.buildBigServiceNames(serviceIds, supServiceIdsOrigin, bigServiceNames) : '';
  423. const rate = this.calculateDiscountRate(product);
  424. const validityPeriod = this.calculateValidityPeriod(
  425. parsedFilter,
  426. product
  427. );
  428. const supExplanation = parsedFilter.supExplanation
  429. const dataCount = this.buildDataCount(product, parsedFilter);
  430. const reportNumber = this.buildReportNumber(product, parsedFilter);
  431. const coinCount = this.buildCoinCount(product, parsedFilter);
  432. const buyProductName = this.buildBuyProductName(product, parsedFilter);
  433. const subAccountCount = this.buildSubAccountCount(product, parsedFilter);
  434. const mainAccountCount = '1个';
  435. // const linkedOrder = this.processLinkedOrder(product);
  436. const finalPrice = this.formatNumber(product.final_price);
  437. let originalPrice = this.formatNumber(product.original_price);
  438. const linkedOrder = product.linkedOrder || {}; // 关联订单信息
  439. const { buySubject, empowerCount, name, serviceStartTime, serviceEndTime, provinceCount, productCode, orderArr } = linkedOrder || {}
  440. const options = [{
  441. buySubject,
  442. comboId: 0, // 套餐id
  443. empowerCount, // 人数
  444. name,
  445. phone: this.orderData.personPhone, // 开通权益手机号
  446. provinceCount, // 订阅省份
  447. serviceStartTime, // 服务开始时间
  448. serviceEndTime, // 结束时间
  449. serviceList: [],
  450. linkedOrder: orderArr,
  451. vipExist: false, //当前服务是否在有限期内
  452. productCode
  453. }]
  454. const item = options[0]
  455. const arr = []
  456. if (Array.isArray(item.linkedOrder) && item.linkedOrder.length > 0) {
  457. item.linkedOrder.forEach(t => {
  458. arr.push({
  459. ...item,
  460. linkedId: item.linkedId,
  461. _linkedId: `${item.linkedId}-${t.order_code}`,
  462. linkedOrderSplit: t,
  463. })
  464. })
  465. } else {
  466. // 关联订单只有1个的情况
  467. arr.push({
  468. ...item,
  469. _linkedId: item.linkedId + '',
  470. })
  471. }
  472. const associationOrder = arr.map(t => {
  473. return {
  474. ...t,
  475. ...this.sortTableText(t),
  476. }
  477. })
  478. const productName = this.buildProductName(product, parsedFilter)
  479. const email = product.product_type === '数据报告' ? parsedFilter.email : '';
  480. const newProduct = {
  481. ...product,
  482. filter: parsedFilter,
  483. rate,
  484. productName,
  485. reportNumber,
  486. coinCount,
  487. buyProductName,
  488. validity_period: validityPeriod,
  489. data_count: dataCount,
  490. subAccountCount,
  491. mainAccountCount,
  492. linkedOrder,
  493. supServiceIds,
  494. bigServiceNamesPro,
  495. email,
  496. final_price: finalPrice,
  497. sale_final_price: finalPrice,
  498. original_price: originalPrice,
  499. supExplanation,
  500. ..._productArr[index] || {},
  501. options: associationOrder
  502. };
  503. newProduct._a = showActivityCardRateModule(_productArr, newProduct)
  504. return newProduct
  505. } catch (error) {
  506. console.error('产品信息初始化失败:', error);
  507. return product;
  508. }
  509. })
  510. );
  511. }
  512. this.setTotalAmounts(productData);
  513. this.productData = productData;
  514. },
  515. sortTableText(service) {
  516. const order = service.linkedOrderSplit || {}
  517. return {
  518. // productTypeText: service.name || '-',
  519. empowerCountText: service.empowerCount ? `${service.empowerCount}个` : '-',
  520. serviceEndTimeText: service.serviceEndTime ? service.serviceEndTime : '-',
  521. buySubjectText: service.buySubject === 1 ? '个人' : '企业',
  522. phone: service.phone,
  523. short_phone: maskPhone(service.phone),
  524. order_code: order.order_code || '-',
  525. service_type: order.service_type || '-',
  526. service_type_text: findPaymentType(order.service_type)?.label || '-',
  527. create_time: order.create_time || '-',
  528. productCode: service.productCode || ''
  529. }
  530. },
  531. // 权限开通
  532. openPermissionActivation() {
  533. this.$refs.newDetailModel.permissionActivationShow = true;
  534. },
  535. isShowPermission (product) {
  536. const { auto, attribute } = product
  537. const { return_status } = this.orderData?.return_status || 0
  538. // 仅当该产品类型支持系统自动开通权限,且产品属性为会员服务或资源包,且“回款状态”为“全额回款”才展示,否则不展示;
  539. return auto === 1 && (attribute === 1 || attribute === 2) && return_status === 1
  540. },
  541. // 设置红冲标识显示字段
  542. setRedPunchDisplay(product) {
  543. const { isUpCommission, isUpEnt, isUpCash } = this.orderDetail?.redPunchData || {};
  544. const RETURN_BOOL = {
  545. '合同金额': isUpCash,
  546. '销售费用': isUpCommission,
  547. '签约主体': isUpEnt,
  548. '折扣率': isUpCash
  549. }
  550. return RETURN_BOOL[product] || false;
  551. },
  552. buildProductName(product, parsedFilter) {
  553. const { product_type: type, productName: defaultName } = product;
  554. const { finalAreaCount, report_name: reportName, addAreaCount, area_count: areaCount, s_name: sName } = parsedFilter || {};
  555. let result = defaultName;
  556. if (type === '数据报告') {
  557. result = reportName || defaultName;
  558. } else if (type === 'VIP订阅' || type === '省份订阅包') {
  559. const count = addAreaCount || 0;
  560. const suffix = count > 0 ? `<span style="color:#f74e29">(此次增购${count}个省)</span>` : '';
  561. // 1购买2续费3升级4试用
  562. const serviceType = product.service_type;
  563. const final = finalAreaCount > 0 ? `${finalAreaCount}个省` : '全国';
  564. if(serviceType == 1) {
  565. result = type === '省份订阅包' ? `${final}${suffix}` : final;
  566. } else if (serviceType == 2) {
  567. result = final;
  568. } else if(serviceType == 3) {
  569. result = `${final}${suffix}`
  570. }
  571. } else if (type === '物业|采购意向+最新采购') {
  572. const count = areaCount || 0;
  573. result = count ? `${count}个省` : '全国';
  574. } else if (type === '中标必听课') {
  575. result = sName || defaultName;
  576. }
  577. return result;
  578. },
  579. buildSupServiceIds(parfilter) {
  580. const { supServiceIds, buyAccountCount, giftAccountCount } = parfilter
  581. const upgradeContent = []
  582. if (supServiceIds && supServiceIds.length > 0) {
  583. upgradeContent.push('补充权益')
  584. }
  585. if(buyAccountCount || giftAccountCount) {
  586. upgradeContent.push('增购子账号')
  587. }
  588. return upgradeContent.length ? upgradeContent.join('、') : '-'
  589. },
  590. buildBigServiceNames(serviceIds, supServiceIds, bigServiceNames) {
  591. return new Promise((resolve) => {
  592. if (!serviceIds || serviceIds.length === 0) {
  593. resolve('');
  594. return;
  595. }
  596. // 避免修改原始数组,创建新的整型数组
  597. const numericServiceIds = serviceIds.map(id => parseInt(id));
  598. let sameValues = [], differentValues = [];
  599. const hasBigServiceNames = bigServiceNames ? `、${bigServiceNames}` : ''
  600. this.$nextTick(() => {
  601. try {
  602. const serviceListRef = this.$refs.serviceListRef;
  603. if (!serviceListRef || typeof serviceListRef.calcAlreadyBuyServiceNamesArr !== 'function') {
  604. resolve('');
  605. return;
  606. }
  607. if(supServiceIds && supServiceIds.length > 0) {
  608. // 比较supServiceIds和serviceIds相同的值,并返回相同的值组成的数组,不同的值也返回一个数组
  609. // 已购服务
  610. // 不同的值组成的数组
  611. differentValues = serviceIds.filter(value => !supServiceIds.includes(value));
  612. const differentValuesArr = differentValues.map(id => parseInt(id));
  613. if(differentValuesArr.length === 0) {
  614. console.warn('differentValuesArr为空')
  615. }
  616. const baseServiceIdsArr = serviceListRef.calcAlreadyBuyServiceNamesArr(differentValuesArr);
  617. const baseServiceData = Array.isArray(baseServiceIdsArr)? baseServiceIdsArr.join('、') : ''
  618. // 补充服务
  619. // 相同的值组成的数组
  620. sameValues = supServiceIds.filter(value => serviceIds.includes(value));
  621. if (sameValues.length && sameValues.includes(4) && sameValues.includes(19)) {
  622. // 同时有4和19时,去掉 4
  623. sameValues = sameValues.filter(value => value !== 4);
  624. }
  625. const supServiceIdsArr = sameValues.map(id => parseInt(id));
  626. if(supServiceIdsArr.length === 0) {
  627. console.warn('supServiceIdsArr为空')
  628. }
  629. const resSupServiceIdsArr = serviceListRef.calcAlreadyBuyServiceNamesArr(supServiceIdsArr);
  630. // 如果resSupServiceData中包含基础服务+企业管理,需要去掉, buy_subject为1时,去掉企业管理
  631. const baseServiceIndex = resSupServiceIdsArr.findIndex(item =>
  632. item === (this.orderData.buy_subject === 2 ? '基础服务+企业管理' : '基础服务')
  633. );
  634. if (baseServiceIndex !== -1) {
  635. resSupServiceIdsArr.splice(baseServiceIndex, 1);
  636. }
  637. const resSupServiceData = Array.isArray(resSupServiceIdsArr)? resSupServiceIdsArr.join('、') : '';
  638. // 如果有补充服务,就拼接,如果没有补充服务,就不拼接,直接返回原已购服务名连接字符串
  639. const supplement = resSupServiceData ? `【补充服务】:${resSupServiceData}` : ''
  640. // 最终服务字符串:补充服务名称;原已购服务名称
  641. const serviceStr = `${supplement};【原已购服务】:${baseServiceData}${hasBigServiceNames}`
  642. resolve(serviceStr);
  643. } else {
  644. if(numericServiceIds.length === 0) {
  645. console.warn('numericServiceIds为空')
  646. }
  647. const res = serviceListRef.calcAlreadyBuyServiceNamesArr(numericServiceIds);
  648. const serviceData = Array.isArray(res) ? res.join('、') + hasBigServiceNames : '';
  649. resolve(serviceData);
  650. }
  651. } catch (error) {
  652. console.error('Error calculating service names:', error);
  653. resolve('');
  654. }
  655. });
  656. });
  657. },
  658. parseProductFilter(product) {
  659. let parsedFilter = {};
  660. if (typeof product.filter === 'string') {
  661. try {
  662. parsedFilter = JSON.parse(product.filter || '{}');
  663. } catch {
  664. parsedFilter = {};
  665. }
  666. } else {
  667. parsedFilter = product.filter || {};
  668. }
  669. return parsedFilter;
  670. },
  671. // 计算折扣率
  672. calculateDiscountRate(product) {
  673. let rate = '无法计算';
  674. if (product.original_price && Number(product.original_price) >= 0) {
  675. rate = (calcDiscountRate(product.final_price, product.original_price)) + '%';
  676. }
  677. return rate;
  678. },
  679. // 构造子账号数量字符串。
  680. buildSubAccountCount(product, filter) {
  681. const buyCount = Number(filter?.buyAccountCount) || 0;
  682. const giftCount = Number(filter?.giftAccountCount) || 0;
  683. if (buyCount || giftCount) {
  684. const countTotal = buyCount + giftCount;
  685. return `付费${buyCount}个,赠送${giftCount}个,合计:<span class="color_main">${countTotal}</span>个`;
  686. }
  687. return '';
  688. },
  689. // 处理关联订单数据。
  690. // processLinkedOrder(product) {
  691. // if (product.linkedOrder && Object.keys(product.linkedOrder).length > 0) {
  692. // const orderList = [product.linkedOrder];
  693. // return this.flattenLinkOrderList(orderList);
  694. // }
  695. // return [];
  696. // },
  697. // 设置合同金额合计和标准售价合计,以及计算折扣率总和。
  698. setTotalAmounts(productData) {
  699. this.orderData = this.orderDetail.orderData || {};
  700. const totalFinalPrice = this.isBackstageOrder ? (this.formatNumber(this.orderData.pay_money) || '0.00') : (this.formatNumber(this.orderData.order_money) || '0.00')
  701. let totalOriginalPrice = productData.reduce((acc, cur) => acc + Number(cur.original_price), 0).toFixed(2);
  702. const totalDiscountRate = div(totalFinalPrice, totalOriginalPrice) * 100
  703. const rateTotal = totalDiscountRate ? (div(totalFinalPrice, totalOriginalPrice) * 100).toFixed(2) + '%' : '0%'
  704. // 无标准售价产品,标准售价合计展示为无法计算
  705. let noOriginalPriceProductBool = false
  706. productData.forEach(product => {
  707. if(!product.original_price) {
  708. noOriginalPriceProductBool = true
  709. }
  710. })
  711. const originalPriceTotal = !noOriginalPriceProductBool
  712. if(originalPriceTotal) {
  713. totalOriginalPrice = this.isBackstageOrder ? this.formatNumber(this.orderData.order_money || '0.00') : this.formatNumber(this.orderData.original_price || '0.00')
  714. }
  715. this.orderData = {
  716. ...this.orderData,
  717. final_price_total: totalFinalPrice,
  718. original_price_total: originalPriceTotal ? '¥' + totalOriginalPrice : '无法计算',
  719. rate_total: originalPriceTotal && Number(totalOriginalPrice) ? rateTotal : '无法计算'
  720. }
  721. },
  722. // flattenLinkOrderList(linkOrderList) {
  723. // const result = [];
  724. // if (!linkOrderList || linkOrderList.length === 0) return result;
  725. // linkOrderList.forEach(item => {
  726. // const { orderArr, ...rest } = item; // 拆分 orderArr 和其他字段
  727. // if (!orderArr || orderArr.length === 0) return;
  728. // orderArr.forEach(order => {
  729. // result.push({
  730. // ...rest, // 非 orderArr 的字段(如 name, empowerCount 等)
  731. // ...order // orderArr 中的字段(如 order_code, service_type 等)
  732. // });
  733. // });
  734. // });
  735. // return result;
  736. // },
  737. shouldRenderItem(item, product) {
  738. if (item.condition && typeof item.condition === 'function') {
  739. const conditionResult = item.condition(product)
  740. if(!conditionResult) {
  741. // item.span = 0
  742. }
  743. return conditionResult
  744. }
  745. return true;
  746. },
  747. buildDataCount(product, parsedFilter) {
  748. const { pNum, give_cycle } = parsedFilter;
  749. const totalNums = Number(pNum) + (Number(give_cycle) || 0);
  750. const { product_type } = product;
  751. const includeProductType = ['数据流量包', '历史数据', '结构化数据']
  752. if(includeProductType.includes(product_type) && totalNums) {
  753. return `付费${pNum || 0}条,赠送${give_cycle || 0}条,合计<span class="color_main">${totalNums}</span>条`;
  754. } else {
  755. return '';
  756. }
  757. },
  758. buildReportNumber(product, parsedFilter) {
  759. const { num } = parsedFilter;
  760. const { product_type } = product;
  761. const includeProductType = this.reportType
  762. if(includeProductType.includes(product_type) && num) {
  763. return `${num || 0}份`;
  764. } else {
  765. if(product_type === '投标企业信用报告') {
  766. return '1份';
  767. } else {
  768. return '';
  769. }
  770. }
  771. },
  772. buildCoinCount(product, parsedFilter) {
  773. const { num } = parsedFilter;
  774. const { product_type } = product;
  775. const includeProductType = ['剑鱼币']
  776. if(includeProductType.includes(product_type) && num) {
  777. return `${num || 0}个`;
  778. } else {
  779. return '';
  780. }
  781. },
  782. buildBuyProductName(product, parsedFilter) {
  783. const { product: buy_product } = parsedFilter;
  784. const { product_type } = product;
  785. const includeProductType = ['结构化数据']
  786. if(includeProductType.includes(product_type) && buy_product) {
  787. return buy_product || '-';
  788. } else {
  789. return '';
  790. }
  791. },
  792. divided(a, b) {
  793. return div(a, b)
  794. },
  795. calculateValidityPeriod(parsedFilter, product) {
  796. const { buy_cycle, buy_type, give_cycle, give_type, validYear, activity_give_cycle, activity_give_type } = parsedFilter;
  797. const { product_type, returned_open } = product;
  798. const returned_opens = returned_open === 1 ? '(全额回款当日开通)' : '';
  799. if (product_type === '数据流量包') {
  800. // 数据流量包默认2年
  801. return `${validYear || 2}年${returned_opens}`;
  802. }
  803. const TIME_MAP = {
  804. '1': '日',
  805. '2': '月',
  806. '3': '年',
  807. '4': '季度'
  808. };
  809. // 安全地将字符串转为整数,默认为 0
  810. const parseCycle = (cycle) => {
  811. const num = parseInt(cycle);
  812. return isNaN(num) ? 0 : num;
  813. };
  814. // 处理单个周期,返回文本和总天数
  815. const processCycle = (cycle, type) => {
  816. const textValue = TIME_MAP[type] || '';
  817. const value = parseCycle(cycle);
  818. let days = 0;
  819. let text = '';
  820. if (textValue.includes('日')) {
  821. days += value;
  822. text = `${value}日`;
  823. } else if (textValue.includes('月')) {
  824. days += value * 30;
  825. text = `${value}个月`;
  826. } else if (textValue.includes('季度')) {
  827. days += value * 90;
  828. text = `${value}季度`;
  829. } else if (textValue.includes('年')) {
  830. days += value * 365;
  831. text = `${value}年`;
  832. }
  833. return { days, text };
  834. };
  835. // 计算购买和赠送部分
  836. const buyResult = processCycle(buy_cycle, buy_type);
  837. const giveResult = processCycle(give_cycle, give_type);
  838. // 总天数
  839. let totalDays = buyResult.days + giveResult.days;
  840. // 活动赠送时长
  841. let activityGiveResult = ''
  842. if (activity_give_cycle) {
  843. activityGiveResult = processCycle(activity_give_cycle, activity_give_type);
  844. totalDays += activityGiveResult.days;
  845. }
  846. if (totalDays <= 0) return '-';
  847. let totalText = '';
  848. if(buy_type === 1) {
  849. totalText = `${totalDays}日`;
  850. } else if(buy_type === 3) {
  851. if(totalDays % 365 === 0) {
  852. totalText = `${Math.floor(totalDays / 365)}年`;
  853. } else {
  854. // 获取年份
  855. const year = Math.floor(totalDays / 365);
  856. // 获取不够一年的天数
  857. const lessYearDays = totalDays % 365;
  858. if(lessYearDays % 30 === 0) {
  859. totalText = `${year ? year + '年' : ''}${Math.floor(lessYearDays / 30)}个月`;
  860. } else {
  861. totalText = `${year ? year + '年' : ''}${lessYearDays % 30}日`;
  862. }
  863. }
  864. } else {
  865. if(totalDays % 30 === 0) {
  866. totalText = `${Math.floor(totalDays / 30)}个月`;
  867. } else {
  868. // 如果天数小于30天,则不显示月,只显示天
  869. const isHas30Days = totalDays / 30 < 1;
  870. totalText = `${isHas30Days ? totalDays % 30 : Math.floor(totalDays / 30)}个月${totalDays % 30}日`;
  871. if(isHas30Days) {
  872. totalText = `${totalDays % 30}日`;
  873. } else {
  874. totalText = `${Math.floor(totalDays / 30)}个月${totalDays % 30}日`;
  875. }
  876. }
  877. }
  878. let giveResultText = giveResult.text ? `赠送${giveResult.text},` : '';
  879. if(activityGiveResult) {
  880. giveResultText = giveResult.text ? `销售赠${giveResult.text},` : '';
  881. return `付费${buyResult.text},活动赠${activityGiveResult.text},${giveResultText}合计<span class="color_main">${totalText}</span>${returned_opens}`;
  882. }
  883. // 如果是赠送类产品,则只显示赠送时长和合计时长, 不显示付费时长
  884. if(product.tactics === '2') {
  885. return `${giveResultText}合计<span class="color_main">${totalText}</span>${returned_opens}`
  886. }
  887. return `付费${buyResult.text},${giveResultText}合计<span class="color_main">${totalText}</span>${returned_opens}`;
  888. },
  889. getValidityPeriodHtml(product, item) {
  890. const label = item.label;
  891. const value = product[item.key] || '-';
  892. return `${label}:${value}`;
  893. },
  894. // 替代过滤器的通用方法
  895. getFilteredValue(value, filterName) {
  896. // 判断value是否是数字,或者是字符串数字
  897. if (typeof value === 'number' || /^\d+$/.test(value)) {
  898. if(!value && filterName !== 'orderServiceType') return '0.00'
  899. }
  900. if (!filterName) return value || '-';
  901. return this[filterName](value);
  902. },
  903. setProductTitle(product, index) {
  904. const tactics = product.tactics === '2' ? '【赠送】' : '【售卖】';
  905. if(product.product_type === 'VIP订阅') {
  906. return `${index + 1}.${tactics}超级订阅`;
  907. }
  908. return `${index + 1}.${tactics}${product.product_type}`;
  909. },
  910. // 格式化数字,保留两位小数
  911. formatNumber(num, x = 2) {
  912. if(!num) return 0.00
  913. const newnum = Number(num) / 100;
  914. return roundToTwoDecimals(newnum, x)
  915. },
  916. orderCoursed(val) {
  917. if (val == 0) {
  918. return '待提交'
  919. } else if (val == 1) {
  920. return '待一审'
  921. } else if (val == 2) {
  922. return '待二审'
  923. } else if (val == 4) {
  924. return '待三审'
  925. } else if (val == 3) {
  926. return '已通过'
  927. } else if (val == -2 || val == -3 || val == -4) {
  928. return '已退回'
  929. }
  930. },
  931. orderServiceType(val) {
  932. const matchedOption = paymentTypeOptions.find(option => option.value === val);
  933. return matchedOption ? matchedOption.label : val; // 如果未找到匹配项,返回原始值
  934. },
  935. orderStatus(val) {
  936. if (!val || val == 0) {
  937. return '未完成'
  938. } else if (val == 1) {
  939. return '已完成'
  940. } else if (val == -1) {
  941. return '逻辑删除'
  942. } else if (val == -2) {
  943. return '已取消'
  944. } else if (val == -3) {
  945. return '已退款'
  946. } else if (val == -3) {
  947. return '已退款'
  948. }
  949. },
  950. objectSpanMethod({ row, column, rowIndex, columnIndex }) {
  951. if (columnIndex <= 3) {
  952. if (rowIndex % 2 === 0) {
  953. return {
  954. rowspan: 2,
  955. colspan: 1
  956. };
  957. } else {
  958. return {
  959. rowspan: 0,
  960. colspan: 0
  961. };
  962. }
  963. }
  964. }
  965. }
  966. }
  967. </script>
  968. <style lang="scss" scoped>
  969. .order-detail-card-container {
  970. background: #F2F2F4;
  971. .product-form {
  972. padding: 16px 8px;
  973. ::v-deep {
  974. .el-form-item__label {
  975. font-size: 14px;
  976. line-height: 22px;
  977. color: #1d1d1d;
  978. }
  979. }
  980. }
  981. ::v-deep {
  982. .info-card-header {
  983. display: none;
  984. }
  985. .info-card {
  986. margin-bottom: 16px;
  987. padding: 0;
  988. }
  989. }
  990. .order-detail-card-content {
  991. display: flex;
  992. flex-wrap: wrap;
  993. align-items: center;
  994. margin-bottom: 6px;
  995. }
  996. .order-detail-product-actions-btn {
  997. padding: 4px 17px;
  998. background: $color_main;
  999. font-size: 14px;
  1000. line-height: 22px;
  1001. color: $white;
  1002. border-radius: 4px;
  1003. border: none;
  1004. cursor: pointer;
  1005. }
  1006. .grouped-items {
  1007. display: flex;
  1008. flex-wrap: wrap;
  1009. margin-bottom: 10px;
  1010. .item-span-1 {
  1011. width: 100%;
  1012. }
  1013. .item-span-3 {
  1014. min-width: 255px;
  1015. }
  1016. }
  1017. .order-detail-product-content {
  1018. display: flex;
  1019. flex-wrap: wrap;
  1020. align-items: center;
  1021. margin-bottom: 6px;
  1022. padding: 0 24px;
  1023. }
  1024. .order-detail-card-item {
  1025. min-width: 255px;
  1026. margin-right: 32px;
  1027. margin-bottom: 10px;
  1028. font-size: 14px;
  1029. line-height: 22px;
  1030. color: $gray_10;
  1031. .linkedOrder {
  1032. display: flex;
  1033. align-items: flex-start;
  1034. }
  1035. }
  1036. .red-chong {
  1037. color: $red_light;
  1038. }
  1039. .no_open_root {
  1040. color: $red_light;
  1041. }
  1042. ::v-deep {
  1043. .color_main {
  1044. color: #2ABED1;
  1045. }
  1046. }
  1047. .activity-product-container {
  1048. padding: 14px 20px;
  1049. border-radius: 8px;
  1050. border: 1px solid $main;
  1051. background-color: rgba($main, 0.08);
  1052. .desc-detail-info-list {
  1053. margin-top: 8px;
  1054. }
  1055. ::v-deep {
  1056. .desc-label {
  1057. font-size: 14px;
  1058. line-height: 22px;
  1059. color: #686868;
  1060. }
  1061. .desc-value {
  1062. font-size: 14px;
  1063. line-height: 22px;
  1064. color: #1D1D1D;
  1065. }
  1066. }
  1067. }
  1068. .detail-order-table {
  1069. &.relate-order-table {
  1070. width: 100%;
  1071. }
  1072. }
  1073. }
  1074. </style>