feat(client): A3 客户与行业目录(两级行业、客户组、字段审批)
背景:A3 需把行业 / 客户 / 客户组 / 客户级字段立为资产根节点;用户确认两级行业树与混搭 UI(行业 B / 客户列表 A / 详情 A / 新建 B / 客户组 B / 字段 A)。 改动: - 新增 ADR-002,行业改为两级树,客户挂末级行业;同步 PRD / 技术架构 / 开发计划。 - 新增 client 模块:industries / customers / customer-groups / customer-fields 接口、真实 PrismaCustomerDirectory、客户状态机与客户转交、客户组数据范围解析、跨客户 404。 - 新增迁移与幂等种子(电商/金融两级行业、2 客户、1 客户组、4 条字段)。 - 前端新增行业/客户/客户组/字段审批页面与路由、导航更名(客户管理 / 行业管理 / 客户组),复用现有 token。 - 补充 shared 契约单测、client 集成用例、A3 浏览器 e2e。 影响面:client 模块与 client 表、shared 契约、web 路由/导航、ScopeContext 数据范围、OpenAPI 与种子;A1 客户转交占位由 client 实现替换。 回滚:回滚本提交;数据库保留新增表或按报告执行 down SQL;CUSTOMER_DIRECTORY 可切回空集实现。 验证:format:check / lint / typecheck / test 全绿(shared 131 / api 90 / web 125,共 346);test:int 5 文件 39 条;A3 e2e 1/1;build 8/8;真实浏览器六页验证通过。
This commit is contained in:
1 parent
8626996bb1
commit
140dc386c1
54 files changed
+9018
-147
No files matched your search
@@ -0,0 +1,526 @@
|
||||
/* A3 客户与行业页面(混搭布局:客户域 A / 行业与客户组 B / 字段审批 A) */
|
||||
.lx-client-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.lx-client-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
.lx-client-header h1 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.lx-client-header p {
|
||||
margin: 2px 0 0;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: var(--lx-text-2, #8f959e);
|
||||
}
|
||||
.lx-client-header__actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex: none;
|
||||
}
|
||||
.lx-client-filters {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.lx-client-split {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
.lx-client-left {
|
||||
width: 380px;
|
||||
flex: none;
|
||||
background: #fff;
|
||||
border: 1px solid var(--lx-border, #e5e8ef);
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
.lx-client-left__head {
|
||||
height: 48px;
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 0 14px;
|
||||
border-bottom: 1px solid #eef1f6;
|
||||
}
|
||||
.lx-client-left__head b {
|
||||
font-size: 13px;
|
||||
}
|
||||
.lx-client-left__head span {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-left__body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 8px;
|
||||
}
|
||||
.lx-client-left__foot {
|
||||
height: 40px;
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 14px;
|
||||
border-top: 1px solid #eef1f6;
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-right {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
background: #fff;
|
||||
border: 1px solid var(--lx-border, #e5e8ef);
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
.lx-client-right__head {
|
||||
flex: none;
|
||||
padding: 16px 18px;
|
||||
border-bottom: 1px solid #eef1f6;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
.lx-client-right__head h2 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.lx-client-right__head p {
|
||||
margin: 2px 0 0;
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-right__head .lx-client-header__actions {
|
||||
margin-left: auto;
|
||||
}
|
||||
.lx-client-right__body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 16px 18px 20px;
|
||||
}
|
||||
.lx-client-tree {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.lx-client-tree__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
height: 34px;
|
||||
padding: 0 8px;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: #646a73;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
}
|
||||
.lx-client-tree__row:hover {
|
||||
background: #f2f4f7;
|
||||
color: #1f2329;
|
||||
}
|
||||
.lx-client-tree__row.is-active {
|
||||
background: #e8f1ff;
|
||||
color: #1677ff;
|
||||
}
|
||||
.lx-client-tree__row.is-child {
|
||||
padding-left: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
.lx-client-tree__name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.lx-client-tree__count {
|
||||
font-size: 11px;
|
||||
color: #8f959e;
|
||||
}
|
||||
.lx-client-tree__row.is-active .lx-client-tree__count {
|
||||
color: #1677ff;
|
||||
}
|
||||
.lx-client-table-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e8ef;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.lx-client-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 48px 24px;
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
}
|
||||
.lx-client-empty b {
|
||||
font-size: 13px;
|
||||
color: #1f2329;
|
||||
}
|
||||
.lx-client-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 200px minmax(0, 1fr) 280px;
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
}
|
||||
.lx-client-anchors {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e8ef;
|
||||
border-radius: 10px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
.lx-client-anchors a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
padding: 0 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-anchors a:hover {
|
||||
background: #f2f4f7;
|
||||
color: #1f2329;
|
||||
}
|
||||
.lx-client-panel {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e8ef;
|
||||
border-radius: 10px;
|
||||
padding: 18px 20px;
|
||||
min-width: 0;
|
||||
}
|
||||
.lx-client-side {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
.lx-client-sidecard {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e8ef;
|
||||
border-radius: 10px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
.lx-client-sidecard h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.lx-client-kv {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px 28px;
|
||||
}
|
||||
.lx-client-kv__item span {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-kv__item b {
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.lx-client-section {
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid #eef1f6;
|
||||
}
|
||||
.lx-client-section:last-child {
|
||||
border-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.lx-client-section__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.lx-client-section__head b {
|
||||
font-size: 14px;
|
||||
}
|
||||
.lx-client-hint {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 22px;
|
||||
padding: 0 7px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.lx-client-tag--active {
|
||||
background: #e8ffea;
|
||||
color: #008a24;
|
||||
}
|
||||
.lx-client-tag--paused {
|
||||
background: #fff4e8;
|
||||
color: #d25f00;
|
||||
}
|
||||
.lx-client-tag--ended {
|
||||
background: #f2f4f7;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-tag--disabled {
|
||||
background: #f2f4f7;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.lx-client-banner--paused {
|
||||
background: #fff4e8;
|
||||
color: #d25f00;
|
||||
}
|
||||
.lx-client-banner--ended {
|
||||
background: #f2f4f7;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
.lx-client-chip {
|
||||
height: 28px;
|
||||
padding: 0 9px;
|
||||
border-radius: 6px;
|
||||
background: #fafbfc;
|
||||
border: 1px solid #eef1f6;
|
||||
font-size: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.lx-client-newgrid {
|
||||
display: grid;
|
||||
grid-template-columns: 260px minmax(0, 1fr);
|
||||
gap: 16px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
.lx-client-stepnav {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e8ef;
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
}
|
||||
.lx-client-formcard {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e8ef;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
.lx-client-formcard__body {
|
||||
padding: 20px 22px;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
.lx-client-formcard__foot {
|
||||
height: 60px;
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 0 20px;
|
||||
border-top: 1px solid #eef1f6;
|
||||
}
|
||||
.lx-client-formcard__foot .spacer {
|
||||
flex: 1;
|
||||
}
|
||||
.lx-client-formgrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px 20px;
|
||||
}
|
||||
.lx-client-formgrid .is-full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.lx-client-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
.lx-client-field > label {
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-field > label i {
|
||||
color: #f53f3f;
|
||||
font-style: normal;
|
||||
}
|
||||
.lx-client-memberlist {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.lx-client-memberrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #eef1f6;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.lx-client-memberrow b {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.lx-client-memberrow span {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-assetempty {
|
||||
border: 1px dashed #d0d5dd;
|
||||
border-radius: 6px;
|
||||
background: #fafbfc;
|
||||
padding: 20px;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-assetempty b {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
color: #1f2329;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
@media (max-width: 1280px) {
|
||||
.lx-client-grid {
|
||||
grid-template-columns: 180px minmax(0, 1fr);
|
||||
}
|
||||
.lx-client-grid .lx-client-side {
|
||||
display: none;
|
||||
}
|
||||
.lx-client-left {
|
||||
width: 330px;
|
||||
}
|
||||
}
|
||||
.lx-client-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
table-layout: fixed;
|
||||
}
|
||||
.lx-client-table th {
|
||||
height: 40px;
|
||||
padding: 0 14px;
|
||||
text-align: left;
|
||||
background: #fafbfc;
|
||||
border-bottom: 1px solid #eef1f6;
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.lx-client-table td {
|
||||
padding: 12px 14px;
|
||||
border-bottom: 1px solid #eef1f6;
|
||||
color: #646a73;
|
||||
vertical-align: middle;
|
||||
line-height: 20px;
|
||||
}
|
||||
.lx-client-table tbody tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
.lx-client-table tbody tr:hover {
|
||||
background: #f8fbff;
|
||||
}
|
||||
.lx-client-table td b {
|
||||
font-weight: 500;
|
||||
color: #1f2329;
|
||||
}
|
||||
.lx-client-sub {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: #646a73;
|
||||
}
|
||||
.lx-client-muted {
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
}
|
||||
.lx-client-count {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 22px;
|
||||
padding: 0 7px;
|
||||
border-radius: 4px;
|
||||
background: #f2f4f7;
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.lx-client-count.is-full {
|
||||
background: #fff4e8;
|
||||
color: #d25f00;
|
||||
}
|
||||
.lx-client-tablefoot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 14px;
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
border-top: 1px solid #eef1f6;
|
||||
}
|
||||
.lx-client-tablefoot .semi-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
.lx-client-back {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.lx-client-back:hover {
|
||||
color: #1677ff;
|
||||
}
|
||||
Reference in new issue
Block a user