76f266645d
含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。 排除 node_modules、构建产物、安装包与 .env 密钥。 Co-authored-by: Cursor <cursoragent@cursor.com>
408 lines
11 KiB
Dart
408 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'theme.dart';
|
|
|
|
/// 后端枚举 / 码 → 中文。对齐 web `labels.ts` + 投标状态。
|
|
const zhMap = <String, String>{
|
|
'TECH_INITIAL': '技术初审',
|
|
'FETCH': '取标',
|
|
'TECH_FINAL': '技术终审',
|
|
'DRAFTING': '制标',
|
|
'REVIEW1': '商务一审',
|
|
'REVIEW2': '商务二审',
|
|
'PRINT': '印封',
|
|
'LIST': '投标执行',
|
|
'PENDING': '待审批',
|
|
'WON': '中标',
|
|
'LOST': '未中标',
|
|
'FAILED': '流标废标',
|
|
'TERMINATED': '已终止',
|
|
'DRAFT': '草稿',
|
|
'APPROVED': '已通过',
|
|
'REJECTED': '已驳回',
|
|
'CANCELLED': '已取消',
|
|
'ACTIVE': '启用',
|
|
'DISABLED': '停用',
|
|
'OPEN': '进行中',
|
|
'OFFSET': '已冲账',
|
|
'DONE': '已完成',
|
|
'RETURNED': '已交回',
|
|
'PAID': '已付',
|
|
'UNPAID': '待付',
|
|
'TO_ISSUE': '待开',
|
|
'ISSUED': '已开',
|
|
'RECEIVED': '已回',
|
|
'OVERDUE': '逾期',
|
|
'INTERNAL_ACCEPTED': '内部已验收',
|
|
'ACCEPTED': '已验收',
|
|
'ALL': '全部',
|
|
'DEPT': '本部门',
|
|
'SELF': '本人',
|
|
'BID': '投标',
|
|
'PROJECT': '正式项目',
|
|
'ADMIN': '行政',
|
|
'MARKETING': '营销',
|
|
'LABOR': '人工',
|
|
'TRAVEL': '差旅',
|
|
'PURCHASE': '采购',
|
|
'TENDER': '招标',
|
|
'PERFORMANCE': '履约保证金',
|
|
'WARRANTY': '项目质保金',
|
|
'EXPENSE': '报销',
|
|
'BOND': '保证金打出',
|
|
'LOAN': '借款',
|
|
'OFFICE': '办公申请',
|
|
'SEAL': '用章',
|
|
'HR': '人事',
|
|
'LEAVE': '请假',
|
|
'OVERTIME': '加班',
|
|
'TRIP': '出差',
|
|
'OUT': '外出',
|
|
'RESIGN': '离职',
|
|
'PROMOTE': '晋升',
|
|
'INTERN': '实习',
|
|
'PROBATION': '试用',
|
|
'REGULAR': '正式',
|
|
'LEFT': '离职',
|
|
'SINGLE': '单休',
|
|
'DOUBLE': '双休',
|
|
'MALE': '男',
|
|
'FEMALE': '女',
|
|
'HIGH_SCHOOL': '高中及以下',
|
|
'COLLEGE': '专科',
|
|
'BACHELOR': '本科',
|
|
'MASTER': '硕士',
|
|
'DOCTOR': '博士',
|
|
'HIRE': '入职',
|
|
'REGULARIZE': '转正',
|
|
'TRANSFER': '调岗',
|
|
'CREATE': '发起',
|
|
'SUBMIT': '提交',
|
|
'APPROVE': '同意',
|
|
'REJECT': '驳回',
|
|
'DRAFTER': '制标人',
|
|
'FETCHER': '取标人',
|
|
'COORDINATOR': '特殊协调人',
|
|
'BIDDER': '投标人',
|
|
'SPECIAL': '特殊筛选',
|
|
'FLOW': '普通筛选',
|
|
'BID_TECH_INITIAL': '技术初审',
|
|
'BID_TECH_FINAL': '技术终审',
|
|
'BID_FETCH': '取标',
|
|
'BID_ASSIGN_FETCH': '派取标人',
|
|
'BID_ASSIGN_DRAFT': '派制标人',
|
|
'BID_FINAL_PASSED': '终审通过通知',
|
|
'BID_DRAFT_DONE': '制标完成通知',
|
|
'BID_TERMINATED': '投标终止通知',
|
|
'BID_DRAFT': '制标',
|
|
'BID_REVIEW1': '商务一审',
|
|
'BID_REVIEW2': '商务二审',
|
|
'BID_PRINT': '印封',
|
|
'BID_COMPLETE': '补全投标',
|
|
'BID_EXECUTION': '投标执行',
|
|
'BID_ASSIGN_FETCHER': '派取标人',
|
|
'UPDATE_COMPETITOR_WIN': '登记对方中标信息',
|
|
'UPDATE_LEGAL_ENTITY': '修改参与主体',
|
|
'CONFIRMED': '已确认',
|
|
'INTERNAL': '内部验收',
|
|
'CUSTOMER': '客户验收',
|
|
'CONTRACT_APPROVAL': '合同审批',
|
|
'CONTRACT_DRAFT': '完善合同草稿',
|
|
'EXPENSE_APPROVAL': '报销确认',
|
|
'LOAN_APPROVAL': '借款确认',
|
|
'LOAN_OFFSET': '借款冲账',
|
|
'PROJECT_TIMESHEET': '工时填报',
|
|
'PROJECT_ACCEPT': '项目验收',
|
|
'SEAL_APPROVAL': '用章审批',
|
|
'SEAL_RETURN': '印章归还',
|
|
'CREDENTIAL_RETURN': '证照归还',
|
|
'BID_OPEN': '开标反馈',
|
|
'WORK_ASSIGN': '工作安排',
|
|
'OFFICE_APPLY': '办公申请',
|
|
'HR_LEAVE': '人事申请',
|
|
'SEAL_REQUEST': '用章申请',
|
|
'WORK_REPORT': '工作汇报',
|
|
'NOTICE': '通知公告',
|
|
'ASSET_BUY': '资产购置',
|
|
'ASSET_USE': '资产领用',
|
|
'VEHICLE': '用车',
|
|
'DIGITAL': '数字资产',
|
|
'FIXED': '固定资产',
|
|
'IN_USE': '占用中',
|
|
'IN_STOCK': '在库',
|
|
'OUT_STOCK': '已出库',
|
|
'TAKEN_OUT': '外带',
|
|
'LENT': '外借',
|
|
'BORROWED': '已外借',
|
|
'IDLE': '闲置',
|
|
'SCRAPPED': '已报废',
|
|
'AVAILABLE': '可用',
|
|
'MAINTAIN': '维修中',
|
|
'OCCUPIED': '占用中',
|
|
'PENDING_IN': '待入库',
|
|
'ID_CARD': '身份证',
|
|
'PASSPORT': '护照',
|
|
'OTHER_ID': '其他证件',
|
|
'GENERAL': '通用',
|
|
'TODO': '待办',
|
|
'TASK': '任务截止',
|
|
'BORROW': '证照归还',
|
|
'LOGIN': '登录',
|
|
'LOGIN_FAIL': '登录失败',
|
|
'APPROVAL': '审批',
|
|
'FORBIDDEN': '越权',
|
|
'ACL': '权限',
|
|
'SENT': '已提交',
|
|
'UPDATE': '修改',
|
|
'DELETE': '删除',
|
|
'OK': '成功',
|
|
'FAIL': '失败',
|
|
'BUSINESS': '商务',
|
|
'TECH': '技术',
|
|
'FINANCE': '财务',
|
|
'PUBLISHED': '已发布',
|
|
'ANNOUNCE': '公告',
|
|
'POLICY': '制度',
|
|
'direct': '单聊',
|
|
'group': '群聊',
|
|
'admin': '管理员',
|
|
'owner': '负责人',
|
|
'hr': '人事',
|
|
'finance': '财务',
|
|
'finance_manager': '财务经理',
|
|
'cashier': '出纳',
|
|
'seal': '用章管理员',
|
|
'asset': '资产管理员',
|
|
'project_manager': '项目经理',
|
|
'tech_director': '技术总监',
|
|
'marketing': '营销',
|
|
'bidder': '投标',
|
|
'staff': '员工',
|
|
'user': '用户',
|
|
'true': '是',
|
|
'false': '否',
|
|
'INQUIRY': '询价',
|
|
'OPEN_TENDER': '公开招标',
|
|
'INVITE': '邀请招标',
|
|
'COMPETITIVE': '竞争性谈判',
|
|
'SINGLE_SOURCE': '单一来源',
|
|
};
|
|
|
|
/// 详情页字段名。没有映射的 camelCase 一律不展示,避免露出英文列名。
|
|
const fieldZh = <String, String>{
|
|
'title': '标题',
|
|
'name': '名称',
|
|
'purpose': '事由',
|
|
'reason': '事由',
|
|
'remark': '备注',
|
|
'status': '状态',
|
|
'kind': '类型',
|
|
'source': '来源',
|
|
'sourceType': '筛选类型',
|
|
'bizType': '业务',
|
|
'amount': '金额',
|
|
'days': '天数',
|
|
'claimNo': '报销单号',
|
|
'loanNo': '借款单号',
|
|
'bidNo': '招标编号',
|
|
'externalNo': '招标编号',
|
|
'contractNo': '合同号',
|
|
'projectNo': '项目编号',
|
|
'projectType': '项目类型',
|
|
'sealType': '印章类型',
|
|
'destination': '目的地',
|
|
'assetKind': '资产类型',
|
|
'createdAt': '创建时间',
|
|
'updatedAt': '更新时间',
|
|
'dueAt': '截止',
|
|
'startAt': '开始',
|
|
'endAt': '结束',
|
|
'decidedAt': '审批时间',
|
|
'comment': '意见',
|
|
'createdBy': '发起人',
|
|
'bidCase': '关联投标',
|
|
'legalEntity': '参与主体',
|
|
'qualificationNeed': '所需资质',
|
|
'tenderMethod': '招标方式',
|
|
'applyStartAt': '申领开始',
|
|
'applyEndAt': '申领截止',
|
|
'applyMethod': '申领方式',
|
|
'openAt': '开标时间',
|
|
'openMethod': '开标方式',
|
|
'openPlace': '开标地点',
|
|
'bondMethod': '保证金',
|
|
'bondStatus': '保证金状态',
|
|
'bondType': '保证金类型',
|
|
'delivererName': '递交人',
|
|
'contactName': '联系人',
|
|
'contactPhone': '联系电话',
|
|
'nasPath': '标书路径',
|
|
'summary': '摘要',
|
|
'resultNote': '结果说明',
|
|
'printCost': '印封费用',
|
|
'printNote': '印封备注',
|
|
'projectNature': '项目性质',
|
|
'securityLevel': '密级',
|
|
'priceCap': '限价',
|
|
'quoteAmount': '报价',
|
|
'needSurvey': '需要踏勘',
|
|
'fetchedAt': '取标时间',
|
|
'printedAt': '印封时间',
|
|
'openedResultAt': '开标反馈时间',
|
|
'actionLogs': '操作记录',
|
|
'projects': '关联项目',
|
|
'files': '附件',
|
|
'reviewers': '审批人',
|
|
'assignees': '经办人',
|
|
'versions': '标书版本',
|
|
'displayName': '姓名',
|
|
'departmentName': '部门',
|
|
'department': '部门',
|
|
'mobile': '手机',
|
|
'email': '邮箱',
|
|
'party': '客商',
|
|
'applicant': '申请人',
|
|
'assignee': '经办人',
|
|
'keeper': '保管人',
|
|
'holder': '占用人',
|
|
'spec': '规格',
|
|
'unit': '单位',
|
|
'qty': '数量',
|
|
'quantity': '数量',
|
|
'location': '位置',
|
|
'address': '地址',
|
|
'code': '编号',
|
|
'no': '编号',
|
|
'type': '类型',
|
|
'category': '分类',
|
|
'level': '级别',
|
|
'priority': '优先级',
|
|
'progress': '进度',
|
|
'budget': '预算',
|
|
'cost': '成本',
|
|
'owner': '负责人',
|
|
'manager': '负责人',
|
|
'description': '说明',
|
|
'content': '内容',
|
|
'body': '正文',
|
|
'note': '备注',
|
|
'result': '结果',
|
|
'outcome': '结果',
|
|
'method': '方式',
|
|
'channel': '渠道',
|
|
'scope': '范围',
|
|
'role': '角色',
|
|
'roles': '角色',
|
|
'employmentStatus': '用工状态',
|
|
'gender': '性别',
|
|
'idNo': '证件号',
|
|
'idType': '证件类型',
|
|
'idNumber': '证件号码',
|
|
'idCard': '身份证号',
|
|
'jobTitle': '岗位',
|
|
'hireDate': '入职日期',
|
|
'hiredAt': '入职日期',
|
|
'birthday': '出生日期',
|
|
'birthDate': '出生日期',
|
|
'education': '学历',
|
|
'school': '毕业院校',
|
|
'major': '专业',
|
|
'emergencyContact': '紧急联系人',
|
|
'emergencyPhone': '紧急电话',
|
|
'bankName': '开户行',
|
|
'bankAccount': '银行账号',
|
|
'workCity': '工作城市',
|
|
'officeLocation': '办公地点',
|
|
'employeeNo': '工号',
|
|
'staffNo': '工号',
|
|
'jobNo': '工号',
|
|
'realName': '姓名',
|
|
'username': '账号',
|
|
'phone': '手机',
|
|
'wechat': '微信',
|
|
'nation': '民族',
|
|
'nativePlace': '籍贯',
|
|
'maritalStatus': '婚姻状况',
|
|
'addressHome': '家庭住址',
|
|
'entryDate': '入职日期',
|
|
'regularDate': '转正日期',
|
|
'leaveDate': '离职日期',
|
|
'contractEnd': '合同到期',
|
|
'keeperName': '保管人',
|
|
'holderName': '占用人',
|
|
'legalEntityName': '参与主体',
|
|
'winnerName': '中标单位',
|
|
'winnerAmount': '中标金额',
|
|
};
|
|
|
|
String zh(dynamic v) {
|
|
if (v == null) return '—';
|
|
if (v is bool) return v ? '是' : '否';
|
|
final s = '$v'.trim();
|
|
if (s.isEmpty) return '—';
|
|
return zhMap[s] ?? s;
|
|
}
|
|
|
|
/// 字段名转中文。仍是英文驼峰的返回空串,调用方应跳过。
|
|
String fieldLabel(String k) {
|
|
final key = k.contains('.') ? k.split('.').last : k;
|
|
if (fieldZh.containsKey(k)) return fieldZh[k]!;
|
|
if (fieldZh.containsKey(key)) return fieldZh[key]!;
|
|
if (zhMap.containsKey(key)) return zhMap[key]!;
|
|
if (RegExp(r'[\u4e00-\u9fff]').hasMatch(key)) return key;
|
|
return '';
|
|
}
|
|
|
|
Color statusColor(dynamic v) {
|
|
switch ('$v') {
|
|
case 'APPROVED':
|
|
case 'DONE':
|
|
case 'OFFSET':
|
|
case 'RETURNED':
|
|
case 'PAID':
|
|
case 'WON':
|
|
case 'ACCEPTED':
|
|
case 'ISSUED':
|
|
case 'RECEIVED':
|
|
return kWeGreen;
|
|
case 'PENDING':
|
|
case 'OPEN':
|
|
case 'DRAFT':
|
|
case 'FETCH':
|
|
case 'DRAFTING':
|
|
case 'TECH_INITIAL':
|
|
case 'TECH_FINAL':
|
|
case 'REVIEW1':
|
|
case 'REVIEW2':
|
|
case 'PRINT':
|
|
case 'LIST':
|
|
return const Color(0xFFFA9D3B);
|
|
case 'REJECTED':
|
|
case 'CANCELLED':
|
|
case 'OVERDUE':
|
|
case 'TERMINATED':
|
|
case 'FAILED':
|
|
case 'LOST':
|
|
return kDanger;
|
|
default:
|
|
return kMute;
|
|
}
|
|
}
|
|
|
|
class StatusDot extends StatelessWidget {
|
|
const StatusDot(this.code, {super.key});
|
|
final dynamic code;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
|
decoration: BoxDecoration(
|
|
color: statusColor(code).withValues(alpha: 0.12),
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Text(zh(code), style: TextStyle(fontSize: 11, color: statusColor(code), fontWeight: FontWeight.w600)),
|
|
);
|
|
}
|
|
}
|