Commit 78ec6a79d3b243399fbea8840a157e6d3ae89798

Authored by boyang
1 parent b1027295

完成第三版与第四版

src/api/project/global.ts
... ... @@ -3,6 +3,9 @@ import { defHttp } from '/@/utils/http/axios';
3 3 enum Api {
4 4 DATA = '/order/erp/index/data',
5 5 CHART_DATA = '/order/erp/index/chartData',
  6 +
  7 + SALES_DATA = '/order/erp/index/totalSalesStatistics', //销售额完成情况
  8 + CUSTOMER_SALES = '/order/erp/index/salesStatusEveryCustomer', //每个客户销售额情况
6 9 }
7 10  
8 11 export const getApiData = async () => {
... ... @@ -14,3 +17,13 @@ export const getChartData = async () => {
14 17 const res = await defHttp.get<any>({ url: Api.CHART_DATA });
15 18 return res;
16 19 };
  20 +
  21 +export const getSalesData = async (params?) => {
  22 + const res = await defHttp.get<any>({ url: Api.SALES_DATA, params });
  23 + return res;
  24 +};
  25 +
  26 +export const getCustomerSalesData = async (params?) => {
  27 + const res = await defHttp.get<any>({ url: Api.CUSTOMER_SALES, params });
  28 + return res;
  29 +};
... ...
src/api/project/order.ts
... ... @@ -34,6 +34,13 @@ enum Api {
34 34  
35 35 TRACK_HISTORY = '/order/erp/opinion/log/query_by_id', //跟单结果记录
36 36 PASS_CALCULATE = '/order/erp/order/passRate', //一次性通过率
  37 + CREATE_PRODUCT_TEXT = '/order/erp/order/produceReport', //生成生产指标书
  38 + EXPORT_PRODUCT_TEXT = '/order/erp/order/send', //导出生产指标书
  39 +
  40 + BUSINESS_PROFIT_RATIO = '/order/erp/calculate_profit/business_profit_ratio', //业务/研发净利润分析
  41 + BUSINESS_PROFIT_RATIO_EXPORT = '/order/erp/calculate_profit/business_profit_ratio_export', //业务/研发净利润分析_导出
  42 + INNER_PROFIT_RATIO = '/order/erp/calculate_profit/inner_profit_ratio', //内部生产净利润分析表
  43 + INNER_PROFIT_RATIO_EXPORT = '/order/erp/calculate_profit/inner_profit_ratio_export', //内部生产净利润分析表_导出
37 44 }
38 45  
39 46 export const formatSearchData = (params) => {
... ... @@ -315,3 +322,51 @@ export const passCalculate = async (data: any) =&gt; {
315 322 });
316 323 return res;
317 324 };
  325 +
  326 +export const createProductText = async (data: any) => {
  327 + const res = await defHttp.post<any>({
  328 + url: Api.CREATE_PRODUCT_TEXT,
  329 + data: data,
  330 + });
  331 + return res;
  332 +};
  333 +
  334 +export const exportProductText = async (data: any) => {
  335 + const res = await defHttp.post<any>({
  336 + url: Api.EXPORT_PRODUCT_TEXT,
  337 + data: data,
  338 + });
  339 + return res;
  340 +};
  341 +
  342 +export const calculateBusinessProfit = async (data: any) => {
  343 + const res = await defHttp.post<any>({
  344 + url: Api.BUSINESS_PROFIT_RATIO,
  345 + data: data,
  346 + });
  347 + return res;
  348 +};
  349 +
  350 +export const exportBusinessProfit = async (data: any) => {
  351 + const res = await defHttp.post<any>({
  352 + url: Api.BUSINESS_PROFIT_RATIO_EXPORT,
  353 + data: data,
  354 + });
  355 + return res;
  356 +};
  357 +
  358 +export const calculateInnerProfitRatio = async (data: any) => {
  359 + const res = await defHttp.post<any>({
  360 + url: Api.INNER_PROFIT_RATIO,
  361 + data: data,
  362 + });
  363 + return res;
  364 +};
  365 +
  366 +export const exportInnerProfitRatio = async (data: any) => {
  367 + const res = await defHttp.post<any>({
  368 + url: Api.INNER_PROFIT_RATIO_EXPORT,
  369 + data: data,
  370 + });
  371 + return res;
  372 +};
... ...
src/enums/roleEnum.ts
... ... @@ -5,4 +5,9 @@ export enum RoleEnum {
5 5 // tester
6 6 TEST = 'test',
7 7 DATA_REPORT_USER = 'data_report_user',
  8 + BUSINESS = 'business_user', // 业务员
  9 + TRACKER = 'tracker_user', // 跟单员
  10 + INSPECT = 'inspect_user', // 质检员
  11 + PRODUCE = 'produce_user', //生产科
  12 + FINANCE = 'finance_user', //财务
8 13 }
... ...
src/router/routes/modules/project/approve.ts
... ... @@ -10,6 +10,13 @@ const order: AppRouteModule = {
10 10 redirect: '/approve',
11 11 meta: {
12 12 hideChildrenInMenu: true,
  13 + roles: [
  14 + RoleEnum.ADMIN,
  15 + RoleEnum.DATA_REPORT_USER,
  16 + RoleEnum.BUSINESS,
  17 + RoleEnum.TRACKER,
  18 + RoleEnum.INSPECT,
  19 + ],
13 20 orderNo: 3,
14 21 icon: 'ion:grid-outline',
15 22 title: '审批管理',
... ...
src/router/routes/modules/project/finance.ts
... ... @@ -13,6 +13,7 @@ const finance: AppRouteModule = {
13 13 orderNo: 3,
14 14 icon: 'ant-design:pay-circle-outlined',
15 15 title: '财务管理',
  16 + roles: [RoleEnum.ADMIN, RoleEnum.FINANCE],
16 17 },
17 18 children: [
18 19 {
... ...
src/store/modules/data.ts
... ... @@ -3,9 +3,16 @@ import type { UserInfo } from &#39;/#/store&#39;;
3 3 import { defineStore } from 'pinia';
4 4 import { store } from '/@/store';
5 5 import { RoleEnum } from '/@/enums/roleEnum';
  6 +import { exchangeTab } from '@/store/modules/user';
6 7  
7   -import { getApiData, getChartData } from '/@/api/project/global';
  8 +import {
  9 + getApiData,
  10 + getChartData,
  11 + getSalesData,
  12 + getCustomerSalesData,
  13 +} from '/@/api/project/global';
8 14 import { formatToDate } from '/@/utils/dateUtil';
  15 +import { watch } from 'vue';
9 16  
10 17 interface UserState {
11 18 userInfo: Nullable<UserInfo>;
... ... @@ -22,6 +29,10 @@ export const useDataStore = defineStore({
22 29 data: {},
23 30 // token
24 31 chartData: {},
  32 + //销售额完成情况
  33 + salesData: {},
  34 + //每个客户销售额
  35 + customerChartData1: {},
25 36 }),
26 37 getters: {
27 38 getData(state) {
... ... @@ -30,6 +41,12 @@ export const useDataStore = defineStore({
30 41 getChartData(state) {
31 42 return state.chartData;
32 43 },
  44 + getSalesData(state) {
  45 + return state.salesData;
  46 + },
  47 + getCustomerChartData1(state) {
  48 + return state.customerChartData1;
  49 + },
33 50 },
34 51 actions: {
35 52 setData(info) {
... ... @@ -38,6 +55,12 @@ export const useDataStore = defineStore({
38 55 setChartData(data) {
39 56 this.chartData = data;
40 57 },
  58 + setSalesData(info) {
  59 + this.salesData = info ? info : '';
  60 + },
  61 + setCustomerChartData1(data) {
  62 + this.customerChartData1 = data;
  63 + },
41 64  
42 65 async getFetchData(): Promise<any> {
43 66 try {
... ... @@ -47,6 +70,14 @@ export const useDataStore = defineStore({
47 70 return Promise.reject(error);
48 71 }
49 72 },
  73 + async getFetchData2(): Promise<any> {
  74 + try {
  75 + const data = await getSalesData();
  76 + this.salesData = data;
  77 + } catch (error) {
  78 + return Promise.reject(error);
  79 + }
  80 + },
50 81 async getFetchChartData(): Promise<any> {
51 82 try {
52 83 const data = await getChartData();
... ... @@ -61,6 +92,23 @@ export const useDataStore = defineStore({
61 92 return Promise.reject(error);
62 93 }
63 94 },
  95 + async getCustomerSalesData1(): Promise<any> {
  96 + try {
  97 + const data = await getCustomerSalesData();
  98 +
  99 + const x = [],
  100 + y = [],
  101 + z = [];
  102 + for (const key in data) {
  103 + x.push(key);
  104 + y.push(data[key].salesAmount);
  105 + z.push(data[key].target);
  106 + }
  107 + this.customerChartData1 = { x, y, z };
  108 + } catch (error) {
  109 + return Promise.reject(error);
  110 + }
  111 + },
64 112 },
65 113 });
66 114  
... ...
src/store/modules/user.ts
... ... @@ -191,3 +191,9 @@ export function useUserStoreWithOut() {
191 191  
192 192 //分析台切换页面变量
193 193 export const exchangeTab = ref('tab1');
  194 +//分析台时期
  195 +// export const period = ref('');
  196 +//分析台业务员
  197 +export const businessPersonIn = ref();
  198 +//分析台客户编码
  199 +export const customerCodeIn = ref();
... ...
src/utils/project.ts
... ... @@ -13,8 +13,15 @@ export const getProfitDisable = (field, code, id, value) =&gt; {
13 13 };
14 14  
15 15 // 基本信息的权限校验 客户编码、项目号、生产科、内部编号、业务员 才需要审核
16   -export const getBaseDisable = (field, code, id) => {
17   - if (
  16 +//业务员可更改的权限:订单基本信息:客户编码、项目号、内部编码
  17 +//跟单员可更改的权限:订单基本信息:客户po号、客户style、Mode(REFERENCE)、COLLECTION(style desciption)、PO COLOR、订单图片、颜色中文、生产要求、订单成分、款式类型、生产科拖货时间、订单上HOD时间、出库类型、包装类型
  18 +
  19 +export const getBaseDisable = (field, code, id, role) => {
  20 + if (['picUrl', 'productionComment'].includes(field) && role == 'business_user') {
  21 + return code === 'LOCKED' && !!id;
  22 + } else if (!['customerCode', 'projectNo', 'innerNo'].includes(field) && role == 'business_user') {
  23 + return code === 'LOCKED' && !!id;
  24 + } else if (
18 25 [
19 26 'customerCode',
20 27 'projectNo',
... ... @@ -22,12 +29,28 @@ export const getBaseDisable = (field, code, id) =&gt; {
22 29 'innerNo',
23 30 'orderCount',
24 31 'businessPerson',
25   - ].includes(field)
  32 + ].includes(field) &&
  33 + (role === 'admin' || role === 'tracker_user')
26 34 ) {
27 35 return code === 'LOCKED' && !!id;
28 36 }
29 37 return false;
30 38 };
  39 +// export const getBaseDisable = (field, code, id) => {
  40 +// if (
  41 +// [
  42 +// 'customerCode',
  43 +// 'projectNo',
  44 +// 'productionDepartment',
  45 +// 'innerNo',
  46 +// 'orderCount',
  47 +// 'businessPerson',
  48 +// ].includes(field)
  49 +// ) {
  50 +// return code === 'LOCKED' && !!id;
  51 +// }
  52 +// return false;
  53 +// };
31 54  
32 55 // 质量检测的disable函数
33 56 // 中期验货:只要是PASS, PASS 2ND, PASS 3RD, FAIL-RELEASE,就锁定。
... ...
src/views/dashboard/analysis/components/GrowCard.vue
1 1 <template>
2   - <div class="md:flex">
  2 + <div class="md:flex" v-if="exchangeTab === 'tab1'">
3 3 <template v-for="(item, index) in growCardList" :key="item.title">
4 4 <Card
5 5 size="small"
... ... @@ -24,6 +24,21 @@
24 24 </Card>
25 25 </template>
26 26 </div>
  27 + <div class="md:flex" v-if="exchangeTab === 'tab2'">
  28 + <template v-for="(item, index) in growCardList2" :key="item.title">
  29 + <Card
  30 + size="small"
  31 + :loading="loading"
  32 + :title="item.title"
  33 + class="md:w-1/4 w-full !md:mt-0"
  34 + :class="{ '!md:mr-4': index + 1 < 5, '!mt-4': index > 0 }"
  35 + >
  36 + <div class="py-4 px-4 flex justify-between items-center" style="font-size: 20px">
  37 + {{ item.value }}
  38 + </div>
  39 + </Card>
  40 + </template>
  41 + </div>
27 42 </template>
28 43 <script lang="ts" setup>
29 44 import { CountTo } from '/@/components/CountTo/index';
... ... @@ -31,12 +46,17 @@
31 46 import { Tag, Card } from 'ant-design-vue';
32 47 import { growCardList } from '../data';
33 48 import { useDataStoreWithOut } from '/@/store/modules/data';
34   - import { computed } from 'vue';
  49 + import { computed, onMounted, ref, watch, watchEffect } from 'vue';
  50 + import { exchangeTab, businessPersonIn, customerCodeIn } from '/@/store/modules/user';
  51 + import { number } from 'vue-types';
  52 + import { getSalesData, getCustomerSalesData } from '/@/api/project/global';
  53 + import { useOrderStoreWithOut } from '/@/store/modules/order';
35 54  
36 55 const dataStore = useDataStoreWithOut();
37 56  
38 57 const growCardList = computed(() => {
39 58 const data = dataStore.getData;
  59 +
40 60 return [
41 61 {
42 62 title: '订单完成',
... ... @@ -80,10 +100,83 @@
80 100 },
81 101 ];
82 102 });
  103 + const data2 = ref();
  104 + // watchEffect(async () => {
  105 + // data2.value = await getSalesData({
  106 + // businessPersonIn: businessPersonIn,
  107 + // customerCodeIn: customerCodeIn,
  108 + // });
  109 + // });
  110 + onMounted(async () => {
  111 + data2.value = await getSalesData({
  112 + businessPersonIn: businessPersonIn.value,
  113 + customerCodeIn: customerCodeIn.value,
  114 + });
  115 + });
  116 + watch(
  117 + [businessPersonIn, customerCodeIn],
  118 + async ([newBusinessPersonIn, newCustomerCodeIn], [oldBusinessPersonIn, oldCustomerCodeIn]) => {
  119 + console.log('businessPersonIn changed from', oldBusinessPersonIn, 'to', newBusinessPersonIn);
  120 + console.log('customerCodeIn changed from', oldCustomerCodeIn, 'to', newCustomerCodeIn);
  121 +
  122 + // 在这里添加你希望在这两个值改变时执行的逻辑
  123 + data2.value = await getSalesData({
  124 + businessPersonIn: newBusinessPersonIn,
  125 + customerCodeIn: newCustomerCodeIn,
  126 + });
  127 + },
  128 + );
  129 + const growCardList2 = computed(() => {
  130 + return [
  131 + {
  132 + title: '总销售额',
  133 + icon: 'visit-count|svg',
  134 + value: data2.value?.yearlySalesAmountTarget || 0,
  135 + total: 120000,
  136 + color: 'green',
  137 + action: '年',
  138 + },
  139 + {
  140 + title: '总销售额完成率',
  141 + icon: 'visit-count|svg',
  142 + value: data2.value?.yearlySalesAmountCompletionRate || 0,
  143 + total: 120000,
  144 + color: 'green',
  145 + action: '年',
  146 + },
  147 + {
  148 + title: '每月销售额目标',
  149 + icon: 'visit-count|svg',
  150 + value: data2.value?.monthlySalesAmountTarget || 0,
  151 + total: 120000,
  152 + color: 'green',
  153 + action: '月',
  154 + },
  155 + {
  156 + title: '当月销售额完成率',
  157 + icon: 'visit-count|svg',
  158 + value: data2.value?.monthlySalesAmountCompletionRate || 0,
  159 + total: 120000,
  160 + color: 'green',
  161 + action: '月',
  162 + },
  163 + {
  164 + title: '今日销售额',
  165 + icon: 'visit-count|svg',
  166 + value: data2.value?.daylySalesAmount || 0,
  167 + total: 120000,
  168 + color: 'green',
  169 + action: '日',
  170 + },
  171 + ];
  172 + });
83 173  
84 174 defineProps({
85 175 loading: {
86 176 type: Boolean,
87 177 },
  178 + exchangeTab: {
  179 + type: String,
  180 + },
88 181 });
89 182 </script>
... ...
src/views/dashboard/analysis/components/SiteAnalysis.vue
... ... @@ -9,7 +9,7 @@
9 9 <VisitAnalysis />
10 10 </p>
11 11 <p v-if="activeKey === 'tab2'">
12   - <VisitAnalysisBar />
  12 + <VisitAnalysisBar style="margin-top: 100px" />
13 13 </p>
14 14 </Card>
15 15 </template>
... ... @@ -19,6 +19,7 @@
19 19 import VisitAnalysis from './VisitAnalysis.vue';
20 20 import VisitAnalysisBar from './VisitAnalysisBar.vue';
21 21 import { exchangeTab } from '@/store/modules/user';
  22 + import type { SelectProps } from 'ant-design-vue';
22 23  
23 24 const activeKey = ref('tab1');
24 25  
... ... @@ -38,3 +39,29 @@
38 39 exchangeTab.value = key;
39 40 }
40 41 </script>
  42 +<style scoped>
  43 + .custom-radio-group .ant-radio-button-wrapper {
  44 + background-color: white; /* 未选中按钮的背景颜色为白色 */
  45 + color: #1684fc; /* 未选中按钮的文字颜色为 #1684fc */
  46 + border-color: #1684fc; /* 未选中按钮的边框颜色 */
  47 + }
  48 +
  49 + .custom-radio-group .ant-radio-button-wrapper-checked {
  50 + background-color: #1684fc; /* 选中按钮的背景颜色为 #1684fc */
  51 + color: white; /* 选中按钮的文字颜色为白色 */
  52 + border-color: #1684fc; /* 选中按钮的边框颜色 */
  53 + }
  54 +
  55 + .custom-radio-group .ant-radio-button-wrapper-checked:hover {
  56 + background-color: #1684fc; /* 悬停在选中按钮时保持相同的背景颜色 */
  57 + color: white; /* 悬停在选中按钮时保持文字为白色 */
  58 + }
  59 +
  60 + .custom-radio-group .ant-radio-button-wrapper:hover {
  61 + border-color: #1684fc; /* 悬停在未选中按钮时的边框颜色 */
  62 + }
  63 +
  64 + .custom-radio-group .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-checked):hover {
  65 + color: #1684fc; /* 悬停在未选中按钮时文字颜色为 #1684fc */
  66 + }
  67 +</style>
... ...
src/views/dashboard/analysis/components/VisitAnalysisBar.vue
1 1 <template>
  2 + <div style="margin-bottom: 30px">
  3 + <span v-if="role === ROLE.ADMIN">
  4 + <span style="font-size: 18px; margin-right: 10px">业务员</span>
  5 + <a-select
  6 + v-model:value="value1"
  7 + show-search
  8 + placeholder="请选择"
  9 + style="width: 200px"
  10 + :options="customerCodeOptions"
  11 + :filter-option="filterOption"
  12 + @focus="handleFocus"
  13 + @blur="handleBlur"
  14 + @change="handleChangeBusiness"
  15 + />
  16 + <span style="font-size: 18px; margin-left: 50px; margin-right: 10px">客户编码</span>
  17 + <a-select
  18 + v-model:value="value2"
  19 + show-search
  20 + placeholder="请选择"
  21 + style="width: 200px"
  22 + :options="businessPersonOptions"
  23 + :filter-option="filterOption"
  24 + @focus="handleFocus"
  25 + @blur="handleBlur"
  26 + @change="handleChangeCustomer"
  27 + />
  28 + <span style="right: 10px"
  29 + ><a-button
  30 + type="default"
  31 + @click="clearData"
  32 + :style="{ marginLeft: '100px', borderRadius: '5px 5px 5px 5px' }"
  33 + >
  34 + 重置
  35 + </a-button>
  36 + <a-button
  37 + :style="{ marginLeft: '20px', borderRadius: '5px 5px 5px 5px' }"
  38 + shape="default"
  39 + type="primary"
  40 + @click="updateData"
  41 + >查询</a-button
  42 + ></span
  43 + ></span
  44 + >
  45 + <div style="position: fixed; top: 80px; right: 70px">
  46 + <a-radio-group
  47 + v-model:value="periodChange"
  48 + class="custom-radio-group"
  49 + :style="{ marginLeft: '100px' }"
  50 + >
  51 + <a-radio-button value="MONTH" @click="handlePeriodChange('MONTH')">月</a-radio-button>
  52 + <a-radio-button value="QUARTER" @click="handlePeriodChange('QUARTER')">季度</a-radio-button>
  53 + <a-radio-button value="YEAR" @click="handlePeriodChange('YEAR')">年</a-radio-button>
  54 + </a-radio-group>
  55 + </div></div
  56 + >
  57 + <div v-if="role !== ROLE.ADMIN" style="margin-bottom: 60px"></div>
2 58 <div ref="chartRef" :style="{ height, width }"></div>
3 59 </template>
4 60 <script lang="ts">
5 61 import { basicProps } from './props';
6 62 </script>
7 63 <script lang="ts" setup>
8   - import { onMounted, ref, Ref } from 'vue';
  64 + import { computed, onMounted, ref, Ref, watchEffect } from 'vue';
9 65 import { useECharts } from '/@/hooks/web/useECharts';
  66 + import { useDataStoreWithOut } from '/@/store/modules/data';
  67 + import { businessPersonIn, customerCodeIn, useUserStoreWithOut } from '@/store/modules/user';
  68 +
  69 + //biaoji
  70 + import type { SelectProps } from 'ant-design-vue';
  71 + import { getCustomerSalesData } from '/@/api/project/global';
  72 + import { useOrderStoreWithOut } from '/@/store/modules/order';
  73 + import { useOrderInfo } from '/@/hooks/component/order';
  74 + import { ROLE } from '/@/views/project/order/type.d';
  75 +
  76 + const userStore = useUserStoreWithOut();
  77 + const user = userStore.getUserInfo;
  78 + const role = computed(() => {
  79 + return user?.roleSmallVO?.code;
  80 + });
  81 + const orderStore = useOrderStoreWithOut();
  82 + console.log(orderStore, '5656order');
  83 + onMounted(async () => {
  84 + await orderStore.getDict();
  85 + });
  86 + const { customerCode: customerCodeOptions, businessPerson: businessPersonOptions } =
  87 + useOrderInfo(orderStore);
  88 +
  89 + const periodChange = ref('MONTH');
  90 + // const businessPersonInChange = ref();
  91 + const businessPersonInChange: string[] = [];
  92 + const customerCodeInChange: string[] = [];
  93 + const handleChangeBusiness = (value: string) => {
  94 + if (businessPersonInChange.length === 0) {
  95 + // 如果数组为空,直接将 value 值放入数组
  96 + businessPersonInChange.push(value);
  97 + } else {
  98 + // 如果数组不为空,清空数组再将 value 值放入数组
  99 + businessPersonInChange.length = 0; // 也可以使用 array.splice(0, array.length);
  100 + businessPersonInChange.push(value);
  101 + }
  102 + console.log(`5656selected ${businessPersonInChange}`);
  103 + };
  104 + const handleChangeCustomer = (value: string) => {
  105 + if (customerCodeInChange.length === 0) {
  106 + // 如果数组为空,直接将 value 值放入数组
  107 + customerCodeInChange.push(value);
  108 + } else {
  109 + // 如果数组不为空,清空数组再将 value 值放入数组
  110 + customerCodeInChange.length = 0; // 也可以使用 array.splice(0, array.length);
  111 + customerCodeInChange.push(value);
  112 + }
  113 + console.log(`5656selected ${customerCodeInChange}`);
  114 + };
  115 + const handleBlur = () => {
  116 + console.log('5656blur');
  117 + };
  118 + const handleFocus = () => {
  119 + console.log('5656focus');
  120 + };
  121 + const filterOption = (input: string, option: any) => {
  122 + return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
  123 + };
  124 +
  125 + const value1 = ref();
  126 + const value2 = ref();
  127 +
  128 + function handlePeriodChange(value) {
  129 + periodChange.value = value;
  130 + }
  131 + //biaoji
10 132  
11 133 defineProps({
12 134 ...basicProps,
13 135 });
  136 + const dataStore = useDataStoreWithOut();
14 137  
15 138 const chartRef = ref<HTMLDivElement | null>(null);
16 139 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
17   - onMounted(() => {
  140 +
  141 + function updateData() {
  142 + customerCodeIn.value = value1.value;
  143 + businessPersonIn.value = value2.value;
  144 + }
  145 + function clearData() {
  146 + customerCodeIn.value = null;
  147 + businessPersonIn.value = null;
  148 + value1.value = null;
  149 + value2.value = null;
  150 + }
  151 +
  152 + watchEffect(async () => {
  153 + // const data1 = dataStore?.getCustomerChartData1 || {};
  154 + console.log(periodChange.value, '56565656periodChange');
  155 + const data2 = await getCustomerSalesData({
  156 + businessPersonIn: businessPersonIn.value,
  157 + customerCodeIn: customerCodeIn.value,
  158 + period: periodChange.value,
  159 + });
  160 + console.log(data2, '5656data2');
  161 + const x = [],
  162 + y = [],
  163 + z = [];
  164 + for (const key in data2) {
  165 + x.push(key);
  166 + y.push(data2[key].salesAmount);
  167 + z.push(data2[key].target);
  168 + }
  169 + const customerChartData1 = { x, y, z };
18 170 setOptions({
19 171 tooltip: {
20 172 trigger: 'axis',
... ... @@ -28,22 +180,22 @@
28 180 grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
29 181 xAxis: {
30 182 type: 'category',
31   - data: [...new Array(12)].map((_item, index) => `${index + 1}月`),
  183 + data: customerChartData1?.x,
32 184 },
33 185 yAxis: {
34 186 type: 'value',
35   - max: 8000,
  187 + // max: 8000,
36 188 splitNumber: 4,
37 189 },
38 190 series: [
39 191 {
40   - data: [3000, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000, 3200, 4800],
  192 + data: customerChartData1?.y,
41 193 type: 'bar',
42 194 barMaxWidth: 80,
43 195 },
44 196 {
45 197 smooth: true,
46   - data: [111, 222, 4000, 1800, 3333, 555, 666, 333],
  198 + data: customerChartData1?.z,
47 199 type: 'line',
48 200 areaStyle: {
49 201 color: 'rgba(255, 255, 255, 0)',
... ... @@ -56,3 +208,29 @@
56 208 });
57 209 });
58 210 </script>
  211 +<style scoped>
  212 + .custom-radio-group .ant-radio-button-wrapper {
  213 + background-color: white; /* 未选中按钮的背景颜色为白色 */
  214 + color: #1684fc; /* 未选中按钮的文字颜色为 #1684fc */
  215 + border-color: #1684fc; /* 未选中按钮的边框颜色 */
  216 + }
  217 +
  218 + .custom-radio-group .ant-radio-button-wrapper-checked {
  219 + background-color: #1684fc; /* 选中按钮的背景颜色为 #1684fc */
  220 + color: white; /* 选中按钮的文字颜色为白色 */
  221 + border-color: #1684fc; /* 选中按钮的边框颜色 */
  222 + }
  223 +
  224 + .custom-radio-group .ant-radio-button-wrapper-checked:hover {
  225 + background-color: #1684fc; /* 悬停在选中按钮时保持相同的背景颜色 */
  226 + color: white; /* 悬停在选中按钮时保持文字为白色 */
  227 + }
  228 +
  229 + .custom-radio-group .ant-radio-button-wrapper:hover {
  230 + border-color: #1684fc; /* 悬停在未选中按钮时的边框颜色 */
  231 + }
  232 +
  233 + .custom-radio-group .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-checked):hover {
  234 + color: #1684fc; /* 悬停在未选中按钮时文字颜色为 #1684fc */
  235 + }
  236 +</style>
... ...
src/views/dashboard/analysis/data.ts
... ... @@ -49,3 +49,54 @@ export const growCardList: GrowCardItem[] = [
49 49 action: '年',
50 50 },
51 51 ];
  52 +
  53 +export const growCardList2: GrowCardItem[] = [
  54 + {
  55 + title: '总销售额',
  56 + icon: 'visit-count|svg',
  57 + value: 2000,
  58 + total: 120000,
  59 + color: 'green',
  60 + action: '年',
  61 + },
  62 + {
  63 + title: '总销售额完成率',
  64 + icon: 'visit-count|svg',
  65 + value: 2000,
  66 + total: 120000,
  67 + color: 'green',
  68 + action: '年',
  69 + },
  70 + {
  71 + title: '每月销售额目标',
  72 + icon: 'visit-count|svg',
  73 + value: 2000,
  74 + total: 120000,
  75 + color: 'green',
  76 + action: '月',
  77 + },
  78 + {
  79 + title: '当月销售额完成率',
  80 + icon: 'visit-count|svg',
  81 + value: 2000,
  82 + total: 120000,
  83 + color: 'green',
  84 + action: '月',
  85 + },
  86 + {
  87 + title: '当月销售额完成率',
  88 + icon: 'visit-count|svg',
  89 + value: 2000,
  90 + total: 120000,
  91 + color: 'green',
  92 + action: '日',
  93 + },
  94 + {
  95 + title: '今日销售额',
  96 + icon: 'visit-count|svg',
  97 + value: 2000,
  98 + total: 120000,
  99 + color: 'green',
  100 + action: '日',
  101 + },
  102 +];
... ...
src/views/dashboard/analysis/index.vue
1 1 <template>
2 2 <div class="p-4">
3   - <GrowCard :loading="loading" class="enter-y" />
  3 + <GrowCard :loading="loading" class="enter-y" :exchangeTab="exchangeTab" />
4 4 <SiteAnalysis class="!my-4 enter-y" :loading="loading" />
5 5 <!-- <div class="md:flex enter-y">
6 6 <VisitRadar class="md:w-1/3 w-full" :loading="loading" />
... ... @@ -20,13 +20,14 @@
20 20 const dataStore = useDataStoreWithOut();
21 21 watch(
22 22 () => exchangeTab.value,
23   - () => {
24   - console.log(exchangeTab.value, 56562);
25   - },
  23 + () => {},
26 24 );
27 25 onMounted(() => {
28 26 dataStore.getFetchData();
  27 + dataStore.getFetchData2();
29 28 dataStore.getFetchChartData();
  29 + dataStore.getCustomerSalesData1();
  30 + exchangeTab.value;
30 31 });
31 32  
32 33 setTimeout(() => {
... ...
src/views/project/approve/PayPanel.vue
... ... @@ -122,25 +122,22 @@
122 122 dataIndex: 'createBy',
123 123 width: 150,
124 124 },
125   - // {
126   - // title: '内部编号',
127   - // dataIndex: 'innerNo',
128   - // width: 150,
129   - // customRender: (column) => {
130   - // const { record } = column || {};
131   - // return record?.fieldInfos?.invoiceBillOrderDO?.innerNo;
132   - // },
133   - // },
134 125 {
135   - title: 'INVOICE编号',
136   - dataIndex: 'invoiceNo',
  126 + title: '内部编号',
  127 + dataIndex: 'innerNo',
137 128 width: 150,
138 129 customRender: (column) => {
139 130 const { record } = column || {};
140   - return record?.fieldInfos?.invoiceBillOrderDO?.invoiceNo;
  131 + console.log(record, '56565repro');
  132 + return record?.fieldInfos?.invoiceBillOrderDO?.innerNo;
141 133 },
142 134 },
143 135 {
  136 + title: '审核类型',
  137 + dataIndex: 'productionDepartment',
  138 + width: 150,
  139 + },
  140 + {
144 141 title: '生产科',
145 142 dataIndex: 'productionDepartment',
146 143 width: 150,
... ...
src/views/project/approve/ReceivePanel.vue
... ... @@ -127,10 +127,20 @@
127 127 dataIndex: 'invoiceNo',
128 128 width: 150,
129 129 customRender: (column) => {
  130 + console.log(column, '5656coapprove');
130 131 const { record } = column || {};
131 132 return record?.fieldInfos?.invoiceBillOrderDO?.invoiceNo;
132 133 },
133 134 },
  135 + {
  136 + title: '内部编号',
  137 + dataIndex: 'innerNo',
  138 + width: 150,
  139 + customRender: (column) => {
  140 + const { record } = column || {};
  141 + return record?.fieldInfos?.invoiceBillOrderDO?.innnerNo;
  142 + },
  143 + },
134 144 // {
135 145 // title: '内部编号',
136 146 // dataIndex: 'innerNo',
... ...
src/views/project/config/CreateModal.vue
... ... @@ -63,6 +63,8 @@
63 63 ? '利润率'
64 64 : props.column === 5
65 65 ? '回款时间'
  66 + : props.column === 6
  67 + ? '固定成本'
66 68 : props.column === 7
67 69 ? '销售额'
68 70 : '包装费用',
... ...
src/views/project/config/DrawerCreate.vue
... ... @@ -66,24 +66,24 @@
66 66 event: 'LATEST_DC_EVENT',
67 67 emails: [],
68 68 },
69   - // {
70   - // fieldName: '尾期验货日期',
71   - // fieldValue: 'endCheckDate',
72   - // event: 'END_CHECK_DATE_EVENT',
73   - // emails: [],
74   - // },
75   - // {
76   - // fieldName: '中期验货报告',
77   - // fieldValue: 'midCheckReport',
78   - // event: 'MID_CHECK_REPORT_EVENT',
79   - // emails: [],
80   - // },
81   - // {
82   - // fieldName: '尾期验货报告',
83   - // fieldValue: 'endCheckReport',
84   - // event: 'END_CHECK_REPORT_EVENT',
85   - // emails: [],
86   - // },
  69 + {
  70 + fieldName: '尾期验货日期',
  71 + fieldValue: 'endCheckDate',
  72 + event: 'END_CHECK_DATE_EVENT',
  73 + emails: [],
  74 + },
  75 + {
  76 + fieldName: '中期验货报告',
  77 + fieldValue: 'midCheckReport',
  78 + event: 'MID_CHECK_REPORT_EVENT',
  79 + emails: [],
  80 + },
  81 + {
  82 + fieldName: '尾期验货报告',
  83 + fieldValue: 'endCheckReport',
  84 + event: 'END_CHECK_REPORT_EVENT',
  85 + emails: [],
  86 + },
87 87 ];
88 88 const schemas: FormSchema[] = [
89 89 {
... ...
src/views/project/config/DrawerEdit.vue
... ... @@ -103,36 +103,36 @@
103 103 label: '最晚订舱时间(填写邮箱)',
104 104 rules: [{ required: true }],
105 105 },
106   - // {
107   - // field: 'endCheckDate',
108   - // component: 'InputTextArea',
109   - // labelWidth: 250,
110   - // colProps: {
111   - // span: 23,
112   - // },
113   - // label: '尾期验货日期(填写邮箱)',
114   - // rules: [{ required: true }],
115   - // },
116   - // {
117   - // field: 'midCheckReport',
118   - // component: 'InputTextArea',
119   - // labelWidth: 250,
120   - // colProps: {
121   - // span: 23,
122   - // },
123   - // label: '中期验货报告(填写邮箱)',
124   - // rules: [{ required: true }],
125   - // },
126   - // {
127   - // field: 'endCheckReport',
128   - // component: 'InputTextArea',
129   - // labelWidth: 250,
130   - // colProps: {
131   - // span: 23,
132   - // },
133   - // label: '尾期验货报告(填写邮箱)',
134   - // rules: [{ required: true }],
135   - // },
  106 + {
  107 + field: 'endCheckDate',
  108 + component: 'InputTextArea',
  109 + labelWidth: 250,
  110 + colProps: {
  111 + span: 23,
  112 + },
  113 + label: '尾期验货日期(填写邮箱)',
  114 + rules: [{ required: true }],
  115 + },
  116 + {
  117 + field: 'midCheckReport',
  118 + component: 'InputTextArea',
  119 + labelWidth: 250,
  120 + colProps: {
  121 + span: 23,
  122 + },
  123 + label: '中期验货报告(填写邮箱)',
  124 + rules: [{ required: true }],
  125 + },
  126 + {
  127 + field: 'endCheckReport',
  128 + component: 'InputTextArea',
  129 + labelWidth: 250,
  130 + colProps: {
  131 + span: 23,
  132 + },
  133 + label: '尾期验货报告(填写邮箱)',
  134 + rules: [{ required: true }],
  135 + },
136 136 {
137 137 field: 'id',
138 138 },
... ...
src/views/project/config/TablePanel.vue
... ... @@ -84,7 +84,6 @@
84 84 }
85 85  
86 86 async function handleSave(record) {
87   - console.log(record, '5656s');
88 87 if (props.column === 3) {
89 88 await saveConfig({ id: record.id, settingValue: record.settingValue });
90 89 } else {
... ... @@ -95,7 +94,6 @@
95 94 }
96 95  
97 96 function handleEdit(record: any) {
98   - console.log(record, '5656e');
99 97 record.onEdit?.(true);
100 98 }
101 99  
... ... @@ -104,7 +102,6 @@
104 102 }
105 103  
106 104 function handleModalSuccess() {
107   - console.log('5656ss');
108 105 reload();
109 106 }
110 107  
... ...
src/views/project/config/data.tsx
... ... @@ -158,21 +158,21 @@ export const columns: BasicColumn[] = [
158 158 dataIndex: 'latestDc',
159 159 width: 300,
160 160 },
161   - // {
162   - // title: '尾期验货日期',
163   - // dataIndex: 'endCheckDate',
164   - // width: 300,
165   - // },
166   - // {
167   - // title: '中期验货报告',
168   - // dataIndex: 'midCheckReport',
169   - // width: 300,
170   - // },
171   - // {
172   - // title: '尾期验货报告',
173   - // dataIndex: 'endCheckReport',
174   - // width: 300,
175   - // },
  161 + {
  162 + title: '尾期验货日期',
  163 + dataIndex: 'endCheckDate',
  164 + width: 300,
  165 + },
  166 + {
  167 + title: '中期验货报告',
  168 + dataIndex: 'midCheckReport',
  169 + width: 300,
  170 + },
  171 + {
  172 + title: '尾期验货报告',
  173 + dataIndex: 'endCheckReport',
  174 + width: 300,
  175 + },
176 176 ];
177 177  
178 178 export const columnsProduct: BasicColumn[] = [
... ...
src/views/project/finance/pay/index.vue
... ... @@ -220,7 +220,7 @@
220 220 // 单选函数
221 221 async function onSelect(record, selected: boolean) {
222 222 const res = await checkDetail({ checkNo: record.checkNo });
223   -
  223 + console.log(res, '5656respau');
224 224 const customerCode = res[0].customerCode;
225 225 const productionDepartment = res[0].productionDepartment;
226 226  
... ...
src/views/project/finance/receive/type.d.ts
... ... @@ -6,4 +6,5 @@ export enum ROLE {
6 6 TRACKER = 'tracker_user', // 跟单员
7 7 INSPECT = 'inspect_user', // 质检员
8 8 PRODUCE = 'produce_user', //生产科
  9 + FINANCE = 'finance_user', //生产科
9 10 }
... ...
src/views/project/order/ExportModal.vue
... ... @@ -125,6 +125,10 @@
125 125 [item.dataIndex]: 'selected',
126 126 }));
127 127 fieldVO.baseFields = merge({}, ...fieldVO.baseFields);
  128 + if (props.role === ROLE.PRODUCE) {
  129 + // 从 baseFields 中去除 orderHod
  130 + delete fieldVO.baseFields['orderHodTime'];
  131 + }
128 132 } else if (item === 'reportFields') {
129 133 fieldVO.reportFields = ORDER_LIST_REPORT_FIELDS[0].children.map((item) => ({
130 134 [item.dataIndex]: 'selected',
... ...
src/views/project/order/FormDetail/BaseFormPanel.vue
... ... @@ -165,6 +165,12 @@
165 165 },
166 166 componentProps: {
167 167 imgUrl: picUrl.value,
  168 + disabled: getBaseDisable(
  169 + item.field,
  170 + get(fields.value, `${item.field}`),
  171 + props.id,
  172 + role.value,
  173 + ),
168 174 // disabled: getDisable(get(fields.value, 'picUrl'), props.id),
169 175 onChange: (res) => {
170 176 if (res.file?.response?.data) {
... ... @@ -178,38 +184,89 @@
178 184 },
179 185 };
180 186 }
181   - const businessNotDisabledFields = ['customerCode', 'projectNo', 'innerNo'];
182   - const trackerNotDisabledFields = [
183   - 'customerPo',
184   - 'customerStyle',
185   - 'modeleLo',
186   - 'collection',
187   - 'poColor',
188   - 'cnColor',
189   - 'picUrl',
190   - 'productStyle',
191   - 'orderComposition',
192   - 'productionDepartmentConsignTime',
193   - 'orderHodTime',
194   - 'outboundType',
195   - 'packetType',
196   - 'productionComment',
197   - ];
198   - const isFieldNotDisabledForBusiness = businessNotDisabledFields.includes(item.field);
199   - const isFieldNotDisabledForTracker = trackerNotDisabledFields.includes(item.field);
  187 + if (item.field === 'productionComment') {
  188 + return {
  189 + field: 'productionComment',
  190 + component: 'InputTextArea',
  191 + rules: [{ required: true }],
  192 + componentProps: {
  193 + rows: 10,
  194 + disabled: getBaseDisable(
  195 + item.field,
  196 + get(fields.value, `${item.field}`),
  197 + props.id,
  198 + role.value,
  199 + ),
  200 + },
  201 + labelWidth: 600,
  202 + label: '产品意见',
  203 + };
  204 + }
  205 + if (item.field === 'returnOrder') {
  206 + return {
  207 + field: 'returnOrder',
  208 + component: 'Select',
  209 + rules: [{ required: true }],
  210 + componentProps: {
  211 + options: [
  212 + {
  213 + label: '是',
  214 + value: '1',
  215 + },
  216 + {
  217 + label: '否',
  218 + value: '0',
  219 + },
  220 + ],
  221 + disabled: getBaseDisable(
  222 + item.field,
  223 + get(fields.value, `${item.field}`),
  224 + props.id,
  225 + role.value,
  226 + ),
  227 + },
  228 + labelWidth: 600,
  229 + // default: '请选择',
  230 + label: '是否返单 ',
  231 + };
  232 + }
  233 + // const businessNotDisabledFields = ['customerCode', 'projectNo', 'innerNo'];
  234 + // const trackerNotDisabledFields = [
  235 + // 'customerPo',
  236 + // 'customerStyle',
  237 + // 'modeleLo',
  238 + // 'collection',
  239 + // 'poColor',
  240 + // 'cnColor',
  241 + // 'picUrl',
  242 + // 'productStyle',
  243 + // 'orderComposition',
  244 + // 'productionDepartmentConsignTime',
  245 + // 'orderHodTime',
  246 + // 'outboundType',
  247 + // 'packetType',
  248 + // 'productionComment',
  249 + // ];
  250 + // const isFieldNotDisabledForBusiness = businessNotDisabledFields.includes(item.field);
  251 + // const isFieldNotDisabledForTracker = trackerNotDisabledFields.includes(item.field);
200 252 return {
201 253 ...item,
202 254 field: `${item.field}`,
203 255 componentProps: {
204 256 ...(item.component === 'Select' && { showSearch: true }),
205 257 ...(item.component === 'Select' && { options: options[item.field] }),
206   - disabled:
207   - role.value === ROLE.BUSINESS
208   - ? !isFieldNotDisabledForBusiness
209   - : role.value === ROLE.TRACKER
210   - ? !isFieldNotDisabledForTracker
211   - : getBaseDisable(item.field, get(fields.value, `${item.field}`), props.id),
212   - // disabled: getBaseDisable(item.field, get(fields.value, `${item.field}`), props.id),
  258 + // disabled:
  259 + // role.value === ROLE.BUSINESS
  260 + // ? !isFieldNotDisabledForBusiness
  261 + // : role.value === ROLE.TRACKER
  262 + // ? !isFieldNotDisabledForTracker
  263 + // : getBaseDisable(item.field, get(fields.value, `${item.field}`), props.id),
  264 + disabled: getBaseDisable(
  265 + item.field,
  266 + get(fields.value, `${item.field}`),
  267 + props.id,
  268 + role.value,
  269 + ),
213 270 onChange: async (val) => {
214 271 if (item.field === 'customerCode' && !isCopy.value) {
215 272 if (!props.id) {
... ...
src/views/project/order/PassCalculate.vue
... ... @@ -3,6 +3,7 @@
3 3 v-bind="$attrs"
4 4 :title="title"
5 5 @register="register"
  6 + @visible-change="handleShow"
6 7 width="500px"
7 8 :bodyStyle="{ height: '100px' }"
8 9 @ok="handleOk"
... ... @@ -17,7 +18,7 @@
17 18  
18 19 const [register, { closeModal }] = useModalInner(async (data) => {
19 20 title.value = data.title;
20   - const id = data.check[0];
  21 + const ids = data.check;
21 22 const opinionType = ref();
22 23 if (data.title == '确认意见') {
23 24 opinionType.value = 'pp样品确认意见';
... ... @@ -26,13 +27,18 @@
26 27 } else if (data.title == '测试样品') {
27 28 opinionType.value = 'Altex测试结果';
28 29 }
29   - num.value = await passCalculate({ orderId: id, opinionType: opinionType.value });
  30 + num.value = await passCalculate({ ids: ids, opinionType: opinionType.value });
30 31 });
31 32 const title = ref('');
32 33 const num = ref();
33 34  
34 35 async function handleOk() {
35   - closeModal();
36 36 num.value = '';
  37 + closeModal();
  38 + }
  39 + function handleShow(visible: boolean) {
  40 + if (visible) {
  41 + num.value = '';
  42 + }
37 43 }
38 44 </script>
... ...
src/views/project/order/ProductProfit.vue
... ... @@ -16,11 +16,14 @@
16 16 title="内部生产净利润分析表"
17 17 width="60%"
18 18 :bodyStyle="{ height: '360px' }"
  19 + @visible-change="handleShow"
19 20 @ok="handleOk"
20 21 okText="导出"
21 22 >
22 23 <template #appendFooter>
23   - <a-button style="background-color: #1890ff; color: white">计算</a-button>
  24 + <a-button style="background-color: #1890ff; color: white" @click="handleCalculate"
  25 + >计算</a-button
  26 + >
24 27 </template>
25 28 <table
26 29 style="
... ... @@ -39,7 +42,7 @@
39 42 <tbody>
40 43 <tr>
41 44 <td style="border: 1px solid black; width: 20%">项目号</td>
42   - <td style="border: 1px solid black; width: 20%"></td>
  45 + <td style="border: 1px solid black; width: 20%">{{ projectNo }}</td>
43 46 <td style="border: 1px solid black; width: 20%">开始时间</td>
44 47 <td style="border: 1px solid black; width: 20%">结束时间</td>
45 48 <td style="border: 1px solid black; width: 20%">生产持续时间</td>
... ... @@ -47,56 +50,64 @@
47 50 <tr>
48 51 <td style="border: 1px solid black; width: 20%"></td>
49 52 <td style="border: 1px solid black; width: 20%">项目开发时间</td>
50   - <td style="border: 1px solid black; width: 20%"></td>
51   - <td style="border: 1px solid black; width: 20%"></td>
52   - <td style="border: 1px solid black; width: 20%"></td>
  53 + <td style="border: 1px solid black; width: 20%">
  54 + <input type="date" v-model="projectStartTime" style="width: 100%" />
  55 + </td>
  56 + <td style="border: 1px solid black; width: 20%">
  57 + <input type="date" v-model="projectEndTime" style="width: 100%" />
  58 + </td>
  59 + <td style="border: 1px solid black; width: 20%">{{ projectDays }}</td>
53 60 </tr>
54 61 <tr>
55 62 <td style="border: 1px solid black; width: 20%">客户编码</td>
56   - <td style="border: 1px solid black; width: 20%"></td>
  63 + <td style="border: 1px solid black; width: 20%">{{ customerCode }}</td>
57 64 <td style="border: 1px solid black; width: 20%">明细</td>
58 65 <td style="border: 1px solid black; width: 20%">金额</td>
59 66 <td style="border: 1px solid black; width: 20%">订单数量</td>
60 67 </tr>
61 68 <tr>
62 69 <td style="border: 1px solid black; width: 40%" colspan="2">生产科总价合计</td>
63   - <td style="border: 1px solid black; width: 20%"></td>
64   - <td style="border: 1px solid black; width: 20%"></td>
65   - <td style="border: 1px solid black; width: 20%"></td>
  70 + <td style="border: 1px solid black; width: 20%">{{ productionDepartmentTotalPrice }}</td>
  71 + <td style="border: 1px solid black; width: 20%">{{ sumMoney }}</td>
  72 + <td style="border: 1px solid black; width: 20%">{{ sumCount }}</td>
66 73 </tr>
67 74 <tr>
68 75 <td style="border: 1px solid black; width: 40%" colspan="2">生产科预算金额</td>
69   - <td style="border: 1px solid black; width: 20%"></td>
  76 + <td style="border: 1px solid black; width: 20%"
  77 + ><a-input v-model:value="productionDepartmentPredictPrice" placeholder="请输入"
  78 + /></td>
70 79 <td style="border: 1px solid black; width: 20%">预算占比</td>
71 80 <td style="border: 1px solid black; width: 20%">预算占比与实际占比差</td>
72 81 </tr>
73 82 <tr>
74 83 <td style="border: 1px solid black; width: 40%" colspan="2">实际发生费用</td>
75   - <td style="border: 1px solid black; width: 20%"></td>
76   - <td style="border: 1px solid black; width: 20%"></td>
77   - <td style="border: 1px solid black; width: 20%"></td>
  84 + <td style="border: 1px solid black; width: 20%"
  85 + ><a-input v-model:value="productionActualPrice" placeholder="请输入"
  86 + /></td>
  87 + <td style="border: 1px solid black; width: 20%">{{ predictRatio }}</td>
  88 + <td style="border: 1px solid black; width: 20%">{{ predictRatioDeduct }}</td>
78 89 </tr>
79 90 <tr>
80 91 <td style="border: 1px solid black; width: 40%" colspan="2">内部生产毛利润</td>
81   - <td style="border: 1px solid black; width: 20%"></td>
  92 + <td style="border: 1px solid black; width: 20%">{{ grossProfit }}</td>
82 93 <td style="border: 1px solid black; width: 20%"></td>
83 94 <td style="border: 1px solid black; width: 20%"></td>
84 95 </tr>
85 96 <tr>
86 97 <td style="border: 1px solid black; width: 40%" colspan="2">内部生产固定成本</td>
87   - <td style="border: 1px solid black; width: 20%"></td>
  98 + <td style="border: 1px solid black; width: 20%">{{ innerProduceFixProfit }}</td>
88 99 <td style="border: 1px solid black; width: 20%"></td>
89 100 <td style="border: 1px solid black; width: 20%"></td>
90 101 </tr>
91 102 <tr>
92 103 <td style="border: 1px solid black; width: 40%" colspan="2">内部生产提交</td>
93   - <td style="border: 1px solid black; width: 20%"></td>
  104 + <td style="border: 1px solid black; width: 20%">{{ innerProduceTotalPrice }}</td>
94 105 <td style="border: 1px solid black; width: 20%"></td>
95 106 <td style="border: 1px solid black; width: 20%"></td>
96 107 </tr>
97 108 <tr>
98 109 <td style="border: 1px solid black; width: 40%" colspan="2">内部生产净利润</td>
99   - <td style="border: 1px solid black; width: 20%"></td>
  110 + <td style="border: 1px solid black; width: 20%">{{ innerProduceTotalProfit }}</td>
100 111 <td style="border: 1px solid black; width: 20%"></td>
101 112 <td style="border: 1px solid black; width: 20%"></td>
102 113 </tr>
... ... @@ -108,17 +119,83 @@
108 119 import { BasicModal, useModalInner } from '@/components/Modal';
109 120 import { computed, ref } from 'vue';
110 121 import { payDate, checkCreate } from '@/api/project/invoice';
  122 + import type { Dayjs } from 'dayjs';
  123 + import { calculateInnerProfitRatio, exportInnerProfitRatio } from '@/api/project/order';
111 124  
112 125 const Input1 = ref('');
113 126 const Input2 = ref();
114 127 const res = ref();
  128 + const customerCode = ref(); // 客户编码
  129 +
  130 + const grossProfit = ref(); // 内部生产毛利润
  131 + const innerProduceFixProfit = ref(); // 内部生产固定成本
  132 + const innerProduceTotalPrice = ref(); // 内部生产提成
  133 + const innerProduceTotalProfit = ref(); // 内部生产净利润
  134 + const predictRatio = ref(); // 预算占比
  135 + const predictRatioDeduct = ref(); // 预算占比差
  136 + const productionActualPrice = ref(); // 实际发生费用
  137 + const productionDepartmentPredictPrice = ref(); // 生产科预算金额
  138 + const productionDepartmentTotalPrice = ref(); // 生产科总价合计
  139 + const projectDays = ref(); // 生产持续时间
  140 + const sumCount = ref(0);
  141 + const sumMoney = ref(0);
  142 +
  143 + const projectEndTime = ref(); // 项目开发结束时间
  144 + const projectNo = ref(); // 项目号
  145 + const projectStartTime = ref(); // 项目开发开始时间
  146 + const filteredItems = ref();
  147 +
115 148 const [register, { closeModal }] = useModalInner(async (data) => {
116 149 res.value = data.data;
  150 + customerCode.value = data.customerCode[0][0];
  151 + projectNo.value = data.projectNo[0][0];
117 152 console.log(Input2.value, 565656);
  153 + filteredItems.value = data.filteredItems;
118 154 });
119 155 async function handleOk() {
120 156 closeModal();
121 157 }
  158 + function handleShow(visible: boolean) {
  159 + if (visible) {
  160 + projectStartTime.value = null;
  161 + projectEndTime.value = null;
  162 + productionDepartmentPredictPrice.value = null;
  163 + productionActualPrice.value = null;
  164 + }
  165 + }
  166 + function formatDateTime(dateTime: string): string {
  167 + return dateTime.split('T')[0];
  168 + }
  169 +
  170 + async function handleCalculate() {
  171 + projectStartTime.value = formatDateTime(projectStartTime.value);
  172 + projectEndTime.value = formatDateTime(projectEndTime.value);
  173 + const res = await calculateInnerProfitRatio({
  174 + customerCode: customerCode.value,
  175 + projectNo: projectNo.value,
  176 + projectStartTime: projectStartTime.value,
  177 + projectEndTime: projectEndTime.value,
  178 + productionDepartmentPredictPrice: productionDepartmentPredictPrice.value,
  179 + productionActualPrice: productionActualPrice.value,
  180 + });
  181 + console.log(res, '5656resproductprofit');
  182 + grossProfit.value = res.grossProfit;
  183 + innerProduceFixProfit.value = res.innerProduceFixProfit;
  184 + innerProduceTotalPrice.value = res.innerProduceTotalPrice;
  185 + innerProduceTotalProfit.value = res.innerProduceTotalProfit;
  186 + predictRatio.value = res.predictRatio;
  187 + productionActualPrice.value = res.productionActualPrice;
  188 + productionDepartmentPredictPrice.value = res.productionDepartmentPredictPrice;
  189 + productionDepartmentTotalPrice.value = res.productionDepartmentTotalPrice;
  190 + projectDays.value = res.projectDays;
  191 + predictRatioDeduct.value = 1 - predictRatio.value;
  192 + console.log(filteredItems.value, '5656filteredItems');
  193 + filteredItems.value.forEach((item) => {
  194 + sumMoney.value += item.profitAnalysisInfo.productionDepartmentTotalPrice;
  195 + sumCount.value += item.orderCount;
  196 + });
  197 + console.log(sumMoney.value, '5656filteredItems', sumCount.value);
  198 + }
122 199 </script>
123 200 <style scoped>
124 201 .divAll {
... ...
src/views/project/order/ProductText.vue
... ... @@ -18,7 +18,7 @@
18 18 width="500px"
19 19 @visible-change="handleShow"
20 20 :footer="null"
21   - :bodyStyle="{ height: '180px' }"
  21 + :bodyStyle="{ height: '210px' }"
22 22 >
23 23 <div class="container">
24 24 <div v-if="isShow1 == true" style="margin-top: 50px; text-align: center">
... ... @@ -55,6 +55,7 @@
55 55 import { RadioGroup } from 'ant-design-vue';
56 56 import { EyeOutlined, FilePptOutlined } from '@ant-design/icons-vue';
57 57 import { useMessage } from '@/hooks/web/useMessage';
  58 + import { createProductText, exportProductText } from '@/api/project/order';
58 59  
59 60 export default defineComponent({
60 61 props: {
... ... @@ -70,16 +71,13 @@
70 71 const loading = ref(true);
71 72 const exportLoading = ref(false);
72 73 const info = ref();
  74 + const checkedKeys = ref();
73 75 const choose = ref(); //选择公司
74 76 const isShow1 = ref(true); //选择公司页面
75 77 const isShow2 = ref(false); //生成PDF页面
76 78 const pdf = ref(['/pdf.png']);
77 79 const [register, { setModalProps, closeModal }] = useModalInner(async (data) => {
78   - // if (data.customers.length == 0) {
79   - // error('请选择订单');
80   - // closeModal();
81   - // }
82   - console.log(data, '5656data');
  80 + checkedKeys.value = data.checkedKeys;
83 81 });
84 82 const options = computed(() => {
85 83 // 运营总监-基本信息,跟单,质检
... ... @@ -89,7 +87,7 @@
89 87 ];
90 88 });
91 89 const { createMessage } = useMessage();
92   - const { error } = createMessage;
  90 + const { error, success } = createMessage;
93 91 const customerCodeToCompanyMap: Record<string, string> = {
94 92 A01: '青岛翱特逸格饰品有限公司',
95 93 A04: '青岛吉庆天成饰品有限公司',
... ... @@ -139,10 +137,21 @@
139 137 return true;
140 138 }
141 139 //生成pdf
  140 + const resText = ref();
142 141 // const customerCodeList: string[] = props.customerCodes;
143   - function handleProduct() {
  142 + let isDisabled = false;
  143 + async function handleProduct() {
  144 + if (isDisabled) {
  145 + error('请勿连续点击生成按钮,需要等待三秒再点击生成');
  146 + return;
  147 + }
144 148 const customerCodeList = props.customerCodes;
145 149 const areValid = validateCompany(customerCodeList, choose.value);
  150 + // // 设置禁用标志并在 3 秒后重置
  151 + isDisabled = true;
  152 + setTimeout(() => {
  153 + isDisabled = false;
  154 + }, 3000);
146 155 //如果选错了,弹出提示
147 156 if (!areValid) {
148 157 error('勾选订单与选择的公司不匹配');
... ... @@ -151,6 +160,11 @@
151 160 choose.value == '青岛翱特逸格饰品有限公司' ||
152 161 choose.value == '青岛吉庆天成饰品有限公司'
153 162 ) {
  163 + resText.value = await createProductText({
  164 + ids: checkedKeys.value,
  165 + companyName: choose.value,
  166 + });
  167 + console.log(resText.value, '5656restextF');
154 168 //此处设置接口,传递选择的公司值
155 169 isShow1.value = false;
156 170 isShow2.value = true;
... ... @@ -159,11 +173,17 @@
159 173 }
160 174 //查看pdf
161 175 function handlePdf() {
162   - // const pdfUrl = './pdfs.pdf';
163   - // window.open(pdfUrl, '_blank');
  176 + window.open(resText.value.produceFile);
164 177 }
165 178 //发送按钮
166 179 async function handleExport() {
  180 + const res = await exportProductText({
  181 + productionUrl: resText.value.productionUrl,
  182 + productionDepartment: resText.value.productionDepartment,
  183 + isSend: true,
  184 + });
  185 + console.log();
  186 + success('操作成功,' + res);
167 187 closeModal();
168 188 }
169 189 return {
... ... @@ -198,23 +218,27 @@
198 218 flex-direction: column;
199 219 min-height: 20vh;
200 220 }
  221 +
201 222 .bottom {
202 223 margin-top: auto;
203   - margin-left: auto;
204 224 margin-right: 20px;
  225 + margin-left: auto;
205 226 }
  227 +
206 228 .showPdf {
  229 + display: flex;
  230 + position: relative;
  231 + height: 30px;
207 232 border: 2px solid;
208   - border-color: #d8d8d8;
209 233 border-radius: 5px;
210   - height: 30px;
211   - position: relative;
212   - display: flex;
  234 + border-color: #d8d8d8;
213 235 }
  236 +
214 237 .FilePptOutlined {
215 238 margin-top: 3px;
216 239 margin-left: 30px;
217 240 }
  241 +
218 242 .EyeOutlined {
219 243 margin-top: 3px;
220 244 margin-left: 290px;
... ...
src/views/project/order/ServiceProfit.vue
... ... @@ -16,11 +16,14 @@
16 16 title="净利润分析表"
17 17 width="60%"
18 18 :bodyStyle="{ height: '455px' }"
  19 + @visible-change="handleShow"
19 20 @ok="handleOk"
20 21 okText="导出"
21 22 >
22 23 <template #appendFooter>
23   - <a-button style="background-color: #1890ff; color: white">计算</a-button>
  24 + <a-button style="background-color: #1890ff; color: white" @click="handleCalculate"
  25 + >计算</a-button
  26 + >
24 27 </template>
25 28 <table
26 29 style="
... ... @@ -39,25 +42,29 @@
39 42 <tbody>
40 43 <tr>
41 44 <td style="border: 1px solid black; width: 25%">项目号</td>
42   - <td style="border: 1px solid black; width: 25%">M01-214425</td>
  45 + <td style="border: 1px solid black; width: 25%">{{ projectNo }}</td>
43 46 <td style="border: 1px solid black; width: 25%">开始时间</td>
44 47 <td style="border: 1px solid black; width: 25%">结束时间</td>
45 48 </tr>
46 49 <tr>
47 50 <td style="border: 1px solid black"></td>
48 51 <td style="border: 1px solid black">项目开始时间</td>
49   - <td style="border: 1px solid black"></td>
50   - <td style="border: 1px solid black"></td>
  52 + <td style="border: 1px solid black"
  53 + ><a-date-picker v-model:value="projectStartTime"
  54 + /></td>
  55 + <td style="border: 1px solid black"><a-date-picker v-model:value="projectEndTime" /></td>
51 56 </tr>
52 57 <tr>
53 58 <td style="border: 1px solid black"></td>
54 59 <td style="border: 1px solid black">生产进行时间</td>
55   - <td style="border: 1px solid black"></td>
56   - <td style="border: 1px solid black"></td>
  60 + <td style="border: 1px solid black"
  61 + ><a-date-picker v-model:value="produceStartTime"
  62 + /></td>
  63 + <td style="border: 1px solid black"><a-date-picker v-model:value="produceEndTime" /></td>
57 64 </tr>
58 65 <tr>
59 66 <td style="border: 1px solid black">客户编码</td>
60   - <td style="border: 1px solid black">M01</td>
  67 + <td style="border: 1px solid black">{{ customerCode }}</td>
61 68 <td style="border: 1px solid black"></td>
62 69 <td style="border: 1px solid black">备注</td>
63 70 </tr>
... ... @@ -78,12 +85,16 @@
78 85 </tr>
79 86 <tr>
80 87 <td style="border: 1px solid black" colspan="2">研发开发费合计</td>
81   - <td style="border: 1px solid black"></td>
  88 + <td style="border: 1px solid black"
  89 + ><a-input v-model:value="developTotalPrice" placeholder="请输入"
  90 + /></td>
82 91 <td style="border: 1px solid black"></td>
83 92 </tr>
84 93 <tr>
85 94 <td style="border: 1px solid black" colspan="2">复制费用合计</td>
86   - <td style="border: 1px solid black"></td>
  95 + <td style="border: 1px solid black"
  96 + ><a-input v-model:value="copyTotalPrice" placeholder="请输入"
  97 + /></td>
87 98 <td style="border: 1px solid black"></td>
88 99 </tr>
89 100 <tr>
... ... @@ -123,7 +134,9 @@
123 134 </tr>
124 135 <tr>
125 136 <td style="border: 1px solid black" colspan="2">包装费用实际金额</td>
126   - <td style="border: 1px solid black"></td>
  137 + <td style="border: 1px solid black"
  138 + ><a-input v-model:value="packetActualTotalPrice" placeholder="请输入"
  139 + /></td>
127 140 <td style="border: 1px solid black"></td>
128 141 </tr>
129 142 <tr>
... ... @@ -162,20 +175,116 @@
162 175 </template>
163 176 <script lang="ts" setup>
164 177 import { BasicModal, useModalInner } from '@/components/Modal';
165   - import { computed, ref } from 'vue';
166   - import { payDate, checkCreate } from '@/api/project/invoice';
167   - import { Description, DescItem, useDescription } from '@/components/Description';
  178 + import { computed, ref, toRaw } from 'vue';
  179 + // import { payDate, checkCreate } from '@/api/project/invoice';
  180 + import { calculateBusinessProfit, exportBusinessProfit } from '@/api/project/order';
  181 + import type { Dayjs } from 'dayjs';
168 182  
  183 + const projectStartTime = ref();
  184 + const projectEndTime = ref();
  185 + const produceStartTime = ref();
  186 + const produceEndTime = ref();
  187 +
  188 + const testinput = ref();
169 189 const Input1 = ref('');
170 190 const Input2 = ref();
171 191 const res = ref();
  192 + const orderList = ref();
  193 + const customerCode = ref();
  194 + const projectNo = ref();
  195 + const developTotalPrice = ref();
  196 + const copyTotalPrice = ref();
  197 + const packetActualTotalPrice = ref();
  198 + const spainRatio = ref();
  199 + const chinaRatio = ref();
  200 + const actualRmbPrice = ref(0); //实际跟单单价
  201 + const actualPrice = ref(0); //实际跟单单价折算美金
  202 + const actualRatio = ref(6.2); //实际汇率
  203 + const actualRatiactualRatioProfitPriceo = ref(); //汇率收益计算
  204 + const chinaRatioProfitPrice = ref(); //中国团队提成比例
  205 + const developProfit = ref(); //研发贸易利润
  206 + const fixCost = ref(); // 固定成本
  207 + const orderCount = ref(); //订单总数量
  208 + const outTotalPrice = ref(); //支出合计计
  209 + const packetProfitPrice = ref(); //包装费用收益计算
  210 + const packetTotalPrice = ref(); //包装费用合计¥
  211 + const productionDepartmentTotalPrice = ref(); //生成科总价¥
  212 + const totalProfitPrice = ref(); //综合收益计算
  213 + const spainRatioProfitPrice = ref(); //西班牙提成金额
  214 +
  215 + // const orderRes = await getOrderList({});
  216 + // console.log(orderRes, '5656orderRes');
172 217 const [register, { closeModal }] = useModalInner(async (data) => {
173 218 res.value = data.data;
174   - console.log(Input2.value, 565656);
  219 + orderList.value = data.res;
  220 + customerCode.value = data.customerCode[0][0];
  221 + projectNo.value = data.projectNo[0][0];
  222 + console.log(orderList.value, 565656);
175 223 });
176 224 async function handleOk() {
177 225 closeModal();
178 226 }
  227 + function handleShow(visible: boolean) {
  228 + if (visible) {
  229 + projectStartTime.value = null;
  230 + projectEndTime.value = null;
  231 + produceStartTime.value = null;
  232 + produceEndTime.value = null;
  233 + customerCode.value = null;
  234 + projectNo.value = null;
  235 + developTotalPrice.value = null;
  236 + copyTotalPrice.value = null;
  237 + packetActualTotalPrice.value = null;
  238 + }
  239 + }
  240 + async function handleCalculate() {
  241 + const packetCalculatePrice = ref(0);
  242 + const orderCalculateCount = ref(0);
  243 + const allList = toRaw(orderList.value);
  244 + console.log(allList, '5656allList');
  245 + allList.forEach((item) => {
  246 + console.log(item, '5656orderList');
  247 + console.log(item.profitAnalysisInfo.packetPrice, '5656orderList');
  248 + packetCalculatePrice.value += item.profitAnalysisInfo.packetPrice;
  249 + orderCalculateCount.value += item.orderCount;
  250 + actualRmbPrice.value += packetCalculatePrice.value / orderCalculateCount.value;
  251 + });
  252 + actualPrice.value = actualRmbPrice.value / actualRatio.value;
  253 + console.log(actualPrice.value, '5656actualPrice');
  254 +
  255 + const res = await calculateBusinessProfit({
  256 + customerCode: customerCode.value,
  257 + projectNo: projectNo.value,
  258 + projectStartTime: projectStartTime.value,
  259 + projectEndTime: projectEndTime.value,
  260 + produceStartTime: produceStartTime.value,
  261 + produceEndTime: produceEndTime.value,
  262 + developTotalPrice: developTotalPrice.value,
  263 + copyTotalPrice: copyTotalPrice.value,
  264 + spainRatio: spainRatio.value,
  265 + chinaRatio: chinaRatio.value,
  266 + packetActualTotalPrice: packetActualTotalPrice.value,
  267 + actualRmbPrice: actualRmbPrice.value,
  268 + actualPrice: actualPrice.value,
  269 + actualRatio: actualRatio.value,
  270 + });
  271 + console.log(res, '5656resservice');
  272 +
  273 + actualRmbPrice.value = res.data.actualRmbPrice;
  274 + actualPrice.value = res.data.actualPrice;
  275 + actualRatio.value = res.data.actualRatio;
  276 + actualRatiactualRatioProfitPriceo.value = res.data.actualRatioProfitPrice; //汇率收益计算
  277 + chinaRatioProfitPrice.value = res.data.chinaRatioProfitPrice; //中国团队提成比例
  278 + developProfit.value = res.data.developProfit; //研发贸易利润
  279 + fixCost.value = res.data.fixCost; // 固定成本
  280 + orderCount.value = res.data.orderCount; //订单总数量
  281 + outTotalPrice.value = res.data.outTotalPrice; //支出合计
  282 + packetProfitPrice.value = res.data.packetProfitPrice; //包装费用收益计算
  283 + packetTotalPrice.value = res.data.packetTotalPrice; //包装费用合计
  284 + productionDepartmentTotalPrice.value = res.data.productionDepartmentTotalPrice; //生成科总价
  285 + totalProfitPrice.value = res.data.totalProfitPrice; //综合收益计算
  286 + spainRatioProfitPrice.value = res.data.spainRatioProfitPrice; //西班牙提成金额
  287 + }
179 288 </script>
180 289 <style scoped>
181 290 .divAll {
... ...
src/views/project/order/TrackHistory.vue
... ... @@ -91,16 +91,22 @@
91 91 // 如果没有找到".",直接返回原始字符串
92 92 return Result;
93 93 }
94   - const pagination1 = computed(() => {
95   - return {
96   - show: true,
97   - pageSize: 20,
98   - page: page1.value,
99   - total: total1.value,
100   - onChange(cur) {
101   - console.log(cur);
102   - getOrderOptLogFunc(orderId.value, 1, cur);
103   - },
104   - };
105   - });
  94 + // const pagination1 = computed(() => {
  95 + // return {
  96 + // show: true,
  97 + // pageSize: 20,
  98 + // page: page1.value,
  99 + // total: total1.value,
  100 + // onChange(cur) {
  101 + // console.log(cur);
  102 + // getOrderOptLogFunc(orderId.value, 1, cur);
  103 + // },
  104 + // };
  105 + // });
  106 + const pagination1 = {
  107 + onChange: (page: number) => {
  108 + console.log(page);
  109 + },
  110 + pageSize: 5,
  111 + };
106 112 </script>
... ...
src/views/project/order/index.vue
... ... @@ -164,7 +164,7 @@
164 164 :style="{ borderRadius: '5px 5px 5px 5px' }"
165 165 type="primary"
166 166 @click="handleProductModal"
167   - v-if="role === ROLE.ADMIN || role === ROLE.BUSINESS || role === ROLE.TRACKER"
  167 + v-if="role === ROLE.ADMIN || role === ROLE.TRACKER"
168 168 >生产指标书</a-button
169 169 >
170 170 <a-button
... ... @@ -366,15 +366,91 @@
366 366 console.log(getForm().getFieldsValue());
367 367 }
368 368  
  369 + // type CustomerCodeEntry = [string, number]; // 定义二维数组类型 [customerCode, count]
  370 + // type ProductionDepartmentEntry = [string, number]; // 定义二维数组类型 [productionDepartment, count]
  371 +
  372 + // const selectedCustomCodes = ref<CustomerCodeEntry[]>([]); // 创建一个二维数组的 ref
  373 + // const selectedProductionDepartment = ref<ProductionDepartmentEntry[]>([]); // 创建一个二维数组的 ref
  374 +
  375 + // // 单选处理函数
  376 + // function onSelect(
  377 + // record: { customerCode: string; productionDepartment: string; id: string },
  378 + // selected: boolean,
  379 + // ) {
  380 + // // 查找 customerCode 在 selectedCustomCodes 中的位置
  381 + // const customerCodeIndex = selectedCustomCodes.value.findIndex(
  382 + // ([customerCode]) => customerCode === record.customerCode,
  383 + // );
  384 +
  385 + // // 查找 productionDepartment 在 selectedProductionDepartment 中的位置
  386 + // const productionDepartmentIndex = selectedProductionDepartment.value.findIndex(
  387 + // ([department]) => department === record.productionDepartment,
  388 + // );
  389 +
  390 + // if (selected) {
  391 + // // 添加到 checkedKeys
  392 + // checkedKeys.value = [...checkedKeys.value, record.id];
  393 +
  394 + // // 更新 selectedCustomCodes
  395 + // if (customerCodeIndex !== -1) {
  396 + // // 如果已存在,增加计数
  397 + // selectedCustomCodes.value[customerCodeIndex][1] += 1;
  398 + // } else {
  399 + // // 如果不存在,添加新项 [customerCode, 1]
  400 + // selectedCustomCodes.value.push([record.customerCode, 1]);
  401 + // }
  402 +
  403 + // // 更新 selectedProductionDepartment
  404 + // if (productionDepartmentIndex !== -1) {
  405 + // // 如果已存在,增加计数
  406 + // selectedProductionDepartment.value[productionDepartmentIndex][1] += 1;
  407 + // } else {
  408 + // // 如果不存在,添加新项 [productionDepartment, 1]
  409 + // selectedProductionDepartment.value.push([record.productionDepartment, 1]);
  410 + // }
  411 + // } else {
  412 + // // 从 checkedKeys 中移除
  413 + // checkedKeys.value = checkedKeys.value.filter((id) => id !== record.id);
  414 +
  415 + // // 更新 selectedCustomCodes
  416 + // if (customerCodeIndex !== -1) {
  417 + // if (selectedCustomCodes.value[customerCodeIndex][1] > 1) {
  418 + // selectedCustomCodes.value[customerCodeIndex][1] -= 1;
  419 + // } else {
  420 + // selectedCustomCodes.value.splice(customerCodeIndex, 1);
  421 + // }
  422 + // }
  423 +
  424 + // // 更新 selectedProductionDepartment
  425 + // if (productionDepartmentIndex !== -1) {
  426 + // if (selectedProductionDepartment.value[productionDepartmentIndex][1] > 1) {
  427 + // selectedProductionDepartment.value[productionDepartmentIndex][1] -= 1;
  428 + // } else {
  429 + // selectedProductionDepartment.value.splice(productionDepartmentIndex, 1);
  430 + // }
  431 + // }
  432 + // }
  433 +
  434 + // console.log('5656Checked Keys:', checkedKeys.value);
  435 + // console.log('5656Selected Customer Codes:', selectedCustomCodes.value);
  436 + // console.log('5656Selected Production Departments:', selectedProductionDepartment.value);
  437 + // }
369 438 type CustomerCodeEntry = [string, number]; // 定义二维数组类型 [customerCode, count]
370 439 type ProductionDepartmentEntry = [string, number]; // 定义二维数组类型 [productionDepartment, count]
  440 + type ProjectNoEntry = [string, number]; // 定义二维数组类型 [innerNo, count]
371 441  
372 442 const selectedCustomCodes = ref<CustomerCodeEntry[]>([]); // 创建一个二维数组的 ref
373 443 const selectedProductionDepartment = ref<ProductionDepartmentEntry[]>([]); // 创建一个二维数组的 ref
  444 + const selectedProjectNos = ref<ProjectNoEntry[]>([]); // 创建一个二维数组的 ref
374 445  
375 446 // 单选处理函数
376 447 function onSelect(
377   - record: { customerCode: string; productionDepartment: string; id: string },
  448 + record: {
  449 + customerCode: string;
  450 + productionDepartment: string;
  451 + projectNo: string;
  452 + id: string;
  453 + },
378 454 selected: boolean,
379 455 ) {
380 456 // 查找 customerCode 在 selectedCustomCodes 中的位置
... ... @@ -387,6 +463,11 @@
387 463 ([department]) => department === record.productionDepartment,
388 464 );
389 465  
  466 + // 查找 projectNo 在 selectedProjectNos 中的位置
  467 + const projectNoIndex = selectedProjectNos.value.findIndex(
  468 + ([projectNo]) => projectNo === record.projectNo,
  469 + );
  470 +
390 471 if (selected) {
391 472 // 添加到 checkedKeys
392 473 checkedKeys.value = [...checkedKeys.value, record.id];
... ... @@ -408,6 +489,15 @@
408 489 // 如果不存在,添加新项 [productionDepartment, 1]
409 490 selectedProductionDepartment.value.push([record.productionDepartment, 1]);
410 491 }
  492 +
  493 + // 更新 selectedProjectNos
  494 + if (projectNoIndex !== -1) {
  495 + // 如果已存在,增加计数
  496 + selectedProjectNos.value[projectNoIndex][1] += 1;
  497 + } else {
  498 + // 如果不存在,添加新项 [projectNo, 1]
  499 + selectedProjectNos.value.push([record.projectNo, 1]);
  500 + }
411 501 } else {
412 502 // 从 checkedKeys 中移除
413 503 checkedKeys.value = checkedKeys.value.filter((id) => id !== record.id);
... ... @@ -429,18 +519,100 @@
429 519 selectedProductionDepartment.value.splice(productionDepartmentIndex, 1);
430 520 }
431 521 }
  522 +
  523 + // 更新 selectedProjectNos
  524 + if (projectNoIndex !== -1) {
  525 + if (selectedProjectNos.value[projectNoIndex][1] > 1) {
  526 + selectedProjectNos.value[projectNoIndex][1] -= 1;
  527 + } else {
  528 + selectedProjectNos.value.splice(projectNoIndex, 1);
  529 + }
  530 + }
432 531 }
433 532  
434 533 console.log('5656Checked Keys:', checkedKeys.value);
435 534 console.log('5656Selected Customer Codes:', selectedCustomCodes.value);
436   - console.log('5656Selected Production Departments:', selectedProductionDepartment.value);
  535 + console.log('56565Selected Production Departments:', selectedProductionDepartment.value);
  536 + console.log('5656Selected projectNo:', selectedProjectNos.value);
437 537 }
438 538  
  539 + // // 全选处理函数
  540 + // function onSelectAll(selected: boolean, selectedRows: any[], changeRows: any[]) {
  541 + // const changeIds = changeRows.map((item) => item.id);
  542 + // const changeCustomerCodes = changeRows.map((item) => item.customerCode);
  543 + // const changeProductionDepartments = changeRows.map((item) => item.productionDepartment);
  544 +
  545 + // if (selected) {
  546 + // // 添加到 checkedKeys
  547 + // checkedKeys.value = [...checkedKeys.value, ...changeIds];
  548 +
  549 + // // 更新 selectedCustomCodes
  550 + // changeCustomerCodes.forEach((code) => {
  551 + // const index = selectedCustomCodes.value.findIndex(
  552 + // ([customerCode]) => customerCode === code,
  553 + // );
  554 + // if (index !== -1) {
  555 + // selectedCustomCodes.value[index][1] += 1;
  556 + // } else {
  557 + // selectedCustomCodes.value.push([code, 1]);
  558 + // }
  559 + // });
  560 +
  561 + // // 更新 selectedProductionDepartment
  562 + // changeProductionDepartments.forEach((department) => {
  563 + // const index = selectedProductionDepartment.value.findIndex(
  564 + // ([prodDepartment]) => prodDepartment === department,
  565 + // );
  566 + // if (index !== -1) {
  567 + // selectedProductionDepartment.value[index][1] += 1;
  568 + // } else {
  569 + // selectedProductionDepartment.value.push([department, 1]);
  570 + // }
  571 + // });
  572 + // } else {
  573 + // // 从 checkedKeys 中移除
  574 + // checkedKeys.value = checkedKeys.value.filter((id) => !changeIds.includes(id));
  575 +
  576 + // // 更新 selectedCustomCodes
  577 + // changeCustomerCodes.forEach((code) => {
  578 + // const index = selectedCustomCodes.value.findIndex(
  579 + // ([customerCode]) => customerCode === code,
  580 + // );
  581 + // if (index !== -1) {
  582 + // if (selectedCustomCodes.value[index][1] > 1) {
  583 + // selectedCustomCodes.value[index][1] -= 1;
  584 + // } else {
  585 + // selectedCustomCodes.value.splice(index, 1);
  586 + // }
  587 + // }
  588 + // });
  589 +
  590 + // // 更新 selectedProductionDepartment
  591 + // changeProductionDepartments.forEach((department) => {
  592 + // const index = selectedProductionDepartment.value.findIndex(
  593 + // ([prodDepartment]) => prodDepartment === department,
  594 + // );
  595 + // if (index !== -1) {
  596 + // if (selectedProductionDepartment.value[index][1] > 1) {
  597 + // selectedProductionDepartment.value[index][1] -= 1;
  598 + // } else {
  599 + // selectedProductionDepartment.value.splice(index, 1);
  600 + // }
  601 + // }
  602 + // });
  603 + // }
  604 +
  605 + // console.log('5656Checked Keys:', checkedKeys.value);
  606 + // console.log('5656Selected Customer Codes:', selectedCustomCodes.value);
  607 + // console.log('5656Selected Production Departments:', selectedProductionDepartment.value);
  608 + // }
  609 +
439 610 // 全选处理函数
440 611 function onSelectAll(selected: boolean, selectedRows: any[], changeRows: any[]) {
441 612 const changeIds = changeRows.map((item) => item.id);
442 613 const changeCustomerCodes = changeRows.map((item) => item.customerCode);
443 614 const changeProductionDepartments = changeRows.map((item) => item.productionDepartment);
  615 + const changeProjectNos = changeRows.map((item) => item.projectNo);
444 616  
445 617 if (selected) {
446 618 // 添加到 checkedKeys
... ... @@ -469,6 +641,16 @@
469 641 selectedProductionDepartment.value.push([department, 1]);
470 642 }
471 643 });
  644 +
  645 + // 更新 selectedProjectNos
  646 + changeProjectNos.forEach((projectNo) => {
  647 + const index = selectedProjectNos.value.findIndex(([no]) => no === projectNo);
  648 + if (index !== -1) {
  649 + selectedProjectNos.value[index][1] += 1;
  650 + } else {
  651 + selectedProjectNos.value.push([projectNo, 1]);
  652 + }
  653 + });
472 654 } else {
473 655 // 从 checkedKeys 中移除
474 656 checkedKeys.value = checkedKeys.value.filter((id) => !changeIds.includes(id));
... ... @@ -500,11 +682,24 @@
500 682 }
501 683 }
502 684 });
  685 +
  686 + // 更新 selectedProjectNos
  687 + changeInnerNos.forEach((projectNo) => {
  688 + const index = selectedProjectNos.value.findIndex(([no]) => no === projectNo);
  689 + if (index !== -1) {
  690 + if (selectedProjectNos.value[index][1] > 1) {
  691 + selectedProjectNos.value[index][1] -= 1;
  692 + } else {
  693 + selectedProjectNos.value.splice(index, 1);
  694 + }
  695 + }
  696 + });
503 697 }
504 698  
505 699 console.log('5656Checked Keys:', checkedKeys.value);
506 700 console.log('5656Selected Customer Codes:', selectedCustomCodes.value);
507 701 console.log('5656Selected Production Departments:', selectedProductionDepartment.value);
  702 + console.log('5656Selected projectNos:', selectedProjectNos.value);
508 703 }
509 704  
510 705 function handleEdit(record, e) {
... ... @@ -621,7 +816,7 @@
621 816 // }
622 817 const { createMessage } = useMessage();
623 818 const { error } = createMessage;
624   - function handleProductModal() {
  819 + function handleProductModal(record) {
625 820 const form = getForm();
626 821 const values = form.getFieldsValue();
627 822 console.log(selectedCustomCodes.value, 5656);
... ... @@ -674,21 +869,50 @@
674 869 });
675 870 }
676 871  
677   - function handleServiceProfitModal() {
  872 + async function handleServiceProfitModal() {
  873 + if (selectedCustomCodes.value.length > 1) {
  874 + error('勾选订单的客户编码需一致');
  875 + return;
  876 + }
  877 + if (selectedProjectNos.value.length > 1) {
  878 + error('勾选订单的项目号需一致');
  879 + return;
  880 + }
678 881 const form = getForm();
679 882 const values = form.getFieldsValue();
  883 + const resAll = await getOrderList({});
  884 + console.log(resAll, '5656resall');
  885 + const filteredItems = resAll.items.filter((item: { id: string }) =>
  886 + checkedKeys.value.includes(item.id),
  887 + );
  888 + console.log(filteredItems, '5656filteredItems');
680 889 openServiceProfitModal(true, {
  890 + res: filteredItems,
681 891 data: checkedKeys.value,
  892 + orderList: resAll,
  893 + customerCode: selectedCustomCodes.value,
  894 + projectNo: selectedProjectNos.value,
682 895 searchData: values,
683 896 });
684 897 }
685 898  
686   - function handleProductProfitModal() {
  899 + async function handleProductProfitModal() {
  900 + if (selectedCustomCodes.value.length > 1) {
  901 + error('勾选订单的客户编码需一致');
  902 + return;
  903 + }
687 904 const form = getForm();
688 905 const values = form.getFieldsValue();
  906 + const resAll = await getOrderList({});
  907 + const filteredItems = resAll.items.filter((item: { id: string }) =>
  908 + checkedKeys.value.includes(item.id),
  909 + );
689 910 openProductProfitModal(true, {
  911 + filteredItems: filteredItems,
690 912 data: checkedKeys.value,
691 913 searchData: values,
  914 + customerCode: selectedCustomCodes.value,
  915 + projectNo: selectedProjectNos.value,
692 916 });
693 917 }
694 918  
... ...
src/views/project/order/tableData.tsx
... ... @@ -51,6 +51,11 @@ export const ORDER_LIST_BASE_FIELDS = [
51 51 dataIndex: 'projectNo',
52 52 },
53 53 {
  54 + title: 'Invoice编号',
  55 + width: 150,
  56 + dataIndex: 'invoiceNo',
  57 + },
  58 + {
54 59 title: '生产科',
55 60 width: 150,
56 61 dataIndex: 'productionDepartment',
... ... @@ -102,7 +107,7 @@ export const ORDER_LIST_BASE_FIELDS = [
102 107 },
103 108 {
104 109 title: '产品意见',
105   - width: 150,
  110 + width: 400,
106 111 dataIndex: 'productionComment',
107 112 },
108 113 {
... ... @@ -153,6 +158,13 @@ export const ORDER_LIST_BASE_FIELDS = [
153 158 width: 150,
154 159 dataIndex: 'businessPerson',
155 160 },
  161 + {
  162 + field: 'returnOrder',
  163 + component: 'Select',
  164 + default: '请选择',
  165 + label: '是否返单 ',
  166 + rules: [{ required: true }],
  167 + },
156 168 ];
157 169  
158 170 export const ORDER_LIST_REPORT_FIELDS = [
... ... @@ -869,7 +881,7 @@ export const FIELDS_BASE_INFO = [
869 881 },
870 882 {
871 883 field: 'productionComment',
872   - component: 'Input',
  884 + component: 'InputTextArea',
873 885 rules: [{ required: true }],
874 886 labelWidth: 150,
875 887 label: '产品意见',
... ... @@ -1013,20 +1025,20 @@ export const FIELDS_TRACK_STAGE_INFO = [
1013 1025 value: '1.1st Fail',
1014 1026 },
1015 1027 {
1016   - label: '2.2st ok',
1017   - value: '2.2st ok',
  1028 + label: '2.2nd ok',
  1029 + value: '2.2nd ok',
1018 1030 },
1019 1031 {
1020   - label: '2.2st Fail',
1021   - value: '2.2st Fail',
  1032 + label: '2.2nd Fail',
  1033 + value: '2.2nd Fail',
1022 1034 },
1023 1035 {
1024   - label: '3.3st ok',
1025   - value: '3.3st ok',
  1036 + label: '3.3rd ok',
  1037 + value: '3.3rd ok',
1026 1038 },
1027 1039 {
1028   - label: '3.3st Fail',
1029   - value: '3.3st Fail',
  1040 + label: '3.3rd Fail',
  1041 + value: '3.3rd Fail',
1030 1042 },
1031 1043 ],
1032 1044 },
... ... @@ -1059,20 +1071,20 @@ export const FIELDS_TRACK_STAGE_INFO = [
1059 1071 value: '1.1st Fail',
1060 1072 },
1061 1073 {
1062   - label: '2.2st ok',
1063   - value: '2.2st ok',
  1074 + label: '2.2nd ok',
  1075 + value: '2.2nd ok',
1064 1076 },
1065 1077 {
1066   - label: '2.2st Fail',
1067   - value: '2.2st Fail',
  1078 + label: '2.2nd Fail',
  1079 + value: '2.2nd Fail',
1068 1080 },
1069 1081 {
1070   - label: '3.3st ok',
1071   - value: '3.3st ok',
  1082 + label: '3.3rd ok',
  1083 + value: '3.3rd ok',
1072 1084 },
1073 1085 {
1074   - label: '3.3st Fail',
1075   - value: '3.3st Fail',
  1086 + label: '3.3rd Fail',
  1087 + value: '3.3rd Fail',
1076 1088 },
1077 1089 ],
1078 1090 },
... ... @@ -1105,20 +1117,20 @@ export const FIELDS_TRACK_STAGE_INFO = [
1105 1117 value: '1.1st Fail',
1106 1118 },
1107 1119 {
1108   - label: '2.2st ok',
1109   - value: '2.2st ok',
  1120 + label: '2.2nd ok',
  1121 + value: '2.2nd ok',
1110 1122 },
1111 1123 {
1112   - label: '2.2st Fail',
1113   - value: '2.2st Fail',
  1124 + label: '2.2nd Fail',
  1125 + value: '2.2nd Fail',
1114 1126 },
1115 1127 {
1116   - label: '3.3st ok',
1117   - value: '3.3st ok',
  1128 + label: '3.3rd ok',
  1129 + value: '3.3rd ok',
1118 1130 },
1119 1131 {
1120   - label: '3.3st Fail',
1121   - value: '3.3st Fail',
  1132 + label: '3.3rd Fail',
  1133 + value: '3.3rd Fail',
1122 1134 },
1123 1135 ],
1124 1136 },
... ... @@ -1144,20 +1156,20 @@ export const FIELDS_TRACK_STAGE_INFO = [
1144 1156 value: '1.1st Fail',
1145 1157 },
1146 1158 {
1147   - label: '2.2st ok',
1148   - value: '2.2st ok',
  1159 + label: '2.2nd ok',
  1160 + value: '2.2nd ok',
1149 1161 },
1150 1162 {
1151   - label: '2.2st Fail',
1152   - value: '2.2st Fail',
  1163 + label: '2.2nd Fail',
  1164 + value: '2.2nd Fail',
1153 1165 },
1154 1166 {
1155   - label: '3.3st ok',
1156   - value: '3.3st ok',
  1167 + label: '3.3rd ok',
  1168 + value: '3.3rd ok',
1157 1169 },
1158 1170 {
1159   - label: '3.3st Fail',
1160   - value: '3.3st Fail',
  1171 + label: '3.3rd Fail',
  1172 + value: '3.3rd Fail',
1161 1173 },
1162 1174 ],
1163 1175 },
... ... @@ -1705,20 +1717,20 @@ export function getFormConfig(businessUsers: any[]): Partial&lt;FormProps&gt; {
1705 1717 value: '1.1st Fail',
1706 1718 },
1707 1719 {
1708   - label: '2.2st ok',
1709   - value: '2.2st ok',
  1720 + label: '2.2nd ok',
  1721 + value: '2.2nd ok',
1710 1722 },
1711 1723 {
1712   - label: '2.2st Fail',
1713   - value: '2.2st Fail',
  1724 + label: '2.2nd Fail',
  1725 + value: '2.2nd Fail',
1714 1726 },
1715 1727 {
1716   - label: '3.3st ok',
1717   - value: '3.3st ok',
  1728 + label: '3.3rd ok',
  1729 + value: '3.3rd ok',
1718 1730 },
1719 1731 {
1720   - label: '3.3st Fail',
1721   - value: '3.3st Fail',
  1732 + label: '3.3rd Fail',
  1733 + value: '3.3rd Fail',
1722 1734 },
1723 1735 ],
1724 1736 },
... ...
src/views/project/order/type.d.ts
... ... @@ -6,4 +6,5 @@ export enum ROLE {
6 6 TRACKER = 'tracker_user', // 跟单员
7 7 INSPECT = 'inspect_user', // 质检员
8 8 PRODUCE = 'produce_user', //生产科
  9 + FINANCE = 'finance_user', //财务
9 10 }
... ...
vite.config.ts
... ... @@ -20,8 +20,8 @@ export default defineApplicationConfig({
20 20 server: {
21 21 proxy: {
22 22 '/basic-api/order': {
23   - // target: 'http://47.104.8.35:18000',
24   - target: 'http://localhost:8000',
  23 + target: 'http://47.104.8.35:18000',
  24 + // target: 'http://localhost:8001',
25 25 // target: 'http://39.108.227.113:8000',
26 26 // target: 'http://localhost:8000',
27 27 // target: 'http://39.108.227.113:3000/mock/35',
... ... @@ -31,8 +31,8 @@ export default defineApplicationConfig({
31 31 rewrite: (path) => path.replace(new RegExp(`^/basic-api`), ''),
32 32 },
33 33 '/api/localStorage/upload': {
34   - // target: 'http://47.104.8.35:18000',
35   - target: 'http://localhost:8000',
  34 + target: 'http://47.104.8.35:18000',
  35 + // target: 'http://localhost:8001',
36 36 // target: 'http://39.108.227.113:8000',
37 37 // target: '192.168.31.250:18000',
38 38 // target: 'http://localhost:8000',
... ...