Commit 6dbc1c8f6c57d73ffd7d080434d6f6ab231477a1

Authored by
2 parents 387323ff d3186a39

Merge branch 'zwl-develop' into 'develop'

feat: 业务研发明细表



See merge request !25
src/api/project/invoice.ts
... ... @@ -43,20 +43,23 @@ enum Api {
43 43 PACKAGEPROFIT = '/order/cost/BusinessProfitDetail/listByPage', //包装费用明细表
44 44 INNERPROFIT = '/order/cost/InnerProfitDetail/listByPage', //内部生产费用明细表
45 45 PACKAGEEDIT = '/order/cost/edit', //编辑
  46 + BUSINESSDEVELOPMENTEDIT = '/project/edit', //业务研发明细表编辑
46 47 PACKAGEAPPLYEDIT = '/order/cost/applyEditFileds', //申请编辑字段
47 48 SERVICEPROFIT = '/project/BusinessProfitInfo/listByPage', //业务研发净利润分析表
48 49 INNERPRODUCEPROFIT = '/project/InnerProfitInfo/listByPage', //内部生产利润分析表
49 50 SERVICEPROFITEXPORT = '/project/businessProfit/export', //业务研发净利润分析表导出
50 51 INNERPROFITEXPORT = '/project/innerProfit/export', //内部研发净利润分析表导出
51 52 SERVICEEDIT = '/project/edit', //编辑
52   - SERVICEAPPLYEDIT = '/project/applyEditFileds', //申请修改
  53 + SERVICEAPPLYEDIT = '/project/applyEditFileds', //申请字段编辑权限申请
53 54 APPLYLIST = '/project/pageProjectLockFieldApply', //分页查询项目字段申请记录
54 55 AUDITAPPLY = '/project/audit', //审核
55 56 ACTIONRECORD = '/projectOptLog/listByPage', //业务/内部研发净利润操作记录以及申请记录
56 57 ORDERCOSTDETAILEDOPTLOG = '/orderCostDetailedOptLog/listByPage', //包装费用明细/内部生产明细操作记录表
  58 + PROJECTCOSTDETAILEDOPTLOG = '/projectOptLog/listByPage', //业务研发明细表操作记录表
57 59 SETPACKSTATUS = '/order/cost/setPackStatus', //包装费用明细表审批
58 60 SETINNERSTATUS = '/order/cost/setInnerStatus', //内部生产明细表审批
59 61 PROJECTBUSINESSPROFITSETSTATUS = '/project/businessProfit/setStatus', //业务研发净利润分析表审批
  62 + PROJECTBUSINESSDEVELOPMENTSETSTATUS = '/project/setDetailBusinessProfitStatus', //业务研发明细表审核通过
60 63 PROJECTINNERPROFITSETSTATUS = '/project/innerProfitInfo/setStatus', //内部研发净利润分析表审批
61 64 }
62 65  
... ... @@ -427,12 +430,25 @@ export const getServiceEdit = async (params: any) => {
427 430 });
428 431 };
429 432  
  433 +export const getBusinessDevelopmentEdit = async (params: any) => {
  434 + return await defHttp.post<any>({
  435 + url: Api.BUSINESSDEVELOPMENTEDIT,
  436 + params,
  437 + });
  438 +};
  439 +
430 440 export const getServiceApplyEdit = async (params: any) => {
431 441 return await defHttp.post<any>({
432 442 url: Api.SERVICEAPPLYEDIT,
433 443 params,
434 444 });
435 445 };
  446 +export const getBusinessDevelopmentApplyEdit = async (params: any) => {
  447 + return await defHttp.post<any>({
  448 + url: Api.SERVICEAPPLYEDIT,
  449 + params,
  450 + });
  451 +};
436 452  
437 453 export const getApplyList = async (params: any) => {
438 454 // const res = ref();
... ... @@ -516,16 +532,43 @@ export const getProjectOptLog = async (params: any) =&gt; {
516 532 });
517 533 };
518 534  
  535 +// 业务研发明细表操作记录表
  536 +export const getProjectCostDetailedOptLog = async (params: any) => {
  537 + const res = await defHttp.post<any>({
  538 + url: Api.PROJECTCOSTDETAILEDOPTLOG,
  539 + params,
  540 + });
  541 +
  542 +
  543 + const formattedRecords = res.records.map((record: any) => {
  544 + return {
  545 + ...record,
  546 + };
  547 + });
  548 +
  549 + const orderStore = useOrderStoreWithOut();
  550 + orderStore.setTotal(res.total);
  551 + orderStore.setQueryVO(params);
  552 + return new Promise((resolve) => {
  553 + resolve({
  554 + items: formattedRecords,
  555 + total: res.total,
  556 + });
  557 + });
  558 +};
519 559 export const getOrderCostDetailedOptLog = async (params: any) => {
520 560 const res = await defHttp.post<any>({
521 561 url: Api.ORDERCOSTDETAILEDOPTLOG,
522 562 params,
523 563 });
  564 +
  565 +
524 566 const formattedRecords = res.records.map((record: any) => {
525 567 return {
526 568 ...record,
527 569 };
528 570 });
  571 +
529 572 const orderStore = useOrderStoreWithOut();
530 573 orderStore.setTotal(res.total);
531 574 orderStore.setQueryVO(params);
... ... @@ -569,3 +612,56 @@ export const setCommissionStatus = async (data: { orderId: number[] }) =&gt; {
569 612 data,
570 613 });
571 614 };
  615 +// 业务研发明细表接口
  616 +export const getBusinessDevelopmentDetail = async (params: any) => {
  617 + const res = await defHttp.post<any>({
  618 + url: '/project/BusinessDevelopmentDetail/listByPage',
  619 + params,
  620 + });
  621 + const formattedRecords = res.records.map((record: any) => ({
  622 + ...record,
  623 + }));
  624 + return new Promise((resolve) => {
  625 + resolve({
  626 + items: formattedRecords,
  627 + total: res.total,
  628 + });
  629 + });
  630 +};
  631 +
  632 +export const setBusinessDevelopmentStatus = async (params: any) => {
  633 + return await defHttp.post<any>({
  634 + url: Api.PROJECTBUSINESSDEVELOPMENTSETSTATUS,
  635 + params,
  636 + });
  637 +};
  638 +
  639 +// 项目列表接口
  640 +export const getProjectList = async (params: any) => {
  641 + console.log('getProjectList - params:', params);
  642 + const res = await defHttp.post<any>({
  643 + url: '/project/list_by_/project/list',
  644 + params,
  645 + });
  646 + console.log('getProjectList - raw response:', res);
  647 +
  648 + // 处理返回的数据格式 - 直接返回数组
  649 + const formattedRecords = (res || []).map((record: any) => ({
  650 + ...record,
  651 + }));
  652 + console.log('getProjectList - formattedRecords:', formattedRecords);
  653 +
  654 + return new Promise((resolve) => {
  655 + resolve({
  656 + items: formattedRecords,
  657 + total: formattedRecords.length,
  658 + });
  659 + });
  660 +};
  661 +
  662 +export const setBusinessDevStatusSend = async (params: any) => {
  663 + return await defHttp.post<any>({
  664 + url: '/project/setDetailBusinessProfitStatus',
  665 + params,
  666 + });
  667 +}
... ...
src/components/SimpleMenu/src/components/menu.less
... ... @@ -302,6 +302,7 @@
302 302 .@{menu-prefix-cls}-submenu-has-parent-submenu {
303 303 .@{menu-prefix-cls}-submenu-title {
304 304 background-color: transparent;
  305 + color: rgb(0, 147, 245);
305 306 }
306 307 }
307 308 }
... ...
src/router/routes/modules/project/finance.ts
... ... @@ -59,16 +59,6 @@ const finance: AppRouteModule = {
59 59 ],
60 60 },
61 61 children: [
62   - // {
63   - // path: '',
64   - // name: 'Receive',
65   - // meta: {
66   - // title: '财务管理',
67   - // roles: [RoleEnum.ADMIN, RoleEnum.FINANCE, RoleEnum.TRACKER, RoleEnum.BUSINESS],
68   - // ignoreKeepAlive: false,
69   - // },
70   - // // component: () => import('/@/views/project/finance/index.vue'),
71   - // },
72 62 {
73 63 path: 'serviceProfit',
74 64 name: 'ServiceProfit',
... ... @@ -82,7 +72,7 @@ const finance: AppRouteModule = {
82 72 path: 'ServiceProfit',
83 73 name: 'ServiceProfit',
84 74 meta: {
85   - title: '业务研发净利润分析',
  75 + title: '业务研发净利润分析报价',
86 76 roles: [RoleEnum.ADMIN, RoleEnum.FINANCE],
87 77 ignoreKeepAlive: false,
88 78 },
... ... @@ -92,6 +82,43 @@ const finance: AppRouteModule = {
92 82 ),
93 83 },
94 84 {
  85 + path: 'BusinessDevelopmentDetail',
  86 + name: 'BusinessDevelopmentDetail',
  87 + meta: {
  88 + title: '业务研发明细表',
  89 + roles: [RoleEnum.ADMIN, RoleEnum.FINANCE],
  90 + ignoreKeepAlive: false,
  91 + },
  92 + component: () =>
  93 + import(
  94 + '/@/views/project/finance/financeProfit/ServiceProfit/BusinessDevelopmentDetail/index.vue'
  95 + ),
  96 + },
  97 + ],
  98 + },
  99 + {
  100 + path: 'PackageProfit',
  101 + name: 'PackageProfit',
  102 + meta: {
  103 + title: '包装费用净利润分析',
  104 + roles: [RoleEnum.ADMIN, RoleEnum.FINANCE],
  105 + ignoreKeepAlive: false,
  106 + },
  107 + children: [
  108 + {
  109 + path: 'PackageProfitReport',
  110 + name: 'PackageProfitReport',
  111 + meta: {
  112 + title: '包装费用净利润分析报表',
  113 + roles: [RoleEnum.ADMIN, RoleEnum.FINANCE],
  114 + ignoreKeepAlive: false,
  115 + },
  116 + component: () =>
  117 + import(
  118 + '/@/views/project/finance/financeProfit/PackageProfit/PackageProfitReport/index.vue'
  119 + ),
  120 + },
  121 + {
95 122 path: 'PackageProfit',
96 123 name: 'PackageProfit',
97 124 meta: {
... ... @@ -119,7 +146,7 @@ const finance: AppRouteModule = {
119 146 path: 'InnerProduce',
120 147 name: 'InnerProduce',
121 148 meta: {
122   - title: '内部生产净利润分析表',
  149 + title: '内部生产净利润分析表',
123 150 roles: [RoleEnum.ADMIN, RoleEnum.FINANCE],
124 151 ignoreKeepAlive: false,
125 152 },
... ... @@ -141,6 +168,41 @@ const finance: AppRouteModule = {
141 168 },
142 169 ],
143 170 },
  171 + {
  172 + path: 'ExchangeProfit',
  173 + name: 'ExchangeProfit',
  174 + meta: {
  175 + title: '汇率收益利润汇总保报表',
  176 + roles: [RoleEnum.ADMIN, RoleEnum.FINANCE],
  177 + ignoreKeepAlive: false,
  178 + },
  179 + children: [
  180 + {
  181 + path: 'ExchangeProfitReport',
  182 + name: 'ExchangeProfitReport',
  183 + meta: {
  184 + title: '汇率收益利润汇总报表',
  185 + roles: [RoleEnum.ADMIN, RoleEnum.FINANCE],
  186 + ignoreKeepAlive: false,
  187 + },
  188 + component: () =>
  189 + import(
  190 + '/@/views/project/finance/financeProfit/ExchangeProfit/ExchangeProfitReport/index.vue'
  191 + ),
  192 + },
  193 + {
  194 + path: 'ExchangeDetail',
  195 + name: 'ExchangeDetail',
  196 + meta: {
  197 + title: '汇率兑换明细表',
  198 + roles: [RoleEnum.ADMIN, RoleEnum.FINANCE],
  199 + ignoreKeepAlive: false,
  200 + },
  201 + component: () =>
  202 + import('/@/views/project/finance/financeProfit/ExchangeProfit/ExchangeDetail/index.vue'),
  203 + },
  204 + ],
  205 + },
144 206 ],
145 207 },
146 208 ],
... ...
src/views/project/approve/data.ts
... ... @@ -109,6 +109,20 @@ export const FIELDS_BASE_INFO = [
109 109 rules: [{ required: true }],
110 110 },
111 111 {
  112 + field: 'detailSpainPaidRmbCommission',
  113 + component: 'Select',
  114 + labelWidth: 150,
  115 + label: '西班牙已发提成¥',
  116 + rules: [{ required: true }],
  117 + },
  118 + {
  119 + field: 'detailPaidRmbCommission',
  120 + component: 'Select',
  121 + labelWidth: 150,
  122 + label: '中国团队已发提成¥',
  123 + rules: [{ required: true }],
  124 + },
  125 + {
112 126 field: 'actualExchangeRate',
113 127 component: 'Select',
114 128 labelWidth: 150,
... ...
src/views/project/finance/financeProfit/ServiceProfit/BusinessDevelopmentDetail/ApproveReason.vue 0 → 100644
  1 +<template>
  2 + <BasicModal
  3 + v-bind="$attrs"
  4 + destroyOnClose
  5 + @register="register"
  6 + title="申请原因"
  7 + :helpMessage="['提示1', '提示2']"
  8 + @open-change="handleShow"
  9 + :bodyStyle="{ height: '200px' }"
  10 + @ok="handleOk"
  11 + z-index="9999"
  12 + >
  13 + <a-textarea v-model:value="input" :rows="7" />
  14 + </BasicModal>
  15 +</template>
  16 +<script lang="ts" setup>
  17 + import { ref, watch } from 'vue';
  18 + import { BasicModal, useModalInner } from '@/components/Modal';
  19 + import { orderAuth } from '/@/api/project/order';
  20 + import { getBusinessDevelopmentApplyEdit } from '/@/api/project/invoice';
  21 + import { getPackageApplyEdit } from '/@/api/project/invoice';
  22 +
  23 + const emit = defineEmits(['success']);
  24 + const input = ref('');
  25 + const baseFieldValues = ref();
  26 + const [register, { setModalProps, redoModalHeight, closeModal }] = useModalInner(async (data) => {
  27 + baseFieldValues.value = data.data;
  28 + baseFieldValues.value.projectNoPrefix = data.projectNoPrefix;
  29 + });
  30 + async function handleOk() {
  31 + baseFieldValues.value.applyRemark = input.value;
  32 + await getBusinessDevelopmentApplyEdit({
  33 + projectNoPrefix: baseFieldValues.value.projectNoPrefix,
  34 + detailPaidRmbCommission: baseFieldValues.value.detailPaidRmbCommission,
  35 + detailSpainPaidRmbCommission: baseFieldValues.value.detailSpainPaidRmbCommission,
  36 + applyRemark: baseFieldValues.value.applyRemark,
  37 + type: 'ORDER_DEVELOPMENT_COST_FIELD_EDIT_APPLY',
  38 + });
  39 + emit('success');
  40 + setTimeout(() => {
  41 + closeModal();
  42 + }, 50);
  43 + }
  44 + function handleShow() {
  45 + input.value = '';
  46 + closeModal();
  47 + }
  48 +</script>
... ...
src/views/project/finance/financeProfit/ServiceProfit/BusinessDevelopmentDetail/CheckDetail.vue 0 → 100644
  1 +<template>
  2 + <div class="container">
  3 + <BasicDrawer
  4 + @register="register"
  5 + v-bind="$attrs"
  6 + showFooter
  7 + title="字段编辑权限申请"
  8 + width="60%"
  9 + :destroyOnClose="true"
  10 + :isDetail="true"
  11 + @ok="handleSubmit"
  12 + :showDetailBack="false"
  13 + okText="申请"
  14 + ><input />
  15 + <div>
  16 + <template v-if="role === ROLE.ADMIN || role === ROLE.FINANCE">
  17 + <BasicForm @register="registerForm" />
  18 + </template>
  19 + </div>
  20 + <!-- <template #titleToolbar> <a-button type="primary"> 申请编辑权限 </a-button></template> -->
  21 +
  22 + <!-- <template #appendFooter>
  23 + <a-button type="primary" @click="onGoFormDetail"> 返回编辑</a-button>
  24 + </template> -->
  25 + </BasicDrawer>
  26 + <ApproveReason @register="approveReasonRegister" @success="handleCloseModal" />
  27 + </div>
  28 +</template>
  29 +<script lang="ts">
  30 + import { computed, defineComponent, reactive, ref } from 'vue';
  31 + import { BasicForm, useForm } from '/@/components/Form/index';
  32 + import { getServiceApplyEdit } from '/@/api/project/invoice';
  33 + import { ROLE } from '../../../financeList/type.d';
  34 + import { useModal } from '/@/components/Modal';
  35 + import ApproveReason from './ApproveReason.vue';
  36 + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  37 + import { FIELDS_BASE_INFO } from './tableData';
  38 + import { useUserStoreWithOut } from '/@/store/modules/user';
  39 +
  40 + const userStore = useUserStoreWithOut();
  41 + const getSchema = (fields) =>
  42 + fields.map((item) => ({
  43 + field: `${item.field}`,
  44 + dataIndex: `${item.field}`,
  45 + label: item.label,
  46 + component: 'Switch',
  47 + componentProps: {
  48 + checkedValue: 'UN_LOCKED',
  49 + unCheckedValue: 'LOCKED',
  50 + },
  51 + colProps: {
  52 + span: 8,
  53 + },
  54 + }));
  55 +
  56 + export default defineComponent({
  57 + components: { BasicDrawer, BasicForm, ApproveReason },
  58 + props: {
  59 + onGoFormDetail: {
  60 + type: Function,
  61 + },
  62 + },
  63 + setup() {
  64 + const projectNoPrefix = ref('');
  65 + const schemas = getSchema(FIELDS_BASE_INFO);
  66 + const [registerForm, { getFieldsValue }] = useForm({
  67 + labelWidth: 180,
  68 + schemas,
  69 + showActionButtonGroup: false,
  70 + actionColOptions: {
  71 + span: 24,
  72 + },
  73 + });
  74 + const [approveReasonRegister, { openModal: openApproveReasonDrawer }] = useModal();
  75 +
  76 + const lockFields = reactive({});
  77 + const [register, { closeDrawer }] = useDrawerInner((data) => {
  78 + Object.assign(lockFields, data.lockFields);
  79 + projectNoPrefix.value = data.projectNoPrefix; // 保存projectNoPrefix
  80 + });
  81 + function handleCloseModal() {
  82 + closeDrawer();
  83 + }
  84 +
  85 + const role = computed(() => {
  86 + return userStore.getUserInfo?.roleSmallVO?.code;
  87 + });
  88 +
  89 + const handleSubmit = async () => {
  90 + const baseFieldValues = getFieldsValue();
  91 + openApproveReasonDrawer(true, {
  92 + data: baseFieldValues,
  93 + projectNoPrefix: projectNoPrefix.value, // 传递projectNoPrefix,参数名为projectNo
  94 + });
  95 + };
  96 +
  97 + return {
  98 + register,
  99 + schemas,
  100 + registerForm,
  101 + handleSubmit,
  102 + handleCloseModal,
  103 + approveReasonRegister,
  104 + openApproveReasonDrawer,
  105 + ROLE,
  106 + role,
  107 + projectNoPrefix,
  108 + };
  109 + },
  110 + });
  111 +</script>
  112 +<style>
  113 + .container {
  114 + position: fixed; /* 或 absolute, fixed */
  115 + z-index: 10;
  116 + }
  117 +</style>
... ...
src/views/project/finance/financeProfit/ServiceProfit/BusinessDevelopmentDetail/FinanceEdit.vue 0 → 100644
  1 +<template>
  2 + <template>
  3 + <BasicDrawer
  4 + @register="register"
  5 + v-bind="$attrs"
  6 + title="编辑"
  7 + width="30%"
  8 + :isDetail="true"
  9 + @ok="handleSubmit"
  10 + :showDetailBack="false"
  11 + okText="保存"
  12 + @visible-change="handleShow"
  13 + showFooter
  14 + :destroyOnClose="true"
  15 + >
  16 + <!-- <div>
  17 + <BasicForm @register="registerForm" />
  18 + </div> -->
  19 + <div style="font-size: 15px">西班牙已发提成¥</div>
  20 + <a-input
  21 + v-model:value="input1"
  22 + placeholder="请输入"
  23 + :disabled="status2 === 'LOCKED'"
  24 + auto-size
  25 + />
  26 + <div style="margin: 16px 0"></div>
  27 + <div style="font-size: 15px">中国团队已发提成¥</div>
  28 + <a-input
  29 + v-model:value="input2"
  30 + placeholder="请输入"
  31 + :disabled="status5 === 'LOCKED'"
  32 + auto-size
  33 + />
  34 + <div style="margin: 16px 0"></div>
  35 + <!-- <template #titleToolbar> <a-button type="primary"> 申请编辑权限 </a-button></template> -->
  36 + <template #appendFooter>
  37 + <!-- <a-button type="primary" @click="onGoCheckDetail"> 申请权限</a-button> -->
  38 + </template>
  39 + </BasicDrawer>
  40 + </template>
  41 +</template>
  42 +<script lang="ts" setup>
  43 + import { BasicDrawer, useDrawerInner } from '@/components/Drawer';
  44 + import { BasicForm, FormSchema, useForm } from '@/components/Form';
  45 + import { defineComponent, ref, computed, unref, toRaw, reactive } from 'vue';
  46 + import { getBusinessDevelopmentEdit } from '@/api/project/invoice';
  47 + import { useMessage } from '/@/hooks/web/useMessage';
  48 + import { useUserStoreWithOut } from '/@/store/modules/user';
  49 + import { ROLE } from './type.d';
  50 + import type { Dayjs } from 'dayjs';
  51 + import dayjs from 'dayjs';
  52 +
  53 + const emit = defineEmits(['success']);
  54 + const userStore = useUserStoreWithOut();
  55 + const user = userStore.getUserInfo;
  56 + const role = computed(() => {
  57 + return user?.roleSmallVO?.code;
  58 + });
  59 + const schemas: FormSchema[] = [
  60 + // {
  61 + // field: 'totalPayAmount',
  62 + // component: 'InputNumber',
  63 + // labelWidth: 250,
  64 + // colProps: {
  65 + // span: 23,
  66 + // },
  67 + // label: '实际应收金额',
  68 + // },
  69 + {
  70 + field: 'developmentCopyRmbTotalPrice',
  71 + component: 'InputNumber',
  72 + labelWidth: 250,
  73 + colProps: {
  74 + span: 23,
  75 + },
  76 + // componentProps: () => ({
  77 + // disabled: status.value === 10,
  78 + // }),
  79 + label: '研发复制费合计¥',
  80 + },
  81 + {
  82 + field: 'actualPayedAmount2',
  83 + component: 'InputNumber',
  84 + labelWidth: 250,
  85 + colProps: {
  86 + span: 23,
  87 + },
  88 +
  89 + label: '实际应收金额2$',
  90 + },
  91 + {
  92 + field: 'actualPayedAmount3',
  93 + component: 'InputNumber',
  94 + labelWidth: 250,
  95 + colProps: {
  96 + span: 23,
  97 + },
  98 +
  99 + label: '实际应收金额3$',
  100 + },
  101 + {
  102 + field: 'otherAmount',
  103 + component: 'InputNumber',
  104 + labelWidth: 250,
  105 + colProps: {
  106 + span: 23,
  107 + },
  108 +
  109 + label: '其他费用金额$',
  110 + },
  111 + ];
  112 + const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
  113 + labelWidth: 120,
  114 + schemas,
  115 + layout: 'vertical',
  116 + showActionButtonGroup: false,
  117 + actionColOptions: {
  118 + span: 24,
  119 + },
  120 + });
  121 + const { createMessage } = useMessage();
  122 + const { error } = createMessage;
  123 +
  124 + const update = ref();
  125 + const status2 = ref();
  126 + const status5 = ref();
  127 +
  128 + const input1 = ref();
  129 + const input2 = ref();
  130 + const projectNoPrefix = ref();
  131 + // function formatDate(dateStr: string): string {
  132 + // const date = new Date(dateStr);
  133 + // const year = date.getFullYear();
  134 + // const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,+1
  135 + // const day = String(date.getDate()).padStart(2, '0');
  136 + // const hours = String(date.getHours()).padStart(2, '0');
  137 + // const minutes = String(date.getMinutes()).padStart(2, '0');
  138 + // const seconds = String(date.getSeconds()).padStart(2, '0');
  139 +
  140 + // // 返回格式化后的字符串:'YYYY-MM-DD HH:mm:ss'
  141 + // return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  142 + // }
  143 + function formatDateToDateOnly(dateStr: string): string {
  144 + const date = new Date(dateStr);
  145 + const year = date.getFullYear();
  146 + const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,+1
  147 + const day = String(date.getDate()).padStart(2, '0');
  148 +
  149 + // 返回格式化后的日期字符串:'YYYY-MM-DD'
  150 + return `${year}-${month}-${day}`;
  151 + }
  152 + const [register, { setDrawerProps, closeDrawer }] = useDrawerInner((data) => {
  153 + // 方式1
  154 + if (data.data.lockFields) {
  155 + status2.value = data?.data?.lockFields?.detailSpainPaidRmbCommission;
  156 + status5.value = data?.data?.lockFields?.detailPaidRmbCommission;
  157 + }
  158 + projectNoPrefix.value = data?.data?.projectNoPrefix;
  159 + input1.value = data?.data?.spainPaidRmbCommission?.toFixed(2) || '';
  160 +
  161 + input2.value = data?.data?.paidRmbCommission?.toFixed(2) || '';
  162 +
  163 + resetFields();
  164 + setDrawerProps({ confirmLoading: false });
  165 + setFieldsValue({
  166 + ...toRaw(data.data),
  167 + });
  168 + update.value = data;
  169 + });
  170 + const loading = ref(false);
  171 + //完成编辑
  172 + async function handleSubmit() {
  173 + if (loading.value) return;
  174 + loading.value = true;
  175 + setDrawerProps({ confirmLoading: true });
  176 + // const values = await validate();
  177 + // const updatedValues = {
  178 + // ...values,
  179 + // id: update.value.data.id,
  180 + // bgUrl: update.value.data.bgUrl,
  181 + // };
  182 + if (!input1.value || !input2.value) {
  183 + error('选项不能为空');
  184 + loading.value = false;
  185 + setDrawerProps({ confirmLoading: false });
  186 + } else {
  187 + try {
  188 + await getBusinessDevelopmentEdit({
  189 + projectNoPrefix: projectNoPrefix.value,
  190 + detailSpainPaidRmbCommission: input1.value,
  191 + detailPaidRmbCommission: input2.value,
  192 + });
  193 + emit('success');
  194 + } finally {
  195 + loading.value = false;
  196 + setDrawerProps({ confirmLoading: false });
  197 + closeDrawer();
  198 + }
  199 + }
  200 + }
  201 + function handleShow(visible: boolean) {
  202 + if (!visible) {
  203 + input1.value = '';
  204 + input2.value = '';
  205 + }
  206 + }
  207 +</script>
... ...
src/views/project/finance/financeProfit/ServiceProfit/BusinessDevelopmentDetail/HistoryDetail.vue 0 → 100644
  1 +<template>
  2 + <BasicDrawer
  3 + @register="register"
  4 + v-bind="$attrs"
  5 + title="操作记录"
  6 + width="60%"
  7 + :isDetail="true"
  8 + :showDetailBack="false"
  9 + okText="保存"
  10 + :destroyOnClose="true"
  11 + >
  12 + <Tabs v-model:activeKey="activeKey" className="my-0">
  13 + <TabPanel :key="1" tab="操作记录" className="w-full">
  14 + <a-list :pagination="pagination1" className="w-full">
  15 + <template v-for="item in list1" :key="item.id">
  16 + <a-list-item class="list">
  17 + <a-list-item-meta>
  18 + <template #avatar> </template>
  19 + <template #title>
  20 + <span>{{ item.userName }}</span>
  21 + </template>
  22 + <template #description>
  23 + <div class="description">
  24 + {{ item.optType }}
  25 + </div>
  26 + <div class="info">
  27 + <div><span>操作时间:</span>{{ formatToDateTime(item.createTime) }}</div>
  28 + </div>
  29 + </template>
  30 + </a-list-item-meta>
  31 + </a-list-item>
  32 + </template>
  33 + </a-list>
  34 + </TabPanel>
  35 + <TabPanel :key="2" tab="审批记录" className="w-full">
  36 + <a-list :pagination="pagination2" className="w-full">
  37 + <template v-for="item in list2" :key="item.id">
  38 + <a-list-item class="list">
  39 + <a-list-item-meta>
  40 + <template #avatar> </template>
  41 + <template #title>
  42 + <span>{{ item.userName }}</span>
  43 + </template>
  44 + <template #description>
  45 + <div class="description">
  46 + {{ item.optType }}
  47 + </div>
  48 + <div class="info">
  49 + <div><span>操作时间:</span>{{ formatToDateTime(item.createTime) }}</div>
  50 + </div>
  51 + </template>
  52 + </a-list-item-meta>
  53 + </a-list-item>
  54 + </template>
  55 + </a-list>
  56 + </TabPanel>
  57 + </Tabs>
  58 +
  59 + <!-- <template #titleToolbar> <a-button type="primary"> 申请编辑权限 </a-button></template> -->
  60 + <template #appendFooter>
  61 + <!-- <a-button type="primary" @click="onGoCheckDetail"> 申请权限</a-button> -->
  62 + </template>
  63 + </BasicDrawer>
  64 +</template>
  65 +<script lang="ts">
  66 + import { defineComponent, ref, computed } from 'vue';
  67 + import { Tabs, Progress, Row, Col, List } from 'ant-design-vue';
  68 + import { FormSchema, useForm } from '/@/components/Form/index';
  69 +
  70 + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  71 + import { getProjectCostDetailedOptLog } from '/@/api/project/invoice';
  72 + import { formatToDateTime } from '/@/utils/dateUtil';
  73 +
  74 + const TabPanel = Tabs.TabPane;
  75 +
  76 + const schemas: FormSchema[] = [
  77 + {
  78 + field: '订单号',
  79 + component: 'Input',
  80 + label: '字段1',
  81 + componentProps: {
  82 + readonly: true,
  83 + disabled: true,
  84 + },
  85 + colProps: {
  86 + span: 12,
  87 + },
  88 + defaultValue: '111',
  89 + },
  90 + {
  91 + field: 'field2',
  92 + component: 'Input',
  93 + label: '字段2',
  94 + colProps: {
  95 + span: 12,
  96 + },
  97 + },
  98 + ];
  99 + const achieveList = [
  100 + {
  101 + key: '1',
  102 + name: '操作记录',
  103 + },
  104 + {
  105 + key: '2',
  106 + name: '审批记录',
  107 + },
  108 + ];
  109 + export default defineComponent({
  110 + components: {
  111 + BasicDrawer,
  112 + Tabs,
  113 + TabPanel,
  114 + [List.name]: List,
  115 + [List.Item.name]: List.Item,
  116 + AListItemMeta: List.Item.Meta,
  117 + },
  118 + props: {
  119 + onGoCheckDetail: {
  120 + type: Function,
  121 + },
  122 + },
  123 + setup() {
  124 + const list1 = ref([]);
  125 + const total1 = ref(0);
  126 + const page1 = ref(1);
  127 +
  128 + const list2 = ref([]);
  129 + const total2 = ref(0);
  130 + const page2 = ref(1);
  131 + const projectNoPrefix = ref('');
  132 + const activeKey = ref(1);
  133 +
  134 + const getOrderOptLogFunc = async (data, index, page) => {
  135 + // console.log('%c [ data ]-135', 'font-size:13px; background:pink; color:#bf2c9f;', data);
  136 + if (index === 1) {
  137 + const res = await getProjectCostDetailedOptLog({
  138 + projectNoPrefix: data,
  139 + type: [50],
  140 + page: page,
  141 + pageSize: 20
  142 + });
  143 + list1.value = res.items;
  144 + total1.value = res.total;
  145 + page1.value = page;
  146 + } else {
  147 + const res = await getProjectCostDetailedOptLog({
  148 + projectNoPrefix: data,
  149 + type: [4,5,6],
  150 + page: page,
  151 + pageSize: 20
  152 + });
  153 + list2.value = res.items;
  154 + total2.value = res.total;
  155 + page2.value = page;
  156 + }
  157 + };
  158 + const [register] = useDrawerInner((data) => {
  159 + projectNoPrefix.value = data.data.projectNoPrefix;
  160 + getOrderOptLogFunc(projectNoPrefix.value, 1, 1);
  161 + getOrderOptLogFunc(projectNoPrefix.value, 2, 1);
  162 + });
  163 +
  164 + const pagination1 = computed(() => {
  165 + return {
  166 + show: true,
  167 + pageSize: 20,
  168 + page: page1.value,
  169 + total: total1.value,
  170 + onChange(cur) {
  171 + getOrderOptLogFunc(projectNoPrefix.value, 1, cur);
  172 + },
  173 + };
  174 + });
  175 +
  176 + const pagination2 = computed(() => {
  177 + return {
  178 + show: true,
  179 + pageSize: 20,
  180 + page: page1.value,
  181 + total: total1.value,
  182 + onChange(cur) {
  183 + getOrderOptLogFunc(projectNoPrefix.value, 2, cur);
  184 + },
  185 + };
  186 + });
  187 +
  188 + return {
  189 + register,
  190 + schemas,
  191 + achieveList,
  192 + list1,
  193 + list2,
  194 + prefixCls: 'account-center',
  195 + pagination1,
  196 + pagination2,
  197 + activeKey,
  198 + formatToDateTime,
  199 + };
  200 + },
  201 + });
  202 +</script>
... ...
src/views/project/finance/financeProfit/ServiceProfit/BusinessDevelopmentDetail/InvoiceDetail.vue 0 → 100644
  1 +<template>
  2 + <BasicDrawer
  3 + @register="register"
  4 + v-bind="$attrs"
  5 + title="订单信息"
  6 + width="60%"
  7 + :isDetail="true"
  8 + :showDetailBack="false"
  9 + :destroyOnClose="true"
  10 + >
  11 + <div class="p-4">
  12 + <BasicTable @register="registerTable" :bordered="true">
  13 + <template #bodyCell="{ column, record }">
  14 + <template v-if="column.key === 'picUrl'">
  15 + <img :z-index="100000" :width="50" :height="50" :src="record?.picUrl" />
  16 + </template>
  17 + </template>
  18 + </BasicTable>
  19 + </div>
  20 + </BasicDrawer>
  21 +</template>
  22 +
  23 +<script lang="ts" setup>
  24 + import { BasicDrawer, useDrawerInner } from '@/components/Drawer';
  25 + import { BasicColumn, useTable, BasicTable } from '/@/components/Table';
  26 + import { getProjectList } from '/@/api/project/invoice';
  27 + import { ref } from 'vue';
  28 +
  29 + // 定义表格列
  30 + const columns: BasicColumn[] = [
  31 + {
  32 + title: '客户编码',
  33 + dataIndex: 'customerCode',
  34 + width: 100,
  35 + },
  36 + {
  37 + title: '项目号',
  38 + dataIndex: 'projectNo',
  39 + width: 100,
  40 + },
  41 + {
  42 + title: '内部编号',
  43 + dataIndex: 'innerNo',
  44 + width: 100,
  45 + },
  46 + {
  47 + title: '图片地址',
  48 + width: 150,
  49 + dataIndex: 'picUrl',
  50 + },
  51 + {
  52 + title: '生产部门',
  53 + width: 150,
  54 + dataIndex: 'productionDepartment',
  55 + },
  56 + {
  57 + title: '订单成分',
  58 + width: 150,
  59 + dataIndex: 'orderComposition',
  60 + },
  61 + {
  62 + title: '客户po号',
  63 + width: 150,
  64 + dataIndex: 'customerPo',
  65 + },
  66 + {
  67 + title: '款式类型',
  68 + width: 150,
  69 + dataIndex: 'productStyle',
  70 + },
  71 + {
  72 + title: '客户Style',
  73 + width: 150,
  74 + dataIndex: 'customerStyle',
  75 + },
  76 + {
  77 + title: '订单数量',
  78 + width: 150,
  79 + dataIndex: 'orderCount',
  80 + },
  81 + {
  82 + title: '客户总价¥',
  83 + width: 150,
  84 + dataIndex: 'customerRmbTotalPrice',
  85 + },
  86 + {
  87 + title: '客户总价$',
  88 + width: 150,
  89 + dataIndex: 'customerTotalPrice',
  90 + },
  91 + {
  92 + title: '西班牙提成¥',
  93 + width: 150,
  94 + dataIndex: 'spainRmbCommission',
  95 + },
  96 + {
  97 + title: '中国团队提成¥',
  98 + width: 150,
  99 + dataIndex: 'rmbCommission',
  100 + },
  101 + {
  102 + title: '提成合计¥',
  103 + width: 150,
  104 + dataIndex: 'rmbTotalExpense',
  105 + },
  106 + ];
  107 +
  108 + const projectNo = ref('');
  109 +
  110 + // 注册抽屉
  111 + const [register] = useDrawerInner((data) => {
  112 + projectNo.value = data.data.projectNo;
  113 + });
  114 +
  115 + // 注册表格
  116 + const [registerTable] = useTable({
  117 + api: async () => {
  118 + const result = await getProjectList({ projectNo: projectNo.value });
  119 + return result;
  120 + },
  121 + columns: columns,
  122 + bordered: true,
  123 + });
  124 +</script>
  125 +
  126 +<style scoped>
  127 +</style>
0 128 \ No newline at end of file
... ...
src/views/project/finance/financeProfit/ServiceProfit/BusinessDevelopmentDetail/data.tsx 0 → 100644
  1 +import { InputNumber, Tag } from 'ant-design-vue';
  2 +import { BasicColumn } from '@/components/Table';
  3 +import { func } from 'vue-types';
  4 +import { h, ref } from 'vue';
  5 +import { FilePptOutlined } from '@ant-design/icons-vue';
  6 +import axios from 'axios';
  7 +import { queryNoOptions } from '/@/api/project/order';
  8 +import { useOrderStoreWithOut } from '/@/store/modules/order';
  9 +import { useOrderInfo } from '/@/hooks/component/order';
  10 +import { useUserStoreWithOut } from '/@/store/modules/user';
  11 +
  12 +const userStore = useUserStoreWithOut();
  13 +// export const COLUMNS = [
  14 +// {
  15 +// title: '客户编码',
  16 +// dataIndex: 'settingValue',
  17 +// width: 150,
  18 +// },
  19 +// {
  20 +// title: '利润率',
  21 +// dataIndex: 'relationValue',
  22 +// width: 150,
  23 +// editComponent: 'InputNumber',
  24 +// editRow: true,
  25 +// scopedSlots: { customRender: 'name' }
  26 +// },
  27 +// ];
  28 +const innerNoOptions = ref([]);
  29 +const projectNoOptions = ref([]);
  30 +const allProjectNoOptions = ref<any[]>([]);
  31 +export { allProjectNoOptions };
  32 +const orderStore = useOrderStoreWithOut();
  33 +const {
  34 + customerCode,
  35 + productionDepartment,
  36 +} = useOrderInfo(orderStore);
  37 +export const searchFormSchema = [
  38 + {
  39 + field: 'customerCode',
  40 + label: '客户编码',
  41 + component: 'Select',
  42 + colProps: { span: 8 },
  43 +
  44 + componentProps: {
  45 + options: customerCode,
  46 + showSearch: true,
  47 + mode: 'multiple',
  48 + },
  49 + },
  50 + {
  51 + field: 'projectNo',
  52 + label: '项目号',
  53 + component: 'Select',
  54 + colProps: { span: 8 },
  55 +
  56 + componentProps: {
  57 + options: projectNoOptions,
  58 + showSearch: true,
  59 + filterOption: false,
  60 + mode: 'multiple',
  61 + onSearch: async (value: any) => {
  62 + if (!value || value.trim() === '') {
  63 + return;
  64 + }
  65 + const result = await queryNoOptions('projectNo', value);
  66 + projectNoOptions.value = result;
  67 + allProjectNoOptions.value = result;
  68 + },
  69 + },
  70 + },
  71 + {
  72 + field: 'productionDepartment',
  73 + label: '生产科',
  74 + component: 'Select',
  75 + colProps: { span: 8 },
  76 +
  77 + componentProps: {
  78 + mode: 'multiple',
  79 +
  80 + options: productionDepartment,
  81 + showSearch: true,
  82 + },
  83 + },
  84 + {
  85 + field: 'innerNo',
  86 + label: '内部编号',
  87 + component: 'Select',
  88 + colProps: { span: 8 },
  89 +
  90 + componentProps: {
  91 + options: innerNoOptions,
  92 + showSearch: true,
  93 + mode: 'multiple',
  94 + onSearch: async (value: any) => {
  95 + innerNoOptions.value = await queryNoOptions('innerNo', value);
  96 + },
  97 + },
  98 + },
  99 + {
  100 + field: 'developmentStatus',
  101 + label: '状态',
  102 + component: 'Select',
  103 + colProps: { span: 8 },
  104 +
  105 + componentProps: {
  106 + options: [ {
  107 + label: '未完成',
  108 + value: -1,
  109 + },{
  110 + label: '待审核',
  111 + value: 0,
  112 + }, {
  113 + label: '已发放',
  114 + value: 1,
  115 + }, {
  116 + label: '应发但不发',
  117 + value: 2,
  118 + }],
  119 + },
  120 + },
  121 + // {
  122 + // field: 'productionDepartment',
  123 + // label: '生产科',
  124 + // component: 'Select',
  125 + // colProps: { span: 8 },
  126 +
  127 + // componentProps: {
  128 + // mode: 'multiple',
  129 +
  130 + // options: productionDepartment,
  131 + // showSearch: true,
  132 + // },
  133 + // },
  134 + // {
  135 + // field: 'innerNo',
  136 + // label: '内部编号',
  137 + // component: 'Select',
  138 + // colProps: { span: 8 },
  139 +
  140 + // componentProps: {
  141 + // options: innerNoOptions,
  142 + // showSearch: true,
  143 + // mode: 'multiple',
  144 + // onSearch: async (value: any) => {
  145 + // innerNoOptions.value = await queryNoOptions('innerNo', value);
  146 + // },
  147 + // },
  148 + // },
  149 +]
  150 +export const COLUMNS = [
  151 + {
  152 + title: '客户编码',
  153 + dataIndex: 'customerCode',
  154 + width: 150,
  155 + },
  156 + {
  157 + title: '项目号',
  158 + dataIndex: 'projectNoPrefix',
  159 + // dataIndex: 'projectNo',
  160 + width: 140,
  161 + },
  162 + {
  163 + title: '订单总数量',
  164 + dataIndex: 'orderCount',
  165 + width: 120,
  166 + customRender: (column) => {
  167 + return column.record?.orderCount;
  168 + },
  169 + },
  170 + {
  171 + title: '客户总金额¥',
  172 + width: 150,
  173 + dataIndex: 'customerTotalPrice',
  174 + customRender: (column) => {
  175 + return column.record?.customerRmbTotalPrice?.toFixed(2);
  176 + },
  177 + },
  178 + {
  179 + title: '客户总金额$',
  180 + width: 150,
  181 + dataIndex: 'customerTotalPrice',
  182 + customRender: (column) => {
  183 + return column.record?.customerTotalPrice?.toFixed(2);
  184 + },
  185 + },
  186 + {
  187 + title: '西班牙提成¥',
  188 + dataIndex: 'spainRmbCommission',
  189 + width: 150,
  190 + customRender: (column) => {
  191 + return column.record?.spainRmbCommission?.toFixed(2);
  192 + },
  193 + },
  194 + {
  195 + title: '已发提成¥',
  196 + dataIndex: 'spainPaidRmbCommission',
  197 + width: 120,
  198 + customRender: (column) => {
  199 + return column.record?.spainPaidRmbCommission?.toFixed(2);
  200 + },
  201 + },
  202 + {
  203 + title: '未发提成¥',
  204 + dataIndex: 'spainUnpaidRmbCommission',
  205 + width: 120,
  206 + customRender: (column) => {
  207 + return column.record?.spainUnpaidRmbCommission?.toFixed(2);
  208 + },
  209 + },
  210 + {
  211 + title: '中国团队提成¥',
  212 + dataIndex: 'rmbCommission',
  213 + width: 150,
  214 + customRender: (column) => {
  215 + return column.record?.rmbCommission?.toFixed(2);
  216 + },
  217 + },
  218 + {
  219 + title: '已发提成¥',
  220 + dataIndex: 'paidRmbCommission',
  221 + width: 120,
  222 + customRender: (column) => {
  223 + return column.record?.paidRmbCommission?.toFixed(2);
  224 + },
  225 + },
  226 + {
  227 + title: '未发提成¥',
  228 + dataIndex: 'unpaidRmbCommission',
  229 + width: 120,
  230 + customRender: (column) => {
  231 + return column.record?.unpaidRmbCommission?.toFixed(2);
  232 + },
  233 + },
  234 + {
  235 + title: '提成合计¥',
  236 + dataIndex: 'rmbTotalExpense',
  237 + width: 120,
  238 + customRender: (column) => {
  239 + return column.record?.rmbTotalExpense?.toFixed(2);
  240 + },
  241 + },
  242 + {
  243 + title: '状态',
  244 + dataIndex: 'detailDevelopmentStatus',
  245 + width: 120,
  246 + customRender: (column) => {
  247 + if (column.record?.detailDevelopmentStatus === null || column.record?.detailDevelopmentStatus === -1) {
  248 + return '未完成';
  249 + } else if (column.record?.detailDevelopmentStatus === 0) {
  250 + return '待审核';
  251 + } else if (column.record?.detailDevelopmentStatus === 1) {
  252 + return '已发放';
  253 + } else if (column.record?.detailDevelopmentStatus === 2) {
  254 + return '应发但不发';
  255 + }
  256 + },
  257 + },
  258 +];
0 259 \ No newline at end of file
... ...
src/views/project/finance/financeProfit/ServiceProfit/BusinessDevelopmentDetail/index.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <BasicTable @register="registerTable" :bordered="true" @field-value-change="handleFieldValueChange">
  4 + <template #headerTop>
  5 + <a-alert type="info" show-icon>
  6 + <template #message>
  7 + <template v-if="checkedKeys.length > 0">
  8 + <span>已选中{{ checkedKeys.length }}条记录(可跨页)</span>
  9 + <a-button
  10 + :style="{ borderRadius: '5px 5px 5px 5px' }"
  11 + type="link"
  12 + @click="handleClearChoose"
  13 + size="small"
  14 + >清空</a-button
  15 + >
  16 + </template>
  17 + <template v-else>
  18 + <span>未选中任何订单</span>
  19 + </template>
  20 + </template>
  21 + </a-alert>
  22 + </template>
  23 + <template #bodyCell="{ column, record }">
  24 + <template v-if="column.key === 'picUrl'">
  25 + <img :z-index="100000" :width="50" :height="50" :src="record?.picUrl" />
  26 + </template>
  27 + <template v-if="column.key === 'action'">
  28 + <TableAction
  29 + :actions="createActions(record)"
  30 + :dropDownActions="createDropActions(record)"
  31 + />
  32 + </template>
  33 + </template>
  34 + <template #toolbar>
  35 + <a-dropdown
  36 + v-if="role == ROLE.ADMIN || role == ROLE.FINANCE"
  37 + :style="{ borderRadius: '5px 5px 5px 5px' }"
  38 + >
  39 + <a-button type="primary">
  40 + 导出
  41 + <DownOutlined />
  42 + </a-button>
  43 + <template #overlay>
  44 + <a-menu @click="handleExportWithTeam">
  45 + <a-menu-item key="0">中国团队</a-menu-item>
  46 + <a-menu-item key="1">西班牙团队</a-menu-item>
  47 + <a-menu-item key="2">所有团队</a-menu-item>
  48 + </a-menu>
  49 + </template>
  50 + </a-dropdown>
  51 + <a-button
  52 + type="primary"
  53 + @click="handleAllProjectNoQuery"
  54 + style="margin-left: 8px;position: fixed;right: 29%;top: 107.5px;"
  55 + >全选</a-button>
  56 + </template>
  57 + </BasicTable>
  58 + <CheckDetail @register="checkModalRegister" />
  59 + <FinanceEdit @register="registerFinanceEdit" @success="handleSuccess" />
  60 + <HistoryDetail @register="registerHistoryDetail" />
  61 + <InvoiceDetail @register="registerOrderDetail" />
  62 + </div>
  63 +</template>
  64 +<script setup lang="ts">
  65 + import { BasicTable, useTable, TableAction } from '/@/components/Table';
  66 + import { getBusinessDevelopmentDetail, setBusinessDevelopmentStatus, setBusinessDevStatusSend } from '@/api/project/invoice';
  67 + import { saveConfig } from '@/api/sys/config';
  68 + import { searchFormSchema, COLUMNS, allProjectNoOptions } from './data';
  69 + import axios from 'axios';
  70 + import { useMessage } from '/@/hooks/web/useMessage';
  71 + import { onMounted, ref, computed, unref, h } from 'vue';
  72 + import { Modal } from 'ant-design-vue';
  73 + import { DownOutlined } from '@ant-design/icons-vue';
  74 + import { useDrawer } from '/@/components/Drawer';
  75 + import FinanceEdit from './FinanceEdit.vue';
  76 + import HistoryDetail from './HistoryDetail.vue';
  77 + import CheckDetail from './CheckDetail.vue';
  78 + import InvoiceDetail from './InvoiceDetail.vue';
  79 + import { useUserStoreWithOut } from '/@/store/modules/user';
  80 + import { ROLE } from '../../../type.d';
  81 + import { useOrderStoreWithOut } from '/@/store/modules/order';
  82 +
  83 + const { createMessage } = useMessage();
  84 + const projectNoPrefixs = ref<string[]>([]);
  85 + const checkedKeys = ref<string[]>([]);
  86 + const invoiceIdKeys = ref<string[]>([]);
  87 + const checkIdKeys = ref<string[]>([]);
  88 + const detailProjectNoKeys = ref<string[]>([]);
  89 + const orderStore = useOrderStoreWithOut();
  90 + const [checkModalRegister, { openDrawer: openCheckDetailDrawer }] = useDrawer();
  91 + const [registerFinanceEdit, { openDrawer: openFinanceEdit }] = useDrawer();
  92 + const [registerHistoryDetail, { openDrawer: openHistoryDetail }] = useDrawer();
  93 + const [registerOrderDetail, { openDrawer: openOrderDetail }] = useDrawer();
  94 + const userStore = useUserStoreWithOut();
  95 + const user = userStore.getUserInfo;
  96 + const role = computed(() => {
  97 + return user?.roleSmallVO?.code;
  98 + });
  99 + const [registerTable, { reload, getSelectRowKeys, getDataSource, setSelectedRowKeys, setProps, getForm }] = useTable({
  100 + title: '',
  101 + api: getBusinessDevelopmentDetail,
  102 + bordered: true,
  103 + columns: COLUMNS,
  104 + clickToRowSelect: false,
  105 + formConfig: {
  106 + labelWidth: 120,
  107 + schemas: searchFormSchema,
  108 + autoSubmitOnEnter: true,
  109 + resetFunc: async () => {
  110 + localStorage.removeItem('isAllSelected');
  111 + setProps({
  112 + searchInfo: {
  113 + projectNo: []
  114 + }
  115 + });
  116 + },
  117 + },
  118 + handleSearchInfoFn: (searchInfo) => {
  119 +
  120 + // 获取表单实例
  121 + const formInstance = getForm();
  122 + if (formInstance) {
  123 + const formValues = formInstance.getFieldsValue();
  124 +
  125 + // 强制覆盖searchInfo,确保使用表单中的值
  126 + if (formValues.projectNo && formValues.projectNo.length > 0) {
  127 + // 确保传递的是数组而不是Proxy对象,并去重
  128 + const projectNoArray = [...new Set(formValues.projectNo)];
  129 + // 强制覆盖,不使用原始值
  130 + searchInfo = {
  131 + ...searchInfo,
  132 + projectNo: projectNoArray
  133 + };
  134 + } else {
  135 + // 如果表单中没有项目号,清空查询条件
  136 + searchInfo = {
  137 + ...searchInfo,
  138 + projectNo: []
  139 + };
  140 + }
  141 + }
  142 +
  143 +
  144 + return searchInfo;
  145 + },
  146 + rowKey: (record) => record.projectNoPrefix,
  147 + rowSelection: {
  148 + type: 'checkbox',
  149 + selectedRowKeys: checkedKeys as any,
  150 + onSelect: onSelect,
  151 + onSelectAll: onSelectAll,
  152 + },
  153 + useSearchForm: true,
  154 + showTableSetting: true,
  155 + showIndexColumn: false,
  156 + tableSetting: {
  157 + setting: false,
  158 + },
  159 + actionColumn: {
  160 + width: 260,
  161 + title: 'Action',
  162 + dataIndex: 'action',
  163 + },
  164 + });
  165 +
  166 + function createActions(record: any): any[] {
  167 + if (!record.editable) {
  168 + const actions = [
  169 + {
  170 + label: '财务编辑',
  171 + onClick: handleFinanceEdit.bind(null, record),
  172 + },
  173 + {
  174 + label: '申请权限',
  175 + onClick: handleFalse.bind(null, record),
  176 + },
  177 + ...(role.value === ROLE.ADMIN ? [
  178 + {
  179 + label: '审核通过',
  180 + onClick: () => {
  181 + // 如果状态为未完成(-1),则不响应
  182 + if (record.detailDevelopmentStatus === -1) {
  183 + createMessage.warning('订单状态未完成');
  184 + return;
  185 + }
  186 + else{
  187 + showAuditOptions(record);
  188 + }
  189 + },
  190 + },
  191 + ] : []),
  192 + ];
  193 + return actions;
  194 + }
  195 + return [
  196 + {
  197 + label: '保存',
  198 + onClick: handleSave.bind(null, record),
  199 + },
  200 + {
  201 + label: '取消',
  202 + popConfirm: {
  203 + title: '是否取消编辑',
  204 + confirm: handleCancel.bind(null, record),
  205 + },
  206 + },
  207 + ];
  208 + }
  209 + function createDropActions(record: any) {
  210 + if (!record.editable) {
  211 + const actions = [
  212 + {
  213 + label: '订单信息',
  214 + onClick: handleOrderDetail.bind(null, record),
  215 + },
  216 + {
  217 + label: '历史记录',
  218 + onClick: handleHistoryDetail.bind(null, record),
  219 + },
  220 + // {
  221 + // label: '设置为应发但不发',
  222 + // onClick: handleSetStatus.bind(null, record),
  223 + // }
  224 + ];
  225 + return actions;
  226 + }
  227 + }
  228 +
  229 + onMounted(async () => {
  230 + await orderStore.getDict();
  231 + });
  232 +
  233 + // 监听表单字段变化
  234 + function handleFieldValueChange(field: string, value: any) {
  235 + // 如果是项目号字段变化且处于全选状态,同步更新查询条件
  236 + if (field === 'projectNo' && localStorage.getItem('isAllSelected') === 'true') {
  237 +
  238 + // 确保value是数组格式,并去重
  239 + const projectNoArray = Array.isArray(value) ? [...new Set(value)] : [];
  240 +
  241 + // 强制重新加载表格,使用新的查询条件
  242 + reload({
  243 + searchInfo: {
  244 + projectNo: projectNoArray
  245 + }
  246 + });
  247 +
  248 + // 同时更新detailProjectNoKeys数组
  249 + detailProjectNoKeys.value = projectNoArray;
  250 +
  251 + }
  252 + }
  253 +
  254 + function handleFinanceEdit(record) {
  255 + openFinanceEdit(true, {
  256 + data: record,
  257 + });
  258 + }
  259 +
  260 + function handleFalse(record, e) {
  261 + openCheckDetailDrawer(true, record);
  262 + e?.stopPropagation();
  263 + return false;
  264 + }
  265 +
  266 + function handleSuccess() {
  267 + setTimeout(() => {
  268 + reload();
  269 + }, 50);
  270 + }
  271 +
  272 + async function handleSave(record) {
  273 + await saveConfig({ projectNo: record.projectNoPrefix, relationValue: record.relationValue });
  274 + handleCancel(record);
  275 + reload();
  276 + }
  277 +
  278 + function handleCancel(record) {
  279 + record.onEdit?.(false, false);
  280 + }
  281 +
  282 + async function handleStatus(record, status) {
  283 + try {
  284 + // 检查必要参数是否存在
  285 + if (!record.projectNoPrefix) {
  286 + createMessage.error('缺少必要的参数:projectNoPrefix');
  287 + return;
  288 + }
  289 +
  290 + // 根据不同的状态调用不同的接口
  291 + if (status === 'approved') {
  292 + await setBusinessDevelopmentStatus({
  293 + projectNo: record.projectNoPrefix,
  294 + customerCode: record.customerCode,
  295 + detailDevelopmentStatus: 1 // 审核通过状态
  296 + });
  297 + createMessage.success('审核通过成功!');
  298 + } else if (status === 'completed') {
  299 + await setBusinessDevelopmentStatus({
  300 + projectNo: record.projectNoPrefix,
  301 + customerCode: record.customerCode,
  302 + detailDevelopmentStatus: 1 // 已完成状态
  303 + });
  304 + createMessage.success('设置为已发放成功!');
  305 + } else {
  306 + // 默认审核通过
  307 + await setBusinessDevelopmentStatus({
  308 + projectNo: record.projectNoPrefix,
  309 + customerCode: record.customerCode,
  310 + detailDevelopmentStatus: 1 // 默认审核通过状态
  311 + });
  312 + createMessage.success('状态更新成功!');
  313 + }
  314 +
  315 + reload();
  316 + } catch (error) {
  317 + console.error('Error updating status:', error);
  318 + createMessage.error('状态更新失败:' + (error.message || '未知错误'));
  319 + }
  320 + }
  321 +
  322 + async function handleHistoryDetail(record) {
  323 + openHistoryDetail(true, {
  324 + data: record,
  325 + });
  326 + }
  327 +
  328 + async function handleSetStatus(record) {
  329 + await setBusinessDevStatusSend({
  330 + projectNo: record.projectNoPrefix,
  331 + customerCode: record.customerCode,
  332 + detailDevelopmentStatus: 2 // 应发但不发状态
  333 + });
  334 + // 重新加载
  335 + reload();
  336 + }
  337 +
  338 + // 显示审核选项
  339 + function showAuditOptions(record) {
  340 + // 检查当前状态
  341 + const isReviewed = record.detailDevelopmentStatus === 1; // 已发放状态
  342 + const isSentButNotPaid = record.detailDevelopmentStatus === 2; // 应发但不发状态
  343 +
  344 + // 使用 Modal 显示选项
  345 + Modal.confirm({
  346 + title: '选择审核结果',
  347 + content: h('div', [
  348 + h('div', { style: 'margin-top: 16px;' }, [
  349 + h('button', {
  350 + style: isReviewed
  351 + ? 'margin-right: 8px; padding: 4px 8px; color: #666; background-color: #f5f5f5; border-radius: 2px; cursor: not-allowed; border: 1px solid #d9d9d9;'
  352 + : 'margin-right: 8px; padding: 4px 8px; color: white; background-color: #40a9ff; border-radius: 2px; cursor: pointer;',
  353 + disabled: isReviewed, // 如果已发放则禁用
  354 + onClick: () => {
  355 + if (!isReviewed) {
  356 + Modal.destroyAll();
  357 + handleStatus(record, 'approved'); // 审核通过
  358 + }
  359 + }
  360 + }, isReviewed ? '已发放' : '已发放'),
  361 + h('button', {
  362 + style: (isReviewed || isSentButNotPaid)
  363 + ? 'margin-right: 8px; padding: 4px 8px; color: #666; background-color: #f5f5f5; border-radius: 2px; cursor: not-allowed; border: 1px solid #d9d9d9;'
  364 + : 'margin-right: 8px; padding: 4px 8px; color: white; background-color: #40a9ff; border-radius: 2px; cursor: pointer;',
  365 + disabled: isReviewed || isSentButNotPaid, // 如果已发放或应发但不发则禁用
  366 + onClick: () => {
  367 + if (!isReviewed && !isSentButNotPaid) {
  368 + Modal.destroyAll();
  369 + handleSetStatus(record); // 应发但不发
  370 + }
  371 + }
  372 + }, (isReviewed || isSentButNotPaid) ? '应发但不发' : '应发但不发'),
  373 + ])
  374 + ]),
  375 + onCancel: () => {
  376 + Modal.destroyAll();
  377 + }
  378 + });
  379 + }
  380 +
  381 + function handleExportWithTeam({ key }: { key: string }) {
  382 + // Get current search parameters from the form
  383 + const values = getForm().getFieldsValue();
  384 +
  385 + // Create export parameters based on whether projectNoPrefixs are selected
  386 + let exportParams;
  387 +
  388 + // If projectNoPrefixs are selected, only use those and ignore search params
  389 + if (projectNoPrefixs.value.length > 0) {
  390 + exportParams = {
  391 + projectNos: projectNoPrefixs.value, // 复选选中的项目号使用 projectNos
  392 + selectExcel: parseInt(key) // 添加团队选择参数
  393 + };
  394 + }
  395 + // Otherwise use the search parameters
  396 + else {
  397 + exportParams = {
  398 + ...values,
  399 + projectNo: values.projectNo || undefined, // 搜索框的项目号使用 projectNo
  400 + customerCode: values.customerCode || undefined,
  401 + innerNo: values.innerNo || undefined,
  402 + productionDepartment: values.productionDepartment || undefined,
  403 + selectExcel: parseInt(key) // 添加团队选择参数
  404 + };
  405 + }
  406 +
  407 +
  408 + const token = userStore.getToken;
  409 + axios
  410 + .post(
  411 + '/basic-api/project/detailBusinessProfit/exportExcel',
  412 + exportParams,
  413 + {
  414 + headers: {
  415 + Authorization: `${token}`, // 去掉引号
  416 + },
  417 + responseType: 'blob', // 设置响应类型为 'blob'
  418 + },
  419 + )
  420 + .then((response) => {
  421 + // 创建一个 Blob 对象来保存二进制数据
  422 + const blob = new Blob([response.data], { type: 'application/zip' });
  423 + const getFormattedDate = (): string => {
  424 + const date = new Date();
  425 +
  426 + const year = date.getFullYear();
  427 + const month = String(date.getMonth() + 1).padStart(2, '0');
  428 + const day = String(date.getDate()).padStart(2, '0');
  429 +
  430 + const hours = String(date.getHours()).padStart(2, '0');
  431 + const minutes = String(date.getMinutes()).padStart(2, '0');
  432 + const seconds = String(date.getSeconds()).padStart(2, '0');
  433 +
  434 + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  435 + };
  436 + const date = getFormattedDate();
  437 + // 创建一个链接元素用于下载
  438 + const link = document.createElement('a');
  439 + link.href = window.URL.createObjectURL(blob);
  440 + link.download = `业务研发明细表${date}.xlsx`; // 你可以为文件命名
  441 + document.body.appendChild(link);
  442 + link.click(); // 自动点击链接,触发下载
  443 + document.body.removeChild(link); // 下载完成后移除链接
  444 + })
  445 + .catch((error) => {
  446 + console.error(error);
  447 + });
  448 + handleClearChoose();
  449 + reload();
  450 + }
  451 +
  452 + function handleExport() {
  453 + // Get current search parameters from the form
  454 + const values = getForm().getFieldsValue();
  455 +
  456 + // Create export parameters based on whether projectNoPrefixs are selected
  457 + let exportParams;
  458 +
  459 + // If projectNoPrefixs are selected, only use those and ignore search params
  460 + if (projectNoPrefixs.value.length > 0) {
  461 + exportParams = {
  462 + projectNos: projectNoPrefixs.value // 复选选中的项目号使用 projectNos
  463 + };
  464 + }
  465 + // Otherwise use the search parameters
  466 + else {
  467 + exportParams = {
  468 + ...values,
  469 + projectNo: values.projectNo || undefined, // 搜索框的项目号使用 projectNo
  470 + customerCode: values.customerCode || undefined,
  471 + innerNo: values.innerNo || undefined,
  472 + productionDepartment: values.productionDepartment || undefined
  473 + };
  474 + }
  475 +
  476 +
  477 + const token = userStore.getToken;
  478 + axios
  479 + .post(
  480 + '/basic-api/project/detailBusinessProfit/exportExcel',
  481 + exportParams,
  482 + {
  483 + headers: {
  484 + Authorization: `${token}`, // 去掉引号
  485 + },
  486 + responseType: 'blob', // 设置响应类型为 'blob'
  487 + },
  488 + )
  489 + .then((response) => {
  490 + // 创建一个 Blob 对象来保存二进制数据
  491 + const blob = new Blob([response.data], { type: 'application/zip' });
  492 + const getFormattedDate = (): string => {
  493 + const date = new Date();
  494 +
  495 + const year = date.getFullYear();
  496 + const month = String(date.getMonth() + 1).padStart(2, '0');
  497 + const day = String(date.getDate()).padStart(2, '0');
  498 +
  499 + const hours = String(date.getHours()).padStart(2, '0');
  500 + const minutes = String(date.getMinutes()).padStart(2, '0');
  501 + const seconds = String(date.getSeconds()).padStart(2, '0');
  502 +
  503 + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  504 + };
  505 + const date = getFormattedDate();
  506 + // 创建一个链接元素用于下载
  507 + const link = document.createElement('a');
  508 + link.href = window.URL.createObjectURL(blob);
  509 + link.download = `业务研发明细表${date}.xlsx`; // 你可以为文件命名
  510 + document.body.appendChild(link);
  511 + link.click(); // 自动点击链接,触发下载
  512 + document.body.removeChild(link); // 下载完成后移除链接
  513 + })
  514 + .catch((error) => {
  515 + console.error(error);
  516 + });
  517 + handleClearChoose();
  518 + reload();
  519 + }
  520 +
  521 + function handleClearChoose() {
  522 + checkedKeys.value = [];
  523 + projectNoPrefixs.value = [];
  524 + detailProjectNoKeys.value = [];
  525 + invoiceIdKeys.value = [];
  526 + checkIdKeys.value = [];
  527 + }
  528 +
  529 + async function onSelect(record, selected: boolean) {
  530 + const rowKey = record.projectNoPrefix;
  531 + if (selected) {
  532 + if (!checkedKeys.value.includes(rowKey)) {
  533 + checkedKeys.value.push(rowKey);
  534 + }
  535 + if (record.projectNoPrefix !== undefined && !projectNoPrefixs.value.includes(record.projectNoPrefix)) {
  536 + projectNoPrefixs.value.push(record.projectNoPrefix);
  537 + }
  538 + } else {
  539 + checkedKeys.value = checkedKeys.value.filter((key) => key !== rowKey);
  540 + if (record.projectNoPrefix !== undefined) {
  541 + projectNoPrefixs.value = projectNoPrefixs.value.filter(projectNo => projectNo !== record.projectNoPrefix);
  542 + }
  543 + }
  544 + setSelectedRowKeys(checkedKeys.value as any);
  545 + }
  546 +
  547 + function onSelectAll(selected: boolean, selectedRows: any[]) {
  548 + if (selected) {
  549 + checkedKeys.value = selectedRows
  550 + .map(row => row && row.projectNoPrefix)
  551 + .filter((key, idx, arr) => key !== undefined && arr.indexOf(key) === idx);
  552 + projectNoPrefixs.value = checkedKeys.value.slice();
  553 + } else {
  554 + checkedKeys.value = [];
  555 + projectNoPrefixs.value = [];
  556 + }
  557 + setSelectedRowKeys(checkedKeys.value as any);
  558 + }
  559 + // 6/25未完成工作:全选查询
  560 + function handleAllProjectNoQuery() {
  561 + // 检查是否有项目号选项
  562 + if (!allProjectNoOptions.value || allProjectNoOptions.value.length === 0) {
  563 + createMessage.warn('没有可查询的项目号!');
  564 + return;
  565 + }
  566 +
  567 + // 获取所有项目号的value值
  568 + const allProjectNos = allProjectNoOptions.value.map((item: any) => {
  569 + // 处理不同的数据结构
  570 + if (typeof item === 'string') {
  571 + return item;
  572 + } else if (item && typeof item === 'object' && 'value' in item) {
  573 + return item.value;
  574 + } else if (item && typeof item === 'object' && 'label' in item) {
  575 + return item.label;
  576 + }
  577 + return item;
  578 + }).filter(Boolean); // 过滤掉空值
  579 +
  580 + if (allProjectNos.length === 0) {
  581 + createMessage.warn('没有有效的项目号!');
  582 + return;
  583 + }
  584 +
  585 + try {
  586 + // 提示用户全选成功
  587 + createMessage.success(`已全选 ${allProjectNos.length} 个项目号`);
  588 +
  589 + // 获取表单实例并设置表单值,将项目号填入搜索框
  590 + const formInstance = getForm();
  591 + if (formInstance && formInstance.setFieldsValue) {
  592 + formInstance.setFieldsValue({
  593 + projectNo: allProjectNos
  594 + });
  595 + }
  596 +
  597 + // 将allProjectNoOptions更新为已勾选的项目号数组,这样用户取消勾选时数组会同步更新
  598 + allProjectNoOptions.value = allProjectNos.map((projectNo: any) => ({
  599 + label: projectNo,
  600 + value: projectNo
  601 + }));
  602 +
  603 + // 标记为全选状态
  604 + localStorage.setItem('isAllSelected', 'true');
  605 + } catch (error) {
  606 + console.error('全选失败:', error);
  607 + createMessage.error('全选失败,请检查网络连接!');
  608 + }
  609 + }
  610 +
  611 + async function handleOrderDetail(record) {
  612 + // 打开订单信息抽屉
  613 + openOrderDetail(true, {
  614 + data: {
  615 + projectNo: record.projectNoPrefix || record.projectNo
  616 + },
  617 + });
  618 + }
  619 +</script>
  620 +<style></style>
... ...
src/views/project/finance/financeProfit/ServiceProfit/BusinessDevelopmentDetail/tableData.tsx 0 → 100644
  1 +import { ref } from 'vue';
  2 +import { ROLE } from '../../../financeList/type.d';
  3 +import { queryNoOptions } from '/@/api/project/order';
  4 +import { useOrderInfo } from '/@/hooks/component/order';
  5 +import { useOrderStoreWithOut } from '/@/store/modules/order';
  6 +import { formatToDate } from '/@/utils/dateUtil';
  7 +
  8 +// 角色
  9 +// 业务员- 查看all,编辑-利润分析,报告书
  10 +// 跟单员- 查看利润分析(单价和总金额),跟单,质检,编辑
  11 +// 质检员- 查看跟单,质检,编辑质检
  12 +
  13 +const innerNoOptions = ref([]);
  14 +const projectNoOptions = ref([]);
  15 +const orderStore = useOrderStoreWithOut();
  16 +// const { productionDepartment: productionDepartmentOptions } = useOrderInfo(orderStore);
  17 +
  18 +/**
  19 + * drawer面板的字段
  20 + */
  21 +// 基本信息
  22 +export const FIELDS_BASE_INFO = [
  23 + {
  24 + field: 'detailSpainPaidRmbCommission',
  25 + component: 'Select',
  26 + labelWidth: 150,
  27 + label: '西班牙已发提成¥',
  28 + rules: [{ required: true }],
  29 + },
  30 + {
  31 + field: 'detailPaidRmbCommission',
  32 + component: 'Select',
  33 + labelWidth: 150,
  34 + label: '中国团队已发提成¥',
  35 + rules: [{ required: true }],
  36 + },
  37 +];
... ...