Merge remote-tracking branch 'origin/master'

This commit is contained in:
2026-07-29 00:53:16 +08:00
39 changed files with 9180 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
import { createCrudApi } from './common';
const api = createCrudApi('/blade-transport/common-cargo');
export const getList = api.getList;
export const getDetail = api.getDetail;
export const submit = api.submit;
export const remove = api.remove;

View File

@@ -0,0 +1,8 @@
import { createCrudApi } from './common';
const api = createCrudApi('/blade-transport/common-route');
export const getList = api.getList;
export const getDetail = api.getDetail;
export const submit = api.submit;
export const remove = api.remove;

103
src/api/business/common.js Normal file
View File

@@ -0,0 +1,103 @@
import request from '@/axios';
export const createCrudApi = baseUrl => ({
getList(current, size, params) {
return request({
url: `${baseUrl}/list`,
method: 'get',
params: {
...params,
current,
size,
},
});
},
getDetail(id) {
return request({
url: `${baseUrl}/detail`,
method: 'get',
params: {
id,
},
});
},
submit(row) {
return request({
url: `${baseUrl}/submit`,
method: 'post',
data: row,
});
},
remove(ids) {
return request({
url: `${baseUrl}/remove`,
method: 'post',
params: {
ids,
},
});
},
copy(id) {
return request({
url: `${baseUrl}/copy`,
method: 'post',
params: {
id,
},
});
},
enable(id) {
return request({
url: `${baseUrl}/enable`,
method: 'post',
params: {
id,
},
});
},
disable(id) {
return request({
url: `${baseUrl}/disable`,
method: 'post',
params: {
id,
},
});
},
cancel(id) {
return request({
url: `${baseUrl}/cancel`,
method: 'post',
params: {
id,
},
});
},
complete(id) {
return request({
url: `${baseUrl}/complete`,
method: 'post',
params: {
id,
},
});
},
reassign(id) {
return request({
url: `${baseUrl}/reassign`,
method: 'post',
params: {
id,
},
});
},
batchComplete(ids) {
return request({
url: `${baseUrl}/batch-complete`,
method: 'post',
params: {
ids,
},
});
},
});

View File

@@ -0,0 +1,78 @@
import { createCrudApi } from './common';
import request from '@/axios';
const baseUrl = '/blade-transport/contract-manage';
const api = createCrudApi(baseUrl);
export const getList = api.getList;
export const getDetail = api.getDetail;
export const submit = api.submit;
export const remove = api.remove;
export const copy = api.copy;
export const saveDraft = row =>
request({
url: `${baseUrl}/save-draft`,
method: 'post',
data: row,
});
export const toTemporary = id =>
request({
url: `${baseUrl}/to-temporary`,
method: 'post',
params: { id },
});
export const submitFormal = id =>
request({
url: `${baseUrl}/submit-formal`,
method: 'post',
params: { id },
});
export const approve = id =>
request({
url: `${baseUrl}/approve`,
method: 'post',
params: { id },
});
export const reject = id =>
request({
url: `${baseUrl}/reject`,
method: 'post',
params: { id },
});
export const withdraw = id =>
request({
url: `${baseUrl}/withdraw`,
method: 'post',
params: { id },
});
export const startChange = (id, reason) =>
request({
url: `${baseUrl}/start-change`,
method: 'post',
params: {
id,
changeContent: reason,
changeReason: reason,
},
});
export const terminate = (id, reason) =>
request({
url: `${baseUrl}/terminate`,
method: 'post',
params: { id, reason },
});
export const expireStats = params =>
request({
url: `${baseUrl}/expire-stats`,
method: 'get',
params,
});

View File

@@ -0,0 +1,11 @@
import { createCrudApi } from './common';
const api = createCrudApi('/blade-transport/process-config');
export const getList = api.getList;
export const getDetail = api.getDetail;
export const submit = api.submit;
export const remove = api.remove;
export const copy = api.copy;
export const enable = api.enable;
export const disable = api.disable;

View File

@@ -0,0 +1,63 @@
import { createCrudApi } from './common';
import request from '@/axios';
const baseUrl = '/blade-transport/project-apply';
const api = createCrudApi(baseUrl);
export const getList = api.getList;
export const getDetail = api.getDetail;
export const submit = api.submit;
export const remove = api.remove;
export const saveDraft = row =>
request({
url: `${baseUrl}/save-draft`,
method: 'post',
data: row,
});
export const submitApproval = id =>
request({
url: `${baseUrl}/submit-approval`,
method: 'post',
params: { id },
});
export const approve = id =>
request({
url: `${baseUrl}/approve`,
method: 'post',
params: { id },
});
export const reject = id =>
request({
url: `${baseUrl}/reject`,
method: 'post',
params: { id },
});
export const withdraw = id =>
request({
url: `${baseUrl}/withdraw`,
method: 'post',
params: { id },
});
export const voidProject = (id, reason) =>
request({
url: `${baseUrl}/void`,
method: 'post',
params: { id, reason },
});
export const startChange = (id, reason) =>
request({
url: `${baseUrl}/start-change`,
method: 'post',
params: {
id,
changeContent: reason,
changeReason: reason,
},
});

View File

@@ -0,0 +1,9 @@
import { createCrudApi } from './common';
const api = createCrudApi('/blade-transport/shipping-template');
export const getList = api.getList;
export const getDetail = api.getDetail;
export const submit = api.submit;
export const remove = api.remove;
export const copy = api.copy;

View File

@@ -0,0 +1,45 @@
import { createCrudApi } from './common';
import request from '@/axios';
const baseUrl = '/blade-transport/temporary-credit-limit';
const api = createCrudApi(baseUrl);
export const getList = api.getList;
export const getDetail = api.getDetail;
export const submit = api.submit;
export const remove = api.remove;
export const saveDraft = row =>
request({
url: `${baseUrl}/save-draft`,
method: 'post',
data: row,
});
export const submitApproval = id =>
request({
url: `${baseUrl}/submit-approval`,
method: 'post',
params: { id },
});
export const approve = id =>
request({
url: `${baseUrl}/approve`,
method: 'post',
params: { id },
});
export const reject = id =>
request({
url: `${baseUrl}/reject`,
method: 'post',
params: { id },
});
export const withdraw = id =>
request({
url: `${baseUrl}/withdraw`,
method: 'post',
params: { id },
});

View File

@@ -0,0 +1,11 @@
import { createCrudApi } from './common';
const api = createCrudApi('/blade-transport/transport-plan');
export const getList = api.getList;
export const getDetail = api.getDetail;
export const submit = api.submit;
export const remove = api.remove;
export const copy = api.copy;
export const cancel = api.cancel;
export const complete = api.complete;

View File

@@ -0,0 +1,13 @@
import { createCrudApi } from './common';
const api = createCrudApi('/blade-transport/waybill-manage');
export const getList = api.getList;
export const getDetail = api.getDetail;
export const submit = api.submit;
export const remove = api.remove;
export const copy = api.copy;
export const cancel = api.cancel;
export const reassign = api.reassign;
export const complete = api.complete;
export const batchComplete = api.batchComplete;

View File

@@ -0,0 +1,179 @@
import {
auditColumns,
createCrudOption,
nonNegativeRule,
packageOptions,
priceUnitOptions,
selectRule,
textRule,
} from './common';
export const config = {
title: '常用货物',
permission: 'common_cargo',
exportUrl: '/blade-transport/common-cargo/export-common-cargo',
exportName: '常用货物',
importUrl: '/blade-transport/common-cargo/import-common-cargo',
templateUrl: '/blade-transport/common-cargo/export-template',
enableAllDept: true,
};
export const option = createCrudOption([
{
label: '组织',
prop: 'deptName',
search: true,
minWidth: 150,
addDisplay: false,
editDisplay: false,
},
{
label: '一级货物类型',
prop: 'firstCargoTypeName',
search: true,
minWidth: 150,
editDisabled: true,
rules: textRule('一级货物类型', 100, true),
},
{
label: '一级货物类型编码',
prop: 'firstCargoTypeCode',
hide: true,
editDisabled: true,
},
{
label: '二级货物类型',
prop: 'secondCargoTypeName',
search: true,
minWidth: 150,
editDisabled: true,
rules: textRule('二级货物类型', 100, true),
},
{
label: '二级货物类型编码',
prop: 'secondCargoTypeCode',
search: true,
minWidth: 150,
editDisabled: true,
rules: textRule('二级货物类型编码', 20, true),
},
{
label: '货物名称',
prop: 'cargoName',
search: true,
minWidth: 150,
rules: textRule('货物名称', 100, true),
},
{
label: '货物编号',
prop: 'cargoCode',
search: true,
minWidth: 120,
editDisabled: true,
rules: textRule('货物编号', 20, true),
},
{
label: '品牌',
prop: 'brand',
search: true,
minWidth: 120,
rules: textRule('品牌', 50),
},
{
label: '包装',
prop: 'packageType',
type: 'select',
dicData: packageOptions,
minWidth: 100,
},
{
label: '货值',
prop: 'cargoValue',
type: 'number',
precision: 2,
min: 0,
minWidth: 120,
rules: [nonNegativeRule('货值')],
},
{
label: '规格',
prop: 'specification',
minWidth: 120,
rules: textRule('规格', 50),
},
{
label: '计价单位',
prop: 'priceUnit',
type: 'select',
dicData: priceUnitOptions,
minWidth: 120,
},
{
label: '型号',
prop: 'model',
minWidth: 120,
rules: textRule('型号', 50),
},
{
label: '说明1',
prop: 'descriptionOne',
minWidth: 160,
rules: textRule('说明1', 100),
},
{
label: '尺寸',
prop: 'sizeText',
minWidth: 120,
rules: textRule('尺寸', 50),
},
{
label: '说明2',
prop: 'descriptionTwo',
minWidth: 160,
rules: textRule('说明2', 100),
},
{
label: '数据来源',
prop: 'dataSource',
minWidth: 120,
addDisplay: false,
editDisplay: false,
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
minRows: 4,
span: 24,
hide: true,
maxlength: 200,
showWordLimit: true,
rules: textRule('备注', 200),
},
...auditColumns,
]);
export const excelOption = {
submitBtn: false,
emptyBtn: false,
column: [
{
label: '导入模板',
prop: 'excelTemplate',
formslot: true,
span: 24,
},
{
label: 'Excel文件',
prop: 'excelFile',
type: 'upload',
drag: true,
loadText: '模板上传中,请稍等',
span: 24,
propsHttp: {
res: 'data',
},
tip: '请上传 .xls,.xlsx 标准格式文件',
},
],
};

View File

@@ -0,0 +1,131 @@
import { auditColumns, createCrudOption, phoneRule, textRule } from './common';
export const config = {
title: '常用线路',
permission: 'common_route',
exportUrl: '/blade-transport/common-route/export-common-route',
exportName: '常用线路',
enableAllDept: true,
};
export const option = createCrudOption([
{
label: '组织',
prop: 'deptName',
search: true,
minWidth: 150,
addDisplay: false,
editDisplay: false,
},
{
label: '线路编号',
prop: 'routeCode',
search: true,
minWidth: 120,
addDisplay: false,
editDisabled: true,
},
{
label: '线路名称',
prop: 'routeName',
search: true,
minWidth: 150,
rules: textRule('线路名称', 100, true),
},
{
label: '发货地',
prop: 'departureName',
search: true,
minWidth: 140,
rules: textRule('发货地', 100, true),
},
{
label: '发货地址',
prop: 'departureAddress',
search: true,
minWidth: 220,
rules: textRule('发货地址', 255, true),
},
{
label: '发货经度',
prop: 'departureLongitude',
type: 'number',
precision: 6,
minWidth: 120,
hide: true,
},
{
label: '发货纬度',
prop: 'departureLatitude',
type: 'number',
precision: 6,
minWidth: 120,
hide: true,
},
{
label: '发货联系人',
prop: 'departureContact',
minWidth: 120,
rules: textRule('发货联系人', 50),
},
{
label: '发货联系方式',
prop: 'departurePhone',
minWidth: 140,
rules: [phoneRule('发货联系方式'), ...textRule('发货联系方式', 50)],
},
{
label: '收货地',
prop: 'arrivalName',
search: true,
minWidth: 140,
rules: textRule('收货地', 100, true),
},
{
label: '收货地址',
prop: 'arrivalAddress',
search: true,
minWidth: 220,
rules: textRule('收货地址', 255, true),
},
{
label: '收货经度',
prop: 'arrivalLongitude',
type: 'number',
precision: 6,
minWidth: 120,
hide: true,
},
{
label: '收货纬度',
prop: 'arrivalLatitude',
type: 'number',
precision: 6,
minWidth: 120,
hide: true,
},
{
label: '收货联系人',
prop: 'arrivalContact',
minWidth: 120,
rules: textRule('收货联系人', 50),
},
{
label: '收货联系方式',
prop: 'arrivalPhone',
minWidth: 140,
rules: [phoneRule('收货联系方式'), ...textRule('收货联系方式', 50)],
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
minRows: 4,
span: 24,
hide: true,
maxlength: 500,
showWordLimit: true,
rules: textRule('备注', 500),
},
...auditColumns,
]);

View File

@@ -0,0 +1,244 @@
export const transportTypeOptions = [
{ label: '公路整车', value: '公路整车' },
{ label: '公路零担', value: '公路零担' },
{ label: '铁路运输', value: '铁路运输' },
{ label: '水路运输', value: '水路运输' },
{ label: '跨境海运', value: '跨境海运' },
{ label: '航空运输', value: '航空运输' },
];
export const packageOptions = [
{ label: '袋装', value: '袋装' },
{ label: '散装', value: '散装' },
{ label: '桶装', value: '桶装' },
{ label: '箱装', value: '箱装' },
{ label: '托盘', value: '托盘' },
];
export const priceUnitOptions = [
{ label: '元/吨', value: '元/吨' },
{ label: '元/立方米', value: '元/立方米' },
{ label: '元/件', value: '元/件' },
{ label: '元/车', value: '元/车' },
];
export const templateTypeOptions = [
{ label: '运输计划', value: '运输计划' },
{ label: '常规运单', value: '常规运单' },
];
export const processStatusOptions = [
{ label: '草稿', value: 0 },
{ label: '启用', value: 1 },
{ label: '停用', value: 2 },
];
export const planStatusOptions = [
{ label: '草稿', value: 'draft' },
{ label: '待调度', value: 'waiting_dispatch' },
{ label: '调度中', value: 'dispatching' },
{ label: '已完成', value: 'completed' },
{ label: '已取消', value: 'cancelled' },
];
export const waybillStatusOptions = [
{ label: '草稿', value: 'draft' },
{ label: '待执行', value: 'pending' },
{ label: '进行中', value: 'processing' },
{ label: '已完成', value: 'completed' },
{ label: '已取消', value: 'cancelled' },
];
export const dataSourceOptions = [
{ label: '手工创建', value: '手工创建' },
{ label: '批量导入', value: '批量导入' },
{ label: '模板生成', value: '模板生成' },
{ label: '计划调度', value: '计划调度' },
];
export const projectTypeOptions = [
{ label: '普通项目', value: '普通项目' },
{ label: '重大项目', value: '重大项目' },
];
export const projectSourceOptions = [
{ label: '商务洽谈', value: '商务洽谈' },
{ label: '招投标', value: '招投标' },
{ label: '客户委托', value: '客户委托' },
{ label: '其它', value: '其它' },
];
export const projectApprovalStatusOptions = [
{ label: '草稿', value: 'draft' },
{ label: '审批中', value: 'reviewing' },
{ label: '已撤回', value: 'withdrawn' },
{ label: '审批通过', value: 'approved' },
{ label: '已驳回', value: 'rejected' },
{ label: '变更审批中', value: 'change_reviewing' },
{ label: '变更驳回', value: 'change_rejected' },
{ label: '变更审批通过', value: 'change_approved' },
{ label: '已作废', value: 'voided' },
];
export const approvalStatusOptions = [
{ label: '草稿', value: 'draft' },
{ label: '审批中', value: 'reviewing' },
{ label: '已撤回', value: 'withdrawn' },
{ label: '审批通过', value: 'approved' },
{ label: '已驳回', value: 'rejected' },
];
export const effectiveTypeOptions = [
{ label: '临时', value: 'temporary' },
{ label: '正式', value: 'formal' },
];
export const businessTypeOptions = [
{ label: '一类规模业务', value: '一类规模业务' },
{ label: '二类规模业务', value: '二类规模业务' },
{ label: '普通业务', value: '普通业务' },
];
export const settlementModeOptions = [
{ label: '先票后款', value: '先票后款' },
{ label: '先款后票', value: '先款后票' },
{ label: '月结', value: '月结' },
];
export const contractCategoryOptions = [
{ label: '客户合同', value: '客户合同' },
{ label: '承运商合同', value: '承运商合同' },
{ label: '补充协议', value: '补充协议' },
];
export const signTypeOptions = [
{ label: '新签', value: '新签' },
{ label: '续签', value: '续签' },
];
export const contractStageOptions = [
{ label: '草稿', value: 'draft' },
{ label: '临时合同', value: 'temporary' },
{ label: '正式合同', value: 'formal' },
{ label: '已终止', value: 'terminated' },
];
export const contractApprovalStatusOptions = [
{ label: '草稿', value: 'draft' },
{ label: '审批中', value: 'reviewing' },
{ label: '已驳回', value: 'rejected' },
{ label: '审批通过', value: 'approved' },
{ label: '变更审批中', value: 'change_reviewing' },
{ label: '变更驳回', value: 'change_rejected' },
{ label: '变更审批通过', value: 'change_approved' },
];
export const nodeOptions = [
{ label: '接单', value: '接单' },
{ label: '到场', value: '到场' },
{ label: '装货', value: '装货' },
{ label: '发货', value: '发货' },
{ label: '在途', value: '在途' },
{ label: '到货', value: '到货' },
{ label: '卸货', value: '卸货' },
{ label: '签收', value: '签收' },
{ label: '回单', value: '回单' },
];
export const textRule = (label, max, required = false) => {
const rules = [];
if (required) {
rules.push({ required: true, message: `请输入${label}`, trigger: 'blur' });
}
if (max) {
rules.push({ max, message: `${label}不能超过${max}个字符`, trigger: 'blur' });
}
return rules;
};
export const selectRule = label => [{ required: true, message: `请选择${label}`, trigger: 'change' }];
export const nonNegativeRule = label => ({
validator: (rule, value, callback) => {
if (value !== undefined && value !== null && value !== '' && Number(value) < 0) {
callback(new Error(`${label}不能小于0`));
return;
}
callback();
},
trigger: 'blur',
});
export const phoneRule = label => ({
pattern: /^(1\d{10}|0\d{2,3}-?\d{7,8})$/,
message: `${label}格式不正确`,
trigger: 'blur',
});
export const createCrudOption = columns => ({
height: 'auto',
calcHeight: 32,
dialogWidth: 980,
labelPosition: 'right',
labelWidth: 'auto',
tip: false,
searchBtnText: '查询',
emptyBtnText: '重置',
saveBtnText: '提交',
updateBtnText: '提交',
searchShow: true,
searchMenuSpan: 24,
searchIcon: true,
searchIndex: 8,
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
indexWidth: 70,
selection: true,
addBtn: false,
viewBtn: false,
editBtn: false,
delBtn: false,
dialogClickModal: false,
menuFixed: 'right',
menuWidth: 240,
column: columns,
});
export const auditColumns = [
{
label: '创建人',
prop: 'createUserName',
minWidth: 120,
addDisplay: false,
editDisplay: false,
},
{
label: '更新人',
prop: 'updateUserName',
minWidth: 120,
addDisplay: false,
editDisplay: false,
},
{
label: '创建时间',
prop: 'createTime',
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
minWidth: 160,
addDisplay: false,
editDisplay: false,
},
{
label: '更新时间',
prop: 'updateTime',
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
minWidth: 160,
addDisplay: false,
editDisplay: false,
},
];

View File

@@ -0,0 +1,333 @@
import {
auditColumns,
contractApprovalStatusOptions,
contractCategoryOptions,
contractStageOptions,
createCrudOption,
selectRule,
settlementModeOptions,
signTypeOptions,
textRule,
} from './common';
export const config = {
title: '合同管理',
permission: 'contract_manage',
exportUrl: '/blade-transport/contract-manage/export-contract-manage',
exportName: '合同管理',
enableAllDept: true,
actions: ['copy'],
statusProp: 'approvalStatus',
statusTextProp: 'approvalStatusName',
deleteStatus: ['draft'],
editStatus: ['draft', 'rejected', 'change_rejected'],
operations: [
{
action: 'toTemporary',
label: '转临时',
statusProp: 'approvalStatus',
status: ['draft'],
stage: ['draft'],
permission: 'contract_manage_temp',
},
{
action: 'submitFormal',
label: '转正式',
statusProp: 'approvalStatus',
status: ['draft', 'rejected', 'approved'],
stage: ['temporary'],
permission: 'contract_manage_formal',
},
{
action: 'withdraw',
label: '撤回',
icon: 'el-icon-refresh-left',
statusProp: 'approvalStatus',
status: ['reviewing', 'change_reviewing'],
permission: 'contract_manage_withdraw',
},
{
action: 'approve',
label: '审批通过',
type: 'success',
icon: 'el-icon-check',
statusProp: 'approvalStatus',
status: ['reviewing', 'change_reviewing'],
permission: 'contract_manage_approve',
},
{
action: 'reject',
label: '审批驳回',
type: 'warning',
icon: 'el-icon-close',
statusProp: 'approvalStatus',
status: ['reviewing', 'change_reviewing'],
permission: 'contract_manage_reject',
},
{
action: 'startChange',
label: '发起变更',
icon: 'el-icon-edit-outline',
statusProp: 'approvalStatus',
status: ['approved', 'change_approved'],
stage: ['formal'],
permission: 'contract_manage_change',
prompt: '请输入变更内容与原因',
},
{
action: 'terminate',
label: '终止',
type: 'danger',
icon: 'el-icon-close',
statusProp: 'approvalStatus',
status: ['approved', 'change_approved'],
stage: ['formal'],
permission: 'contract_manage_terminate',
prompt: '请输入终止原因',
},
],
};
export const option = createCrudOption([
{
label: '合同编号',
prop: 'contractNo',
search: true,
minWidth: 160,
addDisplay: false,
editDisabled: true,
},
{
label: '合同名称',
prop: 'contractName',
search: true,
minWidth: 180,
rules: textRule('合同名称', 100, true),
},
{
label: '所属项目',
prop: 'projectName',
search: true,
minWidth: 160,
},
{
label: '所属项目ID',
prop: 'projectId',
hide: true,
},
{
label: '所属组织',
prop: 'organizationName',
search: true,
minWidth: 150,
addDisplay: false,
editDisabled: true,
},
{
label: '合同类别',
prop: 'contractCategory',
type: 'select',
search: true,
dicData: contractCategoryOptions,
minWidth: 130,
rules: selectRule('合同类别'),
},
{
label: '签约类型',
prop: 'signType',
type: 'select',
search: true,
dicData: signTypeOptions,
minWidth: 120,
},
{
label: '甲方',
prop: 'partyA',
minWidth: 160,
},
{
label: '乙方',
prop: 'partyB',
minWidth: 160,
},
{
label: '开始日期',
prop: 'startDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
minWidth: 130,
},
{
label: '结束日期',
prop: 'endDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
minWidth: 130,
},
{
label: '临时效力起',
prop: 'temporaryStartDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
minWidth: 130,
},
{
label: '临时效力止',
prop: 'temporaryEndDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
minWidth: 130,
},
{
label: '经办人',
prop: 'handlerUserName',
minWidth: 120,
},
{
label: '签订日期',
prop: 'signDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
minWidth: 130,
},
{
label: '结算方式',
prop: 'settlementMode',
type: 'select',
dicData: settlementModeOptions,
minWidth: 120,
},
{
label: '合同格式',
prop: 'contractFormat',
type: 'select',
dicData: [
{ label: '电子合同', value: '电子合同' },
{ label: '纸质合同', value: '纸质合同' },
],
minWidth: 120,
},
{
label: '法人章',
prop: 'legalSealFlag',
type: 'switch',
dicData: [
{ label: '否', value: 0 },
{ label: '是', value: 1 },
],
value: 0,
minWidth: 100,
},
{
label: '一式份数',
prop: 'copyCount',
type: 'number',
minWidth: 110,
},
{
label: '回款账期(天)',
prop: 'paymentDays',
type: 'number',
minWidth: 130,
},
{
label: '合同阶段',
prop: 'contractStage',
type: 'select',
search: true,
slot: true,
dicData: contractStageOptions,
minWidth: 120,
addDisplay: false,
editDisplay: false,
fixed: 'right',
},
{
label: '审核状态',
prop: 'approvalStatus',
type: 'select',
search: true,
slot: true,
dicData: contractApprovalStatusOptions,
minWidth: 130,
addDisplay: false,
editDisplay: false,
fixed: 'right',
},
{
label: '当前节点',
prop: 'currentNode',
minWidth: 140,
addDisplay: false,
editDisplay: false,
},
{
label: '当前处理人',
prop: 'currentProcessor',
minWidth: 130,
addDisplay: false,
editDisplay: false,
},
{
label: '计费信息',
prop: 'billingEnabled',
type: 'switch',
dicData: [
{ label: '关闭', value: 0 },
{ label: '开启', value: 1 },
],
value: 0,
minWidth: 110,
},
{
label: '合同主文件JSON',
prop: 'contractFileJson',
type: 'textarea',
span: 24,
hide: true,
},
{
label: '其它附件JSON',
prop: 'attachmentsJson',
type: 'textarea',
span: 24,
hide: true,
},
{
label: '计费方案JSON',
prop: 'billingPlanJson',
type: 'textarea',
span: 24,
hide: true,
},
{
label: '结算生成规则JSON',
prop: 'settlementRuleJson',
type: 'textarea',
span: 24,
hide: true,
},
{
label: '对账配置JSON',
prop: 'reconciliationJson',
type: 'textarea',
span: 24,
hide: true,
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
span: 24,
hide: true,
maxlength: 2000,
showWordLimit: true,
rules: textRule('备注', 2000),
},
...auditColumns,
]);

View File

@@ -0,0 +1,103 @@
import { auditColumns, createCrudOption, nodeOptions, processStatusOptions, selectRule, textRule } from './common';
export const config = {
title: '过程配置',
permission: 'process_config',
exportUrl: '/blade-transport/process-config/export-process-config',
exportName: '过程配置',
enableAllDept: true,
actions: ['copy', 'enable', 'disable'],
statusProp: 'status',
statusTextProp: 'statusName',
deleteStatus: [0, 2],
};
export const option = createCrudOption([
{
label: '配置编号',
prop: 'configCode',
search: true,
minWidth: 130,
addDisplay: false,
editDisabled: true,
},
{
label: '配置名称',
prop: 'configName',
search: true,
minWidth: 150,
rules: textRule('配置名称', 100, true),
},
{
label: '项目ID集合',
prop: 'projectIds',
hide: true,
rules: textRule('项目ID集合', 1000, true),
},
{
label: '项目',
prop: 'projectNames',
search: true,
type: 'textarea',
minRows: 2,
minWidth: 180,
rules: textRule('项目', 1000, true),
},
{
label: '包含过程节点',
prop: 'includedNodes',
search: true,
type: 'select',
multiple: true,
dicData: nodeOptions,
minWidth: 180,
rules: selectRule('包含过程节点'),
},
{
label: '默认完成运输天数',
prop: 'defaultFinishDays',
type: 'number',
min: 1,
max: 999,
minWidth: 150,
},
{
label: '状态',
prop: 'status',
type: 'select',
search: true,
dicData: processStatusOptions,
slot: true,
minWidth: 100,
addDisplay: false,
editDisplay: false,
},
{
label: '所属组织',
prop: 'deptName',
search: true,
minWidth: 150,
addDisplay: false,
editDisplay: false,
},
{
label: '节点配置JSON',
prop: 'nodeConfigJson',
type: 'textarea',
minRows: 6,
span: 24,
hide: true,
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
minRows: 4,
span: 24,
hide: true,
maxlength: 500,
showWordLimit: true,
rules: textRule('备注', 500),
},
...auditColumns,
]);

View File

@@ -0,0 +1,359 @@
import {
auditColumns,
businessTypeOptions,
createCrudOption,
effectiveTypeOptions,
nonNegativeRule,
projectApprovalStatusOptions,
projectSourceOptions,
projectTypeOptions,
selectRule,
settlementModeOptions,
textRule,
transportTypeOptions,
} from './common';
const amountRules = label => [
{ required: true, message: `请输入${label}`, trigger: 'blur' },
nonNegativeRule(label),
];
export const config = {
title: '项目管理',
permission: 'project_apply',
exportUrl: '/blade-transport/project-apply/export-project-apply',
exportName: '项目立项',
enableAllDept: true,
statusProp: 'approvalStatus',
statusTextProp: 'approvalStatusName',
deleteStatus: ['draft', 'withdrawn', 'rejected'],
editStatus: ['draft', 'withdrawn', 'rejected', 'change_rejected'],
operations: [
{
action: 'submitApproval',
label: '提交审批',
icon: 'el-icon-position',
statusProp: 'approvalStatus',
status: ['draft', 'withdrawn', 'rejected'],
permission: 'project_apply_submit',
},
{
action: 'withdraw',
label: '撤回',
icon: 'el-icon-refresh-left',
statusProp: 'approvalStatus',
status: ['reviewing'],
permission: 'project_apply_withdraw',
},
{
action: 'approve',
label: '审批通过',
type: 'success',
icon: 'el-icon-check',
statusProp: 'approvalStatus',
status: ['reviewing', 'change_reviewing'],
permission: 'project_apply_approve',
},
{
action: 'reject',
label: '审批驳回',
type: 'warning',
icon: 'el-icon-close',
statusProp: 'approvalStatus',
status: ['reviewing', 'change_reviewing'],
permission: 'project_apply_reject',
},
{
action: 'startChange',
label: '发起变更',
icon: 'el-icon-edit-outline',
statusProp: 'approvalStatus',
status: ['approved', 'change_approved'],
permission: 'project_apply_change',
prompt: '请输入变更内容与原因',
},
{
action: 'voidProject',
label: '作废',
type: 'danger',
icon: 'el-icon-delete',
statusProp: 'approvalStatus',
status: ['approved', 'change_approved'],
permission: 'project_apply_void',
prompt: '请输入作废原因',
},
],
};
export const option = createCrudOption([
{
label: '立项申请单号',
prop: 'applyNo',
search: true,
minWidth: 160,
addDisplay: false,
editDisabled: true,
},
{
label: '项目编号',
prop: 'projectCode',
minWidth: 150,
addDisplay: false,
editDisabled: true,
},
{
label: '项目名称',
prop: 'projectName',
search: true,
minWidth: 180,
maxlength: 50,
rules: textRule('项目名称', 50, true),
},
{
label: '项目简称',
prop: 'projectShortName',
minWidth: 140,
maxlength: 20,
rules: textRule('项目简称', 20, true),
},
{
label: '项目类型',
prop: 'projectType',
type: 'select',
search: true,
dicData: projectTypeOptions,
minWidth: 120,
rules: selectRule('项目类型'),
},
{
label: '业务部门',
prop: 'businessDeptName',
minWidth: 150,
rules: textRule('业务部门', 50, true),
},
{
label: '承办部门',
prop: 'undertakeDeptName',
search: true,
minWidth: 150,
rules: textRule('承办部门', 50, true),
},
{
label: '承办部门ID',
prop: 'undertakeDeptId',
hide: true,
},
{
label: '项目负责人',
prop: 'principalUserName',
search: true,
minWidth: 130,
rules: textRule('项目负责人', 50, true),
},
{
label: '项目经办人',
prop: 'handlerUserName',
minWidth: 130,
rules: textRule('项目经办人', 50, true),
},
{
label: '项目由来',
prop: 'projectSource',
type: 'select',
dicData: projectSourceOptions,
value: '商务洽谈',
minWidth: 120,
},
{
label: '项目资金使用额度(万元)',
prop: 'fundLimit',
type: 'number',
precision: 2,
minWidth: 190,
rules: amountRules('项目资金使用额度'),
},
{
label: '项目应收账款额度(万元)',
prop: 'receivableLimit',
type: 'number',
precision: 2,
minWidth: 200,
rules: amountRules('项目应收账款额度'),
},
{
label: '应收账款回款期限(天)',
prop: 'receivableDays',
type: 'number',
minWidth: 190,
rules: [{ required: true, message: '请输入应收账款回款期限', trigger: 'blur' }],
},
{
label: '回款账期(天)',
prop: 'paymentDays',
type: 'number',
minWidth: 140,
rules: [{ required: true, message: '请输入回款账期', trigger: 'blur' }],
},
{
label: '客户名称',
prop: 'customerNames',
search: true,
minWidth: 180,
rules: textRule('客户名称', 255, true),
},
{
label: '下游承运商',
prop: 'carrierNames',
minWidth: 180,
rules: textRule('下游承运商', 255, true),
},
{
label: '货物类型',
prop: 'cargoType',
minWidth: 120,
},
{
label: '预估货物数量',
prop: 'cargoQuantity',
minWidth: 150,
maxlength: 100,
},
{
label: '业务周期开始',
prop: 'businessStartDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
minWidth: 130,
},
{
label: '业务周期结束',
prop: 'businessEndDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
minWidth: 130,
},
{
label: '运输线路',
prop: 'transportRoute',
minWidth: 180,
maxlength: 100,
},
{
label: '运输类型',
prop: 'transportType',
type: 'select',
dicData: transportTypeOptions,
minWidth: 120,
},
{
label: '业务类型',
prop: 'businessType',
type: 'select',
dicData: businessTypeOptions,
minWidth: 130,
},
{
label: '项目规模(万元)',
prop: 'projectScale',
type: 'number',
precision: 2,
minWidth: 150,
rules: [nonNegativeRule('项目规模')],
},
{
label: '预计利润(万元)',
prop: 'estimatedProfit',
type: 'number',
precision: 2,
minWidth: 150,
rules: [nonNegativeRule('预计利润')],
},
{
label: '资金需求(万元)',
prop: 'fundDemand',
type: 'number',
precision: 2,
minWidth: 150,
rules: [nonNegativeRule('资金需求')],
},
{
label: '结算方式',
prop: 'settlementMode',
type: 'select',
dicData: settlementModeOptions,
minWidth: 120,
},
{
label: '审批状态',
prop: 'approvalStatus',
type: 'select',
search: true,
slot: true,
dicData: projectApprovalStatusOptions,
minWidth: 130,
addDisplay: false,
editDisplay: false,
fixed: 'right',
},
{
label: '当前节点',
prop: 'currentNode',
minWidth: 140,
addDisplay: false,
editDisplay: false,
},
{
label: '当前处理人',
prop: 'currentProcessor',
minWidth: 130,
addDisplay: false,
editDisplay: false,
},
{
label: '生效类型',
prop: 'effectiveType',
type: 'select',
search: true,
dicData: effectiveTypeOptions,
value: 'temporary',
minWidth: 110,
},
{
label: '审核通过时间',
prop: 'approvedTime',
type: 'datetime',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
minWidth: 170,
addDisplay: false,
editDisplay: false,
},
{
label: '项目由来说明',
prop: 'sourceRemark',
type: 'textarea',
span: 24,
hide: true,
maxlength: 300,
showWordLimit: true,
},
{
label: '项目情况说明',
prop: 'situationRemark',
type: 'textarea',
span: 24,
hide: true,
maxlength: 1000,
showWordLimit: true,
},
{
label: '项目附件JSON',
prop: 'attachmentsJson',
type: 'textarea',
span: 24,
hide: true,
},
...auditColumns,
]);

View File

@@ -0,0 +1,167 @@
import {
auditColumns,
createCrudOption,
templateTypeOptions,
transportTypeOptions,
selectRule,
textRule,
} from './common';
export const config = {
title: '发货模板',
permission: 'shipping_template',
exportUrl: '/blade-transport/shipping-template/export-shipping-template',
exportName: '发货模板',
enableAllDept: true,
actions: ['copy'],
};
export const option = createCrudOption([
{
label: '模板编号',
prop: 'templateCode',
search: true,
minWidth: 150,
addDisplay: false,
editDisabled: true,
},
{
label: '模板名称',
prop: 'templateName',
search: true,
minWidth: 150,
rules: textRule('模板名称', 50, true),
},
{
label: '模板类型',
prop: 'templateType',
type: 'select',
search: true,
dicData: templateTypeOptions,
minWidth: 120,
value: '运输计划',
rules: selectRule('模板类型'),
},
{
label: '项目',
prop: 'projectName',
search: true,
minWidth: 150,
rules: textRule('项目', 100, true),
},
{
label: '项目ID',
prop: 'projectId',
hide: true,
},
{
label: '客户合同',
prop: 'contractName',
minWidth: 160,
rules: textRule('客户合同', 100, true),
},
{
label: '客户合同ID',
prop: 'contractId',
hide: true,
},
{
label: '运输类型',
prop: 'transportType',
type: 'select',
dicData: transportTypeOptions,
minWidth: 130,
rules: selectRule('运输类型'),
},
{
label: '发货地',
prop: 'departureName',
minWidth: 140,
rules: textRule('发货地', 100, true),
},
{
label: '发货地址',
prop: 'departureAddress',
minWidth: 220,
rules: textRule('发货地址', 255, true),
},
{
label: '发货联系人',
prop: 'departureContact',
minWidth: 120,
rules: textRule('发货联系人', 20),
},
{
label: '发货联系方式',
prop: 'departurePhone',
minWidth: 140,
rules: textRule('发货联系方式', 20),
},
{
label: '收货地',
prop: 'arrivalName',
minWidth: 140,
rules: textRule('收货地', 100, true),
},
{
label: '收货地址',
prop: 'arrivalAddress',
minWidth: 220,
rules: textRule('收货地址', 255, true),
},
{
label: '收货联系人',
prop: 'arrivalContact',
minWidth: 120,
rules: textRule('收货联系人', 20),
},
{
label: '收货联系方式',
prop: 'arrivalPhone',
minWidth: 140,
rules: textRule('收货联系方式', 20),
},
{
label: '货物信息JSON',
prop: 'goodsJson',
type: 'textarea',
minRows: 5,
span: 24,
hide: true,
},
{
label: '运费信息JSON',
prop: 'freightJson',
type: 'textarea',
minRows: 4,
span: 24,
hide: true,
},
{
label: '附件JSON',
prop: 'attachmentsJson',
type: 'textarea',
minRows: 3,
span: 24,
hide: true,
},
{
label: '所属组织',
prop: 'deptName',
minWidth: 150,
addDisplay: false,
editDisplay: false,
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
minRows: 4,
span: 24,
hide: true,
maxlength: 200,
showWordLimit: true,
rules: textRule('备注', 200),
},
...auditColumns,
]);

View File

@@ -0,0 +1,191 @@
import { approvalStatusOptions, auditColumns, createCrudOption, nonNegativeRule, textRule } from './common';
export const config = {
title: '临时额度管理',
permission: 'temporary_credit_limit',
exportUrl: '/blade-transport/temporary-credit-limit/export-temporary-credit-limit',
exportName: '临时额度申请',
enableAllDept: true,
statusProp: 'approvalStatus',
statusTextProp: 'approvalStatusName',
deleteStatus: ['draft', 'withdrawn', 'rejected'],
editStatus: ['draft', 'withdrawn', 'rejected'],
operations: [
{
action: 'submitApproval',
label: '提交审批',
statusProp: 'approvalStatus',
status: ['draft', 'withdrawn', 'rejected'],
permission: 'temporary_credit_limit_submit',
},
{
action: 'withdraw',
label: '撤回',
icon: 'el-icon-refresh-left',
statusProp: 'approvalStatus',
status: ['reviewing'],
permission: 'temporary_credit_limit_withdraw',
},
{
action: 'approve',
label: '审批通过',
type: 'success',
icon: 'el-icon-check',
statusProp: 'approvalStatus',
status: ['reviewing'],
permission: 'temporary_credit_limit_approve',
},
{
action: 'reject',
label: '审批驳回',
type: 'warning',
icon: 'el-icon-close',
statusProp: 'approvalStatus',
status: ['reviewing'],
permission: 'temporary_credit_limit_reject',
},
],
};
export const option = createCrudOption([
{
label: '申请单号',
prop: 'applicationNo',
search: true,
minWidth: 160,
addDisplay: false,
editDisabled: true,
},
{
label: '项目名称',
prop: 'projectName',
search: true,
minWidth: 180,
rules: textRule('项目名称', 100, true),
},
{
label: '项目ID',
prop: 'projectId',
hide: true,
},
{
label: '项目编号',
prop: 'projectCode',
minWidth: 150,
rules: textRule('项目编号', 50, true),
},
{
label: '承办部门',
prop: 'undertakeDeptName',
minWidth: 150,
rules: textRule('承办部门', 50, true),
},
{
label: '项目资金使用额度(万元)',
prop: 'projectFundLimit',
type: 'number',
precision: 2,
minWidth: 200,
editDisabled: true,
rules: [nonNegativeRule('项目资金使用额度')],
},
{
label: '已使用项目资金额度(万元)',
prop: 'usedFundLimit',
type: 'number',
precision: 2,
minWidth: 220,
value: 0,
editDisabled: true,
rules: [nonNegativeRule('已使用项目资金额度')],
},
{
label: '剩余项目资金使用额度(万元)',
prop: 'remainingFundLimit',
type: 'number',
precision: 2,
minWidth: 230,
editDisabled: true,
rules: [nonNegativeRule('剩余项目资金使用额度')],
},
{
label: '申请临时额度(万元)',
prop: 'applyLimit',
type: 'number',
precision: 2,
minWidth: 180,
rules: [
{ required: true, message: '请输入申请临时额度', trigger: 'blur' },
nonNegativeRule('申请临时额度'),
],
},
{
label: '申请有效期至',
prop: 'validUntil',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
minWidth: 140,
rules: [{ required: true, message: '请选择申请有效期至', trigger: 'change' }],
},
{
label: '申请部门',
prop: 'applyDeptName',
search: true,
minWidth: 150,
addDisplay: false,
editDisabled: true,
},
{
label: '申请人',
prop: 'applicantName',
search: true,
minWidth: 120,
addDisplay: false,
editDisabled: true,
},
{
label: '审批状态',
prop: 'approvalStatus',
type: 'select',
search: true,
slot: true,
dicData: approvalStatusOptions,
minWidth: 130,
addDisplay: false,
editDisplay: false,
fixed: 'right',
},
{
label: '当前节点',
prop: 'currentNode',
minWidth: 140,
addDisplay: false,
editDisplay: false,
},
{
label: '当前处理人',
prop: 'currentProcessor',
minWidth: 130,
addDisplay: false,
editDisplay: false,
},
{
label: '附件JSON',
prop: 'attachmentsJson',
type: 'textarea',
span: 24,
hide: true,
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
span: 24,
hide: true,
maxlength: 500,
showWordLimit: true,
rules: textRule('备注', 500),
},
...auditColumns,
]);

View File

@@ -0,0 +1,175 @@
import {
auditColumns,
createCrudOption,
dataSourceOptions,
planStatusOptions,
selectRule,
textRule,
transportTypeOptions,
} from './common';
export const config = {
title: '运输计划',
permission: 'transport_plan',
exportUrl: '/blade-transport/transport-plan/export-transport-plan',
exportName: '运输计划',
enableAllDept: true,
actions: ['copy', 'cancel', 'complete'],
statusProp: 'businessStatus',
statusTextProp: 'businessStatusName',
deleteStatus: ['draft'],
editStatus: ['draft', 'waiting_dispatch'],
};
export const option = createCrudOption([
{
label: '计划单号',
prop: 'planNo',
search: true,
minWidth: 150,
addDisplay: false,
editDisabled: true,
},
{
label: '计划名称',
prop: 'planName',
search: true,
minWidth: 150,
rules: textRule('计划名称', 50, true),
},
{
label: '项目',
prop: 'projectName',
minWidth: 150,
rules: textRule('项目', 100, true),
},
{
label: '项目ID',
prop: 'projectId',
hide: true,
},
{
label: '客户合同',
prop: 'contractName',
minWidth: 160,
rules: textRule('客户合同', 100, true),
},
{
label: '客户合同ID',
prop: 'contractId',
hide: true,
},
{
label: '客户名称',
prop: 'customerName',
search: true,
minWidth: 150,
},
{
label: '运输类型',
prop: 'transportType',
type: 'select',
search: true,
dicData: transportTypeOptions,
minWidth: 130,
rules: selectRule('运输类型'),
},
{
label: '计划开始日期',
prop: 'planStartDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
search: true,
minWidth: 130,
},
{
label: '计划结束日期',
prop: 'planEndDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
search: true,
minWidth: 130,
},
{
label: '发货地',
prop: 'departureName',
minWidth: 140,
rules: textRule('发货地', 100, true),
},
{
label: '发货地址',
prop: 'departureAddress',
search: true,
minWidth: 220,
rules: textRule('发货地址', 255, true),
},
{
label: '收货地',
prop: 'arrivalName',
minWidth: 140,
rules: textRule('收货地', 100, true),
},
{
label: '收货地址',
prop: 'arrivalAddress',
search: true,
minWidth: 220,
rules: textRule('收货地址', 255, true),
},
{
label: '货物信息JSON',
prop: 'goodsJson',
type: 'textarea',
minRows: 5,
span: 24,
hide: true,
},
{
label: '附件JSON',
prop: 'attachmentsJson',
type: 'textarea',
minRows: 3,
span: 24,
hide: true,
},
{
label: '数据来源',
prop: 'dataSource',
type: 'select',
search: true,
dicData: dataSourceOptions,
minWidth: 120,
},
{
label: '业务状态',
prop: 'businessStatus',
type: 'select',
slot: true,
search: true,
dicData: planStatusOptions,
minWidth: 120,
addDisplay: false,
editDisplay: false,
},
{
label: '所属组织',
prop: 'deptName',
minWidth: 150,
addDisplay: false,
editDisplay: false,
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
minRows: 4,
span: 24,
hide: true,
maxlength: 200,
showWordLimit: true,
rules: textRule('备注', 200),
},
...auditColumns,
]);

View File

@@ -0,0 +1,254 @@
import {
auditColumns,
createCrudOption,
dataSourceOptions,
selectRule,
textRule,
transportTypeOptions,
waybillStatusOptions,
} from './common';
export const config = {
title: '运单管理',
permission: 'waybill_manage',
exportUrl: '/blade-transport/waybill-manage/export-waybill-manage',
exportName: '运单管理',
enableAllDept: true,
actions: ['copy', 'cancel', 'reassign', 'complete', 'batchComplete'],
statusProp: 'businessStatus',
statusTextProp: 'businessStatusName',
deleteStatus: ['draft'],
editStatus: ['draft', 'pending'],
};
export const option = createCrudOption([
{
label: '运单号',
prop: 'waybillNo',
search: true,
minWidth: 150,
addDisplay: false,
editDisabled: true,
},
{
label: '项目',
prop: 'projectName',
search: true,
minWidth: 150,
rules: textRule('项目', 100, true),
},
{
label: '项目ID',
prop: 'projectId',
hide: true,
},
{
label: '客户合同',
prop: 'contractName',
minWidth: 160,
rules: textRule('客户合同', 100, true),
},
{
label: '客户合同ID',
prop: 'contractId',
hide: true,
},
{
label: '客户名称',
prop: 'customerName',
search: true,
minWidth: 150,
},
{
label: '运输类型',
prop: 'transportType',
type: 'select',
search: true,
dicData: transportTypeOptions,
minWidth: 130,
rules: selectRule('运输类型'),
},
{
label: '货物名称',
prop: 'cargoName',
search: true,
minWidth: 150,
rules: textRule('货物名称', 100, true),
},
{
label: '货物类型',
prop: 'cargoType',
search: true,
minWidth: 130,
rules: textRule('货物类型', 100, true),
},
{
label: '发货地址',
prop: 'departureAddress',
search: true,
minWidth: 220,
rules: textRule('发货地址', 255, true),
},
{
label: '收货地址',
prop: 'arrivalAddress',
search: true,
minWidth: 220,
rules: textRule('收货地址', 255, true),
},
{
label: '承运商名称',
prop: 'carrierName',
search: true,
minWidth: 150,
},
{
label: '司机姓名',
prop: 'driverName',
search: true,
minWidth: 120,
},
{
label: '车/船/航班/班列号',
prop: 'vehicleNo',
search: true,
minWidth: 170,
},
{
label: '原始单号',
prop: 'originalNo',
search: true,
minWidth: 140,
},
{
label: '业务状态',
prop: 'businessStatus',
type: 'select',
slot: true,
search: true,
dicData: waybillStatusOptions,
minWidth: 120,
addDisplay: false,
editDisplay: false,
},
{
label: '数据来源',
prop: 'dataSource',
type: 'select',
search: true,
dicData: dataSourceOptions,
minWidth: 120,
},
{
label: '开始日期',
prop: 'startDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
search: true,
minWidth: 130,
},
{
label: '结束日期',
prop: 'endDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
search: true,
minWidth: 130,
},
{
label: '计划名称',
prop: 'planName',
search: true,
minWidth: 150,
},
{
label: '多联总单',
prop: 'masterNo',
search: true,
minWidth: 140,
},
{
label: '配载单号',
prop: 'loadingNo',
search: true,
minWidth: 140,
},
{
label: '运单批次号',
prop: 'batchNo',
search: true,
minWidth: 140,
},
{
label: '关联单号',
prop: 'relationNo',
search: true,
minWidth: 140,
},
{
label: '当前过程节点',
prop: 'currentProcessNode',
minWidth: 140,
},
{
label: '货物信息JSON',
prop: 'goodsJson',
type: 'textarea',
minRows: 5,
span: 24,
hide: true,
},
{
label: '承运信息JSON',
prop: 'carrierJson',
type: 'textarea',
minRows: 4,
span: 24,
hide: true,
},
{
label: '过程配置JSON',
prop: 'processJson',
type: 'textarea',
minRows: 4,
span: 24,
hide: true,
},
{
label: '运费信息JSON',
prop: 'freightJson',
type: 'textarea',
minRows: 4,
span: 24,
hide: true,
},
{
label: '附件JSON',
prop: 'attachmentsJson',
type: 'textarea',
minRows: 3,
span: 24,
hide: true,
},
{
label: '所属组织',
prop: 'deptName',
minWidth: 150,
addDisplay: false,
editDisplay: false,
},
{
label: '备注',
prop: 'remark',
type: 'textarea',
minRows: 4,
span: 24,
hide: true,
maxlength: 500,
showWordLimit: true,
rules: textRule('备注', 500),
},
...auditColumns,
]);

View File

@@ -0,0 +1,21 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" :crud-excel-option="excelOption" />
</template>
<script>
import BusinessCrudPage from './components/business-crud-page.vue';
import * as api from '@/api/business/common-cargo';
import { config, excelOption, option } from '@/option/business/common-cargo';
export default {
components: { BusinessCrudPage },
data() {
return {
api,
config,
option,
excelOption,
};
},
};
</script>

View File

@@ -0,0 +1,20 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
</template>
<script>
import BusinessCrudPage from './components/business-crud-page.vue';
import * as api from '@/api/business/common-route';
import { config, option } from '@/option/business/common-route';
export default {
components: { BusinessCrudPage },
data() {
return {
api,
config,
option,
};
},
};
</script>

View File

@@ -0,0 +1,673 @@
<template>
<basic-container class="business-crud-page">
<avue-crud
:option="option"
:table-loading="loading"
:data="data"
v-model:page="page"
v-model="form"
ref="crud"
:permission="permissionList"
:before-open="beforeOpen"
@row-save="rowSave"
@row-update="rowUpdate"
@row-del="rowDel"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template #menu-left>
<el-button
type="primary"
icon="el-icon-plus"
plain
v-if="canCreate"
@click="$refs.crud.rowAdd()"
>新增
</el-button>
<el-button
type="primary"
icon="el-icon-upload"
plain
v-if="config.importUrl && hasPermission(`${config.permission}_import`) && !readonlyScope"
@click="handleImport"
>批量导入
</el-button>
<el-button
type="primary"
icon="el-icon-download"
plain
v-if="config.templateUrl && hasPermission(`${config.permission}_template`)"
@click="handleTemplate"
>下载模板
</el-button>
<el-button
type="primary"
icon="el-icon-download"
plain
v-if="config.exportUrl && hasPermission(`${config.permission}_export`)"
@click="handleExport"
>批量导出
</el-button>
<el-button
type="success"
icon="el-icon-check"
plain
v-if="canBatchComplete"
@click="handleBatchComplete"
>批量完成
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
plain
v-if="hasPermission(`${config.permission}_delete`) && !readonlyScope"
@click="handleDelete"
>批量删除
</el-button>
<el-switch
v-if="config.enableAllDept && canViewAllDept"
v-model="allDept"
class="business-crud-page__scope"
active-text="全部组织"
inactive-text="当前组织"
:active-value="1"
:inactive-value="0"
@change="handleScopeChange"
/>
</template>
<template #status="{ row }">
<el-tag :type="statusTagType(row.status)">
{{ displayStatus(row, 'status') }}
</el-tag>
</template>
<template #businessStatus="{ row }">
<el-tag :type="statusTagType(row.businessStatus)">
{{ displayStatus(row, 'businessStatus') }}
</el-tag>
</template>
<template #approvalStatus="{ row }">
<el-tag :type="statusTagType(row.approvalStatus)">
{{ displayStatus(row, 'approvalStatus') }}
</el-tag>
</template>
<template #contractStage="{ row }">
<el-tag :type="stageTagType(row.contractStage)">
{{ displayStatus(row, 'contractStage') }}
</el-tag>
</template>
<template #menu="{ row, index }">
<el-button
type="primary"
text
icon="el-icon-view"
v-if="hasPermission(`${config.permission}_view`)"
@click="$refs.crud.rowView(row, index)"
>
查看
</el-button>
<el-button
type="primary"
text
icon="el-icon-edit"
v-if="canEdit(row)"
@click="$refs.crud.rowEdit(row, index)"
>
编辑
</el-button>
<el-button type="primary" text icon="el-icon-copy-document" v-if="canCopy(row)" @click="handleCopy(row)">
复制
</el-button>
<el-button type="primary" text icon="el-icon-video-play" v-if="canEnable(row)" @click="handleAction('enable', row)">
启用
</el-button>
<el-button type="primary" text icon="el-icon-video-pause" v-if="canDisable(row)" @click="handleAction('disable', row)">
停用
</el-button>
<el-button type="primary" text icon="el-icon-refresh" v-if="canReassign(row)" @click="handleAction('reassign', row)">
重新派单
</el-button>
<el-button type="primary" text icon="el-icon-close" v-if="canCancel(row)" @click="handleAction('cancel', row)">
取消
</el-button>
<el-button type="primary" text icon="el-icon-check" v-if="canComplete(row)" @click="handleAction('complete', row)">
完成
</el-button>
<el-button
v-for="operation in customOperations(row)"
:key="operation.action"
:type="operation.type || 'primary'"
text
:icon="operation.icon || 'el-icon-position'"
@click="handleCustomOperation(operation, row)"
>
{{ operation.label }}
</el-button>
<el-button type="primary" text icon="el-icon-delete" v-if="canDelete(row)" @click="rowDel(row)">
删除
</el-button>
</template>
</avue-crud>
<empty-pagination
:page="page"
@size-change="sizeChange"
@current-change="currentChange"
@load="onLoad(page, query)"
/>
<el-dialog :title="`${config.title}导入`" append-to-body v-model="excelBox" width="555px">
<avue-form :option="excelOptionConfig" v-model="excelForm">
<template #excelTemplate>
<el-button type="primary" @click="handleTemplate">
点击下载<i class="el-icon-download el-icon--right"></i>
</el-button>
</template>
</avue-form>
</el-dialog>
</basic-container>
</template>
<script>
import { exportBlob } from '@/api/common';
import { getToken } from '@/utils/auth';
import { openImportDialog } from '@/utils/import-excel';
import { downloadXls } from '@/utils/util';
import { mapGetters } from 'vuex';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
export default {
name: 'BusinessCrudPage',
props: {
api: {
type: Object,
required: true,
},
config: {
type: Object,
required: true,
},
crudOption: {
type: Object,
required: true,
},
crudExcelOption: {
type: Object,
default: () => ({ column: [] }),
},
},
data() {
return {
form: {},
query: {},
loading: true,
data: [],
allDept: 0,
dialogReadonly: false,
option: this.cloneOption(this.crudOption),
excelBox: false,
excelForm: {},
excelOptionConfig: this.cloneOption(this.crudExcelOption),
page: {
pageSize: 10,
pageSizes: [10, 20, 50, 100],
currentPage: 1,
total: 0,
},
selectionList: [],
};
},
computed: {
...mapGetters(['permission', 'userInfo']),
permissionList() {
return {
addBtn: this.canCreate,
};
},
isAdmin() {
const authority = this.userInfo && this.userInfo.authority;
return Array.isArray(authority) ? authority.includes('admin') : String(authority || '').includes('admin');
},
canViewAllDept() {
return this.isAdmin;
},
readonlyScope() {
return this.config.enableAllDept && this.allDept === 1;
},
canCreate() {
return this.hasPermission(`${this.config.permission}_add`) && !this.readonlyScope;
},
canBatchComplete() {
return (
this.hasAction('batchComplete') &&
this.hasPermission(`${this.config.permission}_batch_complete`) &&
!this.readonlyScope
);
},
ids() {
return this.selectionList.map(item => item.id).join(',');
},
},
methods: {
cloneOption(option) {
return {
...option,
column: (option.column || []).map(column => ({ ...column })),
};
},
hasPermission(code) {
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
},
hasAction(action) {
return (this.config.actions || []).includes(action);
},
statusValue(row) {
return row[this.config.statusProp || 'status'];
},
inStatus(row, statusList) {
if (!statusList || !statusList.length) {
return true;
}
return statusList.includes(this.statusValue(row));
},
canEdit(row) {
return (
this.hasPermission(`${this.config.permission}_edit`) &&
!this.readonlyScope &&
!row.readonly &&
this.inStatus(row, this.config.editStatus)
);
},
canDelete(row) {
return (
this.hasPermission(`${this.config.permission}_delete`) &&
!this.readonlyScope &&
!row.readonly &&
this.inStatus(row, this.config.deleteStatus)
);
},
canCopy(row) {
return this.hasAction('copy') && this.hasPermission(`${this.config.permission}_copy`) && !this.readonlyScope && !row.readonly;
},
canEnable(row) {
return (
this.hasAction('enable') &&
this.hasPermission(`${this.config.permission}_enable`) &&
!this.readonlyScope &&
!row.readonly &&
[0, 2].includes(row.status)
);
},
canDisable(row) {
return (
this.hasAction('disable') &&
this.hasPermission(`${this.config.permission}_disable`) &&
!this.readonlyScope &&
!row.readonly &&
row.status === 1
);
},
canCancel(row) {
if (!this.hasAction('cancel') || !this.hasPermission(`${this.config.permission}_cancel`) || this.readonlyScope || row.readonly) {
return false;
}
const status = this.statusValue(row);
return this.config.permission === 'transport_plan'
? ['waiting_dispatch', 'dispatching'].includes(status)
: ['pending', 'processing'].includes(status);
},
canComplete(row) {
if (!this.hasAction('complete') || !this.hasPermission(`${this.config.permission}_complete`) || this.readonlyScope || row.readonly) {
return false;
}
const status = this.statusValue(row);
return this.config.permission === 'transport_plan' ? status === 'dispatching' : status === 'processing';
},
canReassign(row) {
return (
this.hasAction('reassign') &&
this.hasPermission(`${this.config.permission}_reassign`) &&
!this.readonlyScope &&
!row.readonly &&
this.statusValue(row) === 'pending'
);
},
customOperations(row) {
return (this.config.operations || []).filter(operation => this.canOperation(operation, row));
},
canOperation(operation, row) {
const permission = operation.permission || `${this.config.permission}_${operation.action}`;
if (!this.hasPermission(permission) || this.readonlyScope || row.readonly) {
return false;
}
const prop = operation.statusProp || this.config.statusProp || 'status';
const statusList = operation.status || [];
if (statusList.length && !statusList.includes(row[prop])) {
return false;
}
if (operation.stage && !operation.stage.includes(row.contractStage)) {
return false;
}
return true;
},
displayStatus(row, prop) {
const textProp = prop === this.config.statusProp ? this.config.statusTextProp : '';
if (textProp && row[textProp]) {
return row[textProp];
}
const column = this.findColumn(this.option.column, prop);
const item = column && (column.dicData || []).find(dic => dic.value === row[prop]);
return item ? item.label : row[prop] || '未知';
},
statusTagType(status) {
if ([1, 'pending', 'waiting_dispatch', 'processing', 'dispatching', 'approved', 'change_approved'].includes(status)) {
return 'success';
}
if ([2, 'cancelled', 'withdrawn', 'draft'].includes(status)) {
return 'info';
}
if (['rejected', 'change_rejected'].includes(status)) {
return 'danger';
}
if (status === 'completed') {
return 'primary';
}
return 'warning';
},
stageTagType(stage) {
if (stage === 'formal') {
return 'success';
}
if (stage === 'temporary') {
return 'warning';
}
if (stage === 'terminated') {
return 'danger';
}
return 'info';
},
normalizeRow(row) {
const submitRow = { ...row };
Object.keys(submitRow).forEach(key => {
if (typeof submitRow[key] === 'string') {
submitRow[key] = submitRow[key].trim();
}
});
return submitRow;
},
stopSubmitLoading(loading) {
if (typeof loading === 'function') {
loading();
}
},
rowSave(row, done, loading) {
this.api.submit(this.normalizeRow(row)).then(
() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
done();
},
error => {
window.console.log(error);
this.stopSubmitLoading(loading);
}
);
},
rowUpdate(row, index, done, loading) {
this.api.submit(this.normalizeRow(row)).then(
() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
done();
},
error => {
window.console.log(error);
this.stopSubmitLoading(loading);
}
);
},
rowDel(row) {
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => this.api.remove(row.id))
.then(res => {
this.afterRemove(res.data.data);
});
},
handleDelete() {
if (this.selectionList.length === 0) {
this.$message.warning('请选择至少一条数据');
return;
}
const selection = this.selectionList.filter(item => this.canDelete(item));
if (!selection.length) {
this.$message.warning('所选数据当前状态不可删除');
return;
}
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => this.api.remove(selection.map(item => item.id).join(',')))
.then(res => {
this.afterRemove(res.data.data);
});
},
afterRemove(result = {}) {
this.onLoad(this.page);
const successCount = result.successCount || 0;
const skippedCount = result.skippedCount || 0;
if (skippedCount) {
this.$message.warning(`成功删除${successCount}条,因业务引用跳过${skippedCount}`);
} else {
this.$message({ type: 'success', message: '删除成功!' });
}
this.selectionClear();
},
beforeOpen(done, type) {
this.dialogReadonly = type === 'view';
if (type === 'add') {
this.form = {
...(this.config.defaultForm || {}),
...this.form,
};
done();
return;
}
if (['edit', 'view'].includes(type)) {
this.api.getDetail(this.form.id).then(res => {
this.form = res.data.data || {};
done();
});
return;
}
done();
},
searchReset() {
this.query = {};
this.page.currentPage = 1;
this.onLoad(this.page);
},
searchChange(params, done) {
this.query = params;
this.page.currentPage = 1;
this.onLoad(this.page, params);
done();
},
selectionChange(list) {
this.selectionList = list;
},
selectionClear() {
this.selectionList = [];
if (this.$refs.crud) {
this.$refs.crud.toggleSelection();
}
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
refreshChange() {
this.onLoad(this.page, this.query);
},
handleScopeChange() {
if (!this.canViewAllDept) {
this.allDept = 0;
}
this.page.currentPage = 1;
this.onLoad(this.page);
},
onLoad(page, params = {}) {
this.loading = true;
this.api
.getList(page.currentPage, page.pageSize, { ...params, ...this.query, allDept: this.allDept })
.then(res => {
const result = res.data.data;
this.page.total = result.total;
this.data = result.records;
this.selectionClear();
})
.finally(() => {
this.loading = false;
});
},
buildExportParams() {
return {
...this.query,
allDept: this.allDept,
ids: this.ids,
[this.website.tokenHeader]: getToken(),
};
},
handleExport() {
this.$confirm(`是否导出${this.config.exportName || this.config.title}?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
NProgress.start();
exportBlob(this.config.exportUrl, this.buildExportParams())
.then(res => {
downloadXls(res.data, `${this.config.exportName || this.config.title}${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
})
.finally(() => {
NProgress.done();
});
});
},
handleTemplate() {
NProgress.start();
exportBlob(this.config.templateUrl, {
[this.website.tokenHeader]: getToken(),
})
.then(res => {
downloadXls(res.data, `${this.config.title}模板.xlsx`);
})
.finally(() => {
NProgress.done();
});
},
handleImport() {
const column = this.findColumn(this.excelOptionConfig.column, 'excelFile');
column.action = this.config.importUrl;
openImportDialog(this, this.config.title, () => this.onLoad(this.page, this.query));
},
handleCopy(row) {
this.$confirm(`确定复制该${this.config.title}?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => this.api.copy(row.id))
.then(() => {
this.$message.success('复制成功');
this.onLoad(this.page, this.query);
});
},
handleAction(action, row) {
const actionName = {
enable: '启用',
disable: '停用',
cancel: '取消',
complete: '完成',
reassign: '重新派单',
}[action];
this.$confirm(`确定${actionName}${this.config.title}?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => this.api[action](row.id))
.then(() => {
this.$message.success(`${actionName}成功`);
this.onLoad(this.page, this.query);
});
},
handleCustomOperation(operation, row) {
const run = value => {
const args = operation.prompt ? [row.id, value] : [row.id];
this.api[operation.action](...args).then(() => {
this.$message.success(`${operation.label}成功`);
this.onLoad(this.page, this.query);
});
};
if (operation.prompt) {
this.$prompt(operation.prompt, operation.label, {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /\S+/,
inputErrorMessage: operation.promptError || '请输入内容',
}).then(({ value }) => run(value));
return;
}
this.$confirm(operation.confirm || `确定${operation.label}${this.config.title}?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => run());
},
handleBatchComplete() {
if (!this.selectionList.length) {
this.$message.warning('请选择至少一条数据');
return;
}
this.$confirm('确定完成选中的运单?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => this.api.batchComplete(this.ids))
.then(res => {
const result = res.data.data || {};
this.$message.success(`成功完成${result.successCount || 0}条,跳过${result.skippedCount || 0}`);
this.onLoad(this.page, this.query);
});
},
},
};
</script>
<style lang="scss" scoped>
.business-crud-page {
&__scope {
margin-left: 12px;
vertical-align: middle;
}
}
</style>

View File

@@ -0,0 +1,20 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
</template>
<script>
import BusinessCrudPage from './components/business-crud-page.vue';
import * as api from '@/api/business/contract-manage';
import { config, option } from '@/option/business/contract-manage';
export default {
components: { BusinessCrudPage },
data() {
return {
api,
config,
option,
};
},
};
</script>

View File

@@ -0,0 +1,20 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
</template>
<script>
import BusinessCrudPage from './components/business-crud-page.vue';
import * as api from '@/api/business/process-config';
import { config, option } from '@/option/business/process-config';
export default {
components: { BusinessCrudPage },
data() {
return {
api,
config,
option,
};
},
};
</script>

View File

@@ -0,0 +1,20 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
</template>
<script>
import BusinessCrudPage from './components/business-crud-page.vue';
import * as api from '@/api/business/project-apply';
import { config, option } from '@/option/business/project-apply';
export default {
components: { BusinessCrudPage },
data() {
return {
api,
config,
option,
};
},
};
</script>

View File

@@ -0,0 +1,20 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
</template>
<script>
import BusinessCrudPage from './components/business-crud-page.vue';
import * as api from '@/api/business/shipping-template';
import { config, option } from '@/option/business/shipping-template';
export default {
components: { BusinessCrudPage },
data() {
return {
api,
config,
option,
};
},
};
</script>

View File

@@ -0,0 +1,20 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
</template>
<script>
import BusinessCrudPage from './components/business-crud-page.vue';
import * as api from '@/api/business/temporary-credit-limit';
import { config, option } from '@/option/business/temporary-credit-limit';
export default {
components: { BusinessCrudPage },
data() {
return {
api,
config,
option,
};
},
};
</script>

View File

@@ -0,0 +1,20 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
</template>
<script>
import BusinessCrudPage from './components/business-crud-page.vue';
import * as api from '@/api/business/transport-plan';
import { config, option } from '@/option/business/transport-plan';
export default {
components: { BusinessCrudPage },
data() {
return {
api,
config,
option,
};
},
};
</script>

View File

@@ -0,0 +1,20 @@
<template>
<business-crud-page :api="api" :config="config" :crud-option="option" />
</template>
<script>
import BusinessCrudPage from './components/business-crud-page.vue';
import * as api from '@/api/business/waybill-manage';
import { config, option } from '@/option/business/waybill-manage';
export default {
components: { BusinessCrudPage },
data() {
return {
api,
config,
option,
};
},
};
</script>

View File

@@ -0,0 +1,468 @@
# 临时额度管理需求卡
## 目录
- [REQ-TEMPLIM-001 菜单入口](#req-templim-001-菜单入口)
- [REQ-TEMPLIM-002 列表与分页](#req-templim-002-列表与分页)
- [REQ-TEMPLIM-003 条件查询与重置](#req-templim-003-条件查询与重置)
- [REQ-TEMPLIM-004 列表状态与操作](#req-templim-004-列表状态与操作)
- [REQ-TEMPLIM-005 新增临时额度申请](#req-templim-005-新增临时额度申请)
- [REQ-TEMPLIM-006 项目选择与自动回填](#req-templim-006-项目选择与自动回填)
- [REQ-TEMPLIM-007 额度字段规则](#req-templim-007-额度字段规则)
- [REQ-TEMPLIM-008 有效期与备注规则](#req-templim-008-有效期与备注规则)
- [REQ-TEMPLIM-009 附件上传](#req-templim-009-附件上传)
- [REQ-TEMPLIM-010 保存草稿](#req-templim-010-保存草稿)
- [REQ-TEMPLIM-011 编辑临时额度申请](#req-templim-011-编辑临时额度申请)
- [REQ-TEMPLIM-012 提交审批](#req-templim-012-提交审批)
- [REQ-TEMPLIM-013 撤回审批](#req-templim-013-撤回审批)
- [REQ-TEMPLIM-014 审批通过](#req-templim-014-审批通过)
- [REQ-TEMPLIM-015 审批驳回](#req-templim-015-审批驳回)
- [REQ-TEMPLIM-016 临时额度生效与失效](#req-templim-016-临时额度生效与失效)
- [REQ-TEMPLIM-017 查看与流程](#req-templim-017-查看与流程)
- [REQ-TEMPLIM-018 删除草稿单据](#req-templim-018-删除草稿单据)
- [REQ-TEMPLIM-019 导出列表](#req-templim-019-导出列表)
- [REQ-TEMPLIM-020 权限与审计](#req-templim-020-权限与审计)
- [通用规则](#通用规则)
---
### REQ-TEMPLIM-001 菜单入口
- 功能点:
- 临时额度管理模块归属于“项目管理”主菜单。
- 在“项目管理”菜单下提供“临时额度管理”入口。
- 用户进入临时额度管理页面后,可进行查询、新增、编辑、提交、查看、流程查看、撤回、删除、导出等操作。
- 异常与边界:
- 无菜单权限时不展示“临时额度管理”入口。
- 有菜单权限但无按钮权限时,仅展示允许访问的页面和操作。
- 菜单或页面加载失败时提示失败原因,不影响其他模块访问。
- 反例:
- 不允许把临时额度管理挂到非项目管理主菜单下。
- 不允许无权限用户通过 URL 直接进入模块。
- 不允许菜单入口存在但页面无法访问。
---
### REQ-TEMPLIM-002 列表与分页
- 功能点:
- 默认展示当前用户权限范围内的临时额度申请单。
- 列表展示序号、申请单号、项目名称、申请额度、有效截至时间、申请部门、申请人、审批状态、当前节点、当前处理人、操作等字段。
- 申请额度单位为万元保留2位小数。
- 有效截至时间展示到日期,格式为 yyyy-MM-dd。
- 审批状态和操作列固定在右侧,不随横向滚动条滑动。
- 默认按创建时间或申请时间降序展示。
- 支持分页展示默认10条/页支持20/50/100条/页。
- 异常与边界:
- 查询无数据时展示空状态。
- 当前页数据因保存、提交、撤回、审批、删除等操作发生变化后,应刷新列表并保持分页逻辑正确。
- 列表加载失败时保留当前查询条件和分页条件,提示用户重新查询。
- 反例:
- 不允许审批状态或操作列被横向滚动隐藏。
- 不允许列表展示申请人、当前处理人的用户 ID。
- 不允许分页切换后丢失当前查询条件。
---
### REQ-TEMPLIM-003 条件查询与重置
- 功能点:
- 支持按申请单号、项目名称、申请人、申请部门、状态等条件组合查询。
- 申请单号和项目名称使用输入框,支持模糊查询。
- 申请人使用下拉选择或搜索选择控件,默认全部。
- 申请部门使用下拉选择或部门选择控件,默认全部。
- 状态使用下拉单选控件,默认全部。
- 点击查询后按当前条件刷新列表。
- 点击重置后清空查询条件,恢复默认列表。
- 异常与边界:
- 查询条件为空时点击查询,按默认规则展示全部权限范围内数据。
- 查询结果为空时展示空状态,不清空用户已输入条件。
- 重置后页码恢复第一页,并重新加载列表。
- 反例:
- 不允许重置后仍保留隐藏查询条件。
- 不允许只按单个条件查询而忽略其他已选择条件。
- 不允许查询后改变列表字段或操作范围。
---
### REQ-TEMPLIM-004 列表状态与操作
- 功能点:
- 审批状态包含草稿、审批中、已撤回、审批通过、审核不通过等状态。
- 草稿状态展示编辑、提交、删除操作。
- 审批中状态展示查看、流程操作。
- 已撤回状态展示编辑、流程操作。
- 审核不通过状态展示编辑、流程操作。
- 审批通过状态展示查看、流程操作。
- 操作按钮展示受单据状态和用户权限共同控制。
- 异常与边界:
- 操作执行前需重新校验单据最新状态,避免列表状态过期导致误操作。
- 状态为空或未知时限制编辑、提交、删除等高风险操作。
- 状态变更成功后刷新列表,展示最新状态和可执行操作。
- 反例:
- 不允许审批中单据继续编辑或删除。
- 不允许审批通过单据再次提交审批。
- 不允许仅根据前端状态跳过后端状态校验。
---
### REQ-TEMPLIM-005 新增临时额度申请
- 功能点:
- 点击列表顶部“新增”按钮后打开新增申请弹窗。
- 新增弹窗展示临时额度申请信息和附件上传区域。
- 新增字段包含项目名称、项目编号、承办部门、项目资金使用额度、已使用项目资金额度、剩余项目资金使用额度、申请临时额度、申请有效期至、备注等。
- 项目名称、申请临时额度、申请有效期至为必填字段,并展示“*”标识。
- 新增弹窗底部展示保存、提交、取消按钮。
- 申请单号由系统保存后自动生成,全局唯一,只读不可编辑。
- 异常与边界:
- 新增弹窗打开失败时提示失败原因,不改变列表数据。
- 必填字段为空时阻止提交审批并提示。
- 保存或提交过程中重复点击按钮时应防止重复生成申请单。
- 反例:
- 不允许用户手工填写申请单号。
- 不允许未选择项目直接提交临时额度申请。
- 不允许缺少申请临时额度或有效期后提交审批。
---
### REQ-TEMPLIM-006 项目选择与自动回填
- 功能点:
- 项目名称使用弹窗搜索选择框,从项目管理中选择项目。
- 可选项目范围为已审批通过且生效类型为正式的立项项目。
- 选择项目后,系统自动带出项目编号、承办部门、项目资金使用额度、已使用项目资金额度、剩余项目资金使用额度。
- 项目编号、承办部门、项目资金使用额度、已使用项目资金额度、剩余项目资金使用额度为只读字段,不允许手工修改。
- 承办部门用于数据权限过滤和审批流程匹配。
- 异常与边界:
- 项目未审批通过、生效类型非正式或已作废时,不允许选择。
- 项目额度信息加载失败时,阻止提交并提示重新选择项目。
- 重新选择项目后,应清空并重新回填项目相关只读字段。
- 反例:
- 不允许手工输入项目名称代替项目管理选择。
- 不允许选择草稿、临时生效或已作废项目。
- 不允许修改自动回填的项目编号和额度字段。
---
### REQ-TEMPLIM-007 额度字段规则
- 功能点:
- 项目资金使用额度为只读数字字段,单位万元,来源于项目立项原始审批总资金额度。
- 已使用项目资金额度为只读数字字段,单位万元,来源于系统实时计算的业务单据占用额度。
- 剩余项目资金使用额度为只读数字字段,单位万元,由项目资金使用额度减去已使用额度自动计算回填。
- 申请临时额度为必填数字字段,单位万元,表示本次申请追加的临时资金额度。
- 申请临时额度必须大于0并保留2位小数。
- 异常与边界:
- 申请临时额度为空、小于等于0或小数位超过2位时阻止提交并提示。
- 项目资金使用额度、已使用额度、剩余额度出现负数或无法计算时,应提示数据异常并阻止提交。
- 已使用额度需实时计算业务单据占用,不能仅取静态项目档案值。
- 反例:
- 不允许申请临时额度填写0、负数或非数字。
- 不允许用户手动修改剩余项目资金使用额度。
- 不允许额度数据异常时仍提交审批。
---
### REQ-TEMPLIM-008 有效期与备注规则
- 功能点:
- 申请有效期至为必填日期字段,用于表示临时额度失效截止日期。
- 申请有效期至格式为 yyyy-MM-dd。
- 申请有效期至必须晚于当前系统日期。
- 备注使用大文本域,用于填写临时额度申请原因和业务补充说明。
- 备注非必填最多500字。
- 异常与边界:
- 申请有效期至为空时阻止提交并提示。
- 申请有效期至早于或等于当前系统日期时阻止提交并提示。
- 备注超过500字时阻止保存或提交并提示。
- 反例:
- 不允许选择今天或历史日期作为有效期截止日期。
- 不允许备注超过500字后提交。
- 不允许无有效期的临时额度审批通过。
---
### REQ-TEMPLIM-009 附件上传
- 功能点:
- 新增和编辑弹窗支持上传附件。
- 附件列表展示序号、文件名、附件描述、文件大小、上传人、上传时间、操作等字段。
- 附件上传成功后自动新增附件行,并展示完整文件信息。
- 上传后需填写附件描述。
- 支持删除未提交或允许编辑状态下的附件。
- 支持批量下载已上传附件。
- 单个附件大小限制50M。
- 异常与边界:
- 单个文件超过50M时阻止上传并提示。
- 上传失败时保留当前附件列表,并提示失败原因。
- 提交审批时如业务要求附件必传,应校验附件已上传且描述完整。
- 删除附件前需二次确认,确认后同步删除附件记录和服务器文件。
- 反例:
- 不允许上传超过50M的附件。
- 不允许上传成功后附件列表不展示文件信息。
- 不允许只删除前端附件行而保留有效服务器引用。
---
### REQ-TEMPLIM-010 保存草稿
- 功能点:
- 新增或编辑弹窗底部支持点击保存。
- 保存用于临时保存当前填写内容,单据状态标记为草稿,不触发审批流程。
- 保存草稿无需完整填写全部必填项。
- 保存成功后弹出成功提示,关闭弹窗后列表可重新编辑该草稿单据。
- 异常与边界:
- 保存草稿时至少需满足可生成单据的基本数据要求。
- 保存失败时保留用户已填写内容,方便修正后重新保存。
- 草稿已被删除或状态已变化时,不允许继续保存。
- 反例:
- 不允许保存草稿触发审批流程。
- 不允许保存失败后清空用户已填写内容。
- 不允许同一点击重复生成多条草稿。
---
### REQ-TEMPLIM-011 编辑临时额度申请
- 功能点:
- 草稿、审核不通过状态的临时额度申请单支持编辑。
- 点击编辑后弹窗回显该单据全部历史填写内容。
- 编辑时可修改项目名称、申请临时额度、申请有效期至、备注和附件等可编辑字段。
- 项目相关只读字段仍由项目选择自动带出,不允许手工修改。
- 编辑保存后单据保持草稿或原可编辑状态,提交后进入审批中。
- 异常与边界:
- 编辑前需校验当前单据状态仍允许编辑。
- 编辑的数据已不存在时提示数据不存在,并刷新列表。
- 修改项目后需重新回填额度信息,并重新校验申请临时额度。
- 保存或提交失败时保留用户修改内容。
- 反例:
- 不允许审批中或审批通过单据继续编辑。
- 不允许编辑时修改申请单号。
- 不允许编辑后绕过项目选择和额度校验。
---
### REQ-TEMPLIM-012 提交审批
- 功能点:
- 草稿、已撤回、审核不通过状态的单据支持提交审批。
- 提交审批前校验项目名称、申请临时额度、申请有效期至等必填字段。
- 提交审批前校验有效期日期、额度格式和附件要求。
- 提交成功后单据审批状态更新为审批中。
- 提交成功后推送审批待办消息给当前审批人。
- 异常与边界:
- 必填字段缺失、日期不合法、额度不合法或附件不满足要求时阻止提交。
- 审批流程发起失败时保持原状态,并保留用户输入内容。
- 重复点击提交时应防止重复发起审批。
- 反例:
- 不允许资料不完整的申请单进入审批中。
- 不允许提交失败后仍把状态改为审批中。
- 不允许同一申请单重复发起多个审批流程。
---
### REQ-TEMPLIM-013 撤回审批
- 功能点:
- 申请人可对审批中单据执行撤回。
- 撤回后单据退回草稿状态。
- 撤回后清空审批流程待办节点。
- 撤回成功后刷新列表,并展示最新单据状态。
- 异常与边界:
- 非申请人不允许撤回审批单据。
- 单据已审批通过或已驳回时不允许撤回。
- 撤回失败时保持原审批状态,并提示失败原因。
- 反例:
- 不允许审批通过后继续撤回。
- 不允许非申请人撤回他人申请单。
- 不允许撤回后审批待办仍停留在审批人列表。
---
### REQ-TEMPLIM-014 审批通过
- 功能点:
- 当前审批节点处理人可对审批中单据执行审批通过。
- 审批通过后单据状态更新为审批通过。
- 审批通过后临时额度立即生效,系统更新项目可用资金上限。
- 审批通过后系统设置自动到期失效任务。
- 审批通过成功后页面弹出成功提示,并刷新列表。
- 异常与边界:
- 非当前审批节点处理人不允许审批通过。
- 审批通过时如项目已作废或非正式生效,应阻止通过并提示。
- 更新项目可用资金上限失败时,应保证审批状态和额度生效状态一致。
- 自动到期任务设置失败时需提示并保留可补偿处理记录。
- 反例:
- 不允许未审批通过的临时额度生效。
- 不允许项目已作废时仍让临时额度生效。
- 不允许审批状态成功但项目可用额度未同步且无补偿记录。
---
### REQ-TEMPLIM-015 审批驳回
- 功能点:
- 当前审批节点处理人可对审批中单据执行审批驳回。
- 驳回后单据状态更新为审核不通过。
- 驳回后将驳回意见反馈给申请人。
- 审核不通过单据允许编辑后重新提交审批。
- 异常与边界:
- 非当前审批节点处理人不允许审批驳回。
- 驳回失败时保持原审批状态,并提示失败原因。
- 重新提交审批时需按最新内容重新校验字段和附件。
- 反例:
- 不允许驳回时丢失驳回意见。
- 不允许审核不通过单据直接生效。
- 不允许非审批处理人执行驳回。
---
### REQ-TEMPLIM-016 临时额度生效与失效
- 功能点:
- 审批通过后,申请临时额度叠加到项目可用资金上限。
- 临时额度有效期截止日前,项目资金占用计算需包含有效临时额度。
- 到达申请有效期至后,系统自动将该临时额度标记为失效。
- 临时额度失效后,项目可用资金上限需扣减该笔临时额度。
- 已失效临时额度不影响历史已产生业务单据。
- 异常与边界:
- 到期任务执行失败时,应支持后台补偿处理,避免额度长期错误生效。
- 多笔临时额度同时生效时,应分别按有效期计算,不得互相覆盖。
- 失效扣减后项目已使用额度超过新可用额度时,应保留历史占用并在后续额度校验中提示超额风险。
- 反例:
- 不允许临时额度过期后仍参与新业务额度校验。
- 不允许多笔临时额度只保留最后一笔。
- 不允许失效额度影响历史已完成业务数据。
---
### REQ-TEMPLIM-017 查看与流程
- 功能点:
- 支持通过列表行操作查看临时额度申请详情。
- 查看详情时展示申请基础信息、项目额度信息、附件和审批信息。
- 查看状态下所有字段只读。
- 支持点击流程查看审批流程节点、处理人、处理时间和审批意见。
- 异常与边界:
- 数据已删除或无权限查看时提示无法查看。
- 附件加载失败时不影响基础信息展示。
- 流程信息加载失败时提示失败原因,不改变单据状态。
- 反例:
- 不允许在查看状态下保存修改。
- 不允许查看流程时展示其他申请单的审批记录。
- 不允许流程节点信息缺失却展示审批已完整流转。
---
### REQ-TEMPLIM-018 删除草稿单据
- 功能点:
- 草稿状态的临时额度申请单支持删除。
- 点击删除后弹出二次确认弹窗。
- 删除前校验该临时额度未审批通过、未生效且无业务单据占用。
- 删除成功后刷新列表。
- 异常与边界:
- 非草稿状态不允许删除。
- 存在业务占用临时额度时不允许删除,并提示无法删除。
- 删除时状态已变化为审批中或审批通过时,应阻止删除并刷新列表。
- 反例:
- 不允许删除审批中或审批通过单据。
- 不允许存在业务占用时删除临时额度申请。
- 不允许跳过二次确认直接删除。
---
### REQ-TEMPLIM-019 导出列表
- 功能点:
- 列表顶部支持导出临时额度申请列表。
- 点击导出后,导出当前筛选条件下所有申请单基础信息和附件清单至 Excel 文件。
- 导出字段与列表和详情展示字段保持一致。
- 数据量较大时页面展示加载或导出中提示。
- 异常与边界:
- 当前查询条件下无数据时允许导出空文件或提示无可导出数据。
- 导出失败时提示失败原因,不改变当前列表状态。
- 无导出权限时不展示导出按钮。
- 反例:
- 不允许无权限用户导出临时额度数据。
- 不允许导出时忽略当前查询条件。
- 不允许导出文件展示用户 ID 代替姓名。
---
### REQ-TEMPLIM-020 权限与审计
- 功能点:
- 根据用户权限控制查询、新增、编辑、提交、撤回、审批、查看、流程、删除、导出、附件上传和附件下载等操作入口。
- 列表、详情、导出、审批流中用户可见字段优先展示姓名,不直接展示用户 ID。
- 新增、编辑、保存、提交、撤回、审批通过、审批驳回、删除、额度生效、额度失效、导出等关键操作需记录操作日志。
- 审计信息应包含操作人、操作时间、操作类型、申请单号、项目编号和关键额度变化。
- 异常与边界:
- 无按钮权限时不展示对应按钮,通过 URL 或接口绕过时也应拒绝。
- 操作日志记录失败时不得影响用户已成功的业务操作,但需保留可排查信息。
- 数据权限限制下,用户只能查看和操作授权范围内项目相关临时额度申请。
- 反例:
- 不允许无权限用户审批、删除或导出临时额度申请。
- 不允许列表展示申请人、审批人等用户 ID。
- 不允许额度生效或失效没有审计记录。
---
## 通用规则
- 临时额度管理用于对正式项目申请临时增加项目资金可用额度,归属于“项目管理”主菜单。
- 项目名称必须从项目管理中选择,且仅允许选择审批通过、生效类型为正式、未作废的项目。
- 选择项目后自动带出项目编号、承办部门、项目资金使用额度、已使用项目资金额度、剩余项目资金使用额度,自动带出字段只读不可编辑。
- 申请单号由系统自动生成,全局唯一,只读不可编辑。
- 申请临时额度必须大于0单位万元保留2位小数。
- 申请有效期至必须晚于当前系统日期,格式为 yyyy-MM-dd。
- 备注最多500字。
- 单个附件大小限制50M上传后需填写附件描述。
- 草稿状态不触发审批流程,审批通过前临时额度不得生效。
- 审批通过后临时额度立即生效,系统更新项目可用资金上限,并设置自动到期失效任务。
- 临时额度到期失效后,应从项目可用资金上限中扣减,不影响历史已产生业务单据。
- 草稿状态支持编辑、提交、删除;审批中仅支持查看和流程;审核不通过和已撤回支持编辑后重新提交;审批通过仅支持查看和流程。
- 删除前需校验单据未生效且无业务单据占用临时额度。
- 列表、详情、导出、审批等用户可见场景优先展示姓名字段,不直接展示用户 ID。
- 新增、编辑、保存、提交、撤回、审批、删除、导出、额度生效、额度失效后均需刷新相关列表或详情状态。

View File

@@ -0,0 +1,514 @@
# 发货模板需求卡
## 目录
- [REQ-SHIPTPL-001 菜单入口](#req-shiptpl-001-菜单入口)
- [REQ-SHIPTPL-002 列表与分页](#req-shiptpl-002-列表与分页)
- [REQ-SHIPTPL-003 条件查询与重置](#req-shiptpl-003-条件查询与重置)
- [REQ-SHIPTPL-004 查看发货模板](#req-shiptpl-004-查看发货模板)
- [REQ-SHIPTPL-005 新增发货模板](#req-shiptpl-005-新增发货模板)
- [REQ-SHIPTPL-006 编辑发货模板](#req-shiptpl-006-编辑发货模板)
- [REQ-SHIPTPL-007 模板信息规则](#req-shiptpl-007-模板信息规则)
- [REQ-SHIPTPL-008 基本信息规则](#req-shiptpl-008-基本信息规则)
- [REQ-SHIPTPL-009 收发货信息规则](#req-shiptpl-009-收发货信息规则)
- [REQ-SHIPTPL-010 货物信息规则](#req-shiptpl-010-货物信息规则)
- [REQ-SHIPTPL-011 附件规则](#req-shiptpl-011-附件规则)
- [REQ-SHIPTPL-012 选择常用地址](#req-shiptpl-012-选择常用地址)
- [REQ-SHIPTPL-013 选择常用线路](#req-shiptpl-013-选择常用线路)
- [REQ-SHIPTPL-014 选择常用货物](#req-shiptpl-014-选择常用货物)
- [REQ-SHIPTPL-015 从模板新建](#req-shiptpl-015-从模板新建)
- [REQ-SHIPTPL-016 单条删除](#req-shiptpl-016-单条删除)
- [REQ-SHIPTPL-017 批量删除](#req-shiptpl-017-批量删除)
- [REQ-SHIPTPL-018 权限与审计](#req-shiptpl-018-权限与审计)
- [REQ-SHIPTPL-019 运单模板布局](#req-shiptpl-019-运单模板布局)
- [REQ-SHIPTPL-020 运费信息规则](#req-shiptpl-020-运费信息规则)
- [REQ-SHIPTPL-021 查看过程配置按钮](#req-shiptpl-021-查看过程配置按钮)
- [通用规则](#通用规则)
---
### REQ-SHIPTPL-001 菜单入口
- 功能点:
- 新增主菜单“业务管理”。
- 在“业务管理”菜单下提供“发货模板”模块入口。
- 用户进入发货模板页面后,可进行查询、新建模板、编辑、删除、从模板新建等操作。
- 异常与边界:
- 无菜单权限时不展示“业务管理”或“发货模板”入口。
- 有菜单权限但无按钮权限时,仅展示允许访问的页面和操作。
- 菜单加载失败时提示失败原因,不影响其他菜单访问。
- 反例:
- 不允许把发货模板挂到非业务管理主菜单下。
- 不允许无权限用户通过 URL 直接进入模块。
- 不允许菜单入口存在但页面无法访问。
---
### REQ-SHIPTPL-002 列表与分页
- 功能点:
- 默认展示当前组织下的发货模板数据。
- 列表展示字段与编辑表单字段保持一致,并额外展示更新人、创建时间、更新时间。
- 列表至少展示模板编号、模板名称、模板类型、项目、客户合同、运输类型、发货地址、收货地址、货物信息、备注、更新人、创建时间、更新时间、操作等信息。
- 操作按钮列固定在右侧,不随横向滚动条滑动。
- 支持勾选多行数据进行批量删除。
- 分页默认10条/页支持20/50/100条/页。
- 异常与边界:
- 查询无数据时展示空状态。
- 横向字段较多时允许表格横向滚动,但操作列必须保持固定。
- 当前页删除后无数据时自动回退页码并刷新列表。
- 列表加载失败时保留当前查询条件和分页条件。
- 反例:
- 不允许操作列被横向滚动隐藏。
- 不允许列表字段与编辑表单长期不一致。
- 不允许分页后丢失当前查询条件。
---
### REQ-SHIPTPL-003 条件查询与重置
- 功能点:
- 支持按模板编号、模板名称、模板类型查询。
- 模板编号使用输入框,支持模糊查询。
- 模板名称使用输入框,支持模糊查询。
- 模板类型使用下拉单选控件。
- 点击查询后,按模板编号、模板名称、模板类型组合条件筛选列表。
- 点击重置后清空所有查询条件,恢复默认列表。
- 异常与边界:
- 查询条件为空时点击查询,按默认规则展示当前组织数据。
- 查询结果为空时展示空状态,不清空用户已输入条件。
- 重置后页码恢复第一页,并重新加载列表。
- 反例:
- 不允许重置后仍保留隐藏查询条件。
- 不允许模板编号查询变成精确匹配。
- 不允许查询后改变列表字段展示范围。
---
### REQ-SHIPTPL-004 查看发货模板
- 功能点:
- 支持通过行操作查看发货模板详情。
- 详情展示模板信息、基本信息、收发货信息、货物信息、附件、更新人、创建时间、更新时间等内容。
- 查看状态下所有字段置灰不可编辑。
- 查看页面或弹窗底部仅显示关闭或返回按钮。
- 异常与边界:
- 数据已删除或不存在时提示数据不存在,并刷新列表。
- 详情加载失败时提示失败原因,不改变列表数据。
- 附件加载失败时不影响其他字段展示。
- 反例:
- 不允许查看状态下保存修改。
- 不允许查看页面出现删除货物行、上传附件等编辑操作。
- 不允许查看时自动切换为编辑状态。
---
### REQ-SHIPTPL-005 新增发货模板
- 功能点:
- 点击新建模板后进入新建模板页面,默认进入运输计划模板类型。
- 新建页面包含模板信息、基本信息、收发货信息、货物信息、附件等区域。
- 模板编号由系统自动生成,只读不可编辑。
- 模板名称必填,使用输入框。
- 模板类型默认显示运输计划,可切换至常规运单;切换后下方填写信息按对应模板类型展示。
- 项目必填,使用模糊搜索选择框。
- 客户合同必填,选择项目后自动带出;若项目下存在多个客户合同,则以下拉框供用户选择。
- 运输类型必填,选择项目时自动带出,允许用户修改。
- 发货地址、收货地址必填,控件类型随运输类型变化。
- 货物信息中货物类型必填,支持新增、删除货物行。
- 附件为非必填,支持上传多个 JPG、PNG、PDF 等文件。
- 保存成功后刷新列表。
- 异常与边界:
- 模板名称为空时阻止提交并提示。
- 项目、客户合同、运输类型为空时阻止提交并提示。
- 发货地址或收货地址为空时阻止提交并提示。
- 货物信息中任一货物行的货物类型为空时阻止提交。
- 字段长度、数值、下拉枚举、附件格式不符合要求时阻止提交并提示。
- 提交失败时保留用户已填写内容。
- 反例:
- 不允许用户手工填写模板编号。
- 不允许缺少项目、合同或运输类型仍保存。
- 不允许没有任何货物行时保存。
- 不允许切换模板类型后继续提交旧模板类型的无效字段。
---
### REQ-SHIPTPL-006 编辑发货模板
- 功能点:
- 支持编辑已存在的发货模板。
- 模板编号在编辑状态下只读不可编辑。
- 模板名称、模板类型、备注、基本信息、收发货信息、货物信息、附件可按规则编辑。
- 编辑页面回显模板全部信息。
- 保存成功后刷新列表。
- 异常与边界:
- 编辑时修改模板类型,应按新模板类型刷新下方字段结构,并提示用户确认是否清空不适用字段。
- 编辑的数据已不存在时提示数据不存在,并刷新列表。
- 提交失败时保留用户修改内容。
- 反例:
- 不允许编辑时修改模板编号。
- 不允许编辑后提交不符合当前模板类型的字段。
- 不允许保存后列表仍展示旧数据。
---
### REQ-SHIPTPL-007 模板信息规则
- 功能点:
- 模板编号为模板唯一编码,由系统自动生成,只读展示。
- 模板编号规则为 MBJH + 8位年月日 + 3位流水号最长30字符。
- 模板名称使用输入框必填最长50字符。
- 模板类型为固定枚举,默认显示运输计划,可切换至常规运单。
- 模板备注使用文本输入框非必填最长200字符。
- 异常与边界:
- 模板编号生成失败时阻止保存并提示。
- 模板名称为空或超过50字符时阻止保存。
- 模板类型为空或不在允许枚举内时阻止保存。
- 模板备注超过200字符时阻止保存。
- 反例:
- 不允许手工编辑模板编号。
- 不允许保存无模板名称的数据。
- 不允许提交未知模板类型。
---
### REQ-SHIPTPL-008 基本信息规则
- 功能点:
- 项目使用模糊搜索选择框,必填,用于选择计划所属项目。
- 客户合同使用选择框,必填,选择项目后自动带出,不允许单独修改;若项目下存在多个客户合同,则下拉供用户选择。
- 运输类型使用下拉单选控件,必填,枚举包含公路整车、公路零担、空运、铁路、水运等,默认公路整车。
- 基本信息备注使用文本输入框非必填最长200字符。
- 异常与边界:
- 项目为空时阻止提交。
- 客户合同未能根据项目带出时阻止提交,并提示用户重新选择项目。
- 运输类型为空或不在允许枚举内时阻止提交。
- 基本信息备注超过200字符时阻止提交。
- 反例:
- 不允许未选项目时选择客户合同。
- 不允许客户合同脱离项目单独维护。
- 不允许使用非枚举运输类型。
---
### REQ-SHIPTPL-009 收发货信息规则
- 功能点:
- 公路运输时,发货地址和收货地址使用地址选择框或输入搜索选择框,可从常用地址库选择,也可按地址搜索选择。
- 水运或海运时,发货地址和收货地址显示为港口选择,交互变为从基础数据选择港口。
- 空运时,发货地址和收货地址显示为空港选择,交互变为从基础数据选择空港。
- 铁运时,发货地址和收货地址显示为站点选择,交互变为从基础数据选择站点。
- 非公路运输选择基础数据后,前侧展示名称或编码,后侧展示详细地址。
- 发货联系人、收货联系人使用输入框最长20字符。
- 发货联系方式、收货联系方式使用输入框最长20字符。
- 计划发货日期、计划完成日期使用日期选择控件。
- 支持通过选择常用线路自动回填发货地址、收货地址、联系人、联系方式等信息。
- 异常与边界:
- 发货地址或收货地址为空时阻止提交。
- 非公路运输未选择对应基础数据时阻止提交。
- 联系方式超过20字符或格式不合法时阻止提交。
- 联系人超过20字符时阻止提交。
- 计划完成日期早于计划发货日期时阻止提交。
- 切换运输类型后,不适用的收发货地址字段应清空或重新选择。
- 反例:
- 不允许水运、空运、铁运继续使用公路地址选择逻辑。
- 不允许运输类型切换后沿用旧类型地址数据。
- 不允许保存缺失发货地址或收货地址的模板。
---
### REQ-SHIPTPL-010 货物信息规则
- 功能点:
- 货物信息以明细行方式维护,每行可独立新增或删除。
- 货物名称使用输入框最长50字符非必填。
- 货物类型使用下拉搜索单选控件,必填,来源于货物类型基础数据。
- 包装使用下拉单选控件,非必填。
- 数量使用数字输入框,非必填。
- 数量单位使用下拉单选控件,非必填,枚举包含吨、件、立方米等。
- 品牌、规格、型号使用输入框最长50字符。
- 物料编码、设备编码使用输入框最长30字符。
- 货物行备注使用输入框最长50字符。
- 支持导入货物。
- 支持选择常用货物,将常用货物信息回填到货物明细行。
- 每行支持删除;最后一行支持新增一行。
- 异常与边界:
- 货物类型为空时阻止提交。
- 数量小于0或不是数值时阻止提交。
- 数量单位不在枚举范围内时阻止提交。
- 货物名称、品牌、规格、型号、行备注超过50字符时阻止提交。
- 物料编码、设备编码超过30字符时阻止提交。
- 删除最后一行货物明细时应保留一条空行或提示至少保留一行。
- 反例:
- 不允许无货物明细保存模板。
- 不允许货物类型为空的明细行参与保存。
- 不允许导入货物后覆盖用户已确认的不相关字段。
---
### REQ-SHIPTPL-011 附件规则
- 功能点:
- 支持上传模板相关附件。
- 附件为非必填,支持多个文件。
- 支持 JPG、PNG、PDF 等格式。
- 编辑时支持查看、追加或删除附件。
- 异常与边界:
- 附件格式不支持时阻止上传并提示。
- 附件上传失败时保留页面已填写内容。
- 删除附件时需确认用户操作,避免误删。
- 反例:
- 不允许不支持格式的附件静默上传成功。
- 不允许附件上传失败导致整页已填内容丢失。
- 不允许查看状态下上传或删除附件。
---
### REQ-SHIPTPL-012 选择常用地址
- 功能点:
- 支持打开选择常用地址弹窗。
- 弹窗支持按地址名称、地址类型、详细地址查询。
- 列表展示序号、地址名称、地址编号、类型、站点编码、详细地址、操作等字段。
- 常规地址的站点编码显示为“/”。
- 点击选择后,将地址名称、地址编号、类型、站点编码、详细地址等信息回填到当前发货地址或收货地址。
- 异常与边界:
- 查询无数据时展示空状态。
- 选择的数据已失效或不存在时提示重新选择。
- 地址类型与当前运输类型不匹配时不允许选择。
- 反例:
- 不允许选择后只回填地址名称而丢失详细地址。
- 不允许常规地址展示错误站点编码。
- 不允许水运、空运、铁运选择不匹配的常用地址类型。
---
### REQ-SHIPTPL-013 选择常用线路
- 功能点:
- 支持打开选择常用线路弹窗。
- 弹窗支持按线路名称、收发货地址查询。
- 列表展示序号、线路名称、线路编号、发货地、发货地址、发货联系人、收货地、收货地址、收货联系人、操作等字段。
- 点击选择后,自动回填发货地址、收货地址、发货联系人、发货联系方式、收货联系人、收货联系方式等信息。
- 异常与边界:
- 查询无数据时展示空状态。
- 线路已删除或不可用时提示重新选择。
- 常用线路的运输类型或地址类型与当前模板运输类型不匹配时不允许选择。
- 反例:
- 不允许选择线路后只回填发货地址而不回填收货地址。
- 不允许选择与当前运输类型不匹配的线路。
- 不允许选择失效线路。
---
### REQ-SHIPTPL-014 选择常用货物
- 功能点:
- 支持打开选择常用货物弹窗。
- 弹窗支持按货物名称、货物编号、货物类型等条件查询。
- 选择常用货物后,将货物名称、货物类型、包装、品牌、规格、型号、数量单位等信息回填到货物明细。
- 支持选择一条或多条常用货物,回填为一行或多行货物明细。
- 异常与边界:
- 查询无数据时展示空状态。
- 常用货物已删除或不可用时提示重新选择。
- 回填多条货物时应避免覆盖已有明细。
- 反例:
- 不允许选择常用货物后丢失必填的货物类型。
- 不允许多选回填时覆盖用户已有货物行。
- 不允许选择已失效或不可用货物。
---
### REQ-SHIPTPL-015 从模板新建
- 功能点:
- 支持在列表行操作中点击从模板新建。
- 系统根据当前发货模板的模板类型自动分流:
- 模板类型为运输计划时,跳转至“运输计划”模块的新建页面。
- 模板类型为常规运单或运单时,跳转至“运单管理”模块的新建页面。
- 跳转后目标页面自动回填当前模板可映射的数据,包含模板信息、基本信息、收发货信息、货物信息、附件等内容。
- 回填后用户可继续补充或调整目标模块中允许编辑的字段。
- 若模板包含目标模块特有字段,按目标模块规则进行回填,不适配字段不强制带入。
- 异常与边界:
- 模板数据已删除或不存在时提示数据不存在。
- 模板缺少目标模块必填字段时,跳转后应明确提示用户补齐。
- 模板类型无法识别时,不允许跳转,提示模板类型不支持。
- 跳转失败时不影响原模板数据。
- 反例:
- 不允许从模板新建时修改原模板数据。
- 不允许跳转错误目标模块。
- 不允许跳转后不回填模板数据。
- 不允许缺失模板数据时静默创建失败。
---
### REQ-SHIPTPL-016 单条删除
- 功能点:
- 支持通过行操作删除单条发货模板。
- 点击删除弹出二次确认弹窗。
- 确认后删除该模板,删除成功刷新列表,并提示“删除成功”。
- 异常与边界:
- 数据不存在时提示数据不存在,并刷新列表。
- 删除失败时提示失败原因,不改变列表其他数据。
- 用户取消二次确认时不执行删除。
- 反例:
- 不允许跳过二次确认直接删除。
- 不允许删除失败后仍提示成功。
- 不允许删除成功后列表不刷新。
---
### REQ-SHIPTPL-017 批量删除
- 功能点:
- 支持勾选多行发货模板后批量删除。
- 点击批量删除弹出二次确认弹窗。
- 确认后批量删除所选模板。
- 删除成功后刷新列表,并提示“删除成功”。
- 异常与边界:
- 未勾选数据时点击批量删除,应提示先选择需要删除的数据。
- 部分删除失败时应提示成功数量与失败数量。
- 用户取消二次确认时不执行删除。
- 反例:
- 不允许未选择数据时执行批量删除。
- 不允许因为部分失败而回滚已成功删除的数据,除非后端明确按事务处理。
- 不允许删除后不刷新列表。
---
### REQ-SHIPTPL-018 权限与审计
- 功能点:
- 根据用户权限控制查询、新建模板、编辑、删除、从模板新建、批量删除等操作入口。
- 创建人、创建时间、更新人、更新时间由系统自动记录。
- 列表、详情、导出预览等用户可见场景优先展示用户姓名,不直接展示用户 ID。
- 异常与边界:
- 无权限访问时提示权限不足。
- 无按钮权限时不展示对应按钮。
- 操作失败时不记录为成功日志。
- 反例:
- 不允许无权限用户执行新增、编辑、删除、从模板新建等操作。
- 不允许手动维护创建人、创建时间、更新人、更新时间。
- 不允许删除或篡改审计信息。
---
### REQ-SHIPTPL-019 运单模板布局
- 功能点:
- 运单类型页面采用模板信息、基本信息、收发货信息、货物信息、运费信息、附件等分区布局。
- 模板信息区包含模板编号、模板名称、模板类型、备注。
- 基本信息区包含项目、客户合同、发货计划、运输类型、备注。
- 收发货信息区包含发货地址、发货联系人、联系方式、收货地址、收货联系人、联系方式,以及计划发货日期、计划完成日期等字段。
- 货物信息区以表格方式维护货物明细,支持新增、删除、导入货物、常用货物。
- 底部固定展示保存模板、返回等操作按钮,操作区与内容区分离。
- 异常与边界:
- 切换到运单类型时,模板类型与下方布局必须联动切换。
- 非运单模板不展示运费信息区。
- 运单页面若从模板新建进入,应默认带入模板信息并维持运单布局。
- 反例:
- 不允许运单页面沿用发货模板的表单布局。
- 不允许将运单类型与模板类型混为同一字段。
- 不允许保存按钮被滚动内容遮挡。
---
### REQ-SHIPTPL-020 运费信息规则
- 功能点:
- 运输类型为铁、水、空时显示运费信息区。
- 运费信息区包含运费、数量、其他运费合计、运费合计、币种等字段。
- 运费合计为只读字段,由系统自动计算,规则为运费 + 其他运费合计。
- 如果货物的数量与计量单位已填写,且单价已填写,则系统自动计算运费,运费允许用户修改。
- 若数量、计量单位或单价缺少任一项,则不自动计算运费。
- 运输类型为公路时不展示该运费信息区。
- 币种默认显示 USD 或人民币等对应枚举值,支持下拉选择。
- 异常与边界:
- 运费、其他运费合计为数值输入最多2位小数。
- 运费合计不允许手工编辑。
- 当数量、计量单位、单价完整但自动计算失败时应提示计算异常。
- 运输类型切换后,运费信息区应按当前类型重新展示与计算。
- 反例:
- 不允许公路类型继续显示铁、水、空运费区。
- 不允许运费合计被手工改写。
- 不允许数量或单价缺失时仍强制自动计算运费。
---
### REQ-SHIPTPL-021 查看过程配置按钮
- 功能点:
- 底部提供查看过程配置按钮。
- 点击后弹出过程配置弹窗,展示该项目配置的现行过程节点。
- 若该项目未配置过程配置,则不展示该按钮。
- 弹窗仅用于查看,不允许修改过程节点配置。
- 异常与边界:
- 项目无过程配置时不显示查看过程配置按钮。
- 过程配置加载失败时提示失败原因。
- 弹窗关闭后不影响当前表单内容。
- 反例:
- 不允许未配置项目强制展示查看过程配置按钮。
- 不允许在查看弹窗中修改节点配置。
- 不允许按钮点击后丢失当前运单表单数据。
---
## 通用规则
- 发货模板用于沉淀运输计划或常规运单的常用填写内容,提升创建运输计划和运单的效率。
- 发货模板归属于新增主菜单“业务管理”。
- 模板编号由系统自动生成,规则为 MBJH + 8位年月日 + 3位流水号且不可编辑。
- 模板名称必填最长50字符。
- 项目、客户合同、运输类型、发货地址、收货地址、货物类型为核心必填项。
- 运输类型为公路时,收发货地址按地址选择逻辑处理;运输类型为水运或海运时选择港口,空运时选择空港,铁运时选择站点。
- 收发货地址从基础数据、常用地址或常用线路选择后,必须同步回填名称、编码、详细地址等关联信息。
- 货物信息至少保留一行,货物类型必填。
- 运单类型页面需支持查看过程配置按钮和运费信息区。
- 从模板新建时需按模板类型自动跳转到运输计划或运单管理模块,并回填模板可映射数据。
- 运费合计自动计算为运费加其他运费合计,并只读展示。
- 当数量、计量单位、单价完整时可自动计算运费,计算结果允许用户修改。
- 附件非必填,支持多个 JPG、PNG、PDF 等格式文件。
- 新增、编辑、删除、批量删除成功后均需刷新列表。

View File

@@ -0,0 +1,774 @@
# 合同管理需求卡
## 目录
- [REQ-CONTR-001 菜单入口](#req-contr-001-菜单入口)
- [REQ-CONTR-002 列表与分页](#req-contr-002-列表与分页)
- [REQ-CONTR-003 条件查询与重置](#req-contr-003-条件查询与重置)
- [REQ-CONTR-004 到期快捷筛选](#req-contr-004-到期快捷筛选)
- [REQ-CONTR-005 合同阶段与审核状态](#req-contr-005-合同阶段与审核状态)
- [REQ-CONTR-006 列表操作](#req-contr-006-列表操作)
- [REQ-CONTR-007 新增合同](#req-contr-007-新增合同)
- [REQ-CONTR-008 合同基本信息](#req-contr-008-合同基本信息)
- [REQ-CONTR-009 合同文件与附件](#req-contr-009-合同文件与附件)
- [REQ-CONTR-010 保存草稿](#req-contr-010-保存草稿)
- [REQ-CONTR-011 转临时合同](#req-contr-011-转临时合同)
- [REQ-CONTR-012 临时合同转正式合同](#req-contr-012-临时合同转正式合同)
- [REQ-CONTR-013 正式合同审批通过](#req-contr-013-正式合同审批通过)
- [REQ-CONTR-014 编辑合同](#req-contr-014-编辑合同)
- [REQ-CONTR-015 删除草稿合同](#req-contr-015-删除草稿合同)
- [REQ-CONTR-016 查看合同与流程](#req-contr-016-查看合同与流程)
- [REQ-CONTR-017 复制合同](#req-contr-017-复制合同)
- [REQ-CONTR-018 附件上传](#req-contr-018-附件上传)
- [REQ-CONTR-019 计费信息开关](#req-contr-019-计费信息开关)
- [REQ-CONTR-020 计费方案](#req-contr-020-计费方案)
- [REQ-CONTR-021 计费规则明细](#req-contr-021-计费规则明细)
- [REQ-CONTR-022 计费要素组合](#req-contr-022-计费要素组合)
- [REQ-CONTR-023 计费规则匹配条件](#req-contr-023-计费规则匹配条件)
- [REQ-CONTR-024 结算生成规则](#req-contr-024-结算生成规则)
- [REQ-CONTR-025 对账配置](#req-contr-025-对账配置)
- [REQ-CONTR-026 标准结算流程](#req-contr-026-标准结算流程)
- [REQ-CONTR-027 发起合同变更](#req-contr-027-发起合同变更)
- [REQ-CONTR-028 合同内容变更](#req-contr-028-合同内容变更)
- [REQ-CONTR-029 终止合同](#req-contr-029-终止合同)
- [REQ-CONTR-030 变更审批与生效](#req-contr-030-变更审批与生效)
- [REQ-CONTR-031 变更记录](#req-contr-031-变更记录)
- [REQ-CONTR-032 导出合同列表](#req-contr-032-导出合同列表)
- [REQ-CONTR-033 权限与审计](#req-contr-033-权限与审计)
- [通用规则](#通用规则)
---
### REQ-CONTR-001 菜单入口
- 功能点:
- 合同管理模块归属于“合同管理”主菜单。
- 在“合同管理”菜单下提供“合同管理”入口。
- 用户进入合同管理页面后,可进行查询、新增合同、复制、编辑、删除、转临时合同、转正式合同、变更、终止、附件上传、流程查看、导出等操作。
- 异常与边界:
- 无菜单权限时不展示“合同管理”入口。
- 有菜单权限但无按钮权限时,仅展示允许访问的页面和操作。
- 菜单或页面加载失败时提示失败原因,不影响其他模块访问。
- 反例:
- 不允许把合同管理挂到非合同管理主菜单下。
- 不允许无权限用户通过 URL 直接进入合同管理页面。
- 不允许菜单入口存在但页面无法访问。
---
### REQ-CONTR-002 列表与分页
- 功能点:
- 默认展示当前用户权限范围内的合同数据。
- 列表展示序号、合同编号、合同名称、所属项目、所属组织、合同类别、签约类型、甲方、乙方、开始日期、结束日期、临时效力起、临时效力止、经办人、创建时间、合同阶段、审核状态、当前节点、当前处理人、操作等字段。
- 合同名称支持点击查看合同详情。
- 合同阶段、审核状态和操作列固定在右侧,不随横向滚动条滑动。
- 默认按创建时间降序展示。
- 支持分页展示默认10条/页支持20/50/100条/页。
- 异常与边界:
- 查询无数据时展示空状态。
- 当前页数据因新增、编辑、提交、撤回、审批、变更、删除等操作变化后,应刷新列表并保持分页逻辑正确。
- 列表加载失败时保留当前查询条件和分页条件,提示用户重新查询。
- 反例:
- 不允许合同阶段、审核状态或操作列被横向滚动隐藏。
- 不允许列表展示经办人、当前处理人的用户 ID。
- 不允许分页后丢失当前查询条件。
---
### REQ-CONTR-003 条件查询与重置
- 功能点:
- 支持按合同类别、所属项目、合同名称、合同编号、生效类型、审核状态、所属组织、合同阶段、签约类型等条件组合查询。
- 合同类别、生效类型、审核状态、合同阶段、签约类型使用下拉单选或多选控件。
- 所属项目使用搜索选择控件。
- 合同名称、合同编号、所属组织使用输入框,支持模糊查询。
- 点击查询后按当前条件刷新列表。
- 点击重置后清空所有查询条件,恢复默认列表。
- 异常与边界:
- 查询条件为空时点击查询,按默认规则展示全部权限范围内数据。
- 查询结果为空时展示空状态,不清空用户已输入条件。
- 重置后页码恢复第一页,并重新加载列表。
- 反例:
- 不允许重置后仍保留隐藏查询条件。
- 不允许只按单个条件查询而忽略其他已选择条件。
- 不允许查询后改变列表字段或操作范围。
---
### REQ-CONTR-004 到期快捷筛选
- 功能点:
- 列表支持按合同到期时间快捷筛选。
- 快捷筛选项包含全部、30天内到期、90天内到期、90天以上、已到期。
- 每个筛选项展示当前条件下的数量。
- 点击快捷筛选项后,列表按对应到期范围刷新。
- 异常与边界:
- 合同结束日期为空时不计入到期筛选统计。
- 选择快捷筛选后再点击查询,应与其他查询条件组合生效。
- 重置查询条件后快捷筛选恢复全部。
- 反例:
- 不允许快捷筛选忽略用户已选择的其他查询条件。
- 不允许已到期合同仍统计到30天内到期。
- 不允许数量统计与列表结果不一致。
---
### REQ-CONTR-005 合同阶段与审核状态
- 功能点:
- 合同阶段包含草稿、临时合同、正式合同、已终止。
- 审核状态包含草稿、审批中、已驳回、审批通过、变更审批中、变更驳回、变更审批通过。
- 草稿表示新增合同草稿,尚未转临时合同,无业务生效。
- 临时合同表示草稿转临时成功,临时有效期内可执行短期结算。
- 正式合同表示临时合同或合同草稿经审批通过后正式生效。
- 已终止表示正式合同经终止变更审批通过后生命周期结束。
- 异常与边界:
- 状态为空或未知时,列表应展示兜底文案,并限制高风险操作。
- 状态变更后需刷新列表和详情,确保合同阶段、审核状态、操作按钮一致。
- 审核状态与合同阶段不匹配时,应提示数据异常并限制提交、变更、结算等操作。
- 反例:
- 不允许草稿合同执行业务结算。
- 不允许临时合同永久有效。
- 不允许已终止合同继续新建运单或结算单。
---
### REQ-CONTR-006 列表操作
- 功能点:
- 草稿合同支持复制、编辑、删除。
- 临时合同草稿状态支持复制、编辑。
- 临时合同审批中支持复制、查看流程、撤回。
- 临时合同已驳回支持复制、编辑、重新提交转正。
- 正式合同审批通过支持复制、发起变更、附件上传、查看流程。
- 正式合同变更审批中支持复制、查看流程、撤回变更。
- 正式合同变更驳回支持复制、编辑变更内容、重新提交变更。
- 正式合同变更审批通过支持复制、再次发起变更、附件上传、查看流程。
- 已终止合同仅支持复制和查看流程。
- 异常与边界:
- 操作执行前需重新校验合同最新阶段和审核状态。
- 用户无按钮权限时不展示对应操作入口。
- 状态变更成功后刷新列表,展示最新可操作按钮。
- 反例:
- 不允许临时合同转正审批中继续编辑合同正文。
- 不允许正式合同直接删除。
- 不允许已终止合同发起新变更。
---
### REQ-CONTR-007 新增合同
- 功能点:
- 点击“新增合同”后进入新建合同页面。
- 新建合同页面包含基本信息、计费信息、结算生成规则、对账配置、其它附件、变更记录等区域。
- 新建合同支持保存、提交临时合同、提交正式合同、关闭。
- 合同编号不填写时由系统自动生成,生成后只读不可编辑。
- 草稿保存不触发审批流程。
- 异常与边界:
- 新建页面打开失败时提示失败原因,不改变列表数据。
- 基础信息必填项为空时不允许保存为有效草稿。
- 提交临时合同或正式合同时需按对应规则校验必填项和附件。
- 重复点击保存或提交时应防止重复生成合同。
- 反例:
- 不允许用户重复提交生成多个相同合同。
- 不允许没有合同名称和合同类型的合同草稿保存成功。
- 不允许合同编号由用户随意修改。
---
### REQ-CONTR-008 合同基本信息
- 功能点:
- 基本信息包含签约类型、合同编号、合同名称、合同类型、甲方、乙方、所属项目、合同期限、所属组织、经办人、签订日期、结算方式、合同格式、是否需要加盖法人章、一式份数、回款账期、备注等字段。
- 签约类型为必填字段,使用下拉单选控件,枚举包含新签、续签。
- 合同编号为必填字段,可不填写,由系统自动生成。
- 合同名称为必填字段,使用输入框。
- 合同类型为必填字段,枚举包含客户合同、承运商合同等。
- 甲方、乙方为必填字段,从客商或组织档案选择。
- 所属项目从项目管理中选择。
- 合同期限为必填日期范围字段。
- 签订日期为必填日期字段。
- 结算方式、合同格式、是否需要加盖法人章、一式份数、回款账期、备注按合同业务要求填写。
- 备注最多2000字。
- 异常与边界:
- 转临时合同时,签约人、签约日期、合同期限可不填。
- 提交正式合同时,所有必填字段必须完整,包含签约人、签约日期、合同期限。
- 合同期限开始日期晚于结束日期时阻止提交。
- 备注超过2000字时阻止保存或提交。
- 反例:
- 不允许正式合同缺少签订日期或合同期限。
- 不允许合同期限结束日期早于开始日期。
- 不允许甲方、乙方为空后提交正式合同。
---
### REQ-CONTR-009 合同文件与附件
- 功能点:
- 合同文件区域用于上传合同主文件。
- 其它附件区域用于上传银行承兑汇票、补充材料等合同相关附件。
- 附件列表展示序号、文件名、附件描述、文件大小、上传人、上传时间、操作等字段。
- 支持批量下载附件。
- 支持 pdf、bmp、jpeg、png、jpg、doc、docx、ppt、pptx、xlsx、xls、eml、msg、zip 等格式文件。
- 单个文件大小限制500M。
- 异常与边界:
- 保存草稿或转临时合同时,附件可不上传。
- 提交正式合同时,应校验所有必须附件已上传。
- 文件格式不支持或大小超过500M时阻止上传并提示。
- 删除附件前需二次确认。
- 反例:
- 不允许正式合同缺少必须合同文件仍提交审批。
- 不允许上传超过500M的文件。
- 不允许附件上传成功后列表不展示文件信息。
---
### REQ-CONTR-010 保存草稿
- 功能点:
- 新建或编辑页面点击保存后,临时保存当前填写内容。
- 保存后合同阶段为草稿,审核状态为草稿。
- 保存草稿只要求基础信息满足最小必填校验,附件可不上传。
- 草稿合同无业务生效,不可创建运单和结算单。
- 异常与边界:
- 基础信息缺失导致无法识别合同主体时阻止保存并提示。
- 保存失败时保留用户已填写内容。
- 草稿数据已删除或状态已变化时,不允许继续保存。
- 反例:
- 不允许保存草稿触发审批流程。
- 不允许草稿合同执行结算过程。
- 不允许保存失败后清空用户输入。
---
### REQ-CONTR-011 转临时合同
- 功能点:
- 草稿合同支持提交临时合同。
- 转临时合同时,签约人、签约日期、合同期限可不填。
- 转临时合同只校验基础信息必填项,附件可不上传。
- 转临时成功后合同阶段变为临时合同,审核状态保持草稿。
- 临时合同有效期为30天。
- 临时合同可执行结算过程。
- 转临时合同后不可删除。
- 异常与边界:
- 基础信息缺失时不允许转临时合同。
- 临时有效期计算失败时不允许转临时合同。
- 转临时合同失败时保持草稿状态,并保留用户填写内容。
- 反例:
- 不允许草稿合同未转临时就执行结算。
- 不允许转临时合同后继续删除。
- 不允许临时合同有效期超过30天。
---
### REQ-CONTR-012 临时合同转正式合同
- 功能点:
- 临时合同支持补全信息后提交正式合同审批。
- 转正式合同前需完成所有正式合同必填内容。
- 转正式合同前需上传所有必须附件。
- 转正式合同需走审批流程。
- 提交成功后合同阶段保持临时合同,审核状态更新为审批中。
- 审批通过后合同阶段转为正式合同。
- 异常与边界:
- 正式合同必填字段缺失时阻止提交审批并提示。
- 必须附件缺失时阻止提交审批并提示缺失附件。
- 临时合同已超过30天有效期时应提示临时效力已过期并按业务规则限制继续结算。
- 审批流程发起失败时保持原状态,并保留用户输入内容。
- 反例:
- 不允许资料未补齐的临时合同转正式审批。
- 不允许转正式合同绕过审批。
- 不允许提交失败后仍把合同阶段改为正式合同。
---
### REQ-CONTR-013 正式合同审批通过
- 功能点:
- 当前审批节点处理人可对审批中合同执行审批通过。
- 全部审批通过后,合同阶段更新为正式合同,审核状态更新为审批通过。
- 正式合同永久正式生效,完整开放运单、结算、回款权限。
- 正式合同审批通过后,临时效力不再作为业务有效期判断依据。
- 异常与边界:
- 非当前审批节点处理人不允许审批通过。
- 审批通过时如合同已被终止或数据不存在,应阻止审批并提示。
- 审批状态更新失败时需保持审批前状态。
- 反例:
- 不允许未审批通过的合同成为正式合同。
- 不允许非审批处理人执行审批通过。
- 不允许正式合同仍按30天临时效力限制结算。
---
### REQ-CONTR-014 编辑合同
- 功能点:
- 草稿合同支持编辑。
- 临时合同草稿或已驳回状态支持编辑。
- 正式合同仅可通过变更流程修改合同内容。
- 编辑页面回显合同完整信息,合同编号只读不可编辑。
- 编辑保存后按当前阶段和审核状态保持或更新。
- 异常与边界:
- 编辑前需校验当前合同状态仍允许编辑。
- 合同已进入审批中时不允许直接编辑。
- 修改所属项目或合同类型时,应重新校验计费、结算、对账配置适用性。
- 编辑失败时保留用户修改内容。
- 反例:
- 不允许审批中合同直接编辑。
- 不允许正式合同绕过变更流程直接修改。
- 不允许编辑时修改系统生成的合同编号。
---
### REQ-CONTR-015 删除草稿合同
- 功能点:
- 未转临时的草稿合同支持删除。
- 点击删除后弹出二次确认弹窗。
- 删除前需校验合同未生效且未被业务数据引用。
- 删除成功后刷新列表。
- 异常与边界:
- 临时合同、正式合同、已终止合同均不允许删除。
- 存在运单、结算、应收应付、附件审批等业务引用时不允许删除。
- 删除时状态已变化为临时合同或审批中时,应阻止删除并刷新列表。
- 反例:
- 不允许删除转临时后的合同。
- 不允许删除正式合同。
- 不允许跳过二次确认直接删除。
---
### REQ-CONTR-016 查看合同与流程
- 功能点:
- 支持点击合同名称或行操作查看合同详情。
- 查看详情展示基本信息、计费信息、结算生成规则、对账配置、附件、变更记录等内容。
- 查看状态下所有字段只读。
- 支持查看审批流程节点、处理人、处理时间和审批意见。
- 异常与边界:
- 合同不存在或无权限查看时提示无法查看。
- 附件加载失败时不影响基础信息展示。
- 流程加载失败时提示失败原因,不改变合同状态。
- 反例:
- 不允许查看状态下保存修改。
- 不允许流程查看展示其他合同的审批记录。
- 不允许查看时自动切换为编辑状态。
---
### REQ-CONTR-017 复制合同
- 功能点:
- 支持通过列表行操作复制合同。
- 复制后生成一条新合同草稿。
- 新合同编号重新生成,合同阶段为草稿,审核状态为草稿。
- 复制时可带出原合同基本信息、计费规则、结算规则、对账配置和附件清单。
- 异常与边界:
- 原合同已删除或无权限查看时不允许复制。
- 复制后的合同名称应提示用户调整,避免与原合同混淆。
- 复制失败时不影响原合同数据。
- 反例:
- 不允许复制后沿用原合同编号。
- 不允许复制操作修改原合同。
- 不允许复制后直接生成临时合同或正式合同。
---
### REQ-CONTR-018 附件上传
- 功能点:
- 正式合同审批通过后支持附件上传。
- 合同变更过程中支持上传变更材料。
- 附件上传成功后自动新增附件行,展示文件名、大小、上传人、上传时间。
- 支持批量下载附件。
- 附件删除按合同阶段、审核状态和权限控制。
- 异常与边界:
- 审批中合同不允许随意删除已提交审批附件。
- 附件上传失败时保留当前附件列表并提示失败原因。
- 变更材料与普通附件需按业务场景区分归档。
- 反例:
- 不允许附件上传后不归属任何合同或变更记录。
- 不允许审批材料被无权限用户删除。
- 不允许变更材料混入普通附件后无法追溯。
---
### REQ-CONTR-019 计费信息开关
- 功能点:
- 合同基本信息支持配置是否自动生成费用明细。
- 开启自动生成费用明细时,系统展示计价配置页面,并按合同计费规则自动核算单票运单总费用,生成应收或应付。
- 关闭自动生成费用明细时,不展示计价配置页面。
- 关闭后费用入账依据导入结算账单或手动生成账单。
- 自动生成费用明细适用于先做运单,再按合同运价自动核算的业务场景。
- 异常与边界:
- 开启自动生成费用明细但未配置计费方案时,提交正式合同需阻止并提示。
- 关闭自动生成费用明细后,不得继续要求填写计费方案。
- 合同已产生自动费用明细后,关闭开关需按变更流程处理。
- 反例:
- 不允许开关关闭时仍展示计价配置并强制填写。
- 不允许开关开启但没有任何计费规则就提交正式合同。
- 不允许已生效合同绕过变更直接修改费用生成方式。
---
### REQ-CONTR-020 计费方案
- 功能点:
- 计费信息区域支持新增、编辑、删除计费方案。
- 计费方案包含方案名称、默认方案标识、备注和计费规则明细。
- 方案名称为必填字段。
- 同一合同内可维护多套计费方案。
- 同一合同内只能设置一个默认方案。
- 异常与边界:
- 方案名称为空时阻止提交并提示。
- 删除默认方案前需提示用户重新指定默认方案。
- 已被费用明细引用的计费方案不允许直接删除。
- 反例:
- 不允许一个合同存在多个默认计费方案。
- 不允许无方案名称的计费方案保存。
- 不允许删除已被历史运单费用引用的计费方案。
---
### REQ-CONTR-021 计费规则明细
- 功能点:
- 计费方案内支持添加多条计费规则。
- 计费规则字段包含费用类型、费用项、计费要素、计费类型、计费单位、单价、计费要素下限、计费要素上限、保底计费量、备注和操作。
- 费用类型包含运输费、其它费用等。
- 费用项包含运费、装卸车费、提货费、送货费、保险费等。
- 计费类型包含固定单价、区间单价、阶梯单价、区间阶梯一口价、固定一口价等。
- 同一套计费方案内,同一条计费要素可配置多行区间。
- 同一条计费要素的区间上下限必须连续、不重叠、无间隙。
- 前端保存时需自动校验区间冲突。
- 异常与边界:
- 费用类型、费用项、计费要素、计费类型、计费单位、单价等核心字段为空时阻止保存。
- 计费要素上下限不连续、重叠或存在间隙时阻止保存并提示。
- 单价或保底计费量为负数时阻止保存。
- 已生效计费规则被历史费用引用时,修改需走合同变更流程。
- 反例:
- 不允许同一计费要素区间重叠。
- 不允许单价填写负数。
- 不允许计费规则被引用后直接删除导致历史费用无法追溯。
---
### REQ-CONTR-022 计费要素组合
- 功能点:
- 计费要素支持按重量、按体积、按车辆、按里程、按吨公里、固定金额整单一口价、按数量等类型。
- 按重量支持固定单价、区间单价、阶梯单价、区间阶梯一口价,适用于整车散货按吨计价、分吨位阶梯调价、吨位区间一口价。
- 按体积支持固定单价、区间单价、阶梯单价、区间阶梯一口价,适用于轻泡货按立方计费。
- 按车辆支持固定单价,适用于包月固定车费、单次车辆包干费。
- 按里程支持固定单价、区间单价、阶梯单价、区间阶梯一口价,适用于长途干线按公里计价。
- 按吨公里支持固定单价、区间单价、阶梯单价、区间阶梯一口价,适用于水陆联运复合计量计费。
- 固定金额整单一口价支持固定一口价,适用于同城配送、短途整车包干。
- 按数量支持固定单价、区间单价、阶梯单价、区间阶梯一口价,适用于小件零担按件计费。
- 异常与边界:
- 计费要素与计费类型不匹配时阻止保存并提示。
- 计费单位需与计费要素匹配,例如重量对应元/吨,里程对应元/公里。
- 复合计量场景需明确计费要素来源,避免同一运单重复计费。
- 反例:
- 不允许按车辆计费配置阶梯单价。
- 不允许按里程计费用元/吨作为计费单位。
- 不允许一个费用项重复命中多个同优先级计费规则。
---
### REQ-CONTR-023 计费规则匹配条件
- 功能点:
- 计费规则支持设置匹配条件。
- 匹配条件包含起运地、目的地、运输方式、货物类型等字段。
- 起运地和目的地支持按省、市、区县配置。
- 运输方式和货物类型使用下拉选择。
- 保存匹配条件后,系统在执行运单计费时按条件匹配对应计费规则。
- 支持新增多组匹配条件。
- 异常与边界:
- 匹配条件为空时视为通用规则或按业务默认规则处理。
- 多组匹配条件冲突时,应提示用户调整。
- 保存失败时保留用户已填写条件。
- 反例:
- 不允许同一费用项存在无法区分优先级的重复匹配条件。
- 不允许保存后匹配条件与计费规则脱离。
- 不允许匹配条件冲突时静默覆盖旧规则。
---
### REQ-CONTR-024 结算生成规则
- 功能点:
- 合同支持配置是否自动生成预结算单。
- 开启自动生成预结算单时,到达预设账单日,系统汇总当期所有应收或应付,生成统一内部预结算单。
- 关闭自动生成预结算单时,不自动生成预结算单,可手动选择应收应付转预结算单。
- 账单起始日期、结算类型、账单周期类型、账单截单日、周期天数等字段按结算规则配置。
- 结算时以合同截止时间产生的运单为有效单据,支持超合同有效期发起结算。
- 异常与边界:
- 开启自动生成预结算单时,账单起始日期、结算类型、账单周期类型等必填字段缺失应阻止提交。
- 周期天数不合法时阻止保存。
- 自动预结算任务失败时,应保留补偿处理记录。
- 反例:
- 不允许开启自动预结算但缺少账单周期配置。
- 不允许合同截止前产生的有效运单因合同已过期而不能结算。
- 不允许关闭自动预结算后仍自动生成预结算单。
---
### REQ-CONTR-025 对账配置
- 功能点:
- 合同支持配置是否跳过对账。
- 开启跳过对账后,预结算单手动确认后直接生成正式结算单。
- 关闭跳过对账后,预结算单需按对账模式完成对账后生成正式结算单。
- 对账模式包含明细逐行核对和仅核对总额。
- 明细逐行核对要求外部账单明细与内部账单明细一一匹配,所有明细匹配后才可完成对账。
- 仅核对总额要求外部账单总条数、总金额、总货量一致后即可完成对账,不按明细逐行核对。
- 异常与边界:
- 未选择是否跳过对账时阻止提交正式合同。
- 不跳过对账时,对账模式必填。
- 明细逐行核对存在不匹配明细时,不允许完成对账。
- 仅核对总额存在总条数、总金额或总货量不一致时,不允许完成对账。
- 反例:
- 不允许开启跳过对账后仍强制进入对账流程。
- 不允许未选择对账模式就保存正式合同配置。
- 不允许明细不匹配或总额不一致时完成对账。
---
### REQ-CONTR-026 标准结算流程
- 功能点:
- 合同标准结算流程为合同配置计费规则、执行运单、生成应收应付、账期自动生成预结算、对账、正式结算。
- 自动生成费用明细开启时,运单执行后系统按合同计费规则生成应收应付。
- 自动生成预结算单开启时,系统按账期汇总生成预结算。
- 对账完成或配置跳过对账后,系统生成正式结算单。
- 异常与边界:
- 合同未生效、计费规则不完整或费用明细开关关闭时,不按合同计费规则自动生成应收应付。
- 运单未完成或不满足结算条件时,不进入预结算。
- 合同已终止后,不允许新建运单或新结算单,但历史单据可正常对账回款。
- 反例:
- 不允许草稿合同进入标准结算流程。
- 不允许已终止合同新建业务单据。
- 不允许跳过应收应付直接生成正式结算单,除非业务配置明确支持。
---
### REQ-CONTR-027 发起合同变更
- 功能点:
- 审批通过的正式合同支持发起变更。
- 变更类型包含合同信息变更、终止合同。
- 发起变更后打开变更页面,自动带入原合同完整信息。
- 变更页面支持修改允许变更的合同字段、计费规则、附件和变更说明。
- 变更需要走审核流程。
- 异常与边界:
- 非审批通过正式合同不允许发起变更。
- 存在未完成变更审批时,不允许重复发起变更。
- 已终止合同不允许发起变更。
- 反例:
- 不允许临时合同发起合同内容变更。
- 不允许变更页面为空白而不带入原合同数据。
- 不允许同一合同同时存在多个审批中的变更单。
---
### REQ-CONTR-028 合同内容变更
- 功能点:
- 合同内容变更用于调整正式合同的基础信息、计费规则、结算规则、对账配置和附件。
- 变更内容需填写本次修改说明。
- 变更原因需填写本次变更原因。
- 变更内容和变更原因最多2000字。
- 内容变更审批通过后更新合同计费和基础信息。
- 异常与边界:
- 变更内容或变更原因为空时阻止提交审批。
- 文本超过2000字时阻止提交。
- 变更审批通过前,原合同条款保持正常履约,变更内容暂不生效。
- 反例:
- 不允许变更说明为空后发起审批。
- 不允许审批未通过的变更内容影响正式合同。
- 不允许超过2000字的变更原因提交。
---
### REQ-CONTR-029 终止合同
- 功能点:
- 终止合同属于合同变更类型。
- 终止合同需填写变更原因和上传变更材料。
- 终止申请提交后进入变更审批中。
- 终止审批通过后合同阶段更新为已终止。
- 已终止合同彻底停用,禁止新建任何运单、结算单据。
- 历史单据可正常对账回款。
- 异常与边界:
- 终止审批未通过前,合同仍按原状态处理。
- 存在未完成变更审批时,不允许再次发起终止。
- 终止失败时保持原合同状态,并提示失败原因。
- 反例:
- 不允许未审批通过就终止合同。
- 不允许已终止合同继续新建运单或结算单。
- 不允许终止合同影响历史应收应付对账回款。
---
### REQ-CONTR-030 变更审批与生效
- 功能点:
- 变更提交后审核状态更新为变更审批中。
- 变更审批通过后审核状态更新为变更审批通过。
- 合同内容变更审批通过后,新内容正式生效。
- 终止合同审批通过后,合同阶段更新为已终止。
- 变更驳回后审核状态更新为变更驳回,允许编辑后重新提交。
- 异常与边界:
- 变更审批未通过前,不得覆盖原正式合同信息。
- 审批过程中合同已终止或数据不存在时,不允许继续审批。
- 变更审批失败时保持原合同数据不变,并提示失败原因。
- 反例:
- 不允许变更草稿直接影响正式合同。
- 不允许审批未通过却生成正式变更结果。
- 不允许变更驳回后仍按变更内容计费。
---
### REQ-CONTR-031 变更记录
- 功能点:
- 合同详情和编辑页面展示变更记录。
- 变更记录展示序号、变更日期、经办人、变更类型、变更原因、状态、操作等字段。
- 支持查看变更审批流程。
- 变更记录在变更提交后生成,并在详情页可查看。
- 变更记录需保留变更前后关键字段。
- 异常与边界:
- 变更记录加载失败时提示失败原因,不影响合同基础信息查看。
- 查看历史变更记录时全字段只读。
- 变更记录与合同不匹配时不展示,并提示数据异常。
- 反例:
- 不允许删除已审批通过的变更记录。
- 不允许变更记录只保存变更原因而无变更内容。
- 不允许跨合同展示变更记录。
---
### REQ-CONTR-032 导出合同列表
- 功能点:
- 列表顶部支持导出合同列表。
- 点击导出后,导出当前筛选条件下的合同基础信息、阶段状态、计费方案摘要、附件清单和变更记录到 Excel 文件。
- 导出字段与列表和详情展示字段保持一致。
- 数据量较大时页面展示导出中提示。
- 异常与边界:
- 当前查询条件下无数据时允许导出空文件或提示无可导出数据。
- 导出失败时提示失败原因,不改变当前列表状态。
- 无导出权限时不展示导出按钮。
- 反例:
- 不允许无权限用户导出合同数据。
- 不允许导出时忽略当前查询条件。
- 不允许导出文件展示用户 ID 代替姓名。
---
### REQ-CONTR-033 权限与审计
- 功能点:
- 根据用户权限控制查询、新增、复制、编辑、删除、转临时、转正式、审批、变更、终止、附件上传、流程查看、导出等操作入口。
- 列表、详情、导出、审批流中用户可见字段优先展示姓名,不直接展示用户 ID。
- 新增、保存、提交临时合同、提交正式合同、审批、撤回、复制、删除、变更、终止、附件上传、导出等关键操作需记录操作日志。
- 审计信息应包含操作人、操作时间、操作类型、合同编号、合同阶段、审核状态和关键变更内容。
- 异常与边界:
- 无按钮权限时不展示对应按钮,通过 URL 或接口绕过时也应拒绝。
- 操作日志记录失败时不得影响用户已成功的业务操作,但需保留可排查信息。
- 数据权限限制下,用户只能查看和操作授权范围内合同。
- 反例:
- 不允许无权限用户审批、变更、终止或导出合同。
- 不允许列表展示经办人、审批人等用户 ID。
- 不允许关键状态变更没有审计记录。
---
## 通用规则
- 合同管理用于维护客户合同、承运商合同等合同全生命周期,归属于“合同管理”主菜单。
- 合同阶段包含草稿、临时合同、正式合同、已终止;审核状态包含草稿、审批中、已驳回、审批通过、变更审批中、变更驳回、变更审批通过。
- 合同编号由系统自动生成,全局唯一,只读不可编辑。
- 暂存草稿无业务生效,不可执行结算流程。
- 草稿可提交为临时合同签约人、签约日期、合同期限可不填临时合同有效期30天可执行结算流程且不可删除。
- 临时合同转正式合同需补齐所有必填信息并上传所有必须附件,审批通过后转为正式合同。
- 正式合同可执行完整结算流程,并支持发起合同信息变更或终止合同变更。
- 标准结算流程为合同配置计费规则、执行运单、生成应收应付、账期自动生成预结算、对账、正式结算。
- 自动生成费用明细开启时,需维护计价配置;关闭时不展示计价配置,费用依据导入结算账单或手工生成账单入账。
- 自动生成预结算单开启时,系统按账期生成预结算;关闭时可手动选择应收应付转预结算。
- 是否跳过对账开启时,预结算单手动确认后直接生成正式结算单;关闭时需按明细逐行核对或仅核对总额完成对账。
- 同一套计费方案内,同一计费要素的多行区间必须连续、不重叠、无间隙。
- 变更审批通过前,原正式合同内容继续生效;变更审批通过后新内容生效或合同终止。
- 已终止合同禁止新建运单、结算单据,历史单据可正常对账回款。
- 列表、详情、导出、审批等用户可见场景优先展示姓名字段,不直接展示用户 ID。
- 新增、保存、提交、审批、撤回、复制、删除、变更、终止、附件上传、导出后均需刷新相关列表或详情状态。

View File

@@ -0,0 +1,249 @@
# 常用线路需求卡
## 目录
- [REQ-CYXL-001 列表与分页](#req-cyxl-001-列表与分页)
- [REQ-CYXL-002 条件查询与重置](#req-cyxl-002-条件查询与重置)
- [REQ-CYXL-003 查看常用线路](#req-cyxl-003-查看常用线路)
- [REQ-CYXL-004 新增常用线路](#req-cyxl-004-新增常用线路)
- [REQ-CYXL-005 编辑常用线路](#req-cyxl-005-编辑常用线路)
- [REQ-CYXL-006 线路编号与地址联动规则](#req-cyxl-006-线路编号与地址联动规则)
- [REQ-CYXL-007 批量导出](#req-cyxl-007-批量导出)
- [REQ-CYXL-008 单条删除](#req-cyxl-008-单条删除)
- [REQ-CYXL-009 批量删除](#req-cyxl-009-批量删除)
- [REQ-CYXL-010 组织权限与审计](#req-cyxl-010-组织权限与审计)
- [通用规则](#通用规则)
---
### REQ-CYXL-001 列表与分页
- 功能点:
- 默认展示当前组织全部常用线路数据,列表按更新时间降序排列。
- 展示组织、线路编号、线路名称、发货地、发货地址、收货地、收货地址、创建人、更新时间、创建时间、操作等字段。
- 常规类型站点编码在列表中显示为“/”,其他类型显示对应编码。
- 支持勾选多行数据用于批量导出、批量删除。
- 操作列固定展示查看、编辑、删除,具体显示受权限控制。
- 普通用户默认仅看当前组织数据;具备权限时可切换查看全部组织数据,但切换后的全部组织视图仅作只读查看。
- 分页默认10条/页支持20/50/100条/页。
- 异常与边界:
- 查询无数据时展示空状态。
- 当前页删除后无数据时自动回退页码并刷新列表。
- 切换组织范围时保留当前查询条件,仅重载数据。
- 列表加载失败时保留当前筛选与分页条件。
- 反例:
- 不允许默认按更新时间以外的字段覆盖排序规则。
- 不允许普通用户在全部组织视图下执行编辑、删除等写操作。
- 不允许分页后丢失当前查询条件。
---
### REQ-CYXL-002 条件查询与重置
- 功能点:
- 支持按线路名称、线路编号、发货地、发货地址、收货地、收货地址、组织等条件组合查询。
- 线路名称、发货地、发货地址、收货地、收货地址支持模糊查询。
- 线路编号支持精确匹配。
- 组织默认带出当前用户所属组织。
- 点击查询后按条件展示数据。
- 点击重置后清空所有条件并恢复默认列表。
- 异常与边界:
- 查询条件为空时点击查询,按默认规则展示当前组织全部数据。
- 查询结果为空时展示空状态,不清空用户已输入条件。
- 重置后页码恢复第一页,并按更新时间降序重新加载。
- 反例:
- 不允许重置后仍保留隐藏条件。
- 不允许只按单一条件查询而忽略组合条件。
- 不允许查询后改变列表字段展示范围。
---
### REQ-CYXL-003 查看常用线路
- 功能点:
- 支持通过行操作查看常用线路详情。
- 详情展示所有业务字段,字段全部置灰不可编辑。
- 查看弹窗底部仅显示关闭按钮。
- 异常与边界:
- 数据已删除或不存在时提示数据不存在,并刷新列表。
- 详情加载失败时提示失败原因,不影响列表现状。
- 反例:
- 不允许在查看状态下保存修改。
- 不允许查看时切换组织范围并修改数据。
- 不允许查看弹窗出现提交按钮。
---
### REQ-CYXL-004 新增常用线路
- 功能点:
- 支持新增常用线路,用于运单创建时发货地址与收货地址的快速选择来源。
- 线路编号由系统自动生成,规则为 XL + 5位流水号自增保存后全局唯一。
- 线路名称使用文本输入框必填长度不超过100字符。
- 发货地址使用地址选择器,必填,支持地图搜索选点或从常用地址库中选择。
- 收货地址使用地址选择器,必填,支持地图搜索选点或从常用地址库中选择。
- 选择地址后,系统自动带出地址名称、详细地址、经度、纬度,并回填到表单关联字段。
- 发货联系人、发货联系方式、收货联系人、收货联系方式使用文本输入框长度不超过50字符。
- 备注使用文本输入框长度不超过500字符。
- 保存成功后刷新列表。
- 异常与边界:
- 线路名称为空时阻止提交并提示。
- 同一组织内线路名称重复时阻止提交并提示“同一组织内线路名称不允许重复”。
- 发货地址或收货地址为空时阻止提交并提示。
- 发货地址和收货地址组合在同一组织内重复时阻止提交并提示。
- 发货联系方式、收货联系方式不符合手机号或固定电话格式时阻止提交并提示。
- 发货联系人、收货联系人超过50字符时阻止提交并提示。
- 发货联系方式、收货联系方式超过50字符时阻止提交并提示。
- 备注超过500字符时阻止提交并提示。
- 选择地址后未能正确回填地址名称、详细地址、经纬度时阻止提交。
- 提交失败时保留用户已填写内容,方便修正后重新提交。
- 反例:
- 不允许新增手工填写线路编号。
- 不允许在未选择发货地址或收货地址时保存。
- 不允许同一组织内存在同名线路。
- 不允许同一组织内存在相同发货地址与收货地址组合的线路。
---
### REQ-CYXL-005 编辑常用线路
- 功能点:
- 支持编辑已存在的常用线路。
- 线路编号在编辑状态下置灰不可编辑。
- 线路名称可编辑,校验规则与新增一致。
- 发货地址、收货地址可重新选择,校验规则与新增一致。
- 发货联系人、发货联系方式、收货联系人、收货联系方式、备注可编辑。
- 保存成功后刷新列表。
- 异常与边界:
- 同一组织内线路名称修改后仍需保持唯一。
- 同一组织内发货地址与收货地址组合修改后仍需保持唯一。
- 线路编号为空或被篡改时阻止提交。
- 编辑的数据已不存在时提示数据不存在,并刷新列表。
- 提交失败时保留用户修改内容。
- 反例:
- 不允许编辑时修改线路编号。
- 不允许编辑后产生同名线路。
- 不允许编辑后产生重复起讫点组合。
---
### REQ-CYXL-006 线路编号与地址联动规则
- 功能点:
- 线路编号由系统按 XL + 5位流水号自增自动生成。
- 线路编号在系统中全局唯一。
- 发货地址与收货地址支持地图搜索选点或常用地址库选择。
- 选择地址后自动回填地址名称、详细地址、经度、纬度。
- 切换发货地址或收货地址时,相关回填信息同步更新。
- 异常与边界:
- 编码生成失败时阻止保存并提示。
- 已存在相同线路编号时阻止保存并提示。
- 地址选择返回的信息不完整时阻止保存。
- 发货地址与收货地址任一项变更后应重新校验重复组合。
- 反例:
- 不允许手动编辑线路编号。
- 不允许地址未回填经纬度仍保存。
- 不允许沿用旧地址信息而不重新校验。
---
### REQ-CYXL-007 批量导出
- 功能点:
- 支持勾选多行数据后批量导出 Excel 文件。
- 未勾选数据时点击批量导出,默认导出当前查询条件下的全部数据。
- 导出字段与列表显示字段一致。
- 异常与边界:
- 当前条件下无数据时允许导出空文件或提示无可导出数据。
- 导出失败时提示失败原因,不改变当前勾选状态。
- 反例:
- 不允许只导出当前页数据。
- 不允许未筛选时导出非当前查询条件数据。
---
### REQ-CYXL-008 单条删除
- 功能点:
- 支持通过行操作删除单条常用线路。
- 点击删除弹出二次确认弹窗,提示删除后不可恢复。
- 删除成功后刷新列表,并提示“删除成功”。
- 异常与边界:
- 删除前必须校验是否已被运单、合同等业务数据引用。
- 已被引用时不允许删除,并提示“该线路已被业务引用,不可删除”。
- 数据不存在时提示数据不存在,并刷新列表。
- 反例:
- 不允许跳过二次确认直接删除。
- 不允许删除已被业务引用的常用线路。
---
### REQ-CYXL-009 批量删除
- 功能点:
- 支持勾选多行常用线路后批量删除。
- 点击批量删除弹出二次确认弹窗。
- 删除前逐一校验所选数据是否被业务引用。
- 已引用记录从删除队列剔除并单独提示。
- 删除成功后刷新列表并提示“删除成功XX条因引用跳过XX条”。
- 异常与边界:
- 未勾选数据时点击批量删除,应提示先选择需要删除的数据。
- 所选数据全部被引用时不执行删除,并提示全部跳过原因。
- 部分删除失败时应提示成功数量和跳过数量。
- 反例:
- 不允许批量删除已被业务引用的记录。
- 不允许因为部分记录被引用而影响可删除记录的正常删除。
- 不允许删除后不刷新列表。
---
### REQ-CYXL-010 组织权限与审计
- 功能点:
- 组织列作为数据权限字段,普通用户默认仅可查看当前组织数据。
- 具备权限的用户可切换查看全部组织数据,但该视图仅允许只读查看。
- 根据权限配置控制查看、编辑、删除、导出等操作入口。
- 创建人、创建时间、更新时间由系统自动记录。
- 异常与边界:
- 无权限用户访问时提示权限不足。
- 在全部组织只读视图中不开放新增、编辑、删除、导出等写操作。
- 列表、详情、导出等用户可见场景优先展示创建人姓名,不直接展示用户 ID。
- 反例:
- 不允许普通用户在全部组织视图下修改数据。
- 不允许手动维护创建人、创建时间、更新时间。
- 不允许删除或篡改审计信息。
---
## 通用规则
- 常用线路用于记录企业内高频使用的运输线路,作为运单创建时发货地址与收货地址的快速选择来源。
- 线路编号由系统自动生成,规则为 XL + 5位流水号自增且全局唯一。
- 同一组织内线路名称不允许重复。
- 同一组织内相同的发货地址与收货地址组合不允许重复创建。
- 发货地址与收货地址必须填写,且支持地图搜索选点或从常用地址库选择。
- 选择地址后必须自动回填地址名称、详细地址、经度、纬度。
- 发货联系方式、收货联系方式需校验手机号或固定电话格式。
- 备注最多500字符。
- 删除前必须校验业务引用,已引用线路不允许删除。
- 新增、编辑、导出成功后均需刷新列表。

View File

@@ -0,0 +1,320 @@
# 常用货物需求卡
## 目录
- [REQ-CYHW-001 列表与分页](#req-cyhw-001-列表与分页)
- [REQ-CYHW-002 条件查询与重置](#req-cyhw-002-条件查询与重置)
- [REQ-CYHW-003 查看常用货物](#req-cyhw-003-查看常用货物)
- [REQ-CYHW-004 新增常用货物](#req-cyhw-004-新增常用货物)
- [REQ-CYHW-005 编辑常用货物](#req-cyhw-005-编辑常用货物)
- [REQ-CYHW-006 编号与级联规则](#req-cyhw-006-编号与级联规则)
- [REQ-CYHW-007 下载导入模板](#req-cyhw-007-下载导入模板)
- [REQ-CYHW-008 批量导入](#req-cyhw-008-批量导入)
- [REQ-CYHW-009 批量导出](#req-cyhw-009-批量导出)
- [REQ-CYHW-010 单条删除](#req-cyhw-010-单条删除)
- [REQ-CYHW-011 批量删除](#req-cyhw-011-批量删除)
- [REQ-CYHW-012 组织权限与审计](#req-cyhw-012-组织权限与审计)
- [通用规则](#通用规则)
---
### REQ-CYHW-001 列表与分页
- 功能点:
- 默认展示当前组织全部常用货物数据,列表按更新时间降序排列。
- 展示组织、一级货物类型、二级货物类型、货物名称、货物编号、品牌、包装、货值、规格、计价单位、型号、说明1、尺寸、说明2、备注、创建人、更新时间、创建时间、操作等字段。
- 支持勾选多行数据用于批量导出、批量删除。
- 操作列固定展示查看、编辑、删除,具体显示受权限控制。
- 普通用户默认仅看当前组织数据;具备权限时可切换查看全部组织数据,但切换后的全部组织视图仅作只读查看。
- 分页默认10条/页支持20/50/100条/页。
- 异常与边界:
- 查询无数据时展示空状态。
- 当前页删除后无数据时自动回退页码并刷新列表。
- 切换组织范围时保留当前查询条件,仅重载数据。
- 列表加载失败时保留当前筛选与分页条件。
- 反例:
- 不允许默认按更新时间以外的字段覆盖排序规则。
- 不允许普通用户在全部组织视图下执行编辑、删除等写操作。
- 不允许分页后丢失当前查询条件。
---
### REQ-CYHW-002 条件查询与重置
- 功能点:
- 支持按一级货物类型、二级货物类型、货物名称、货物编号、品牌、组织等条件组合查询。
- 点击查询后按条件展示数据。
- 点击重置后清空所有条件并恢复默认列表。
- 异常与边界:
- 查询条件为空时点击查询,按默认规则展示当前组织全部数据。
- 查询结果为空时展示空状态,不清空用户已输入条件。
- 重置后页码恢复第一页,并按更新时间降序重新加载。
- 反例:
- 不允许重置后仍保留隐藏条件。
- 不允许只按单一条件查询而忽略组合条件。
- 不允许查询后改变列表字段展示范围。
---
### REQ-CYHW-003 查看常用货物
- 功能点:
- 支持通过行操作查看常用货物详情。
- 详情展示所有业务字段,字段全部置灰不可编辑。
- 查看弹窗底部仅显示关闭按钮。
- 异常与边界:
- 数据已删除或不存在时提示数据不存在,并刷新列表。
- 详情加载失败时提示失败原因,不影响列表现状。
- 反例:
- 不允许在查看状态下保存修改。
- 不允许查看时切换组织范围并修改数据。
- 不允许查看弹窗出现提交按钮。
---
### REQ-CYHW-004 新增常用货物
- 功能点:
- 支持新增常用货物档案,货物所属类别通过一级货物类型与二级货物类型级联选择定义。
- 一级货物类型使用下拉搜索选择。
- 二级货物类型使用下拉搜索选择,未选一级货物类型时置灰不可操作;切换一级货物类型时二级选项自动清空并重新加载。
- 一级货物类型、二级货物类型、货物名称、货物编号为必填字段,字段名称需展示“*”标识。
- 选择二级货物类型后其编码自动带出为货物编号前4位前缀。
- 货物编号由二级货物类型编码前4位 + 用户输入2位数字后缀组成共6位。
- 货物名称使用文本输入框必填长度不超过100字符。
- 货物编号后缀使用文本输入框必填仅允许输入2位数字。
- 品牌使用文本输入框选填长度不超过50字符。
- 货值使用数字输入框选填保留2位小数计价单位使用下拉选择。
- 规格使用文本输入框选填长度不超过50字符。
- 型号使用文本输入框选填长度不超过50字符。
- 说明1使用文本输入框选填长度不超过100字符。
- 尺寸使用文本输入框选填长度不超过50字符。
- 说明2使用文本输入框选填长度不超过100字符。
- 备注使用文本域选填长度不超过200字符。
- 包装为下拉选择,选项包含袋装、散装、桶装、箱装、托盘等。
- 计价单位为下拉选择,选项包含元/吨、元/立方米、元/件、元/车。
- 保存成功后刷新列表。
- 异常与边界:
- 一级货物类型为空时阻止提交并提示“请选择一级货物类型”。
- 二级货物类型为空时阻止提交并提示“请选择二级货物类型”。
- 货物名称为空、超长或格式不合法时阻止提交并提示“请输入货物名称”。
- 货物编号后缀为空、不是2位数字或货物编号不满6位时阻止提交并提示“请输入2位数字”。
- 选择二级货物类型后未带出前4位编码时阻止提交。
- 二级货物类型不属于当前一级货物类型时阻止提交。
- 货物编号重复时阻止提交并提示“该货物编号已存在”。
- 货值小于0或小数位超过2位时阻止提交。
- 包装或计价单位不在允许选项内时阻止提交。
- 品牌、规格、型号、尺寸超过50字符时阻止提交。
- 说明1、说明2超过100字符时阻止提交。
- 备注超过200字符时阻止提交并提示。
- 提交失败时保留用户已填写内容。
- 反例:
- 不允许在未选择一级货物类型时选择二级货物类型。
- 不允许手工修改二级货物类型带出的前4位编号前缀。
- 不允许保存重复货物编号。
- 不允许新增启用/停用状态字段。
---
### REQ-CYHW-005 编辑常用货物
- 功能点:
- 支持编辑已存在的常用货物档案。
- 一级货物类型、二级货物类型在编辑状态下置灰不可编辑。
- 货物编号在编辑状态下置灰不可编辑。
- 货物名称、品牌、包装、货值、规格、计价单位、型号、说明1、尺寸、说明2、备注等字段可编辑。
- 可编辑字段的控件类型、长度限制和校验规则与新增一致。
- 保存成功后刷新列表。
- 异常与边界:
- 货物名称、货值、包装、计价单位、备注等字段的校验规则与新增保持一致。
- 编辑的数据已不存在时提示数据不存在,并刷新列表。
- 提交失败时保留用户修改内容。
- 反例:
- 不允许编辑时修改一级货物类型、二级货物类型和货物编号。
- 不允许编辑后产生重复货物编号。
- 不允许编辑状态下绕过新增校验规则。
---
### REQ-CYHW-006 编号与级联规则
- 功能点:
- 常用货物所属类别由一级货物类型和二级货物类型级联定义。
- 一级货物类型变更时,二级货物类型选项同步刷新。
- 二级货物类型必须归属于当前选择的一级货物类型。
- 二级货物类型编码作为货物编号前4位来源。
- 货物编号全局唯一。
- 异常与边界:
- 一级货物类型未选择时二级货物类型下拉置灰不可操作。
- 切换一级货物类型后已选二级货物类型自动清空。
- 二级货物类型编码不存在时不允许继续保存。
- 二级货物类型与一级货物类型不匹配时不允许继续保存。
- 货物编号重复时阻止提交并提示“该货物编号已存在”。
- 反例:
- 不允许货物编号脱离二级货物类型单独维护。
- 不允许一级、二级货物类型选错后仍沿用旧编号前缀。
- 不允许出现同一货物编号多条记录。
---
### REQ-CYHW-007 下载导入模板
- 功能点:
- 支持下载 Excel 导入模板。
- 模板字段包含一级货物类型、二级货物类型编码、货物名称、货物编号后缀、品牌、包装、单价、规格、计价单位、型号、说明1、尺寸、说明2、备注。
- 模板中一级货物类型、二级货物类型编码、货物名称、货物编号后缀为必填字段,字段名称需增加“*”标识。
- 模板中的字段顺序与导入校验顺序保持一致。
- 模板字段说明需标明货物名称不超过100字符品牌、规格、型号、尺寸不超过50字符说明1、说明2不超过100字符备注不超过200字符。
- 模板用于批量导入常用货物档案。
- 异常与边界:
- 模板下载失败时提示失败,允许重新下载。
- 模板字段缺失或顺序错误时视为模板异常,应修正后再发布。
- 模板应附带字段说明明确货物编号后缀为2位数字单价为数值且保留2位小数。
- 反例:
- 不允许模板缺少一级货物类型、二级货物类型编码、货物名称、货物编号后缀等关键字段。
- 不允许模板包含与导入无关的状态字段。
- 不允许模板字段与导入校验字段不一致。
---
### REQ-CYHW-008 批量导入
- 功能点:
- 支持上传 Excel 文件批量导入常用货物。
- 导入时逐条校验必填项、长度、数字格式、货物编号唯一性、二级货物类型编码是否存在等规则。
- 导入时根据二级货物类型编码自动取得4位货物编号前缀并与货物编号后缀组成完整货物编号。
- 导入成功的数据标记为“批量导入”。
- 存在失败行时自动生成错误报告 Excel 并自动下载,失败文件字段顺序与原模板一致,并在最后一列追加“导入失败原因”。
- 导入失败明细文件名包含“常用货物导入失败明细”和时间戳。
- 导入完成后刷新列表,并在页面上方提示“导入数据成功”。
- 异常与边界:
- 文件格式错误时阻止导入并提示失败原因。
- 一级货物类型为空时对应行导入失败并写入错误原因。
- 二级货物类型编码不存在时对应行导入失败并写入错误原因。
- 二级货物类型编码不属于填写的一级货物类型时对应行导入失败。
- 货物名称为空或超过100字符时对应行导入失败。
- 货物编号后缀为空或不是2位数字时对应行导入失败。
- 货物编号重复时对应行导入失败并写入错误原因“该货物编号已存在”。
- 品牌、规格、型号、尺寸超过50字符时对应行导入失败。
- 说明1、说明2超过100字符时对应行导入失败。
- 单价小于0或小数位超过2位时对应行导入失败。
- 包装或计价单位不在允许选项内时对应行导入失败。
- 备注超过200字符时对应行导入失败。
- 单行错误不影响其他正确数据入库。
- 全部失败时不入库,但仍生成错误报告。
- 存在部分失败时下载失败明细,并提示“部分数据导入失败,已下载失败明细”。
- 反例:
- 不允许错误数据静默跳过。
- 不允许错误数据入库。
- 不允许只提示失败数量而不提供失败明细。
---
### REQ-CYHW-009 批量导出
- 功能点:
- 支持勾选多行数据后批量导出 Excel 文件。
- 未勾选数据时点击批量导出,默认导出当前查询条件下的全部数据。
- 导出字段与列表显示字段一致。
- 异常与边界:
- 当前条件下无数据时允许导出空文件或提示无可导出数据。
- 导出失败时提示失败原因,不改变当前勾选状态。
- 反例:
- 不允许只导出当前页数据。
- 不允许未筛选时导出非当前查询条件数据。
---
### REQ-CYHW-010 单条删除
- 功能点:
- 支持通过行操作删除单条常用货物。
- 点击删除弹出二次确认弹窗,提示删除后不可恢复。
- 删除成功后刷新列表,并提示“删除成功”。
- 异常与边界:
- 删除前必须校验是否已被运单、合同、报价单、库存记录等业务数据引用。
- 已被引用时不允许删除,并 toast 提示“已被业务引用,不允许删除”。
- 数据不存在时提示数据不存在,并刷新列表。
- 反例:
- 不允许跳过二次确认直接删除。
- 不允许删除已被业务引用的常用货物。
- 不允许用停用状态替代删除。
---
### REQ-CYHW-011 批量删除
- 功能点:
- 支持勾选多行常用货物后批量删除。
- 点击批量删除弹出二次确认弹窗。
- 删除前逐一校验所选数据是否被业务引用。
- 已引用记录从删除队列剔除并单独提示。
- 删除成功后刷新列表并提示“删除成功XX条因引用跳过XX条”。
- 异常与边界:
- 未勾选数据时点击批量删除,应提示先选择需要删除的数据。
- 所选数据全部被引用时不执行删除,并提示全部跳过原因。
- 部分删除失败时应提示成功数量和跳过数量。
- 反例:
- 不允许批量删除已被业务引用的记录。
- 不允许因为部分记录被引用而影响可删除记录的正常删除。
- 不允许删除后不刷新列表。
---
### REQ-CYHW-012 组织权限与审计
- 功能点:
- 组织列作为数据权限字段,普通用户默认仅可查看当前组织数据。
- 具备权限的用户可切换查看全部组织数据,但该视图仅允许只读查看。
- 根据权限配置控制查看、编辑、删除、导入、导出等操作入口。
- 创建人、创建时间、更新时间由系统自动记录。
- 异常与边界:
- 无权限用户访问时提示权限不足。
- 在全部组织只读视图中不开放新增、编辑、删除、导入等写操作。
- 列表、详情、导出等用户可见场景优先展示创建人姓名,不直接展示用户 ID。
- 反例:
- 不允许普通用户在全部组织视图下修改数据。
- 不允许手动维护创建人、创建时间、更新时间。
- 不允许删除或篡改审计信息。
---
## 通用规则
- 常用货物是实际业务中高频使用的具体货物档案。
- 货物所属类别必须通过一级货物类型与二级货物类型级联确定。
- 货物编号由二级货物类型编码前4位与用户输入的2位数字后缀组成共6位。
- 货物编号在系统中全局唯一,新增、编辑、导入时均需校验重复;重复时提示“该货物编号已存在”。
- 一级货物类型与二级货物类型为级联关系,一级变更时二级选项必须清空并重新加载。
- 货物名称必填且长度不超过100字符。
- 货值为数字保留2位小数不得小于0。
- 品牌、规格、型号、尺寸最多50字符。
- 说明1、说明2最多100字符。
- 备注最多200字符。
- 包装选项包含袋装、散装、桶装、箱装、托盘等;计价单位选项包含元/吨、元/立方米、元/件、元/车。
- 新增、编辑、导入成功后均需刷新列表。
- 删除前必须校验业务引用,已引用记录不允许删除。

View File

@@ -0,0 +1,317 @@
# 过程配置需求卡
## 目录
- [REQ-PROC-001 列表与分页](#req-proc-001-列表与分页)
- [REQ-PROC-002 条件查询与重置](#req-proc-002-条件查询与重置)
- [REQ-PROC-003 查看过程配置](#req-proc-003-查看过程配置)
- [REQ-PROC-004 新增过程配置](#req-proc-004-新增过程配置)
- [REQ-PROC-005 编辑过程配置](#req-proc-005-编辑过程配置)
- [REQ-PROC-006 复制过程配置](#req-proc-006-复制过程配置)
- [REQ-PROC-007 编号与唯一性规则](#req-proc-007-编号与唯一性规则)
- [REQ-PROC-008 过程节点配置与联动](#req-proc-008-过程节点配置与联动)
- [REQ-PROC-009 启用/停用](#req-proc-009-启用停用)
- [REQ-PROC-010 单条删除](#req-proc-010-单条删除)
- [REQ-PROC-011 批量删除](#req-proc-011-批量删除)
- [REQ-PROC-012 组织权限与审计](#req-proc-012-组织权限与审计)
- [通用规则](#通用规则)
---
### REQ-PROC-001 列表与分页
- 功能点:
- 默认展示当前组织全部过程配置数据,列表按创建时间降序排列。
- 展示配置名称、配置编号、项目、包含过程节点、状态、默认后台完成运输天数、备注、组织、创建人、创建时间、更新时间、操作等字段。
- 支持勾选多行数据用于批量删除。
- 操作列根据状态与权限展示查看、编辑、复制、删除、启用、停用等按钮。
- 草稿状态显示查看、编辑、复制、删除。
- 启用状态显示查看、编辑、复制、停用。
- 停用状态显示查看、编辑、复制、删除、启用。
- 分页默认10条/页支持20/50/100条/页。
- 异常与边界:
- 查询无数据时展示空状态。
- 当前页删除后无数据时自动回退页码并刷新列表。
- 切换组织范围时保留当前查询条件,仅重载数据。
- 列表加载失败时保留当前筛选与分页条件。
- 反例:
- 不允许默认按创建时间以外的字段覆盖排序规则。
- 不允许在不满足状态条件时展示删除、启用或停用按钮。
- 不允许分页后丢失当前查询条件。
---
### REQ-PROC-002 条件查询与重置
- 功能点:
- 支持按配置名称、项目、配置编号、包含过程节点、状态、组织等条件组合查询。
- 配置名称支持模糊查询。
- 项目支持下拉模糊搜索选择,默认范围为当前用户所属组织下的项目。
- 配置编号支持精确匹配。
- 包含过程节点支持多选,枚举值为接单、到场、装货、发货、在途、到货、卸货、签收、回单。
- 状态支持全部、草稿、启用、停用,默认全部。
- 组织默认带出当前用户所属组织。
- 点击查询后按条件展示数据。
- 点击重置后清空所有条件并恢复默认列表。
- 异常与边界:
- 查询条件为空时点击查询,按默认规则展示当前组织全部数据。
- 查询结果为空时展示空状态,不清空用户已输入条件。
- 重置后页码恢复第一页,并按创建时间降序重新加载。
- 反例:
- 不允许重置后仍保留隐藏条件。
- 不允许只按单一条件查询而忽略组合条件。
- 不允许查询后改变列表字段展示范围。
---
### REQ-PROC-003 查看过程配置
- 功能点:
- 支持通过行操作查看过程配置详情。
- 详情展示基本信息、现行过程节点、节点启用状态、节点联动配置、默认后台完成运输天数、备注、组织、创建人、创建时间、更新时间等信息。
- 查看状态下字段全部置灰不可编辑。
- 查看弹窗底部仅显示关闭按钮。
- 异常与边界:
- 数据已删除或不存在时提示数据不存在,并刷新列表。
- 详情加载失败时提示失败原因,不影响列表现状。
- 仅展示已勾选启用的现行过程节点,未启用节点不展示在时间线中。
- 反例:
- 不允许在查看状态下保存修改。
- 不允许查看时切换组织范围并修改数据。
- 不允许查看弹窗出现提交按钮。
---
### REQ-PROC-004 新增过程配置
- 功能点:
- 支持新增过程配置,用于按项目维度配置运输过程各节点的司机打卡要求。
- 配置编号由系统自动生成,规则为 GC + 5位流水号保存后全局唯一。
- 配置名称使用文本输入框必填长度1到100字符同一组织内不允许重复。
- 项目使用下拉多选控件,支持模糊搜索,必填,默认仅显示当前用户所属组织下的项目。
- 默认后台完成运输天数使用数字输入框非必填最多3位整数不设置默认值。
- 备注使用文本输入框非必填长度不超过500字符。
- 新建保存后默认状态为草稿。
- 新建弹窗包含基本信息与过程节点配置两个区域。
- 异常与边界:
- 配置名称为空时阻止提交并提示“配置名称不能为空”。
- 配置名称超过100字符时阻止提交。
- 同一组织内配置名称重复时阻止提交并提示“配置名称已存在”。
- 项目为空时阻止提交并提示“项目不能为空”。
- 默认后台完成运输天数不是正整数或超过3位时阻止提交并提示“请输入正整数”。
- 备注超过500字符时阻止提交并提示。
- 提交失败时保留用户已填写内容。
- 反例:
- 不允许手工填写配置编号。
- 不允许新增时跳过项目必填校验。
- 不允许新增后默认直接进入启用状态。
---
### REQ-PROC-005 编辑过程配置
- 功能点:
- 支持编辑已存在的过程配置。
- 配置编号在编辑状态下置灰不可编辑。
- 配置名称可编辑,校验规则同新增。
- 项目可重新选择,但若该配置已被运单引用,则项目不可变更。
- 过程节点配置可调整,校验规则同新增。
- 默认后台完成运输天数、备注可编辑。
- 保存成功后刷新列表。
- 异常与边界:
- 配置名称修改后仍需满足同组织内唯一。
- 项目被运单引用时禁止变更项目并提示。
- 编辑的数据已不存在时提示数据不存在,并刷新列表。
- 提交失败时保留用户修改内容。
- 反例:
- 不允许编辑时修改配置编号。
- 不允许编辑后产生同组织重名配置。
- 不允许绕过新增时的节点联动与校验规则。
---
### REQ-PROC-006 复制过程配置
- 功能点:
- 支持复制已有过程配置生成新配置。
- 复制后配置名称自动追加“ - 副本”。
- 节点设置内容全部复制。
- 配置编号重新生成。
- 新配置默认状态为草稿。
- 复制后的配置可继续编辑后保存。
- 异常与边界:
- 复制后若配置名称仍触发同组织重复校验,则阻止保存并提示。
- 原配置不存在时不允许复制。
- 复制失败时不影响原配置数据。
- 反例:
- 不允许复制后沿用原配置编号。
- 不允许复制后直接变为启用状态。
- 不允许复制时丢失节点配置内容。
---
### REQ-PROC-007 编号与唯一性规则
- 功能点:
- 配置编号由系统自动生成,规则为 GC + 5位流水号。
- 配置编号在系统中全局唯一。
- 配置名称在同一组织内唯一。
- 一个项目在同一时间只能关联一条启用状态的过程配置。
- 异常与边界:
- 编号生成冲突时阻止保存并重新生成。
- 同一组织内配置名称重复时阻止保存。
- 启用前若所选项目中存在其他启用配置时阻止启用,并提示“该项目已有关联的启用配置,请先停用原配置”。
- 项目为多选时,需要逐个校验每个项目的启用唯一性。
- 反例:
- 不允许手工编辑配置编号。
- 不允许同一组织内出现同名配置。
- 不允许同一项目同时存在两条启用配置。
---
### REQ-PROC-008 过程节点配置与联动
- 功能点:
- 过程节点顺序固定,不可拖拽排序,依次为接单、到场、装货、发货、在途、到货、卸货、签收、回单。
- 仅勾选启用的节点会展示在现行过程节点时间线中。
- 节点配置区域以逐行方式展示,每行对应一个节点。
- 各节点的是否打卡、上传货量、上传凭证相互独立,可单独选择或多选。
- 节点是否确认项必须选择确认方式;接单节点可选“无需确认接单”,回单节点可选“无需确认完成”,其他节点必须选择是或否。
- 是否打卡选否时,打卡定位、打卡频次、打卡时段置灰不可编辑。
- 上传货量选否时,上传货量类型置灰不可编辑。
- 上传凭证选否时,凭证类型全量置灰不可编辑。
- 仅在途节点支持打卡频次和打卡时段默认打卡频次为1次默认打卡时段为00:00-24:00。
- 接单节点的是否确认支持“无需确认接单”。
- 回单节点的是否确认支持“无需确认完成”,且选择是时系统自动勾选内部人员。
- 打卡定位、上传货量类型、凭证类型根据当前节点可配置项展示。
- 异常与边界:
- 节点是否确认项为空时阻止提交并提示“请选择确认方式”。
- 节点是否打卡为空时阻止提交并提示“请选择是否打卡”。
- 在途节点是否打卡为是但未配置打卡频次或打卡时段时阻止提交。
- 在途节点打卡频次为空或小于1时阻止提交并提示“请输入打卡频次 / 打卡频次必须≥1”。
- 在途节点打卡时段为空时阻止提交并提示“请选择打卡时段”。
- 非在途节点出现打卡频次或打卡时段时应不展示。
- 保存时若同时勾选签收和回单,且两者都配置上传凭证,则弹窗提示“签收与回单同时勾选了上传凭证,司机将需重复上传凭证,是否确认?”,按钮为确认、返回修改。
- 节点启用状态与时间线展示不一致时阻止保存。
- 选择为否后仍保留被置灰字段的旧值时,保存前应按禁用状态规则处理。
- 反例:
- 不允许拖拽调整节点顺序。
- 不允许非在途节点配置打卡频次和打卡时段。
- 不允许是否打卡为否时仍强制填写打卡定位或打卡时段。
- 不允许上传凭证关闭后仍提交凭证类型。
---
### REQ-PROC-009 启用/停用
- 功能点:
- 配置状态分为草稿、启用、停用三种,新增或复制后默认进入草稿状态。
- 草稿状态下编辑页面点击保存并启用后投入使用,状态变更为启用。
- 启用状态下可停用,状态变更为停用。
- 停用状态下可重新启用。
- 停用的配置在运单创建等下游业务场景中不再展示,但历史已引用数据保留关联关系。
- 异常与边界:
- 草稿启用前若同一项目已有其他启用配置,则阻止启用并提示先停用原配置。
- 启用状态停用前若存在关联的未完成运单,则阻止停用并提示“该条配置已被引用,不可停用”。
- 停用状态重新启用时仍需重新校验同一项目启用唯一性。
- 状态切换失败时保持原状态不变。
- 反例:
- 不允许启用状态直接删除。
- 不允许绕过启用唯一性校验直接投入使用。
- 不允许被引用的配置直接停用。
---
### REQ-PROC-010 单条删除
- 功能点:
- 支持通过行操作删除单条过程配置。
- 点击删除时弹出二次确认弹窗。
- 删除成功后刷新列表,并提示“删除成功”。
- 异常与边界:
- 草稿配置可直接删除。
- 停用配置删除前需校验未被运单引用,已引用则提示“该条配置已被引用,不可删除”。
- 启用配置不可删除,需先停用后再处理。
- 数据不存在时提示数据不存在,并刷新列表。
- 反例:
- 不允许跳过二次确认直接删除。
- 不允许删除启用状态配置。
- 不允许删除已被引用的停用配置。
---
### REQ-PROC-011 批量删除
- 功能点:
- 支持勾选多行过程配置后批量删除。
- 点击批量删除时弹出二次确认弹窗。
- 删除时按单条规则逐一校验状态与业务引用。
- 删除成功后刷新列表并提示“删除成功XX条因引用跳过XX条”。
- 异常与边界:
- 未勾选数据时点击批量删除,应提示先选择需要删除的数据。
- 草稿配置可直接删除。
- 停用配置逐条校验是否被运单引用,已引用记录剔除并单独提示。
- 启用配置不进入删除队列,并提示需先停用。
- 所选数据全部不满足删除条件时不执行删除。
- 反例:
- 不允许批量删除启用状态配置。
- 不允许因为部分记录不可删而影响草稿记录删除。
- 不允许删除后不刷新列表。
---
### REQ-PROC-012 组织权限与审计
- 功能点:
- 组织列作为数据权限字段,普通用户默认仅可查看当前组织数据。
- 具备权限的用户可切换查看全部组织数据,但该视图仅允许只读查看。
- 根据权限配置控制查看、编辑、复制、删除、启用、停用等操作入口。
- 创建人、创建时间、更新时间由系统自动记录。
- 异常与边界:
- 无权限用户访问时提示权限不足。
- 在全部组织只读视图中不开放新增、编辑、删除、启用、停用等写操作。
- 列表、详情等用户可见场景优先展示创建人姓名,不直接展示用户 ID。
- 反例:
- 不允许普通用户在全部组织视图下修改数据。
- 不允许手动维护创建人、创建时间、更新时间。
- 不允许删除或篡改审计信息。
---
## 通用规则
- 过程配置用于按项目维度配置运输过程各节点的司机打卡要求,确保运输过程可追溯。
- 配置编号由系统自动生成,规则为 GC + 5位流水号且全局唯一。
- 配置名称在同一组织内不允许重复。
- 一个项目在同一时间只能关联一条启用状态的过程配置。
- 过程节点顺序固定,不可拖拽排序,依次为接单、到场、装货、发货、在途、到货、卸货、签收、回单。
- 仅勾选启用的节点才会展示在现行过程节点时间线中。
- 是否打卡、上传货量、上传凭证为节点内相互独立的配置项,可单独配置或多选。
- 仅在途节点支持打卡频次和打卡时段默认打卡频次为1次默认打卡时段为00:00-24:00。
- 节点配置触发重复上传凭证提示时,应允许用户确认后继续或返回修改。
- 新增、编辑、复制、启用、停用、删除成功后均需刷新列表。
- 删除前必须校验状态与业务引用,已引用配置不允许直接删除或停用。

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,713 @@
# 项目管理需求卡
## 目录
- [REQ-PROJ-001 菜单入口](#req-proj-001-菜单入口)
- [REQ-PROJ-002 列表与分页](#req-proj-002-列表与分页)
- [REQ-PROJ-003 条件查询与重置](#req-proj-003-条件查询与重置)
- [REQ-PROJ-004 列表状态与操作](#req-proj-004-列表状态与操作)
- [REQ-PROJ-005 新建立项](#req-proj-005-新建立项)
- [REQ-PROJ-006 项目基础字段](#req-proj-006-项目基础字段)
- [REQ-PROJ-007 项目主要内容](#req-proj-007-项目主要内容)
- [REQ-PROJ-008 客户信息选择](#req-proj-008-客户信息选择)
- [REQ-PROJ-009 下游承运商选择](#req-proj-009-下游承运商选择)
- [REQ-PROJ-010 项目情况说明](#req-proj-010-项目情况说明)
- [REQ-PROJ-011 项目附件](#req-proj-011-项目附件)
- [REQ-PROJ-012 附件材料要求](#req-proj-012-附件材料要求)
- [REQ-PROJ-013 保存草稿与临时生效](#req-proj-013-保存草稿与临时生效)
- [REQ-PROJ-014 提交审批](#req-proj-014-提交审批)
- [REQ-PROJ-015 审批通过](#req-proj-015-审批通过)
- [REQ-PROJ-016 审批驳回与撤回](#req-proj-016-审批驳回与撤回)
- [REQ-PROJ-017 重大项目补录](#req-proj-017-重大项目补录)
- [REQ-PROJ-018 客户额度校验](#req-proj-018-客户额度校验)
- [REQ-PROJ-019 编辑立项](#req-proj-019-编辑立项)
- [REQ-PROJ-020 查看立项](#req-proj-020-查看立项)
- [REQ-PROJ-021 删除草稿立项](#req-proj-021-删除草稿立项)
- [REQ-PROJ-022 作废立项](#req-proj-022-作废立项)
- [REQ-PROJ-023 发起变更立项](#req-proj-023-发起变更立项)
- [REQ-PROJ-024 变更项目信息](#req-proj-024-变更项目信息)
- [REQ-PROJ-025 变更内容与原因](#req-proj-025-变更内容与原因)
- [REQ-PROJ-026 变更审批与生效](#req-proj-026-变更审批与生效)
- [REQ-PROJ-027 变更记录](#req-proj-027-变更记录)
- [REQ-PROJ-028 选择关联项目](#req-proj-028-选择关联项目)
- [REQ-PROJ-029 导出立项列表](#req-proj-029-导出立项列表)
- [REQ-PROJ-030 权限与审计](#req-proj-030-权限与审计)
- [通用规则](#通用规则)
---
### REQ-PROJ-001 菜单入口
- 功能点:
- 项目管理模块归属于“项目管理”主菜单。
- 在“项目管理”菜单下提供“项目管理”入口。
- 用户进入项目管理页面后,可进行查询、新建立项、重大项目补录、编辑、提交、查看、删除、变更、作废、导出等操作。
- 异常与边界:
- 无菜单权限时不展示“项目管理”入口。
- 有菜单权限但无按钮权限时,仅展示允许访问的页面和操作。
- 菜单或列表加载失败时提示失败原因,不影响其他模块访问。
- 反例:
- 不允许把项目管理挂到非项目管理主菜单下。
- 不允许无权限用户通过 URL 直接进入项目管理页面。
- 不允许菜单入口存在但页面无法加载。
---
### REQ-PROJ-002 列表与分页
- 功能点:
- 默认展示当前用户权限范围内的项目立项数据。
- 列表展示序号、立项申请单号、项目编号、项目名称、项目类型、客户名称、下游承运商、承办部门、项目负责人、审批状态、当前节点、当前处理人、生效类型、审核通过时间、操作等字段。
- 审批状态和操作列固定在右侧,不随横向滚动条滑动。
- 默认按创建时间或立项申请时间降序展示。
- 支持分页展示默认10条/页支持20/50/100条/页。
- 异常与边界:
- 查询无数据时展示空状态。
- 当前页数据因删除、提交、撤回、审批、变更等操作发生变化后,应刷新列表并保持分页逻辑正确。
- 列表加载失败时保留当前查询条件和分页条件,提示用户重新查询。
- 反例:
- 不允许审批状态或操作列被横向滚动隐藏。
- 不允许列表字段展示用户 ID 代替用户姓名。
- 不允许分页后丢失当前查询条件。
---
### REQ-PROJ-003 条件查询与重置
- 功能点:
- 支持按立项申请单号、项目名称、承办部门、项目负责人、项目类型、审批状态、生效类型等条件组合查询。
- 立项申请单号、项目名称使用输入框,支持模糊查询。
- 承办部门、项目负责人使用输入或搜索选择控件。
- 项目类型、审批状态、生效类型使用下拉单选控件。
- 点击查询后按当前条件刷新列表。
- 点击重置后清空所有查询条件,恢复默认列表。
- 查询条件较多时支持展开和收起。
- 异常与边界:
- 查询条件为空时点击查询,按默认规则展示全部权限范围内数据。
- 查询结果为空时展示空状态,不清空用户已输入条件。
- 重置后页码恢复第一页,并重新加载列表。
- 反例:
- 不允许重置后仍保留隐藏查询条件。
- 不允许只按单个条件查询而忽略其他已选择条件。
- 不允许查询后改变列表字段或操作范围。
---
### REQ-PROJ-004 列表状态与操作
- 功能点:
- 审批状态包含草稿、审批中、已撤回、审批通过、已驳回、变更审批中、变更驳回、变更审批通过等状态。
- 生效类型包含临时、正式。
- 草稿状态展示编辑、提交、删除操作。
- 审批中状态展示查看操作。
- 已撤回状态展示编辑、查看、删除操作。
- 已驳回状态展示编辑、查看、删除操作。
- 审批通过且正式生效的项目展示查看、变更等操作。
- 变更审批中状态展示查看操作。
- 变更驳回状态展示编辑、查看操作。
- 操作按钮展示受项目状态和用户权限共同控制。
- 异常与边界:
- 操作执行前需重新校验项目最新状态,避免列表状态过期导致误操作。
- 状态为空或未知时限制编辑、提交、删除、变更等高风险操作。
- 状态变更成功后刷新列表,展示最新状态和可执行操作。
- 反例:
- 不允许审批中项目继续删除。
- 不允许已审批通过项目按草稿规则编辑基础信息。
- 不允许仅根据前端展示状态跳过后端状态校验。
---
### REQ-PROJ-005 新建立项
- 功能点:
- 点击列表顶部“新建”按钮后打开新建立项页面或弹窗。
- 新建立项页面包含项目基本信息、项目主要内容、客户信息、承运商信息、项目情况说明、项目材料等区域。
- 立项申请单号和项目编号由系统保存草稿后自动生成,只读不可编辑。
- 新建立项支持保存草稿、提交审批、关闭。
- 普通项目和重大项目按项目类型区分审批流程。
- 重大项目如果金额超过8000万需走物流领导审批流程。
- 异常与边界:
- 新建页面打开失败时提示失败原因,不改变列表数据。
- 项目类型、项目名称、项目简称、业务部门、承办部门、客户名称、下游承运商、资金和回款相关必填字段为空时阻止提交审批。
- 保存草稿时仅校验基本信息必填项,不强制上传附件。
- 重复点击保存或提交时应防止重复生成项目单据。
- 反例:
- 不允许用户手工填写或修改立项申请单号、项目编号。
- 不允许缺少项目类型时保存有效项目。
- 不允许金额超过8000万的重大项目走普通审批流程。
---
### REQ-PROJ-006 项目基础字段
- 功能点:
- 项目类型为必填字段,使用下拉单选控件,枚举包含普通项目、重大项目。
- 立项申请单号为只读文本,由系统保存单据后自动生成,用于追踪立项申请流程。
- 项目编号为只读文本,由系统自动生成,全局唯一,示例格式为 LX-2026-000001。
- 项目名称为必填字段使用单行输入框最多50个汉字且不可重复。
- 项目简称为必填字段使用单行输入框最多20个汉字。
- 业务部门为必填字段,使用下拉单选控件,关联用户所属部门。
- 承办部门为必填字段,使用下拉单选控件,来源为组织部门树。
- 项目由来使用下拉单选控件,默认商务洽谈。
- 项目由来说明使用大文本域最多300字。
- 异常与边界:
- 项目名称为空、超过50字或重复时阻止提交并提示。
- 项目简称为空或超过20字时阻止提交并提示。
- 业务部门或承办部门为空时阻止提交并提示。
- 立项申请单号或项目编号生成失败时阻止保存并提示。
- 反例:
- 不允许两个项目使用完全相同的项目名称。
- 不允许把立项申请单号或项目编号作为可编辑输入项。
- 不允许承办部门脱离组织部门树随意录入。
---
### REQ-PROJ-007 项目主要内容
- 功能点:
- 项目资金使用额度为必填数字字段单位万元表示实际业务回款周期内付款额度需大于等于0并保留2位小数。
- 项目应收账款额度为必填数字字段单位万元表示回款周期内最大应收账款额度需大于等于0并保留2位小数。
- 应收账款回款期限为必填数字字段单位天范围为0到999的整数。
- 回款账期为必填数字字段单位天范围为0到999的整数。
- 货物类型使用弹窗选择控件,来源为数据字典或货物类型档案,非必填。
- 预估货物数量使用文本输入框最多100字。
- 业务周期使用日期范围控件,格式为 yyyy-MM-dd 到 yyyy-MM-dd。
- 运输线路使用单行输入框最多100字。
- 运输类型使用下拉单选控件,枚举包含汽运、铁路、海运等运输方式。
- 业务类型使用下拉单选控件,枚举包含一类规模业务、二类规模业务等。
- 项目规模、预计利润、资金需求均为数字字段单位万元需大于等于0并保留2位小数。
- 结算方式使用下拉单选控件,枚举包含先票后款、先款后票等。
- 项目经办人和项目负责人为必填字段,使用人员搜索选择控件,默认当前用户,可重新选择。
- 异常与边界:
- 项目应收账款额度大于项目资金使用额度时阻止提交并提示。
- 天数字段不是0到999整数时阻止提交并提示。
- 金额类字段小于0或小数位超过2位时阻止提交并提示。
- 业务周期开始日期晚于结束日期时阻止提交并提示。
- 反例:
- 不允许项目应收账款额度超过项目资金使用额度。
- 不允许回款账期填写负数或1000天。
- 不允许项目经办人、项目负责人为空后提交审批。
---
### REQ-PROJ-008 客户信息选择
- 功能点:
- 客户名称为必填字段,使用弹窗搜索选择控件,从客商档案选择上游合作客户。
- 支持多选客户至少选择1家客户。
- 选择客户后,系统自动在客户信息表格生成客户行。
- 客户信息表格展示客户、客户信用额度、企业性质、法定代表人、联系地址、联系人、联系方式等字段。
- 删除客户行时需保证至少保留1条客户记录。
- 异常与边界:
- 未选择客户时阻止提交审批并提示。
- 客商档案中客户已停用或不存在时阻止选择。
- 仅剩1条客户记录时删除按钮置灰或隐藏。
- 多选客户确认后应实时刷新客户信息表格。
- 反例:
- 不允许手工输入客户名称代替客商档案选择。
- 不允许删除所有客户记录后提交。
- 不允许选择非客户类型的客商作为客户。
---
### REQ-PROJ-009 下游承运商选择
- 功能点:
- 下游承运商为必填字段,使用弹窗搜索选择控件,从客商档案选择物流承运商。
- 支持多选承运商至少选择1家承运商。
- 选择承运商后,系统自动在承运商信息表格生成承运商行。
- 承运商信息表格展示承运商、企业性质、法定代表人、联系地址、联系人、联系方式等字段。
- 删除承运商行时需保证至少保留1条承运商记录。
- 异常与边界:
- 未选择下游承运商时阻止提交审批并提示。
- 客商档案中承运商已停用或不存在时阻止选择。
- 仅剩1条承运商记录时删除按钮置灰或隐藏。
- 多选承运商确认后应实时刷新承运商信息表格。
- 反例:
- 不允许手工输入承运商名称代替客商档案选择。
- 不允许删除所有承运商记录后提交。
- 不允许选择非承运商类型的客商作为下游承运商。
---
### REQ-PROJ-010 项目情况说明
- 功能点:
- 项目情况说明区域包含项目简介、盈利模式及利润测算情况说明、项目风险点、风险防范措施及应急预案等大文本字段。
- 各说明字段用于补充项目背景、利润来源、风险识别和风控预案。
- 大文本字段支持多行录入,保存草稿时可不完整,提交审批时按项目类型和流程要求校验。
- 异常与边界:
- 大文本字段超过系统限制时阻止提交并提示。
- 提交审批时若流程要求必填但未填写,应阻止提交并提示用户补齐。
- 用户关闭页面前存在未保存说明内容时,应提示确认离开。
- 反例:
- 不允许超长文本绕过前端限制提交。
- 不允许提交审批时丢失已填写说明内容。
- 不允许查看状态下修改项目情况说明。
---
### REQ-PROJ-011 项目附件
- 功能点:
- 项目材料区域支持上传附件。
- 附件列表展示序号、附件类型、文件名、附件描述、文件大小、上传人、上传时间、操作等字段。
- 附件类型使用下拉选择控件。
- 附件描述支持录入和编辑。
- 支持上传 pdf、bmp、jpeg、png、jpg、doc、docx、ppt、pptx、xlsx、xls、eml、msg、zip 等格式文件。
- 单个文件大小限制500M。
- 上传成功后自动新增附件表格行,展示文件名、大小、上传人、上传时间。
- 支持删除未提交或允许编辑状态下的附件。
- 支持按项目材料要求批量下载附件。
- 异常与边界:
- 文件格式不支持或大小超过500M时阻止上传并提示。
- 上传失败时保留当前附件列表并提示失败原因。
- 已提交审批的单据附件删除按钮按权限和状态隐藏或置灰。
- 删除附件前需二次确认,确认后同步删除附件记录和服务器文件。
- 反例:
- 不允许上传无附件类型的有效附件。
- 不允许上传超过500M的文件。
- 不允许删除附件后仅移除前端行而服务器文件仍保留有效引用。
---
### REQ-PROJ-012 附件材料要求
- 功能点:
- 普通项目或中小项目提交审批前需上传利润测算表、单一来源供应商申请表或三方比价表、合同模板、项目操作说明、项目分析报告、项目考察报告。
- 重大项目提交审批前需上传会议纪要、评审表决票、利润测算表、单一来源供应商申请表或三方比价表、项目操作说明、项目分析报告、项目考察报告、合同模板、上游尽职调查报告、下游尽职调查报告、业务承诺函。
- 页面应根据项目类型展示对应附件材料清单,并标识未上传材料。
- 保存草稿时附件可不上传。
- 提交审批时按项目类型校验附件是否齐全。
- 异常与边界:
- 附件材料缺失时阻止提交审批,并提示缺失的附件类型。
- 用户切换项目类型后,附件材料清单需同步切换,已上传且仍适用的附件保留。
- 同一附件类型支持上传多个文件时,应按业务规则合并校验是否满足要求。
- 反例:
- 不允许重大项目按普通项目附件清单提交审批。
- 不允许保存草稿时强制上传全部附件。
- 不允许缺少合同模板仍提交审批。
---
### REQ-PROJ-013 保存草稿与临时生效
- 功能点:
- 新增或变更页面点击保存后,临时保存当前页面填写内容。
- 新建立项保存草稿后,审批状态保持草稿,生效类型默认为临时。
- 临时生效项目允许合同选择该项目。
- 临时生效项目不能执行结算流程。
- 保存草稿只要求基本信息满足最小必填校验,附件可不上传。
- 异常与边界:
- 基本信息必填项缺失时阻止保存草稿并提示。
- 保存失败时保留用户已填写内容,方便修正后重新保存。
- 草稿项目被删除或无权限编辑时,不允许继续保存。
- 反例:
- 不允许草稿临时项目执行结算。
- 不允许保存草稿后丢失已上传附件或表格信息。
- 不允许保存草稿触发审批流程。
---
### REQ-PROJ-014 提交审批
- 功能点:
- 草稿、已撤回、已驳回状态的立项单据支持提交审批。
- 提交审批前校验全部必填字段、客户信息、承运商信息、项目材料和业务规则。
- 不同项目类型、项目金额和变更类型走不同审批流程。
- 提交成功后审批状态更新为审批中。
- 提交审批后应实时推送待办消息给当前审批人。
- 异常与边界:
- 必填字段、客户或承运商信息、附件材料缺失时阻止提交并提示。
- 审批流程发起失败时保持原状态,并保留用户输入内容。
- 项目金额或项目类型变更导致审批流不匹配时,应按最新数据重新匹配审批流。
- 重复点击提交时应防止重复发起审批。
- 反例:
- 不允许资料未完善的项目进入审批中。
- 不允许不同金额重大项目使用错误审批流。
- 不允许提交失败后仍把状态改为审批中。
---
### REQ-PROJ-015 审批通过
- 功能点:
- 当前审批节点处理人可对审批中单据执行审批通过。
- 审批全部通过后,立项审批状态更新为审批通过。
- 首次审批通过后,项目生效类型自动由临时变为正式。
- 正式项目可在合同、运单、结算等下游业务中按权限选择和使用。
- 审批通过时间记录最终审批通过时间。
- 异常与边界:
- 非当前审批节点处理人不允许审批通过。
- 审批过程中项目数据被删除或作废时,不允许继续审批。
- 审批通过状态更新失败时需提示原因,并保持审批前状态。
- 反例:
- 不允许未走完审批流程的项目变为正式项目。
- 不允许临时项目执行结算流程。
- 不允许非审批人代替当前处理人审批通过。
---
### REQ-PROJ-016 审批驳回与撤回
- 功能点:
- 当前审批节点处理人可对审批中单据执行审批驳回。
- 驳回后审批状态更新为已驳回,并将驳回意见反馈给立项发起人。
- 申请人可对审批中单据执行撤回。
- 撤回后审批状态更新为已撤回,并清空流程待办节点。
- 已驳回或已撤回的单据允许编辑后重新提交审批。
- 异常与边界:
- 非申请人不允许撤回审批单据。
- 非当前审批节点处理人不允许驳回。
- 撤回或驳回失败时保持原审批状态,并提示失败原因。
- 重新提交审批时需按最新内容重新校验必填和附件。
- 反例:
- 不允许审批通过后的立项单据继续撤回。
- 不允许驳回时丢失驳回意见。
- 不允许撤回后审批待办仍停留在审批人列表。
---
### REQ-PROJ-017 重大项目补录
- 功能点:
- 列表顶部支持“重大项目补录”入口。
- 补录用于录入历史或线下已完成审批的重大项目。
- 补录重大项目无需走系统审批流程。
- 只要完善全部项目信息并满足附件材料要求,补录提交后立即生效。
- 补录生效后的项目审批状态展示为审批通过或补录生效口径,生效类型为正式。
- 补录生效后的项目可执行合同、运单、结算等正式项目流程。
- 异常与边界:
- 补录时若必填字段或重大项目附件材料缺失,应阻止提交。
- 补录项目名称重复时阻止提交并提示。
- 无重大项目补录权限时不展示入口。
- 反例:
- 不允许补录重大项目资料不完整却直接生效。
- 不允许普通项目从重大项目补录入口提交。
- 不允许无权限用户补录重大项目。
---
### REQ-PROJ-018 客户额度校验
- 功能点:
- 客户存在多个项目时,系统需校验当前客户关联项目是否超过客户信用额度或授信额度。
- 校验范围包含当前客户已生效项目、临时生效草稿项目以及本次新建或变更项目的项目资金使用额度。
- 若超过客户额度,系统进行提示,提醒用户确认风险。
- 客户额度提示不替代审批校验,提交审批时仍需按流程判断是否允许继续。
- 异常与边界:
- 客户额度为空或无法获取时,应提示额度信息缺失,并允许按权限继续保存草稿。
- 多客户项目应分别校验每个客户额度。
- 客户额度校验服务失败时,应提示用户稍后重试或联系管理员。
- 反例:
- 不允许只校验当前项目金额而忽略同一客户其他项目占用额度。
- 不允许多客户项目只校验第一家客户。
- 不允许超过额度时无任何提示。
---
### REQ-PROJ-019 编辑立项
- 功能点:
- 草稿、已撤回、已驳回状态的立项单据支持编辑。
- 编辑页面回显项目完整信息,项目编号只读不可编辑。
- 编辑后可保存草稿或重新提交审批。
- 已提交审批的单据在审批中状态不允许直接编辑。
- 异常与边界:
- 编辑前需校验当前单据状态仍允许编辑。
- 编辑的数据已不存在时提示数据不存在,并刷新列表。
- 修改项目类型后,应重新匹配附件材料要求和审批流程。
- 保存或提交失败时保留用户修改内容。
- 反例:
- 不允许审批中单据绕过撤回直接编辑。
- 不允许编辑时修改项目编号。
- 不允许编辑后产生重复项目名称。
---
### REQ-PROJ-020 查看立项
- 功能点:
- 支持通过列表行操作查看项目详情。
- 查看页面展示项目基本信息、项目主要内容、客户信息、承运商信息、项目情况说明、项目附件、审批信息和变更记录。
- 查看状态下所有字段只读。
- 支持查看附件和审批流转信息。
- 异常与边界:
- 数据已删除、作废或无权限查看时提示无法查看。
- 附件加载失败时不影响其他项目字段展示。
- 历史变更记录加载失败时提示失败原因,不影响当前项目信息展示。
- 反例:
- 不允许在查看状态下保存修改。
- 不允许查看时自动切换到编辑状态。
- 不允许隐藏已生效变更记录。
---
### REQ-PROJ-021 删除草稿立项
- 功能点:
- 草稿、已撤回、已驳回且未被业务引用的立项单据支持删除。
- 点击删除后弹出二次确认弹窗。
- 删除前需校验是否存在关联合同、运单、结算单据和历史变更记录。
- 删除成功后刷新列表。
- 异常与边界:
- 存在业务关联单据时不允许删除,并提示无法删除原因。
- 存在历史变更记录时不允许删除,并提示无法删除。
- 删除时状态已变化为审批中或审批通过时,应阻止删除并刷新列表。
- 反例:
- 不允许删除审批中或审批通过项目。
- 不允许存在合同、运单、结算单据时删除项目。
- 不允许跳过二次确认直接删除。
---
### REQ-PROJ-022 作废立项
- 功能点:
- 审批通过的正式项目支持按权限执行作废。
- 作废前需弹出二次确认提示。
- 作废后禁止基于该项目新建合同、运单、结算单据。
- 作废不影响历史已生成业务单据。
- 作废操作需永久保留审计日志。
- 异常与边界:
- 非审批通过项目不允许作废。
- 作废失败时保持原项目状态并提示失败原因。
- 作废后如已有页面缓存该项目,应在选择时提示项目已作废不可使用。
- 反例:
- 不允许作废后继续新建合同、运单或结算单。
- 不允许作废操作影响历史业务数据。
- 不允许作废记录不留审计日志。
---
### REQ-PROJ-023 发起变更立项
- 功能点:
- 审批通过的正式项目支持发起变更立项。
- 点击变更后打开变更项目页面,自动带入原立项全部历史填写数据。
- 变更页面新增变更内容、变更原因和变更记录相关区域。
- 原立项数据作为变更基础数据展示,允许按变更规则修改。
- 变更类型包含项目变更、项目调整备案,并根据变更类型走不同审批流程。
- 异常与边界:
- 非正式生效项目不允许发起变更。
- 存在未完成变更审批时,不允许重复发起新的变更。
- 原项目已作废或无权限变更时不允许发起变更。
- 反例:
- 不允许草稿或临时项目发起变更立项。
- 不允许变更页面为空白而不带入原项目数据。
- 不允许同一项目同时存在多个审批中的变更单。
---
### REQ-PROJ-024 变更项目信息
- 功能点:
- 变更项目页面包含项目基本信息、项目主要内容、客户信息、承运商信息、项目情况说明、项目材料、变更内容、变更原因等区域。
- 变更类型为必填字段,使用下拉单选控件。
- 项目编号只读不可编辑。
- 项目类型、项目名称、项目简称、业务部门、承办部门、客户信息、承运商信息、资金和回款相关字段按变更规则回显并允许修改。
- 变更页面支持保存、提交、关闭。
- 异常与边界:
- 变更类型为空时阻止保存或提交并提示。
- 变更时如修改项目类型或金额,应重新匹配审批流程和附件材料要求。
- 变更保存失败时保留用户修改内容。
- 关闭前存在未保存内容时,应提示确认是否放弃填写。
- 反例:
- 不允许变更时修改项目编号。
- 不允许变更类型为空后提交审批。
- 不允许变更保存失败后清空用户已填写内容。
---
### REQ-PROJ-025 变更内容与原因
- 功能点:
- 变更内容为必填大文本字段用于填写本次调整前后的全部修改内容最多2000字。
- 变更原因为必填大文本字段用于填写本次变更原因最多2000字。
- 文本框实时统计输入字数,超过上限时禁止继续输入或提交。
- 草稿和驳回状态下可编辑变更内容与变更原因。
- 异常与边界:
- 提交审批时变更内容或变更原因为空,应阻止提交并提示。
- 文本超过2000字时阻止提交并提示。
- 查看历史变更记录时,变更内容和变更原因只读展示。
- 反例:
- 不允许变更内容为空后发起变更审批。
- 不允许超过2000字的变更说明提交。
- 不允许查看历史记录时修改变更原因。
---
### REQ-PROJ-026 变更审批与生效
- 功能点:
- 变更单提交后,项目审批状态更新为变更审批中。
- 变更审批通过后,变更内容正式覆盖原项目信息,并新增一条变更记录。
- 变更审批驳回后,项目状态更新为变更驳回,允许编辑后重新提交。
- 项目调整备案和项目变更按不同流程审批。
- 变更不改变项目原有生效类型,正式项目变更后仍为正式。
- 异常与边界:
- 变更审批未通过前,不得覆盖原正式项目信息。
- 审批过程中项目被作废时,不允许继续完成变更审批。
- 变更审批失败时保持原项目数据不变,并提示失败原因。
- 反例:
- 不允许变更草稿内容直接影响正式项目。
- 不允许审批未通过却生成正式变更记录。
- 不允许变更后把正式项目退回临时生效。
---
### REQ-PROJ-027 变更记录
- 功能点:
- 项目详情和变更页面展示变更记录列表。
- 变更记录展示序号、变更日期、变更类型、经办人、变更内容、变更原因、状态、操作等字段。
- 草稿或驳回状态的变更记录支持编辑。
- 审批中或已通过状态的变更记录支持查看流程或查看详情。
- 变更记录需归集客户、承运商等全部变更内容,保留变更前后字段。
- 异常与边界:
- 变更记录加载失败时提示失败原因,不影响查看项目基础信息。
- 查看历史变更记录时全字段只读。
- 变更记录与项目不匹配时不展示,并提示数据异常。
- 反例:
- 不允许删除已审批通过的变更记录。
- 不允许变更记录只保存部分字段导致无法追溯。
- 不允许跨项目展示变更记录。
---
### REQ-PROJ-028 选择关联项目
- 功能点:
- 支持打开“选择关联项目”弹窗。
- 弹窗支持按项目编号、项目名称进行模糊查询。
- 弹窗列表展示选择、序号、项目编号、项目名称、关联项目、所属部门等字段。
- 支持分页展示关联项目候选数据。
- 用户勾选项目后点击确定,关联项目回填到当前立项或变更单。
- 点击取消或关闭弹窗时不回填数据。
- 异常与边界:
- 查询无数据时展示空状态。
- 已作废项目不可作为有效关联项目选择。
- 无权限查看的项目不出现在候选列表中。
- 重复选择同一项目时应自动去重。
- 反例:
- 不允许选择已作废项目作为关联项目。
- 不允许取消弹窗后仍回填关联项目。
- 不允许关联项目重复展示或重复保存。
---
### REQ-PROJ-029 导出立项列表
- 功能点:
- 列表顶部支持导出立项列表。
- 点击导出后,导出当前查询条件下的项目基础信息、客户、承运商、变更记录和附件清单等数据到 Excel 文件。
- 导出文件字段与列表和详情展示字段保持一致。
- 数据量较大时页面展示加载或导出中提示。
- 异常与边界:
- 当前查询条件下无数据时允许导出空文件或提示无可导出数据。
- 导出失败时提示失败原因,不改变当前列表状态。
- 无导出权限时不展示导出按钮。
- 反例:
- 不允许无权限用户导出项目数据。
- 不允许导出时忽略当前查询条件。
- 不允许导出文件展示用户 ID 代替姓名。
---
### REQ-PROJ-030 权限与审计
- 功能点:
- 根据用户权限控制查询、新建、重大项目补录、编辑、提交、查看、删除、变更、作废、导出、审批等操作入口。
- 列表、详情、导出、审批流中用户可见字段优先展示姓名,不直接展示用户 ID。
- 新增、编辑、提交、审批、撤回、驳回、删除、作废、变更、导出等关键操作需记录操作日志。
- 审计信息应包含操作人、操作时间、操作类型和关键业务对象。
- 异常与边界:
- 无按钮权限时不展示对应按钮,通过 URL 或接口绕过时也应拒绝。
- 操作日志记录失败时不得影响用户已成功的业务操作,但需保留可排查信息。
- 数据权限限制下,用户只能查看和操作授权范围内项目。
- 反例:
- 不允许无权限用户执行项目审批或作废。
- 不允许列表展示创建人、更新人等用户 ID。
- 不允许关键状态变更没有审计记录。
---
## 通用规则
- 项目管理用于维护企业运输业务项目立项、审批、生效、变更和作废全过程,归属于“项目管理”主菜单。
- 项目类型包含普通项目、重大项目重大项目金额超过8000万时需走物流领导审批流程。
- 立项申请单号和项目编号由系统自动生成,全局唯一,只读不可编辑。
- 项目名称必填且不可重复,项目简称必填。
- 保存草稿后项目审批状态为草稿,生效类型为临时;临时项目可被合同选择,但不可执行结算流程。
- 完善全部项目信息并审批通过后,项目生效类型转为正式;正式项目可执行结算流程。
- 重大项目补录无需走系统审批,完善全部信息并提交后即正式生效。
- 客户名称和下游承运商均需从客商档案选择至少各选择1条。
- 客户存在多个项目时,应校验客户额度占用,超过额度需提示风险。
- 普通项目或中小项目、重大项目按各自附件材料清单校验提交审批所需附件;保存草稿时附件可不上传。
- 金额字段需大于等于0并保留2位小数天数字段需为0到999的整数。
- 项目应收账款额度不得大于项目资金使用额度。
- 变更项目需按变更类型走对应审批流程,审批通过后才覆盖原正式项目信息并生成变更记录。
- 删除项目前需校验无关联合同、运单、结算单据和历史变更记录。
- 作废项目禁止新建合同、运单、结算单据,历史已产生业务数据不受影响。
- 列表、详情、导出、审批等用户可见场景优先展示姓名字段,不直接展示用户 ID。
- 新增、编辑、提交、审批、撤回、驳回、删除、作废、变更、导出后均需刷新相关列表或详情状态。