Commit 522f7be02bbd6225eb25f2e53c8f0fe2602faf25

Authored by chenhang4442024
1 parent cf269d58

feat(project): add new feature for approve

1. 添加扣款单状态
2. 添加对账单
src/views/project/approve/PayPanel.vue
@@ -262,6 +262,8 @@ @@ -262,6 +262,8 @@
262 return extractedValues.value.join(','); 262 return extractedValues.value.join(',');
263 } else if (record?.type == 50) { 263 } else if (record?.type == 50) {
264 return record?.fieldInfos?.checkBillVO?.innerNo; 264 return record?.fieldInfos?.checkBillVO?.innerNo;
  265 + }else if (record?.type == 70) {
  266 + return record?.fieldInfos?.deductionUrlFieldVO?.innerNo;
265 } 267 }
266 }, 268 },
267 }, 269 },
@@ -275,6 +277,8 @@ @@ -275,6 +277,8 @@
275 return '应付账单申请'; 277 return '应付账单申请';
276 } else if (record?.type == 50) { 278 } else if (record?.type == 50) {
277 return '生产科发票申请'; 279 return '生产科发票申请';
  280 + }else if (record?.type == 70) {
  281 + return `生产科扣款单申请${record?.fieldInfos?.deductionUrlFieldVO?.deductAmount ? `(¥${record?.fieldInfos?.deductionUrlFieldVO?.deductAmount?.toFixed(2)})` : ''}`;
278 } 282 }
279 }, 283 },
280 }, 284 },
@@ -289,6 +293,8 @@ @@ -289,6 +293,8 @@
289 data.value = record?.fieldInfos?.producePaymentCheckBillFieldVO; 293 data.value = record?.fieldInfos?.producePaymentCheckBillFieldVO;
290 } else if (record?.type == 50) { 294 } else if (record?.type == 50) {
291 data.value = record?.fieldInfos?.checkBillVO; 295 data.value = record?.fieldInfos?.checkBillVO;
  296 + } else if (record?.type == 70) {
  297 + data.value = record?.fieldInfos?.deductionUrlFieldVO;
292 } 298 }
293 return data.value?.productionName; 299 return data.value?.productionName;
294 }, 300 },
@@ -353,7 +359,7 @@ @@ -353,7 +359,7 @@
353 function handleProfitModal() {} 359 function handleProfitModal() {}
354 360
355 async function handleDetail(data) { 361 async function handleDetail(data) {
356 - if (data.type == 50) { 362 + if (data.type == 50 ) {
357 showInvoice.value = true; 363 showInvoice.value = true;
358 mockData.value = data.fieldInfos.checkBillVO; 364 mockData.value = data.fieldInfos.checkBillVO;
359 } else if (data.type == 40) { 365 } else if (data.type == 40) {
@@ -361,6 +367,10 @@ @@ -361,6 +367,10 @@
361 mockData.value = data.fieldInfos.producePaymentCheckBillFieldVO; 367 mockData.value = data.fieldInfos.producePaymentCheckBillFieldVO;
362 financePerson.value = mockData.value?.financePerson; 368 financePerson.value = mockData.value?.financePerson;
363 trackerUser.value = mockData.value?.trackerUser; 369 trackerUser.value = mockData.value?.trackerUser;
  370 + }else if(data.type == 70){
  371 + showInvoice.value = true;
  372 + mockData.value = data.fieldInfos.deductionUrlFieldVO;
  373 + mockData.value.invoiceUrl = mockData.value.deductUrl;
364 } 374 }
365 openModal(true, { data }); 375 openModal(true, { data });
366 id.value = data.id; 376 id.value = data.id;
src/views/project/approve/ReceivePanel.vue
@@ -35,15 +35,15 @@ @@ -35,15 +35,15 @@
35 </template> 35 </template>
36 </BasicTable> 36 </BasicTable>
37 <BasicModal 37 <BasicModal
38 - :formFooter="!isApproved && role === ROLE.ADMIN" 38 + :formFooter="(!isApproved && role === ROLE.ADMIN) || showInvoice"
39 @register="registerModal" 39 @register="registerModal"
40 title="申请信息" 40 title="申请信息"
41 okText="通过" 41 okText="通过"
42 width="1000px" 42 width="1000px"
43 @ok="handleTrue" 43 @ok="handleTrue"
44 @visible-change="handleShow" 44 @visible-change="handleShow"
45 - :showCancelBtn="!isApproved && role === ROLE.ADMIN"  
46 - :showOkBtn="!isApproved && role === ROLE.ADMIN" 45 + :showCancelBtn="(!isApproved && role === ROLE.ADMIN) || (showInvoice && role === ROLE.FINANCE)"
  46 + :showOkBtn="(!isApproved && role === ROLE.ADMIN) || (showInvoice && role === ROLE.FINANCE)"
47 > 47 >
48 <!-- <Description 48 <!-- <Description
49 class="mt-4" 49 class="mt-4"
@@ -104,6 +104,9 @@ @@ -104,6 +104,9 @@
104 </tr> 104 </tr>
105 </tbody> 105 </tbody>
106 </table> 106 </table>
  107 + <a v-if="showInvoice" @click="openPic(mockData.invoiceUrl)" rel="noopener noreferrer">{{
  108 + mockData.invoiceName
  109 + }}</a>
107 <template #appendFooter> 110 <template #appendFooter>
108 <a-button 111 <a-button
109 v-if="!isApproved && (role === ROLE.ADMIN || role === ROLE.FINANCE)" 112 v-if="!isApproved && (role === ROLE.ADMIN || role === ROLE.FINANCE)"
@@ -111,7 +114,7 @@ @@ -111,7 +114,7 @@
111 > 114 >
112 不通过</a-button 115 不通过</a-button
113 > 116 >
114 - <a-button v-if="isApproved && role === ROLE.ADMIN" @click="handleFalse"> 驳回重填</a-button> 117 + <a-button v-if="(isApproved && role === ROLE.ADMIN) || (isApproved && showInvoice && role === ROLE.FINANCE)" @click="handleFalse"> 驳回重填</a-button>
115 <a-button @click="handleExport"> 导出</a-button> 118 <a-button @click="handleExport"> 导出</a-button>
116 </template> 119 </template>
117 </BasicModal> 120 </BasicModal>
@@ -167,7 +170,7 @@ @@ -167,7 +170,7 @@
167 const projectNo = ref(); 170 const projectNo = ref();
168 const financePerson = ref(''); 171 const financePerson = ref('');
169 const trackerUser = ref(''); 172 const trackerUser = ref('');
170 - 173 + const showInvoice = ref(false);
171 const mockData = ref(); 174 const mockData = ref();
172 const schema: DescItem[] = [ 175 const schema: DescItem[] = [
173 { 176 {
@@ -217,7 +220,12 @@ @@ -217,7 +220,12 @@
217 width: 150, 220 width: 150,
218 customRender: (column) => { 221 customRender: (column) => {
219 const { record } = column || {}; 222 const { record } = column || {};
220 - return record?.fieldInfos?.invoiceFieldVO?.invoiceNo; 223 + if(record?.type === 30){
  224 + return record?.fieldInfos?.invoiceFieldVO?.invoiceNo;
  225 + }else if(record?.type === 60){
  226 + return record?.fieldInfos?.deductionUrlFieldVO?.invoiceNo;
  227 + }
  228 +
221 }, 229 },
222 }, 230 },
223 { 231 {
@@ -226,11 +234,28 @@ @@ -226,11 +234,28 @@
226 width: 150, 234 width: 150,
227 customRender: (column) => { 235 customRender: (column) => {
228 const { record } = column || {}; 236 const { record } = column || {};
  237 + if(record?.type === 30){
229 const extractedValues = ref<string[]>( 238 const extractedValues = ref<string[]>(
230 record?.fieldInfos?.invoiceFieldVO?.innerNo?.map((item) => item), 239 record?.fieldInfos?.invoiceFieldVO?.innerNo?.map((item) => item),
231 ); 240 );
232 // return record?.invoiceFieldVo?.innerNo; 241 // return record?.invoiceFieldVo?.innerNo;
233 return extractedValues?.value?.join(','); 242 return extractedValues?.value?.join(',');
  243 + }else if(record?.type === 60){
  244 + return record?.fieldInfos?.deductionUrlFieldVO?.innerNo;
  245 + }
  246 + },
  247 + },
  248 + {
  249 + title: '审核类型',
  250 + dataIndex: 'productionDepartment',
  251 + width: 150,
  252 + customRender: (column) => {
  253 + const { record } = column || {};
  254 + if (record?.type === 30) {
  255 + return '应收账单申请';
  256 + } else if (record?.type == 60) {
  257 + return `Invoice扣款单申请${record?.fieldInfos?.deductionUrlFieldVO?.deductAmount ? `(¥${record?.fieldInfos?.deductionUrlFieldVO?.deductAmount?.toFixed(2)})` : ''}`;
  258 + }
234 }, 259 },
235 }, 260 },
236 ]; 261 ];
@@ -251,12 +276,21 @@ @@ -251,12 +276,21 @@
251 ]); 276 ]);
252 } 277 }
253 278
  279 + const role = computed(() => {
  280 + return userStore.getUserInfo?.roleSmallVO?.code;
  281 + });
  282 +
  283 + // 根据角色动态设置 searchInfo
  284 + const searchInfo = computed(() => {
  285 + if (role.value === ROLE.BUSINESS || role.value === ROLE.TRACKER) {
  286 + return { type: 60 }; // 只显示 invoice 扣款单类型
  287 + }
  288 + return { type: 30 }; // 显示所有类型
  289 + });
  290 +
254 const [registerTable, { reload }] = useTable({ 291 const [registerTable, { reload }] = useTable({
255 api: props.isApproved ? getApprovedListApi : getWaitListApi, 292 api: props.isApproved ? getApprovedListApi : getWaitListApi,
256 - searchInfo: { type: 30 },  
257 - // scroll: {  
258 - // scrollToFirstRowOnChange: true,  
259 - // }, 293 + searchInfo: searchInfo.value,
260 columns, 294 columns,
261 useSearchForm: true, 295 useSearchForm: true,
262 formConfig: getFormConfig('invoiceNo'), 296 formConfig: getFormConfig('invoiceNo'),
@@ -265,7 +299,6 @@ @@ -265,7 +299,6 @@
265 width: 160, 299 width: 160,
266 title: 'Action', 300 title: 'Action',
267 dataIndex: 'action', 301 dataIndex: 'action',
268 - // slots: { customRender: 'action' },  
269 }, 302 },
270 }); 303 });
271 304
@@ -292,10 +325,32 @@ @@ -292,10 +325,32 @@
292 } 325 }
293 326
294 function handleProfitModal() {} 327 function handleProfitModal() {}
  328 + // 打开图片或 PDF
  329 + function openPic(url: string) {
  330 + const baseUrl = url.split('?')[0]; // 获取问号前的部分
  331 + if (isImageUrl(baseUrl)) {
  332 + window.open('', '', '').document.write(`<!DOCTYPE html>
  333 + <html>
  334 + <body style="display: flex; justify-content: center; align-items: center;">
  335 + <img src='${url}' width="300px" height="auto"/>
  336 + </body>
  337 + </html>`);
  338 + } else if (isPdfUrl(baseUrl)) {
  339 + view(url); // 新标签页打开 PDF
  340 + } else {
  341 + console.log('不支持的文件类型');
  342 + }
  343 + }
295 344
296 async function handleDetail(data) { 345 async function handleDetail(data) {
  346 + if(data.type == 60){
  347 + showInvoice.value = true;
  348 + mockData.value = data.fieldInfos.deductionUrlFieldVO;
  349 + }else if(data.type == 30){
  350 + showInvoice.value=false;
  351 + mockData.value=data.fieldInfos.invoiceFieldVO;
  352 + }
297 openModal(true, { data }); 353 openModal(true, { data });
298 - mockData.value = data.fieldInfos.invoiceFieldVO;  
299 id.value = data.id; 354 id.value = data.id;
300 financePerson.value = mockData.value?.financePerson; 355 financePerson.value = mockData.value?.financePerson;
301 trackerUser.value = mockData.value?.trackerUser; 356 trackerUser.value = mockData.value?.trackerUser;
@@ -309,6 +364,13 @@ @@ -309,6 +364,13 @@
309 otherAmount.value = mockData.value.otherAmount?.toFixed(2); 364 otherAmount.value = mockData.value.otherAmount?.toFixed(2);
310 invoiceNo.value = mockData.value.invoiceNo; 365 invoiceNo.value = mockData.value.invoiceNo;
311 payee.value = mockData.value.payee; 366 payee.value = mockData.value.payee;
  367 + const match = mockData.value?.deductUrl?.match(/aliyuncs\.com\/(.*?)\?/);
  368 + if (match && match[1]) {
  369 + // 对提取的部分进行解码
  370 + mockData.value.invoiceName = decodeURIComponent(match[1]);
  371 + } else {
  372 + mockData.value.invoiceName = mockData.value?.deductUrl;
  373 + }
312 } 374 }
313 375
314 async function handleTrue() { 376 async function handleTrue() {
@@ -329,10 +391,6 @@ @@ -329,10 +391,6 @@
329 } 391 }
330 } 392 }
331 393
332 - const role = computed(() => {  
333 - return userStore.getUserInfo?.roleSmallVO?.code;  
334 - });  
335 -  
336 // 定义MsgModalClose的事件,方便子组件调用 394 // 定义MsgModalClose的事件,方便子组件调用
337 const handleMsgModalClose = async (data) => { 395 const handleMsgModalClose = async (data) => {
338 if (data) { 396 if (data) {
@@ -418,6 +476,8 @@ @@ -418,6 +476,8 @@
418 msgVisible, 476 msgVisible,
419 handleMsgModalClose, 477 handleMsgModalClose,
420 handlePreview, 478 handlePreview,
  479 + showInvoice,
  480 + openPic,
421 mockData, 481 mockData,
422 schema, 482 schema,
423 totalPayAmount, 483 totalPayAmount,
src/views/project/approve/index.vue
@@ -16,9 +16,9 @@ @@ -16,9 +16,9 @@
16 v-if=" 16 v-if="
17 role == ROLE.FINANCE || 17 role == ROLE.FINANCE ||
18 role == ROLE.ADMIN 18 role == ROLE.ADMIN
19 - // ||  
20 - // role == ROLE.BUSINESS ||  
21 - // role == ROLE.TRACKER 19 + ||
  20 + role == ROLE.BUSINESS ||
  21 + role == ROLE.TRACKER
22 " 22 "
23 > 23 >
24 <ReceivePanel /> 24 <ReceivePanel />
@@ -60,9 +60,9 @@ @@ -60,9 +60,9 @@
60 v-if=" 60 v-if="
61 role == ROLE.FINANCE || 61 role == ROLE.FINANCE ||
62 role == ROLE.ADMIN 62 role == ROLE.ADMIN
63 - // ||  
64 - // role == ROLE.BUSINESS ||  
65 - // role == ROLE.TRACKER 63 + ||
  64 + role == ROLE.BUSINESS ||
  65 + role == ROLE.TRACKER
66 " 66 "
67 > 67 >
68 <ReceivePanel isApproved /> 68 <ReceivePanel isApproved />
src/views/project/finance/financeList/finance.data.tsx
@@ -38,8 +38,9 @@ export const searchFormSchema: FormSchema[] = [ @@ -38,8 +38,9 @@ export const searchFormSchema: FormSchema[] = [
38 componentProps: { 38 componentProps: {
39 options: [ 39 options: [
40 { label: '未创建', value: -1 }, 40 { label: '未创建', value: -1 },
41 - { label: '未收款', value: 0 },  
42 - { label: '已收款', value: 10 }, 41 + { label: '未收款(已创建未提交审核)', value: 0 },
  42 + { label: '待收款(审核通过,未手动确认)', value: 10 },
  43 + { label: '已收款(手动确认)', value: 40 },
43 ], 44 ],
44 }, 45 },
45 }, 46 },
@@ -51,35 +52,13 @@ export const searchFormSchema: FormSchema[] = [ @@ -51,35 +52,13 @@ export const searchFormSchema: FormSchema[] = [
51 componentProps: { 52 componentProps: {
52 options: [ 53 options: [
53 { label: '未创建', value: -1 }, 54 { label: '未创建', value: -1 },
54 - { label: '未付款', value: 0 },  
55 - { label: '已付款', value: 10 },  
56 - ],  
57 - },  
58 - },  
59 - {  
60 - field: 'invoiceFinishStatus',  
61 - label: 'invoice最终状态',  
62 - component: 'Select',  
63 - colProps: { span: 8 },  
64 - componentProps: {  
65 - options: [  
66 - { label: '未完成', value: 0 },  
67 - { label: '已完成', value: 10 },  
68 - ],  
69 - },  
70 - },  
71 - {  
72 - field: 'checkFinishStatus',  
73 - label: 'checkNo最终状态',  
74 - component: 'Select',  
75 - colProps: { span: 8 },  
76 - componentProps: {  
77 - options: [  
78 - { label: '未完成', value: 0 },  
79 - { label: '已完成', value: 10 }, 55 + { label: '未付款(已创建未提交审核)', value: 0 },
  56 + { label: '待付款(审核通过,未手动确认)', value: 10 },
  57 + { label: '已付款(手动确认)', value: 40 },
80 ], 58 ],
81 }, 59 },
82 }, 60 },
  61 +
83 { 62 {
84 field: 'customerCode', 63 field: 'customerCode',
85 label: '客户编码', 64 label: '客户编码',
@@ -324,7 +303,7 @@ export const columns: BasicColumn[] = [ @@ -324,7 +303,7 @@ export const columns: BasicColumn[] = [
324 width: 80, 303 width: 80,
325 customRender: (column) => { 304 customRender: (column) => {
326 const deductUrl = column.record.invoiceDeductUrl; 305 const deductUrl = column.record.invoiceDeductUrl;
327 - if (deductUrl == undefined) { 306 + if (deductUrl == undefined || !deductUrl) {
328 return; 307 return;
329 } 308 }
330 // return <FilePptOutlined style="font-size:25px" onClick={() => window.open(deductUrl[0])} />; 309 // return <FilePptOutlined style="font-size:25px" onClick={() => window.open(deductUrl[0])} />;
@@ -332,6 +311,20 @@ export const columns: BasicColumn[] = [ @@ -332,6 +311,20 @@ export const columns: BasicColumn[] = [
332 }, 311 },
333 }, 312 },
334 { 313 {
  314 + title: '扣款单状态',
  315 + dataIndex: 'invoiceDeductUrlStatus',
  316 + width: 120,
  317 + customRender: (column) => {
  318 + if(column.record.invoiceDeductUrlStatus == 0){
  319 + return '待审核'
  320 + }else if(column.record.invoiceDeductUrlStatus == 10){
  321 + return '已通过'
  322 + }else if(column.record.invoiceDeductUrlStatus == 20){
  323 + return '已驳回'
  324 + }
  325 + },
  326 + },
  327 + {
335 title: '实际应收金额$', 328 title: '实际应收金额$',
336 dataIndex: 'invoiceActualReceivableAmount', 329 dataIndex: 'invoiceActualReceivableAmount',
337 width: 120, 330 width: 120,
@@ -381,32 +374,23 @@ export const columns: BasicColumn[] = [ @@ -381,32 +374,23 @@ export const columns: BasicColumn[] = [
381 dataIndex: 'invoiceStatus', 374 dataIndex: 'invoiceStatus',
382 width: 120, 375 width: 120,
383 customRender: (column) => { 376 customRender: (column) => {
  377 + let statusText='';
  378 + let statusColor='blank';
384 if (column.record.invoiceStatus == null || column.record.invoiceStatus == undefined) { 379 if (column.record.invoiceStatus == null || column.record.invoiceStatus == undefined) {
385 - return '未创建'; 380 + statusText='未创建';
386 } else if (column.record.invoiceStatus == 0) { 381 } else if (column.record.invoiceStatus == 0) {
387 - return '未收款'; 382 + statusText= '未收款';
388 } else if (column.record.invoiceStatus == 10) { 383 } else if (column.record.invoiceStatus == 10) {
389 - return '已收款'; 384 + statusText= '待收款';
  385 + statusColor='red';
  386 + }else if (column.record.invoiceStatus == 40) {
  387 + statusText= '已收款';
  388 + statusColor='green';
390 } 389 }
  390 + return <span style={{color:statusColor}}>{statusText}</span>
391 }, 391 },
392 }, 392 },
393 - {  
394 - title: '最终状态',  
395 - dataIndex: 'invoiceFinishStatus',  
396 - width: 120,  
397 - customRender: (column) => {  
398 - let statusText = '';  
399 - let statusColor = '';  
400 - if (column.record.invoiceFinishStatus == null || column.record.invoiceFinishStatus == undefined || column.record.invoiceFinishStatus == 0) {  
401 - statusText = '未完成';  
402 - statusColor = 'red'; // 设置红色  
403 - } else if (column.record.checkFinishStatus == 10) {  
404 - statusText = '已完成';  
405 - statusColor = 'green'; // 设置绿色  
406 - }  
407 - return <span style={{ color: statusColor }}>{statusText}</span>;  
408 - },  
409 - }, 393 +
410 { 394 {
411 title: 'Action', 395 title: 'Action',
412 key: 'action', // 对应 #bodyCell 中的 column.key 396 key: 'action', // 对应 #bodyCell 中的 column.key
@@ -460,13 +444,27 @@ export const columns: BasicColumn[] = [ @@ -460,13 +444,27 @@ export const columns: BasicColumn[] = [
460 width: 120, 444 width: 120,
461 customRender: (column) => { 445 customRender: (column) => {
462 const deductUrl = column.record.checkDeductUrl; 446 const deductUrl = column.record.checkDeductUrl;
463 - if (deductUrl == undefined) { 447 + if (deductUrl == undefined || !deductUrl) {
464 return; 448 return;
465 } 449 }
466 return <FilePptOutlined style="font-size:25px" />; 450 return <FilePptOutlined style="font-size:25px" />;
467 }, 451 },
468 }, 452 },
469 { 453 {
  454 + title: '扣款单状态',
  455 + dataIndex: 'checkDeductUrlStatus',
  456 + width: 120,
  457 + customRender: (column) => {
  458 + if(column.record.checkDeductUrlStatus == 0){
  459 + return '待审核'
  460 + }else if(column.record.checkDeductUrlStatus == 10){
  461 + return '已通过'
  462 + }else if(column.record.checkDeductUrlStatus == 20){
  463 + return '已驳回'
  464 + }
  465 + },
  466 + },
  467 + {
470 title: '生产科实际应付金额¥', 468 title: '生产科实际应付金额¥',
471 dataIndex: 'checkActualPayedAmount', 469 dataIndex: 'checkActualPayedAmount',
472 width: 180, 470 width: 180,
@@ -534,32 +532,23 @@ export const columns: BasicColumn[] = [ @@ -534,32 +532,23 @@ export const columns: BasicColumn[] = [
534 dataIndex: 'checkPayStatus', 532 dataIndex: 'checkPayStatus',
535 width: 120, 533 width: 120,
536 customRender: (column) => { 534 customRender: (column) => {
  535 + let statusText='';
  536 + let statusColor='blank';
537 if (column.record.checkPayStatus == null || column.record.checkPayStatus == undefined) { 537 if (column.record.checkPayStatus == null || column.record.checkPayStatus == undefined) {
538 - return '未创建'; 538 + statusText='未创建';
539 } else if (column.record.checkPayStatus == 0) { 539 } else if (column.record.checkPayStatus == 0) {
540 - return '未付款'; 540 + statusText='未付款';
541 } else if (column.record.checkPayStatus == 10) { 541 } else if (column.record.checkPayStatus == 10) {
542 - return '已付款'; 542 + statusText='待付款';
  543 + statusColor='red';
  544 + }else if (column.record.checkPayStatus == 40) {
  545 + statusText='已付款';
  546 + statusColor='green';
543 } 547 }
  548 + return <span style={{color:statusColor}}>{statusText}</span>
544 }, 549 },
545 }, 550 },
546 - {  
547 - title: '最终状态',  
548 - dataIndex: 'checkFinishStatus',  
549 - width: 120,  
550 - customRender: (column) => {  
551 - let statusText = '';  
552 - let statusColor = '';  
553 - if (column.record.checkFinishStatus == null || column.record.checkFinishStatus == undefined || column.record.checkFinishStatus == 0) {  
554 - statusText = '未完成';  
555 - statusColor = 'red'; // 设置红色  
556 - } else if (column.record.checkFinishStatus == 10) {  
557 - statusText = '已完成';  
558 - statusColor = 'green'; // 设置绿色  
559 - }  
560 - return <span style={{ color: statusColor }}>{statusText}</span>;  
561 - },  
562 - }, 551 +
563 { 552 {
564 title: 'Action', 553 title: 'Action',
565 key: 'action2', // 对应 #bodyCell 中的 column.key 554 key: 'action2', // 对应 #bodyCell 中的 column.key
src/views/project/finance/financeList/index.vue
@@ -171,8 +171,9 @@ @@ -171,8 +171,9 @@
171 ...(role == ROLE.ADMIN 171 ...(role == ROLE.ADMIN
172 ? [ 172 ? [
173 { 173 {
174 - label: '设置invoice最终完成', 174 + label: '设置为已收款',
175 onClick: handleInvoiceSetFinishStatus.bind(null, record), 175 onClick: handleInvoiceSetFinishStatus.bind(null, record),
  176 + //需要设置按钮为红色。
176 }, 177 },
177 ] 178 ]
178 : []), 179 : []),
@@ -261,7 +262,7 @@ @@ -261,7 +262,7 @@
261 ...(role == ROLE.ADMIN 262 ...(role == ROLE.ADMIN
262 ? [ 263 ? [
263 { 264 {
264 - label: '设置对账单最终完成', 265 + label: '设置为已付款',
265 onClick: handleCheckSetFinishStatus.bind(null, record), 266 onClick: handleCheckSetFinishStatus.bind(null, record),
266 }, 267 },
267 ] 268 ]
src/views/project/order/Statement.vue 0 → 100644
  1 +<template>
  2 +
  3 + <BasicModal
  4 + v-bind="$attrs"
  5 + destroyOnClose
  6 + @register="register"
  7 + title="LOACL对账单"
  8 + width="1100px"
  9 + @visible-change="handleShow"
  10 + :footer="null"
  11 + :bodyStyle="{ height: '650px', overflow: 'hidden' }"
  12 + >
  13 + <div class="container">
  14 + <div v-if="isShow1 == true" style="margin-top: 30px">
  15 + <RadioGroup v-model:value="selectedCompany" :options="companyOptions" style="display: flex; gap: 200px;"/>
  16 + </div>
  17 + <!-- 代垫费用区域 -->
  18 + <div class="table-container" style="display: flex; flex-wrap: wrap; justify-content: space-between; margin-top: 30px" >
  19 + <div class="table-box" style="flex: 0.5; margin-right: -20px;">
  20 + <h3>代垫费用</h3>
  21 + <div class="input-row" style="display: flex; align-items: center;">
  22 + <a-button type="primary" style="margin-left: 10px;" @click="addAdvanceCost">添加费用</a-button>
  23 + </div>
  24 + <div v-for="(item, index) in advanceCosts" :key="'advance-' + index" class="input-row" style="margin-top: 10px; display: flex;">
  25 + <a-input v-model:value="item.name" placeholder="请输入费用名称" style="width: 200px;" />
  26 + <a-input-number v-model:value="item.value" placeholder="请输入费用金额" style="width: 150px !important; margin-left: 10px;" />
  27 + <a-button type="danger" style="margin-left: 10px;" @click="removeAdvanceCost(index)">删除</a-button>
  28 + </div>
  29 + </div>
  30 + <div class="table-box" style="flex: 1;">
  31 + <h3>费用支出</h3>
  32 + <div class="input-row" style="display: flex; align-items: center;">
  33 + <a-button type="primary" style="margin-left: 10px;" @click="addExpenseCost">添加费用</a-button>
  34 + </div>
  35 + <div v-for="(item, index) in expenseCosts" :key="'expense-' + index" class="input-row" style="margin-top: 10px; display: flex;">
  36 + <a-input v-model:value="item.name" placeholder="请输入费用名称" style="width: 200px;" />
  37 + <a-input-number v-model:value="item.value" placeholder="请输入费用金额" style="width: 150px !important; margin-left: 10px;" />
  38 + <a-button type="danger" style="margin-left: 10px;" @click="removeExpenseCost(index)">删除</a-button>
  39 + </div>
  40 + </div>
  41 + </div>
  42 +
  43 + <div class="bottom">
  44 + <a-button type="primary" @click="handleAccountStatement" className="ml-4" v-if="isShow1 == true"
  45 + >生成</a-button
  46 + >
  47 + <a-button type="primary" @click="handleCancel" className="ml-4" style="margin-left: 6px"
  48 + >取消</a-button
  49 + >
  50 + </div>
  51 + </div>
  52 + </BasicModal>
  53 + </template>
  54 + <script lang="ts">
  55 + import { defineComponent, ref, computed } from 'vue';
  56 + import { BasicModal, useModalInner } from '/@/components/Modal';
  57 + import { RadioGroup } from 'ant-design-vue';
  58 + import { useMessage } from '@/hooks/web/useMessage';
  59 + import axios from 'axios';
  60 +
  61 + export default defineComponent({
  62 + props: {
  63 + role: {
  64 + type: String,
  65 + },
  66 + customerCodes: {
  67 + type: Array<string | number>,
  68 + },
  69 + },
  70 + components: { BasicModal, RadioGroup },
  71 + setup(props) {
  72 + const loading = ref(true);
  73 + const choose = ref();
  74 + const checkedKeys=ref();
  75 + const isShow1 = ref(true);
  76 + const advanceCostValue = ref(0);
  77 + const expenseCostValue = ref(0);
  78 + const advanceCosts = ref<{ name: string; value: number }[]>([]);
  79 + const expenseCosts = ref<{ name: string; value: number }[]>([]);
  80 + const [register, { setModalProps, closeModal }] = useModalInner(async (data)=>{
  81 + checkedKeys.value=data.checkedKeys;
  82 + });
  83 + const { createMessage } = useMessage();
  84 + const { error } = createMessage;
  85 + const selectedCompany = ref();
  86 + const companyOptions = ref([
  87 + { label: '翱特逸格饰品有限公司', value: '翱特逸格饰品有限公司' },
  88 + { label: '吉庆天成饰品有限公司', value: '吉庆天成饰品有限公司' }
  89 + ]);
  90 +
  91 + function addAdvanceCost() {
  92 + advanceCosts.value.push({ name: '', value: advanceCostValue.value });
  93 + }
  94 +
  95 + function addExpenseCost() {
  96 + expenseCosts.value.push({ name: '', value: expenseCostValue.value });
  97 + }
  98 +
  99 + function removeAdvanceCost(index: number) {
  100 + advanceCosts.value.splice(index, 1);
  101 + }
  102 +
  103 + function removeExpenseCost(index: number) {
  104 + expenseCosts.value.splice(index, 1);
  105 + }
  106 +
  107 + function handleCancel() {
  108 + loading.value = true;
  109 + choose.value = '';
  110 + setModalProps({ loading: false, confirmLoading: false });
  111 + isShow1.value = true;
  112 + advanceCosts.value = [];
  113 + expenseCosts.value = [];
  114 + closeModal();
  115 + }
  116 +
  117 + function handleShow(visible: boolean) {
  118 + if (visible) {
  119 + loading.value = true;
  120 + choose.value = '';
  121 + setModalProps({ loading: false, confirmLoading: false });
  122 + isShow1.value = true;
  123 + } else {
  124 + advanceCosts.value = [];
  125 + expenseCosts.value = [];
  126 + }
  127 + }
  128 + async function handleAccountStatement() {
  129 + if (!selectedCompany.value) {
  130 + error('请选择公司'); // 提示用户选择公司
  131 + return; // 结束函数执行
  132 + }
  133 +
  134 + axios
  135 + .post(
  136 + '/basic-api/order/erp/order/accountStatement',
  137 + {
  138 + ids: checkedKeys.value,
  139 + disbursement: advanceCosts.value.map(item => ({ [item.name]: item.value })),
  140 + deduction: expenseCosts.value.map(item => ({ [item.name]: item.value })),
  141 + titleCompany: selectedCompany.value
  142 + },
  143 + {
  144 + responseType: 'blob', // 设置响应类型为 'blob'
  145 + },
  146 + )
  147 + .then((response) => {
  148 + // 创建一个 Blob 对象来保存二进制数据
  149 + const blob = new Blob([response.data], { type: 'application/octet-stream' });
  150 + const getFormattedDate = (): string => {
  151 + const date = new Date();
  152 +
  153 + const year = date.getFullYear();
  154 + const month = String(date.getMonth() + 1).padStart(2, '0');
  155 + const day = String(date.getDate()).padStart(2, '0');
  156 +
  157 + const hours = String(date.getHours()).padStart(2, '0');
  158 + const minutes = String(date.getMinutes()).padStart(2, '0');
  159 + const seconds = String(date.getSeconds()).padStart(2, '0');
  160 +
  161 + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  162 + };
  163 + const date = getFormattedDate();
  164 + // 创建一个链接元素用于下载
  165 + const link = document.createElement('a');
  166 + link.href = window.URL.createObjectURL(blob);
  167 + link.download = `${selectedCompany.value}对账单${date}.xlsx`; // 你可以为文件命名
  168 + document.body.appendChild(link);
  169 + link.click(); // 自动点击链接,触发下载
  170 + document.body.removeChild(link); // 下载完成后移除链接
  171 + })
  172 + .catch((error) => {
  173 + console.error(error);
  174 + });
  175 + closeModal();
  176 + }
  177 + return {
  178 + register,
  179 + loading,
  180 + choose,
  181 + handleShow,
  182 + handleCancel,
  183 + advanceCostValue,
  184 + expenseCostValue,
  185 + advanceCosts,
  186 + expenseCosts,
  187 + addAdvanceCost,
  188 + addExpenseCost,
  189 + isShow1,
  190 + selectedCompany,
  191 + companyOptions,
  192 + removeAdvanceCost,
  193 + removeExpenseCost,
  194 + handleAccountStatement,
  195 + };
  196 + },
  197 + });
  198 + </script>
  199 + <style scoped>
  200 + /* .container {
  201 + position: relative;
  202 + }
  203 + .bottom {
  204 + position: absolute;
  205 + bottom: 0;
  206 + } */
  207 + .container {
  208 + display: flex;
  209 + flex-direction: column;
  210 + /* min-height: 20vh; */
  211 + }
  212 +
  213 + .bottom {
  214 + margin-top: 40px;
  215 + /* margin-right: auto; */
  216 + margin-left: 150px;
  217 + }
  218 +
  219 + .showPdf {
  220 + display: flex;
  221 + position: relative;
  222 + height: 30px;
  223 + width: 470px;
  224 + border: 2px solid;
  225 + border-radius: 5px;
  226 + border-color: #d8d8d8;
  227 + }
  228 +
  229 + .FilePptOutlined {
  230 + margin-top: 3px;
  231 + margin-left: 30px;
  232 + }
  233 +
  234 + .EyeOutlined {
  235 + margin-top: 3px;
  236 + margin-left: 290px;
  237 + }
  238 + </style>
src/views/project/order/index.vue
@@ -118,6 +118,18 @@ @@ -118,6 +118,18 @@
118 <!-- <a-space wrap :size="[8, 16]" :style="{ marginBottom: '2px', marginLeft: '10px' }"> --> 118 <!-- <a-space wrap :size="[8, 16]" :style="{ marginBottom: '2px', marginLeft: '10px' }"> -->
119 <a-space wrap :style="{ marginBottom: '2px', marginTop: '2px' }"> 119 <a-space wrap :style="{ marginBottom: '2px', marginTop: '2px' }">
120 <a-button 120 <a-button
  121 + :style="{backgroundColor: '#ff69b4', borderColor: '#ff69b4', color: 'white',borderRadius: '5px 5px 5px 5px' }"
  122 + shape="default"
  123 + type="primary"
  124 + @click=" handleStatementModal"
  125 + v-if="
  126 + role === ROLE.ADMIN ||
  127 + role === ROLE.BUSINESS ||
  128 + role === ROLE.TRACKER
  129 + "
  130 + >LOACL对账单</a-button
  131 + >
  132 + <a-button
121 :style="{ borderRadius: '5px 5px 5px 5px' }" 133 :style="{ borderRadius: '5px 5px 5px 5px' }"
122 type="primary" 134 type="primary"
123 @click="handleProductInvoiceModal" 135 @click="handleProductInvoiceModal"
@@ -231,6 +243,11 @@ @@ -231,6 +243,11 @@
231 :role="role" 243 :role="role"
232 :customerCodes="selectedCustomCodes" 244 :customerCodes="selectedCustomCodes"
233 /> 245 />
  246 + <Statement
  247 + @register="statementModalRegister"
  248 + :role="role"
  249 + :customerCodes="selectedCustomCodes"
  250 + />
234 <InvoiceCreate @register="invoiceCreateModalRegister" @success="handleFormSuccess" /> 251 <InvoiceCreate @register="invoiceCreateModalRegister" @success="handleFormSuccess" />
235 <ServiceProfit @register="serviceProfitModalRegister" /> 252 <ServiceProfit @register="serviceProfitModalRegister" />
236 <ProductProfit @register="productProfitModalRegister" /> 253 <ProductProfit @register="productProfitModalRegister" />
@@ -250,11 +267,12 @@ @@ -250,11 +267,12 @@
250 import { BasicTable, useTable, TableAction } from '/@/components/Table'; 267 import { BasicTable, useTable, TableAction } from '/@/components/Table';
251 import { FormOutlined } from '@ant-design/icons-vue'; 268 import { FormOutlined } from '@ant-design/icons-vue';
252 import HeaderCell from '/@/components/Table/src/components/HeaderCell.vue'; 269 import HeaderCell from '/@/components/Table/src/components/HeaderCell.vue';
253 - import { Alert } from 'ant-design-vue'; 270 + import { Alert } from 'ant-design-vue'
254 271
255 import { useDrawer } from '/@/components/Drawer'; 272 import { useDrawer } from '/@/components/Drawer';
256 import ProfitAnalysis from './ProfitAnalysis.vue'; 273 import ProfitAnalysis from './ProfitAnalysis.vue';
257 import ProductText from './ProductText.vue'; 274 import ProductText from './ProductText.vue';
  275 + import Statement from './Statement.vue';
258 import RateModal from './RateModal.vue'; 276 import RateModal from './RateModal.vue';
259 import ExportModal from './ExportModal.vue'; 277 import ExportModal from './ExportModal.vue';
260 import PassCalculate from './PassCalculate.vue'; 278 import PassCalculate from './PassCalculate.vue';
@@ -291,6 +309,7 @@ @@ -291,6 +309,7 @@
291 FormDetail, 309 FormDetail,
292 ProfitAnalysis, 310 ProfitAnalysis,
293 ProductText, 311 ProductText,
  312 + Statement,
294 PassCalculate, 313 PassCalculate,
295 FormOutlined, 314 FormOutlined,
296 CheckDetail, 315 CheckDetail,
@@ -316,6 +335,7 @@ @@ -316,6 +335,7 @@
316 const [exportModalRegister, { openModal: openExportModal }] = useModal(); 335 const [exportModalRegister, { openModal: openExportModal }] = useModal();
317 const [productModalRegister, { openModal: openProductModal }] = useModal(); 336 const [productModalRegister, { openModal: openProductModal }] = useModal();
318 const [passModalRegister, { openModal: openPassModal }] = useModal(); 337 const [passModalRegister, { openModal: openPassModal }] = useModal();
  338 + const [statementModalRegister, { openModal: openStatementModal }] = useModal();
319 339
320 const tooltipVisible = ref(false); 340 const tooltipVisible = ref(false);
321 const [formDetailRegister, { openDrawer: openFormDetailDrawer }] = useDrawer(); 341 const [formDetailRegister, { openDrawer: openFormDetailDrawer }] = useDrawer();
@@ -761,6 +781,20 @@ @@ -761,6 +781,20 @@
761 data: values, 781 data: values,
762 }); 782 });
763 } 783 }
  784 + //对账单
  785 + function handleStatementModal(record) {
  786 + const form = getForm();
  787 + const values = form.getFieldsValue();
  788 + if (checkedKeys.value.length == 0) {
  789 + error('请选择订单');
  790 + return;
  791 + }
  792 + openStatementModal(true, {
  793 + checkedKeys: checkedKeys.value,
  794 + customers: selectedCustomCodes.value,
  795 + data: values,
  796 + });
  797 + }
764 798
765 function handleRateModal() { 799 function handleRateModal() {
766 const form = getForm(); 800 const form = getForm();
@@ -871,6 +905,7 @@ @@ -871,6 +905,7 @@
871 rateModalRegister, 905 rateModalRegister,
872 exportModalRegister, 906 exportModalRegister,
873 productModalRegister, 907 productModalRegister,
  908 + statementModalRegister,
874 passModalRegister, 909 passModalRegister,
875 historyDetailRegister, 910 historyDetailRegister,
876 trackHistoryRegister, 911 trackHistoryRegister,
@@ -878,6 +913,7 @@ @@ -878,6 +913,7 @@
878 handleProfitModal, 913 handleProfitModal,
879 handleInvoiceCreateModal, 914 handleInvoiceCreateModal,
880 handleProductInvoiceModal, 915 handleProductInvoiceModal,
  916 + handleStatementModal,
881 registerTable, 917 registerTable,
882 getFormValues, 918 getFormValues,
883 checkedKeys, 919 checkedKeys,
@@ -905,6 +941,7 @@ @@ -905,6 +941,7 @@
905 handleRateModal, 941 handleRateModal,
906 openExportModal, 942 openExportModal,
907 openProductModal, 943 openProductModal,
  944 + openStatementModal,
908 openPassModal, 945 openPassModal,
909 handleDelete, 946 handleDelete,
910 handleServiceProfitModal, 947 handleServiceProfitModal,