Enhance lease contract create flow with customer picker and service fee input.
Add risk-rated customer modal, editable service fee field, and remove list breadcrumb. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -54,6 +54,8 @@ export var MOCK_LEASE_CUSTOMERS = [
|
|||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
name: '嘉兴某某物流有限公司',
|
name: '嘉兴某某物流有限公司',
|
||||||
|
lastRatingTime: '2026-06-08',
|
||||||
|
riskLevel: 'A',
|
||||||
creditCode: '91330400MA2XXXXX1',
|
creditCode: '91330400MA2XXXXX1',
|
||||||
address: '浙江省嘉兴市南湖区科技大道1号',
|
address: '浙江省嘉兴市南湖区科技大道1号',
|
||||||
contact: '张三',
|
contact: '张三',
|
||||||
@@ -75,6 +77,8 @@ export var MOCK_LEASE_CUSTOMERS = [
|
|||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
name: '上海某某运输公司',
|
name: '上海某某运输公司',
|
||||||
|
lastRatingTime: '2025-12-20',
|
||||||
|
riskLevel: 'C',
|
||||||
creditCode: '91310000MA2XXXXX2',
|
creditCode: '91310000MA2XXXXX2',
|
||||||
address: '上海市浦东新区张江高科技园区',
|
address: '上海市浦东新区张江高科技园区',
|
||||||
contact: '李四',
|
contact: '李四',
|
||||||
@@ -96,6 +100,8 @@ export var MOCK_LEASE_CUSTOMERS = [
|
|||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
name: '杭州某某租赁有限公司',
|
name: '杭州某某租赁有限公司',
|
||||||
|
lastRatingTime: '2026-04-15',
|
||||||
|
riskLevel: 'B',
|
||||||
creditCode: '91330100MA2XXXXX3',
|
creditCode: '91330100MA2XXXXX3',
|
||||||
address: '浙江省杭州市余杭区未来科技城',
|
address: '浙江省杭州市余杭区未来科技城',
|
||||||
contact: '王五',
|
contact: '王五',
|
||||||
@@ -116,6 +122,33 @@ export var MOCK_LEASE_CUSTOMERS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** 租赁合同乙方客户风控等级(C/D 不可签约) */
|
||||||
|
export var LEASE_CUSTOMER_RISK_META = {
|
||||||
|
A: { label: 'A(低风险)', selectable: true },
|
||||||
|
B: { label: 'B(中风险)', selectable: true },
|
||||||
|
C: { label: 'C(高风险)', selectable: false },
|
||||||
|
D: { label: 'D(禁入)', selectable: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
export var LEASE_CUSTOMER_RISK_FILTER_OPTIONS = [
|
||||||
|
{ value: 'A', label: 'A(低风险)' },
|
||||||
|
{ value: 'B', label: 'B(中风险)' },
|
||||||
|
{ value: 'C', label: 'C(高风险)' },
|
||||||
|
{ value: 'D', label: 'D(禁入)' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function getLeaseCustomerRiskLevelLabel(level) {
|
||||||
|
if (!level) return '—';
|
||||||
|
var meta = LEASE_CUSTOMER_RISK_META[level];
|
||||||
|
return meta ? meta.label : level;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLeaseCustomerSelectable(customer) {
|
||||||
|
if (!customer) return false;
|
||||||
|
var meta = LEASE_CUSTOMER_RISK_META[customer.riskLevel];
|
||||||
|
return meta ? meta.selectable : false;
|
||||||
|
}
|
||||||
|
|
||||||
export var CUSTOMER_CREDENTIAL_ITEMS = [
|
export var CUSTOMER_CREDENTIAL_ITEMS = [
|
||||||
{ key: 'businessLicense', label: '客户营业执照' },
|
{ key: 'businessLicense', label: '客户营业执照' },
|
||||||
{ key: 'legalIdFront', label: '法人身份证(正面)' },
|
{ key: 'legalIdFront', label: '法人身份证(正面)' },
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Select, InputNumber, Input, Image, Cascader, Button, DatePicker, Popover, Checkbox, Modal } from 'antd';
|
import { Select, InputNumber, Input, Image, Cascader, Button, DatePicker, Popover, Checkbox, Modal, message } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { ChevronDown, Plus, Trash2, Copy } from 'lucide-react';
|
import { ChevronDown, Plus, Trash2, Copy } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
@@ -19,6 +19,9 @@ import {
|
|||||||
PAYMENT_PERIOD_OPTIONS,
|
PAYMENT_PERIOD_OPTIONS,
|
||||||
HYDROGEN_PAYMENT_METHOD_OPTIONS,
|
HYDROGEN_PAYMENT_METHOD_OPTIONS,
|
||||||
CONTRACT_CODE_PREFIX,
|
CONTRACT_CODE_PREFIX,
|
||||||
|
LEASE_CUSTOMER_RISK_FILTER_OPTIONS,
|
||||||
|
getLeaseCustomerRiskLevelLabel,
|
||||||
|
isLeaseCustomerSelectable,
|
||||||
} from '../contract-template-management/contract-template-vars.js';
|
} from '../contract-template-management/contract-template-vars.js';
|
||||||
import {
|
import {
|
||||||
buildBrandModelCascaderOptions,
|
buildBrandModelCascaderOptions,
|
||||||
@@ -32,12 +35,181 @@ import {
|
|||||||
countLeaseOrderRows,
|
countLeaseOrderRows,
|
||||||
normalizeExtraServices,
|
normalizeExtraServices,
|
||||||
getReadyVehiclePlateOptions,
|
getReadyVehiclePlateOptions,
|
||||||
sumExtraServiceFees,
|
|
||||||
calcLeaseOrderKpis,
|
calcLeaseOrderKpis,
|
||||||
formatKpiAmount,
|
formatKpiAmount,
|
||||||
getExtraServiceNames,
|
getExtraServiceNames,
|
||||||
} from './lease-order-vars.js';
|
} from './lease-order-vars.js';
|
||||||
|
|
||||||
|
function LeaseCustomerPickerField(props) {
|
||||||
|
var value = props.value;
|
||||||
|
var onChange = props.onChange;
|
||||||
|
var selectedCustomer = getLeaseCustomerById(value);
|
||||||
|
var modalOpenState = React.useState(false);
|
||||||
|
var pendingIdState = React.useState('');
|
||||||
|
var keywordState = React.useState('');
|
||||||
|
var riskFilterState = React.useState(undefined);
|
||||||
|
var modalOpen = modalOpenState[0];
|
||||||
|
var setModalOpen = modalOpenState[1];
|
||||||
|
var pendingId = pendingIdState[0];
|
||||||
|
var setPendingId = pendingIdState[1];
|
||||||
|
var keyword = keywordState[0];
|
||||||
|
var setKeyword = keywordState[1];
|
||||||
|
var riskFilter = riskFilterState[0];
|
||||||
|
var setRiskFilter = riskFilterState[1];
|
||||||
|
|
||||||
|
function openModal() {
|
||||||
|
setPendingId(value || '');
|
||||||
|
setKeyword('');
|
||||||
|
setRiskFilter(undefined);
|
||||||
|
setModalOpen(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel() {
|
||||||
|
setModalOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleConfirm() {
|
||||||
|
var picked = getLeaseCustomerById(pendingId);
|
||||||
|
if (!picked || !isLeaseCustomerSelectable(picked)) {
|
||||||
|
message.warning('请选择风控等级符合要求的客户');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onChange(picked.id);
|
||||||
|
setModalOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRowClick(customer) {
|
||||||
|
if (!isLeaseCustomerSelectable(customer)) {
|
||||||
|
message.warning('该客户风控等级不符合签约要求,无法选择');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPendingId(customer.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClear(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
onChange('');
|
||||||
|
}
|
||||||
|
|
||||||
|
var filteredCustomers = MOCK_LEASE_CUSTOMERS.filter(function (customer) {
|
||||||
|
if (riskFilter && customer.riskLevel !== riskFilter) return false;
|
||||||
|
if (keyword && customer.name.indexOf(keyword.trim()) < 0) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
var pendingCustomer = getLeaseCustomerById(pendingId);
|
||||||
|
var confirmDisabled = !pendingCustomer || !isLeaseCustomerSelectable(pendingCustomer);
|
||||||
|
|
||||||
|
return React.createElement('div', { className: 'lc-customer-picker' },
|
||||||
|
React.createElement('div', {
|
||||||
|
className: 'lc-customer-picker__trigger vm-focus-border',
|
||||||
|
role: 'button',
|
||||||
|
tabIndex: 0,
|
||||||
|
onClick: openModal,
|
||||||
|
onKeyDown: function (event) {
|
||||||
|
if (event.key === 'Enter' || event.key === ' ') {
|
||||||
|
event.preventDefault();
|
||||||
|
openModal();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'aria-label': '乙方客户',
|
||||||
|
},
|
||||||
|
React.createElement('span', {
|
||||||
|
className: selectedCustomer ? 'lc-customer-picker__value' : 'lc-customer-picker__placeholder',
|
||||||
|
}, selectedCustomer ? selectedCustomer.name : '请选择客户'),
|
||||||
|
React.createElement('span', { className: 'lc-customer-picker__actions' },
|
||||||
|
value ? React.createElement('button', {
|
||||||
|
type: 'button',
|
||||||
|
className: 'lc-customer-picker__clear',
|
||||||
|
onClick: handleClear,
|
||||||
|
'aria-label': '清除乙方客户',
|
||||||
|
}, '×') : null,
|
||||||
|
React.createElement(ChevronDown, { size: 16, 'aria-hidden': true }),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
React.createElement(Modal, {
|
||||||
|
title: '选择乙方客户',
|
||||||
|
open: modalOpen,
|
||||||
|
onCancel: handleCancel,
|
||||||
|
onOk: handleConfirm,
|
||||||
|
okText: '确认',
|
||||||
|
cancelText: '取消',
|
||||||
|
okButtonProps: { disabled: confirmDisabled },
|
||||||
|
mask: true,
|
||||||
|
centered: true,
|
||||||
|
width: 720,
|
||||||
|
rootClassName: 'lc-customer-picker-modal',
|
||||||
|
destroyOnClose: true,
|
||||||
|
},
|
||||||
|
React.createElement('div', { className: 'lc-customer-picker-modal__filters' },
|
||||||
|
React.createElement(Input, {
|
||||||
|
className: 'lc-customer-picker-modal__search vm-focus-border',
|
||||||
|
placeholder: '搜索客户名称',
|
||||||
|
value: keyword,
|
||||||
|
allowClear: true,
|
||||||
|
onChange: function (e) { setKeyword(e.target.value); },
|
||||||
|
'aria-label': '搜索客户名称',
|
||||||
|
}),
|
||||||
|
React.createElement(Select, {
|
||||||
|
className: 'lc-customer-picker-modal__risk-filter vm-focus-border',
|
||||||
|
placeholder: '风控等级',
|
||||||
|
value: riskFilter,
|
||||||
|
allowClear: true,
|
||||||
|
onChange: function (v) { setRiskFilter(v); },
|
||||||
|
options: LEASE_CUSTOMER_RISK_FILTER_OPTIONS,
|
||||||
|
style: { width: '100%' },
|
||||||
|
'aria-label': '按风控等级筛选',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
React.createElement('div', { className: 'lc-customer-picker-modal__table-wrap', role: 'region', 'aria-label': '客户列表' },
|
||||||
|
React.createElement('table', { className: 'lc-customer-picker-modal__table' },
|
||||||
|
React.createElement('thead', null,
|
||||||
|
React.createElement('tr', null,
|
||||||
|
React.createElement('th', { scope: 'col' }, '客户名称'),
|
||||||
|
React.createElement('th', { scope: 'col', className: 'lc-customer-picker-modal__col-time' }, '上次评级时间'),
|
||||||
|
React.createElement('th', { scope: 'col', className: 'lc-customer-picker-modal__col-risk' }, '风控等级'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
React.createElement('tbody', null,
|
||||||
|
filteredCustomers.length
|
||||||
|
? filteredCustomers.map(function (customer) {
|
||||||
|
var selectable = isLeaseCustomerSelectable(customer);
|
||||||
|
var isSelected = pendingId === customer.id;
|
||||||
|
return React.createElement('tr', {
|
||||||
|
key: customer.id,
|
||||||
|
className: 'lc-customer-picker-modal__row'
|
||||||
|
+ (isSelected ? ' is-selected' : '')
|
||||||
|
+ (selectable ? ' is-selectable' : ' is-disabled'),
|
||||||
|
onClick: function () { handleRowClick(customer); },
|
||||||
|
'aria-selected': isSelected,
|
||||||
|
'aria-disabled': !selectable,
|
||||||
|
},
|
||||||
|
React.createElement('td', null, customer.name),
|
||||||
|
React.createElement('td', { className: 'tabular-nums' }, customer.lastRatingTime || '—'),
|
||||||
|
React.createElement('td', null,
|
||||||
|
React.createElement('div', { className: 'lc-customer-picker-modal__risk-cell' },
|
||||||
|
React.createElement('span', {
|
||||||
|
className: 'lc-customer-picker-modal__risk'
|
||||||
|
+ (selectable ? ' lc-customer-picker-modal__risk--ok' : ' lc-customer-picker-modal__risk--blocked'),
|
||||||
|
}, getLeaseCustomerRiskLevelLabel(customer.riskLevel)),
|
||||||
|
!selectable
|
||||||
|
? React.createElement('span', { className: 'lc-customer-picker-modal__blocked-hint' }, '不可选')
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: React.createElement('tr', null,
|
||||||
|
React.createElement('td', { colSpan: 3, className: 'lc-customer-picker-modal__empty' }, '暂无匹配客户'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
React.createElement('p', { className: 'lc-customer-picker-modal__hint' }, '仅 A/B 级客户可选;C/D 级客户风控等级不符合签约要求。'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function LeaseOrderServiceContentField(props) {
|
function LeaseOrderServiceContentField(props) {
|
||||||
var selected = props.value || [];
|
var selected = props.value || [];
|
||||||
var onChange = props.onChange;
|
var onChange = props.onChange;
|
||||||
@@ -241,8 +413,8 @@ function LeaseOrderEditor(props) {
|
|||||||
updateRows(rows.map(function (row) {
|
updateRows(rows.map(function (row) {
|
||||||
if (row.id !== id) return row;
|
if (row.id !== id) return row;
|
||||||
var next = Object.assign({}, row, patch);
|
var next = Object.assign({}, row, patch);
|
||||||
if ('rent' in patch || 'extraServices' in patch) {
|
if ('rent' in patch || 'serviceFee' in patch) {
|
||||||
next.rentServiceSubtotal = calcRentServiceSubtotal(next.rent, normalizeExtraServices(next));
|
next.rentServiceSubtotal = calcRentServiceSubtotal(next.rent, next.serviceFee);
|
||||||
}
|
}
|
||||||
return next;
|
return next;
|
||||||
}));
|
}));
|
||||||
@@ -365,8 +537,7 @@ function LeaseOrderEditor(props) {
|
|||||||
React.createElement('tbody', null,
|
React.createElement('tbody', null,
|
||||||
rows.map(function (row) {
|
rows.map(function (row) {
|
||||||
var extraServices = normalizeExtraServices(row);
|
var extraServices = normalizeExtraServices(row);
|
||||||
var subtotal = calcRentServiceSubtotal(row.rent, extraServices);
|
var subtotal = calcRentServiceSubtotal(row.rent, row.serviceFee);
|
||||||
var serviceFeeTotal = sumExtraServiceFees(extraServices);
|
|
||||||
var plateOptions = getReadyVehiclePlateOptions(row.brandModel);
|
var plateOptions = getReadyVehiclePlateOptions(row.brandModel);
|
||||||
var leasePeriodValue = row.leasePeriodStart && row.leasePeriodEnd
|
var leasePeriodValue = row.leasePeriodStart && row.leasePeriodEnd
|
||||||
? [dayjs(row.leasePeriodStart), dayjs(row.leasePeriodEnd)]
|
? [dayjs(row.leasePeriodStart), dayjs(row.leasePeriodEnd)]
|
||||||
@@ -438,9 +609,18 @@ function LeaseOrderEditor(props) {
|
|||||||
),
|
),
|
||||||
React.createElement('td', null,
|
React.createElement('td', null,
|
||||||
React.createElement(LeaseOrderAmountField, null,
|
React.createElement(LeaseOrderAmountField, null,
|
||||||
React.createElement(ReadonlyValue, {
|
React.createElement(InputNumber, {
|
||||||
value: serviceFeeTotal > 0 ? String(serviceFeeTotal) : '',
|
className: 'lc-form-number vm-focus-border',
|
||||||
label: '服务费',
|
controls: false,
|
||||||
|
min: 0,
|
||||||
|
max: 99,
|
||||||
|
step: 1,
|
||||||
|
precision: 0,
|
||||||
|
value: row.serviceFee,
|
||||||
|
onChange: function (v) { updateRow(row.id, { serviceFee: v }); },
|
||||||
|
placeholder: '服务费',
|
||||||
|
style: { width: '100%' },
|
||||||
|
'aria-label': '服务费',
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -785,18 +965,9 @@ export default function LeaseContractEditorForm(props) {
|
|||||||
label: '乙方',
|
label: '乙方',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
React.createElement(Select, {
|
React.createElement(LeaseCustomerPickerField, {
|
||||||
className: 'lc-form-select vm-focus-border',
|
value: customerId,
|
||||||
placeholder: '请选择或搜索客户',
|
|
||||||
value: customerId || undefined,
|
|
||||||
allowClear: true,
|
|
||||||
showSearch: true,
|
|
||||||
optionFilterProp: 'label',
|
|
||||||
onChange: function (value) { onCustomerChange(value || ''); },
|
onChange: function (value) { onCustomerChange(value || ''); },
|
||||||
options: MOCK_LEASE_CUSTOMERS.map(function (c) {
|
|
||||||
return { value: c.id, label: c.name };
|
|
||||||
}),
|
|
||||||
'aria-label': '乙方客户',
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ const Component = function() {
|
|||||||
var useMemo = React.useMemo;
|
var useMemo = React.useMemo;
|
||||||
var useCallback = React.useCallback;
|
var useCallback = React.useCallback;
|
||||||
var antd = window.antd;
|
var antd = window.antd;
|
||||||
var Breadcrumb = antd.Breadcrumb;
|
|
||||||
var Select = antd.Select;
|
var Select = antd.Select;
|
||||||
var Input = antd.Input;
|
var Input = antd.Input;
|
||||||
var Button = antd.Button;
|
var Button = antd.Button;
|
||||||
@@ -1442,14 +1441,6 @@ const Component = function() {
|
|||||||
'查看需求说明'
|
'查看需求说明'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
React.createElement('div', { className: 'vm-page-header' },
|
|
||||||
React.createElement(Breadcrumb, {
|
|
||||||
items: [
|
|
||||||
{ title: '业务管理' },
|
|
||||||
{ title: '租赁合同管理' }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
),
|
|
||||||
React.createElement('section', { className: 'vm-filter-card' },
|
React.createElement('section', { className: 'vm-filter-card' },
|
||||||
React.createElement('header', { className: 'vm-filter-header' },
|
React.createElement('header', { className: 'vm-filter-header' },
|
||||||
React.createElement('h2', { className: 'vm-filter-title' }, '筛选条件')
|
React.createElement('h2', { className: 'vm-filter-title' }, '筛选条件')
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
CUSTOMER_CREDENTIAL_ITEMS,
|
CUSTOMER_CREDENTIAL_ITEMS,
|
||||||
CONTRACT_CODE_PREFIX,
|
CONTRACT_CODE_PREFIX,
|
||||||
} from '../contract-template-management/contract-template-vars.js';
|
} from '../contract-template-management/contract-template-vars.js';
|
||||||
import { calcRentServiceSubtotal, formatExtraServicesDisplay, formatLeasePeriod, normalizeExtraServices, sumExtraServiceFees } from './lease-order-vars.js';
|
import { calcRentServiceSubtotal, formatExtraServicesDisplay, formatLeasePeriod, normalizeExtraServices } from './lease-order-vars.js';
|
||||||
|
|
||||||
function escapeHtml(value) {
|
function escapeHtml(value) {
|
||||||
return String(value != null ? value : '')
|
return String(value != null ? value : '')
|
||||||
@@ -232,12 +232,11 @@ function buildLeaseOrderPreviewRow(row) {
|
|||||||
var subtotal = row.rentServiceSubtotal != null && row.rentServiceSubtotal !== ''
|
var subtotal = row.rentServiceSubtotal != null && row.rentServiceSubtotal !== ''
|
||||||
? escapeHtml(row.rentServiceSubtotal)
|
? escapeHtml(row.rentServiceSubtotal)
|
||||||
: (function () {
|
: (function () {
|
||||||
var computed = calcRentServiceSubtotal(row.rent, extraServices);
|
var computed = calcRentServiceSubtotal(row.rent, row.serviceFee);
|
||||||
return computed != null ? escapeHtml(computed) : '';
|
return computed != null ? escapeHtml(computed) : '';
|
||||||
}());
|
}());
|
||||||
var rent = row.rent != null && row.rent !== '' ? escapeHtml(row.rent) : '';
|
var rent = row.rent != null && row.rent !== '' ? escapeHtml(row.rent) : '';
|
||||||
var serviceFeeTotal = sumExtraServiceFees(extraServices);
|
var serviceFee = row.serviceFee != null && row.serviceFee !== '' ? escapeHtml(row.serviceFee) : '';
|
||||||
var serviceFee = serviceFeeTotal > 0 ? escapeHtml(serviceFeeTotal) : '';
|
|
||||||
var deposit = row.deposit != null && row.deposit !== '' ? escapeHtml(row.deposit) : '';
|
var deposit = row.deposit != null && row.deposit !== '' ? escapeHtml(row.deposit) : '';
|
||||||
var extra = escapeHtml(formatExtraServicesDisplay(extraServices));
|
var extra = escapeHtml(formatExtraServicesDisplay(extraServices));
|
||||||
var leasePeriod = escapeHtml(formatLeasePeriod(row));
|
var leasePeriod = escapeHtml(formatLeasePeriod(row));
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ export function createEmptyLeaseOrderRow() {
|
|||||||
plateNo: '',
|
plateNo: '',
|
||||||
rentServiceSubtotal: null,
|
rentServiceSubtotal: null,
|
||||||
rent: null,
|
rent: null,
|
||||||
|
serviceFee: null,
|
||||||
deposit: null,
|
deposit: null,
|
||||||
extraServices: [],
|
extraServices: [],
|
||||||
leasePeriodStart: null,
|
leasePeriodStart: null,
|
||||||
@@ -182,8 +183,9 @@ export function formatExtraServicesDisplay(selectedValues) {
|
|||||||
}).join('、');
|
}).join('、');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calcRentServiceSubtotal(rent, extraServices) {
|
export function calcRentServiceSubtotal(rent, serviceFee) {
|
||||||
var serviceFeeTotal = sumExtraServiceFees(extraServices);
|
var serviceFeeTotal = serviceFee != null && serviceFee !== '' ? Number(serviceFee) : 0;
|
||||||
|
if (!Number.isFinite(serviceFeeTotal)) serviceFeeTotal = 0;
|
||||||
var hasRent = rent != null && rent !== '';
|
var hasRent = rent != null && rent !== '';
|
||||||
if (!hasRent && serviceFeeTotal <= 0) return null;
|
if (!hasRent && serviceFeeTotal <= 0) return null;
|
||||||
var rentVal = hasRent ? Number(rent) : 0;
|
var rentVal = hasRent ? Number(rent) : 0;
|
||||||
@@ -197,7 +199,7 @@ export function calcLeaseOrderKpis(rows) {
|
|||||||
var hasSubtotal = false;
|
var hasSubtotal = false;
|
||||||
var hasDeposit = false;
|
var hasDeposit = false;
|
||||||
(rows || []).forEach(function (row) {
|
(rows || []).forEach(function (row) {
|
||||||
var subtotal = calcRentServiceSubtotal(row.rent, normalizeExtraServices(row));
|
var subtotal = calcRentServiceSubtotal(row.rent, row.serviceFee);
|
||||||
if (subtotal != null) {
|
if (subtotal != null) {
|
||||||
rentServiceTaxTotal += subtotal;
|
rentServiceTaxTotal += subtotal;
|
||||||
hasSubtotal = true;
|
hasSubtotal = true;
|
||||||
|
|||||||
@@ -189,6 +189,237 @@
|
|||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vm-page.lc-create-page .lc-customer-picker__trigger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 36px;
|
||||||
|
padding: 4px 11px;
|
||||||
|
border: 1px solid var(--vm-border, #d4d4d8);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vm-page.lc-create-page .lc-customer-picker__trigger:focus-visible {
|
||||||
|
outline: 2px solid rgba(94, 105, 209, 0.45);
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vm-page.lc-create-page .lc-customer-picker__placeholder {
|
||||||
|
color: #a1a1aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vm-page.lc-create-page .lc-customer-picker__value {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: #18181b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vm-page.lc-create-page .lc-customer-picker__actions {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: #71717a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vm-page.lc-create-page .lc-customer-picker__clear {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: transparent;
|
||||||
|
color: #71717a;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vm-page.lc-create-page .lc-customer-picker__clear:hover {
|
||||||
|
background: #f4f4f5;
|
||||||
|
color: #18181b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal 挂载在 body portal,样式挂在 rootClassName 上,不可依赖 .vm-page 祖先 */
|
||||||
|
.lc-customer-picker-modal .ant-modal-container {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .ant-modal-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-height: calc(100vh - 80px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .ant-modal-header {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid var(--ln-hairline, #e4e4e7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .ant-modal-body {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 16px 20px 8px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .ant-modal-footer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
padding: 12px 20px 16px;
|
||||||
|
border-top: 1px solid var(--ln-hairline, #e4e4e7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .ant-modal-footer .ant-btn + .ant-btn {
|
||||||
|
margin-inline-start: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__filters {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) 200px;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__search,
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__risk-filter {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__risk-filter.ant-select {
|
||||||
|
min-height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__table-wrap {
|
||||||
|
max-height: 360px;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px solid var(--ln-hairline, #e4e4e7);
|
||||||
|
border-radius: var(--ln-radius-control, 8px);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__table th,
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__table td {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-bottom: 1px solid var(--ln-hairline, #f4f4f5);
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__table th {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
background: var(--ln-canvas-soft, #fafafa);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ln-muted, #52525b);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__table tbody tr:last-child td {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__row.is-selectable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__row.is-selectable:hover {
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__row.is-selected {
|
||||||
|
background: #eef2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__row.is-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.72;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__table th:nth-child(1),
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__table td:nth-child(1) {
|
||||||
|
width: 42%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__col-time {
|
||||||
|
width: 28%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__col-risk {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__risk-cell {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__risk {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__risk--ok {
|
||||||
|
color: #166534;
|
||||||
|
background: #ecfdf5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__risk--blocked {
|
||||||
|
color: #991b1b;
|
||||||
|
background: #fef2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__blocked-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #a1a1aa;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__empty {
|
||||||
|
text-align: center;
|
||||||
|
color: #a1a1aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__hint {
|
||||||
|
margin: 10px 0 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #71717a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.lc-customer-picker-modal .lc-customer-picker-modal__filters {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.vm-page.lc-create-page .lc-lease-order-editor__table .lc-form-select--multiple .ant-select-selector {
|
.vm-page.lc-create-page .lc-lease-order-editor__table .lc-form-select--multiple .ant-select-selector {
|
||||||
min-height: 36px !important;
|
min-height: 36px !important;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
|
|||||||
Reference in New Issue
Block a user