feat(products): 改版服务器选购界面并新增地区筛选与价格排序
前端验证 / verify (push) Successful in 2m7s

- 云产品选择器新增地区标签页、分组数量及全部选项,自动跟随当前线路所属地区
- 将线路和型号改为紧凑选择按钮,精简型号名称的公共前缀并保留完整名称提示
- 物理服务器目录改为规格列表,支持行内展开、收起线路详情及进入定制页
- 新增商品起价升降序排序,将无有效价格的商品置于末尾
- 将规格筛选改为下拉选项,移动端支持展开更多筛选并显示已选条件数量
- 统一云服务器与物理服务器的横向配置表单,优化磁盘、带宽、镜像选择及移动端布局
- 限制桌面端配置汇总栏高度,使清单内部滚动并保持价格和购买按钮可见
- 统一产品分类标签页与胶囊按钮样式,补充查看全部产品入口
- 将产品选择器和物理服务器目录重构为 script setup、声明式模板及 scoped 样式,删除旧样式文件与相关引用
This commit is contained in:
cneicy committed 2026-11-08 18:54:34 +08:00
1 parent 65c175c8fa
commit e3ad73d397
12 files changed
+1958 -2149

No files matched your search

@@ -1,128 +0,0 @@
/* 云商品选择器:区域/商品选项、选中状态及空目录提示。 */
/* 导入方:app/components/config/CloudProductPicker.vue、app/pages/products/cloud-server.vue。 */
.cloud-product-picker[data-v-a438862d] {
flex-direction: column;
gap: 24px;
min-width: 0;
display: flex;
}
.selection-section[data-v-a438862d] {
min-width: 0;
}
.selection-section h3[data-v-a438862d] {
border-bottom: 1px solid var(--p-content-border-color);
margin: 0 0 21px;
padding-bottom: 15px;
font-size: 1rem;
font-weight: 400;
}
.selection-section .products-title[data-v-a438862d] {
border: 0;
margin-bottom: 8px;
padding-bottom: 0;
}
.selection-label[data-v-a438862d] {
margin-bottom: 8px;
}
.zone-options[data-v-a438862d] {
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 14px;
display: grid;
}
.product-options[data-v-a438862d] {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px;
display: grid;
}
/* 允许连续长文本换行,防止IP、密钥或用户输入撑破当前容器;不会修改原始数据。 */
.product-option[data-v-a438862d],
.zone-option[data-v-a438862d] {
border: 1px solid var(--p-content-border-color);
background: var(--p-content-background);
min-width: 0;
min-height: 60px;
color: var(--p-text-color);
text-align: left;
cursor: pointer;
overflow-wrap: anywhere;
border-radius: var(--radius-md);
flex-direction: column;
gap: 10px;
padding: 17px 14px;
display: flex;
box-shadow: 0 1px 3px #0000001a;
}
.zone-option[data-v-a438862d] {
justify-content: center;
font-weight: 600;
}
.product-option[data-v-a438862d] {
gap: 0;
padding: 0;
overflow: hidden;
}
.product-option strong[data-v-a438862d] {
background: color-mix(
in srgb,
var(--p-primary-color) 8%,
var(--p-content-background)
);
padding: 14px;
font-weight: 600;
display: block;
}
.product-option span[data-v-a438862d] {
color: var(--p-text-muted-color);
padding: 14px;
font-size: 0.875rem;
}
.active[data-v-a438862d] {
outline: 2px solid var(--p-primary-color);
outline-offset: -1px;
background: color-mix(
in srgb,
var(--p-primary-color) 7%,
var(--p-content-background)
);
color: var(--p-primary-700);
}
.product-option[data-v-a438862d]:disabled,
.zone-option[data-v-a438862d]:disabled {
opacity: 0.55;
cursor: not-allowed;
}
/* 键盘操作的可见焦点:不要只保留鼠标 hover 效果而移除此反馈。 */
.product-option[data-v-a438862d]:focus-visible,
.zone-option[data-v-a438862d]:focus-visible {
outline: 2px solid var(--p-primary-color);
outline-offset: 3px;
}
.catalog-empty[data-v-a438862d] {
text-align: center;
color: var(--p-text-muted-color);
border: 1px solid var(--p-content-border-color);
background: var(--p-content-background);
border-radius: var(--radius-md);
padding: 28px;
}
.catalog-empty i[data-v-a438862d] {
font-size: 1.75rem;
}
.catalog-empty a[data-v-a438862d] {
color: var(--p-primary-color);
text-decoration: none;
}
/* 视口条件 (width<768px) 下的响应式覆盖;保留相对基础规则的层叠顺序。 */
@media (width<768px) {
.product-options[data-v-a438862d],
.zone-options[data-v-a438862d] {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
}
/* 视口条件 (width<=390px) 下的响应式覆盖;保留相对基础规则的层叠顺序。 */
@media (width<=390px) {
.product-options[data-v-a438862d] {
grid-template-columns: minmax(0, 1fr);
}
}
+192
View File
@@ -44,3 +44,195 @@
max-width: 470px; max-width: 470px;
} }
} }
/* 横向表单:左列标签、右列控件,所有控件左缘对齐;字段分组容器不参与布局。 */
.form-inline[data-v-e8ce6418] {
display: contents;
}
.config-page .config-options .form-row[data-v-e8ce6418] {
grid-template-columns: 88px minmax(0, 1fr);
align-items: start;
gap: 8px 16px;
margin-bottom: 0;
display: grid;
}
.config-page .config-options .form-row > label[data-v-e8ce6418],
.config-page .storage-field[data-v-e8ce6418] .storage-label,
.config-page .bandwidth-field[data-v-e8ce6418] .bandwidth-label {
color: var(--p-text-muted-color);
margin: 0;
font-size: 0.9375rem;
font-weight: 400;
line-height: 36px;
}
.config-page .config-options .form-row[data-v-e8ce6418] > :not(label) {
grid-column: 2;
}
.config-page .config-options .form-row > .storage-field[data-v-e8ce6418],
.config-page .config-options .form-row > .bandwidth-field[data-v-e8ce6418] {
grid-column: 1 / -1;
}
.config-options > .panel[data-v-e8ce6418] {
flex-direction: column;
gap: 16px;
display: flex;
}
.config-options > .panel > .section-title[data-v-e8ce6418] {
margin-bottom: 6px;
}
.config-page .config-options .image-groups[data-v-e8ce6418] {
margin-bottom: 0;
}
.config-page .storage-panel .form-row > label[data-v-e8ce6418],
.config-page .storage-field[data-v-e8ce6418] .storage-label {
line-height: 46px;
}
.config-page .storage-system[data-v-e8ce6418] {
width: auto;
}
.config-page .traffic-package-value[data-v-e8ce6418] {
min-height: 36px;
width: auto;
justify-self: start;
padding: 6px 14px;
}
/* 磁盘:系统盘与各数据盘统一为单行卡片,列宽一致便于对齐。 */
.config-page .storage-field[data-v-e8ce6418] {
grid-template-columns: 88px minmax(0, 1fr);
align-items: start;
gap: 16px;
display: grid;
}
.config-page .storage-field[data-v-e8ce6418] .storage-card {
grid-template-columns: 48px 150px minmax(80px, 1fr) auto 30px;
grid-template-areas: "type cap slider desc remove";
grid-column: 2;
align-items: center;
gap: 16px;
min-height: 46px;
padding: 4px 12px;
border: 1px solid var(--p-content-border-color);
background: var(--p-content-background);
box-shadow: none;
display: grid;
}
.config-page .storage-field[data-v-e8ce6418] .storage-card-header {
display: contents;
}
.config-page .storage-field[data-v-e8ce6418] .storage-card-header strong {
grid-area: type;
color: var(--p-text-color);
font-size: 0.9375rem;
font-weight: 600;
}
.config-page .storage-field[data-v-e8ce6418] .storage-remove {
grid-area: remove;
margin: 0;
}
.config-page .storage-field[data-v-e8ce6418] .storage-capacity {
grid-area: cap;
}
.config-page
.storage-field[data-v-e8ce6418]
.storage-capacity
.p-inputnumber-input {
min-height: 34px;
padding: 6px 10px;
}
.config-page .storage-field[data-v-e8ce6418] .storage-slider {
grid-area: slider;
margin: 0;
}
.config-page .storage-field[data-v-e8ce6418] .storage-description {
grid-area: desc;
color: var(--p-text-muted-color);
white-space: nowrap;
font-size: 0.8125rem;
}
.config-page .config-options .storage-disks[data-v-e8ce6418] {
grid-template-columns: minmax(0, 1fr);
gap: 8px;
}
.storage-disks[data-v-e8ce6418] > .storage-field {
display: block;
}
.storage-disks > .storage-add[data-v-e8ce6418] {
min-height: 40px;
flex-direction: row;
gap: 8px;
padding: 8px 14px;
border-width: 1px;
font-size: 0.9375rem;
}
.config-page .config-options .form-row > .hint[data-v-e8ce6418] {
font-size: 0.8125rem;
}
/* 带宽:标签、数值输入和滑块同一行。 */
.config-page .bandwidth-field[data-v-e8ce6418] {
grid-template-columns: 88px 150px minmax(80px, 1fr);
align-items: center;
gap: 16px;
max-width: none;
display: grid;
}
.config-page
.bandwidth-field[data-v-e8ce6418]
.bandwidth-capacity
.p-inputnumber-input {
min-height: 34px;
padding: 6px 10px;
}
.config-page .bandwidth-field[data-v-e8ce6418] > .bandwidth-slider {
width: auto;
max-width: 360px;
margin: 0 8px 0 0;
}
.config-page .image-groups[data-v-e8ce6418] {
grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
gap: 10px;
}
.config-page .image-groups[data-v-e8ce6418] .image-group {
min-height: 48px;
gap: 10px;
padding: 8px 12px;
font-size: 0.9375rem;
box-shadow: none;
}
@media (width<640px) {
.config-page .config-options .form-row[data-v-e8ce6418],
.config-page .storage-field[data-v-e8ce6418] {
grid-template-columns: minmax(0, 1fr);
gap: 6px;
}
.config-page .config-options .form-row[data-v-e8ce6418] > :not(label),
.config-page .storage-field[data-v-e8ce6418] .storage-card {
grid-column: 1;
}
.config-page .config-options .form-row > label[data-v-e8ce6418],
.config-page .storage-field[data-v-e8ce6418] .storage-label,
.config-page .bandwidth-field[data-v-e8ce6418] .bandwidth-label {
line-height: 1.5;
}
.config-page .storage-field[data-v-e8ce6418] .storage-card {
grid-template-columns: 40px minmax(0, 1fr) 30px;
grid-template-areas:
"type cap remove"
"slider slider slider"
"desc desc desc";
gap: 10px 12px;
padding: 10px 12px;
}
.config-page .bandwidth-field[data-v-e8ce6418] {
grid-template-columns: 120px minmax(0, 1fr);
gap: 6px 16px;
}
.config-page .bandwidth-field[data-v-e8ce6418] .bandwidth-label {
grid-column: 1 / -1;
}
.config-page .image-groups[data-v-e8ce6418] {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
+163
View File
@@ -179,3 +179,166 @@
width: 125px; width: 125px;
} }
} }
/* 横向表单:左列标签、右列控件,与云服务器配置页保持一致;基础规格卡片内的字段不受影响。 */
.config-options > .panel[data-v-b5ff32bb] {
flex-direction: column;
gap: 16px;
display: flex;
}
.config-options > .panel > .section-title[data-v-b5ff32bb] {
margin-bottom: 6px;
}
.config-options > .panel > .dcim-base[data-v-b5ff32bb] {
margin-bottom: 0;
}
.config-page .config-options > .panel > .form-row[data-v-b5ff32bb] {
grid-template-columns: 88px minmax(0, 1fr);
align-items: start;
gap: 8px 16px;
margin-bottom: 0;
display: grid;
}
.config-page .config-options > .panel > .form-row > label[data-v-b5ff32bb],
.config-page .config-options > .panel > .form-row > span[id][data-v-b5ff32bb],
.config-page .storage-grid > .storage-field[data-v-b5ff32bb] .storage-label {
color: var(--p-text-muted-color);
margin: 0;
font-size: 0.9375rem;
font-weight: 400;
line-height: 36px;
}
.config-page
.config-options
> .panel
> .form-row[data-v-b5ff32bb]
> :where(:not(label, span[id])) {
grid-column: 2;
}
.config-page
.config-options
> .panel
> .form-row[data-v-b5ff32bb]:has(> .network-slider) {
grid-template-columns: 88px 150px minmax(80px, 360px);
align-items: center;
}
.config-page
.config-options
> .panel
> .form-row
> .network-slider[data-v-b5ff32bb] {
grid-column: 3;
max-width: none;
margin: 0 8px;
}
.config-page
.config-options
> .panel
> .form-row
> .p-inputnumber[data-v-b5ff32bb] {
max-width: none;
}
/* 硬盘:每块盘一行,标签在左列。 */
.config-page .storage-grid[data-v-b5ff32bb] {
grid-template-columns: minmax(0, 1fr);
gap: 12px;
}
.config-page .storage-grid > .storage-field[data-v-b5ff32bb] {
grid-template-columns: 88px minmax(0, 1fr);
align-items: start;
gap: 16px;
display: grid;
}
.config-page .storage-grid > .storage-field[data-v-b5ff32bb] .storage-label {
line-height: 44px;
}
.config-page .storage-grid > .storage-field[data-v-b5ff32bb] .storage-card {
border: 1px solid var(--p-content-border-color);
background: var(--p-content-background);
box-shadow: none;
flex-flow: wrap;
align-items: center;
gap: 8px 20px;
min-height: 44px;
padding: 6px 14px;
}
.config-page
.storage-grid
> .storage-field[data-v-b5ff32bb]
.storage-card-header
strong {
color: var(--p-text-color);
font-size: 0.9375rem;
font-weight: 600;
}
.config-page
.storage-grid
> .storage-field[data-v-b5ff32bb]
.storage-description {
color: var(--p-text-muted-color);
margin-left: auto;
font-size: 0.8125rem;
}
.config-page .storage-grid .disk-quantity[data-v-b5ff32bb] {
flex-direction: row;
align-items: center;
gap: 8px;
}
.config-page .storage-grid .disk-quantity[data-v-b5ff32bb] .p-inputnumber {
width: 150px;
}
@media (width<640px) {
.config-page .config-options > .panel > .form-row[data-v-b5ff32bb],
.config-page .storage-grid > .storage-field[data-v-b5ff32bb] {
grid-template-columns: minmax(0, 1fr);
gap: 6px;
}
.config-page
.config-options
> .panel
> .form-row[data-v-b5ff32bb]:has(> .network-slider) {
grid-template-columns: 130px minmax(0, 1fr);
gap: 6px 16px;
}
.config-page .config-options > .panel > .form-row > label[data-v-b5ff32bb] {
grid-column: 1 / -1;
}
.config-page
.config-options
> .panel
> .form-row[data-v-b5ff32bb]
> :where(:not(label, span[id])) {
grid-column: 1;
}
.config-page
.config-options
> .panel
> .form-row
> .network-slider[data-v-b5ff32bb] {
grid-column: 2;
}
.config-page .config-options > .panel > .form-row > label[data-v-b5ff32bb],
.config-page .config-options > .panel > .form-row > span[id][data-v-b5ff32bb],
.config-page .storage-grid > .storage-field[data-v-b5ff32bb] .storage-label {
line-height: 1.5;
}
}
.config-page .config-options .image-groups[data-v-b5ff32bb] {
grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
gap: 10px;
margin-bottom: 0;
}
.config-page .config-options .image-groups[data-v-b5ff32bb] .image-group,
.config-page .config-options .image-group[data-v-b5ff32bb] {
min-height: 48px;
gap: 10px;
padding: 8px 12px;
font-size: 0.9375rem;
box-shadow: none;
}
@media (width<640px) {
.config-page .config-options .image-groups[data-v-b5ff32bb] {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@@ -15,3 +15,18 @@ h2[data-v-ef3931f0] {
font-weight: 500; font-weight: 500;
line-height: 2rem; line-height: 2rem;
} }
.order-head[data-v-ef3931f0] {
justify-content: space-between;
align-items: center;
gap: 14px;
display: flex;
}
.order-head a[data-v-ef3931f0] {
color: var(--p-text-muted-color);
white-space: nowrap;
font-size: 0.875rem;
text-decoration: none;
}
.order-head a[data-v-ef3931f0]:hover {
color: var(--p-primary-color);
}
@@ -1,398 +0,0 @@
/* 独立服务器目录:筛选面板、硬件规格、线路选择与购买入口。 */
/* 导入方:app/pages/products/dedicated-server/customize.vue、app/pages/products/dedicated-server/index.vue。 */
.dedicated-catalog[data-v-e16eb167] {
width: 100%;
max-width: 1280px;
margin: 0 auto;
padding-bottom: 28px;
}
.catalog-head[data-v-e16eb167] {
justify-content: space-between;
align-items: center;
gap: 14px;
margin-bottom: 35px;
display: flex;
}
.catalog-head h1[data-v-e16eb167] {
margin: 0;
font-size: 1.375rem;
font-weight: 400;
}
.catalog-head a[data-v-e16eb167] {
color: var(--p-text-muted-color);
font-size: 0.875rem;
text-decoration: none;
}
.catalog-layout[data-v-e16eb167] {
grid-template-columns: 304px minmax(0, 1fr);
align-items: start;
gap: 21px;
display: grid;
}
.catalog-layout.has-selection[data-v-e16eb167] {
grid-template-columns: 240px minmax(0, 1fr) minmax(0, 1fr);
}
h2[data-v-e16eb167] {
border-bottom: 1px solid var(--p-content-border-color);
margin: 0 0 14px;
padding: 0 0 14px;
font-size: 1rem;
font-weight: 400;
line-height: 1.5rem;
}
.filter-panel[data-v-e16eb167],
.hardware-card[data-v-e16eb167] {
border: 1px solid var(--p-content-border-color);
background: var(--p-content-background);
border-radius: var(--radius-md);
box-shadow: 0 1px 3px #0000001a;
}
.filter-panel[data-v-e16eb167] {
padding: 0 16px 7px;
}
details[data-v-e16eb167] {
border-bottom: 1px solid var(--p-content-border-color);
}
summary[data-v-e16eb167] {
cursor: pointer;
justify-content: space-between;
padding: 16px 0;
font-weight: 500;
list-style: none;
display: flex;
}
summary[data-v-e16eb167]::-webkit-details-marker {
display: none;
}
summary > i[data-v-e16eb167] {
align-self: center;
font-size: 0.875rem;
}
details[open] > summary > i[data-v-e16eb167] {
transform: rotate(180deg);
}
.price-range[data-v-e16eb167] {
align-items: center;
gap: 7px;
margin: 7px 0 12px;
display: flex;
}
.price-range[data-v-e16eb167] .p-inputnumber {
flex: 1;
width: 0;
}
.price-range[data-v-e16eb167] input {
width: 100%;
min-width: 0;
}
.filter-panel small[data-v-e16eb167] {
color: var(--p-text-muted-color);
padding-bottom: 14px;
font-size: 0.8125rem;
line-height: 1.6;
display: block;
}
.filter-options[data-v-e16eb167] {
flex-wrap: wrap;
gap: 7px;
padding-bottom: 14px;
display: flex;
}
.filter-options button[data-v-e16eb167] {
border: 1px solid var(--p-content-border-color);
color: var(--p-text-color);
cursor: pointer;
background: 0 0;
border-radius: var(--radius-sm);
padding: 4px 7px;
font-size: 0.875rem;
}
.filter-options button.active[data-v-e16eb167] {
border-color: var(--p-primary-color);
background: var(--p-highlight-background);
color: var(--p-highlight-color);
}
.filter-empty[data-v-e16eb167] {
color: var(--p-text-muted-color);
font-size: 0.875rem;
}
.catalog-lines[data-v-e16eb167],
.catalog-products[data-v-e16eb167] {
min-width: 0;
}
.hardware-card[data-v-e16eb167] {
margin-bottom: 14px;
padding: 17px 15px;
}
.hardware-card.selected[data-v-e16eb167] {
outline: 2px solid var(--p-primary-color);
background: var(--p-highlight-background);
}
.hardware-card h3[data-v-e16eb167] {
margin: 0 0 24px;
font-size: 1.125rem;
font-weight: 600;
}
.hardware-body[data-v-e16eb167] {
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) auto;
align-items: center;
gap: 21px;
display: grid;
}
.hardware-spec[data-v-e16eb167] {
align-items: center;
gap: 14px;
min-width: 0;
display: flex;
}
.hardware-spec i[data-v-e16eb167],
.line-specs i[data-v-e16eb167] {
color: var(--p-primary-color);
}
.hardware-spec > div[data-v-e16eb167] {
flex-direction: column;
min-width: 0;
display: flex;
}
.hardware-spec span[data-v-e16eb167] {
color: var(--p-text-muted-color);
font-size: 0.875rem;
}
/* 允许连续长文本换行,防止IP、密钥或用户输入撑破当前容器;不会修改原始数据。 */
.hardware-spec strong[data-v-e16eb167] {
overflow-wrap: anywhere;
font-size: 1rem;
font-weight: 500;
}
.hardware-spec small[data-v-e16eb167] {
color: var(--p-text-muted-color);
font-size: 0.8125rem;
}
.hardware-buy[data-v-e16eb167] {
background: var(--p-content-hover-background);
border-radius: var(--radius-md);
align-items: center;
gap: 10px;
padding: 4px;
display: flex;
}
.hardware-buy > span[data-v-e16eb167] {
text-align: right;
flex-direction: column;
display: flex;
}
.hardware-buy strong[data-v-e16eb167] {
white-space: nowrap;
font-size: 1.25rem;
}
.hardware-buy small[data-v-e16eb167] {
color: var(--p-text-muted-color);
font-size: max(11px, 0.625rem);
}
.hardware-buy[data-v-e16eb167] button {
min-width: 112px;
}
.hardware-card footer[data-v-e16eb167] {
color: var(--p-text-muted-color);
justify-content: space-between;
gap: 14px;
margin-top: 24px;
font-size: 0.875rem;
display: flex;
}
.has-selection .hardware-body[data-v-e16eb167] {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.has-selection .hardware-buy[data-v-e16eb167] {
grid-column: 1/-1;
justify-self: end;
margin-top: 7px;
}
.has-selection .hardware-card h3[data-v-e16eb167] {
margin-bottom: 21px;
}
.catalog-state[data-v-e16eb167] {
text-align: center;
border: 1px solid var(--p-content-border-color);
background: var(--p-content-background);
border-radius: var(--radius-md);
flex-direction: column;
align-items: center;
gap: 14px;
min-height: 260px;
padding: 70px 21px;
display: flex;
}
.catalog-state > i[data-v-e16eb167] {
color: var(--p-text-muted-color);
font-size: 2rem;
}
.catalog-state h3[data-v-e16eb167] {
margin: 0;
font-size: 1.125rem;
}
.catalog-state p[data-v-e16eb167] {
color: var(--p-text-muted-color);
margin: 0;
font-size: 0.9375rem;
}
.line-heading[data-v-e16eb167] {
border-bottom: 1px solid var(--p-content-border-color);
justify-content: space-between;
align-items: center;
min-height: 36px;
margin-bottom: 14px;
display: flex;
}
.line-heading h2[data-v-e16eb167] {
border: 0;
margin: 0;
padding: 0;
}
.line-panel[data-v-e16eb167] {
border: 1px solid var(--p-content-border-color);
background: var(--p-highlight-background);
border-radius: var(--radius-md);
padding: 0 17px;
}
.line-card[data-v-e16eb167] {
border-bottom: 1px solid var(--p-content-border-color);
padding: 17px 0;
}
.line-card[data-v-e16eb167]:last-child {
border: 0;
}
.line-tags[data-v-e16eb167] {
flex-wrap: wrap;
gap: 8px;
margin-bottom: 21px;
display: flex;
}
.line-specs[data-v-e16eb167] {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px;
display: grid;
}
.line-specs > div[data-v-e16eb167] {
align-items: center;
gap: 10px;
min-width: 0;
display: flex;
}
.line-specs span[data-v-e16eb167] {
color: var(--p-text-muted-color);
font-size: 0.875rem;
}
.line-specs strong[data-v-e16eb167] {
color: var(--p-text-color);
font-size: 1rem;
font-weight: 500;
display: block;
}
.line-action[data-v-e16eb167] {
justify-content: flex-end;
align-items: center;
gap: 10px;
margin-top: 21px;
display: flex;
}
.line-action .line-order-link[data-v-e16eb167] {
white-space: nowrap;
flex-shrink: 0;
justify-content: center;
align-items: center;
gap: 7px;
min-width: 112px;
text-decoration: none;
display: inline-flex;
}
/* 允许连续长文本换行,防止IP、密钥或用户输入撑破当前容器;不会修改原始数据。 */
.line-action > span[data-v-e16eb167] {
overflow-wrap: anywhere;
min-width: 0;
color: var(--p-text-muted-color);
font-size: 0.8125rem;
}
.line-empty[data-v-e16eb167] {
padding: 21px;
font-size: 0.9375rem;
}
.filter-options button[data-v-e16eb167]:focus-visible,
summary[data-v-e16eb167]:focus-visible {
outline: 2px solid var(--p-primary-color);
outline-offset: 3px;
}
/* 视口条件 (width>=1024px) 下的响应式覆盖;保留相对基础规则的层叠顺序。 */
@media (width>=1024px) {
.catalog-lines[data-v-e16eb167] {
position: sticky;
top: calc(var(--console-top-offset, 56px) + 20px);
}
}
/* 视口条件 (width<1200px) 下的响应式覆盖;保留相对基础规则的层叠顺序。 */
@media (width<1200px) {
.catalog-layout[data-v-e16eb167] {
grid-template-columns: 240px minmax(0, 1fr);
}
.hardware-body[data-v-e16eb167] {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.hardware-buy[data-v-e16eb167] {
grid-column: 1/-1;
justify-self: end;
}
.catalog-layout.has-selection[data-v-e16eb167] {
grid-template-columns: 200px minmax(0, 1fr);
}
.catalog-lines[data-v-e16eb167] {
grid-area: 1/2;
position: static;
}
.has-selection .catalog-products[data-v-e16eb167] {
grid-column: 2;
}
.has-selection .catalog-filters[data-v-e16eb167] {
grid-row: 1 / span 2;
}
}
/* 视口条件 (width<768px) 下的响应式覆盖;保留相对基础规则的层叠顺序。 */
@media (width<768px) {
.catalog-head[data-v-e16eb167] {
margin-bottom: 28px;
}
.catalog-layout[data-v-e16eb167],
.catalog-layout.has-selection[data-v-e16eb167] {
flex-direction: column;
gap: 28px;
display: flex;
}
.catalog-filters[data-v-e16eb167],
.catalog-lines[data-v-e16eb167],
.catalog-products[data-v-e16eb167] {
width: 100%;
}
.catalog-lines[data-v-e16eb167] {
order: -1;
}
.catalog-head h1[data-v-e16eb167] {
font-size: 1.375rem;
}
.hardware-body[data-v-e16eb167] {
gap: 14px;
}
.line-action[data-v-e16eb167] {
flex-wrap: wrap;
}
.line-specs[data-v-e16eb167] {
gap: 8px;
}
.line-specs > div[data-v-e16eb167] {
gap: 6px;
}
.line-specs strong[data-v-e16eb167] {
font-size: 0.875rem;
}
.catalog-state[data-v-e16eb167] {
padding: 42px 14px;
}
}
+30 -4
View File
@@ -1515,22 +1515,25 @@ video {
} }
.config-summary { .config-summary {
flex-direction: column; flex-direction: column;
gap: 10px; gap: 7px;
margin: 0; margin: 0;
display: flex; display: flex;
} }
.config-summary-row { .config-summary-row {
grid-template-columns: 68px minmax(0, 1fr); grid-template-columns: auto minmax(0, 1fr);
align-items: baseline; align-items: baseline;
gap: 12px; gap: 12px;
font-size: 1rem; font-size: 0.9375rem;
line-height: 1.429rem; line-height: 1.429rem;
display: grid; display: grid;
} }
.config-summary-row dt { .config-summary-row dt {
min-width: 0; min-width: 0;
max-width: 7em;
color: var(--p-text-muted-color); color: var(--p-text-muted-color);
overflow-wrap: anywhere; white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
} }
.config-summary-row dd { .config-summary-row dd {
text-align: right; text-align: right;
@@ -1719,9 +1722,32 @@ video {
} }
/* 视口条件 (width>=1024px) 下的响应式覆盖;保留相对基础规则的层叠顺序。 */ /* 视口条件 (width>=1024px) 下的响应式覆盖;保留相对基础规则的层叠顺序。 */
@media (width>=1024px) { @media (width>=1024px) {
/* 汇总栏高度限制在视口内,配置清单内部滚动,价格与购买按钮始终可见。 */
.config-summary-wrap { .config-summary-wrap {
position: sticky; position: sticky;
top: calc(var(--console-top-offset, 56px) + 20px); top: calc(var(--console-top-offset, 56px) + 20px);
max-height: calc(100vh - var(--console-top-offset, 56px) - 40px);
flex-direction: column;
display: flex;
}
.config-summary-wrap > .config-column-title {
flex: none;
}
.config-page .config-summary-wrap > .config-checkout {
flex-direction: column;
min-height: 0;
display: flex;
}
.config-summary-wrap .config-checkout > * {
flex: none;
}
.config-summary-wrap .config-checkout > .config-summary {
flex: 0 1 auto;
min-height: 72px;
margin-right: -6px;
padding-right: 6px;
overflow-y: auto;
scrollbar-width: thin;
} }
} }
/* 视口条件 (width<1200px) 下的响应式覆盖;保留相对基础规则的层叠顺序。 */ /* 视口条件 (width<1200px) 下的响应式覆盖;保留相对基础规则的层叠顺序。 */
+77
View File
@@ -225,6 +225,7 @@
color: var(--p-primary-color); color: var(--p-primary-color);
border-radius: var(--radius-md); border-radius: var(--radius-md);
flex-shrink: 0; flex-shrink: 0;
align-self: center;
place-items: center; place-items: center;
font-size: 1.375rem; font-size: 1.375rem;
display: grid; display: grid;
@@ -446,3 +447,79 @@ button[data-v-a6c63d86]:focus-visible {
flex-wrap: wrap; flex-wrap: wrap;
} }
} }
/* 分类导航:一级为下划线标签页,二级为小号胶囊按钮,徽标弱化为辅助文字;分类区不再单独成卡片。 */
.catalog-categories[data-v-a6c63d86] {
background: none;
border: 0;
border-radius: 0;
padding: 0;
}
.catalog-categories h2[data-v-a6c63d86] {
clip-path: inset(50%);
white-space: nowrap;
width: 1px;
height: 1px;
margin: -1px;
position: absolute;
overflow: hidden;
}
.category-options:not(.secondary-categories)[data-v-a6c63d86] {
border-bottom: 1px solid var(--p-content-border-color);
flex-wrap: nowrap;
gap: 2px;
overflow-x: auto;
scrollbar-width: none;
}
.category-options:not(.secondary-categories) button[data-v-a6c63d86] {
color: var(--p-text-muted-color);
white-space: nowrap;
background: none;
border: 0;
border-bottom: 2px solid transparent;
border-radius: 0;
flex: none;
margin-bottom: -1px;
padding: 6px 12px 8px;
font-size: 0.9375rem;
}
.category-options:not(.secondary-categories) button[data-v-a6c63d86]:hover {
color: var(--p-text-color);
}
.category-options:not(.secondary-categories) button.active[data-v-a6c63d86] {
border-bottom-color: var(--p-primary-color);
color: var(--p-primary-color);
background: none;
font-weight: 500;
}
.category-options.secondary-categories[data-v-a6c63d86] {
border-top: 0;
gap: 8px;
margin-top: 12px;
padding-top: 0;
}
.category-options.secondary-categories button[data-v-a6c63d86] {
min-height: 34px;
padding: 5px 12px;
font-size: 0.875rem;
line-height: 1.4;
}
.category-options.secondary-categories button[data-v-a6c63d86]:hover {
color: var(--p-text-color);
}
.category-options.secondary-categories button.active[data-v-a6c63d86] {
box-shadow: inset 0 0 0 1px var(--p-primary-color);
color: var(--p-highlight-color);
font-weight: 500;
}
.category-options[data-v-a6c63d86] .product-group-badge {
color: inherit;
opacity: 0.6;
background: none;
border: 0;
padding: 0;
font-size: 0.75rem;
font-weight: 400;
}
.category-options[data-v-a6c63d86] .product-group-label {
gap: 2px 6px;
}
+391 -437
View File
@@ -1,453 +1,407 @@
<script> <script setup>
/** /**
* 云商品分组与分页选择器:groups/products/库存文案由父级提供,不直接请求接口。 * 云商品分组与分页选择器:groups/products/库存文案由父级提供,不直接请求接口。
* 分组、商品、重试和分页分别发出意图事件;disabled 将购买提交期间的选择锁定。 * 分组按名称归入地区标签页;分组、商品、重试和分页分别发出意图事件;disabled 将购买提交期间的选择锁定。
*/ */
import "~/assets/styles/ProductGroupLabel.B1kWpFA-.css"; import { computed, ref, useId, watch } from "vue";
import "~/assets/styles/CloudProductPicker.CAm9rmUl.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { useId } from "vue";
import { normalizeClass } from "vue";
import { createBlock } from "vue";
import { withCtx } from "vue";
import { createCommentVNode } from "vue";
import { createElementBlock } from "vue";
import { Fragment } from "vue";
import { createElementVNode } from "vue";
import { createTextVNode } from "vue";
import { unref } from "vue";
import { defineComponent } from "vue";
import { createVNode } from "vue";
import { openBlock } from "vue";
import { NuxtLink } from "#components"; import { NuxtLink } from "#components";
import button from "openvue/button"; import Button from "openvue/button";
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js"; import Message from "openvue/message";
import message from "openvue/message"; import Paginator from "openvue/paginator";
import { n as product_name_n } from "~/lib/product-name.js"; import Skeleton from "openvue/skeleton";
import paginator from "openvue/paginator"; import ProductGroupLabel, {
import skeleton from "openvue/skeleton"; n as parseGroupLabel,
import ProductGroupLabel from "~/components/ProductGroupLabel.vue"; } from "~/components/ProductGroupLabel.vue";
var C = { import { n as displayProductName } from "~/lib/product-name.js";
class: `cloud-product-picker`,
}, const props = defineProps({
w = [`aria-labelledby`], groups: { type: Array, default: () => [] },
T = [`id`],
E = {
class: `zone-options`,
role: `group`,
"aria-label": `产品专区`,
},
D = [`aria-pressed`, `disabled`, `onClick`],
O = {
key: 2,
class: `catalog-empty`,
},
k = [`aria-labelledby`],
A = [`id`],
j = {
key: 2,
class: `catalog-empty`,
},
M = {
key: 3,
class: `product-options`,
role: `group`,
"aria-label": `选择云服务器产品`,
},
N = [`aria-pressed`, `disabled`, `onClick`],
P = {
key: 0,
},
F = {
key: 1,
},
I = Object.assign(
_plugin_vue_export_helper_t(
defineComponent({
__name: `CloudProductPicker`,
props: {
groups: {},
activeGroup: {}, activeGroup: {},
products: {}, products: { type: Array, default: () => [] },
selectedId: {}, selectedId: {},
loading: { loading: Boolean,
type: Boolean, productLoading: Boolean,
},
productLoading: {
type: Boolean,
},
error: {}, error: {},
errorSource: {}, errorSource: {},
total: {}, total: {},
page: {}, page: {},
disabled: { disabled: Boolean,
type: Boolean, soldOut: Function,
}, stockLabel: Function,
soldOut: { });
type: Function, const emit = defineEmits([
}, "choose-group",
stockLabel: { "choose-product",
type: Function, "retry-groups",
}, "retry-products",
}, "paginate",
emits: [ ]);
`choose-group`, const id = useId();
`choose-product`,
`retry-groups`, // 按名称关键字归类;港澳台与海外优先匹配,避免“香港BGP”之类被省份规则误判。
`retry-products`, const REGIONS = [
`paginate`,
],
setup(f, { emit: _ }) {
let I = _,
L = useId();
return (n, _) => {
let R = skeleton,
z = button,
B = message,
V = ProductGroupLabel,
H = NuxtLink,
U = paginator;
return (
openBlock(),
createElementBlock(`div`, C, [
createElementVNode(
`section`,
{
class: `selection-section`,
"aria-labelledby": `${unref(L)}-lines-title`,
},
[ [
createElementVNode( "华东",
`h3`, /上海|江苏|浙江|安徽|福建|江西|山东|华东|宿迁|金华|宁波|温州|绍兴|镇江|盐城|扬州|常州|无锡|徐州|泉州|厦门|福州|芜湖|九江|济南|杭州|南京|苏州/,
{ ],
id: `${unref(L)}-lines-title`, ["华南", /广东|广西|海南|华南|深圳|广州|佛山|东莞|海口|南宁/],
}, ["华中", /湖北|湖南|河南|华中|襄阳|十堰|武汉|郑州|新乡|长沙/],
`选择线路`, ["华北", /北京|天津|河北|山西|内蒙|华北|石家庄|太原/],
8, ["西南", /四川|重庆|云南|贵州|西藏|西南|成都|昆明|德阳|贵阳/],
T, ["西北", /陕西|甘肃|青海|宁夏|新疆|西北|西安|咸阳|兰州/],
), ["东北", /辽宁|吉林|黑龙江|东北|沈阳|鞍山|大连|长春|哈尔滨/],
f.loading ];
? (openBlock(), const ABROAD = [
createBlock(R, { ["港澳台", /香港|澳门|台湾/],
key: 0,
height: `110px`,
}))
: (openBlock(),
createElementBlock(
Fragment,
{
key: 1,
},
[ [
f.error && f.errorSource === `groups` "海外",
? (openBlock(), /美国|日本|韩国|新加坡|越南|马来|泰国|菲律宾|印尼|印度|德国|英国|法国|荷兰|俄罗斯|欧洲|洛杉矶|硅谷|东京|首尔|海外/,
createBlock(
B,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(f.error),
1,
),
createVNode(
z,
{
label: `重新加载线路`,
severity: `secondary`,
text: ``,
disabled: f.disabled,
onClick: (_[0] ||= (e) =>
I(`retry-groups`)),
},
null,
8,
[`disabled`],
),
]),
_: 1,
},
))
: f.groups.length
? (openBlock(),
createElementBlock(
Fragment,
{
key: 1,
},
[
(_[3] ||= createElementVNode(
`div`,
{
class: `selection-label`,
},
`产品专区`,
-1,
)),
createElementVNode(`div`, E, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
f.groups,
(e) => (
openBlock(),
createElementBlock(
`button`,
{
key: e.id,
type: `button`,
class: normalizeClass([
`zone-option`,
{
active:
f.activeGroup === e.id,
},
]),
"aria-pressed":
f.activeGroup === e.id,
disabled: f.disabled,
onClick: (t) =>
I(`choose-group`, e.id),
},
[
createVNode(
V,
{
name: e.name,
},
null,
8,
[`name`],
),
], ],
10, ];
D, const REGION_ORDER = [...REGIONS, ...ABROAD]
) .map(([name]) => name)
), .concat("其他");
),
128, function regionOf(group) {
)), const name = parseGroupLabel(group.name).name;
]), for (const [region, pattern] of [...ABROAD, ...REGIONS])
], if (pattern.test(name)) return region;
64, return "其他";
)) }
: (openBlock(),
createElementBlock(`div`, O, [ const regions = computed(() => {
(_[5] ||= createElementVNode( const buckets = new Map();
`i`, for (const group of props.groups) {
{ const region = regionOf(group);
class: `pi pi-box`, if (!buckets.has(region)) buckets.set(region, []);
"aria-hidden": `true`, buckets.get(region).push(group);
}, }
null, return REGION_ORDER.filter((name) => buckets.has(name)).map((name) => ({
-1, name,
)), groups: buckets.get(name),
(_[6] ||= createElementVNode( }));
`p`, });
null, const activeGroupRegion = computed(() => {
`暂未提供可选的云服务器线路`, const group = props.groups.find((g) => g.id === props.activeGroup);
-1, return group ? regionOf(group) : regions.value[0]?.name;
)), });
createVNode( const chosenRegion = ref(null);
H, watch(
{ () => props.activeGroup,
to: `/products`, () => (chosenRegion.value = null),
},
{
default: withCtx(() => [
...(_[4] ||= [
createTextVNode(`浏览产品`, -1),
]),
]),
_: 1,
},
),
])),
],
64,
)),
],
8,
w,
),
f.groups.length
? (openBlock(),
createElementBlock(
`section`,
{
key: 0,
class: `selection-section`,
"aria-labelledby": `${unref(L)}-products-title`,
},
[
createElementVNode(
`h3`,
{
id: `${unref(L)}-products-title`,
class: `products-title`,
},
`产品`,
8,
A,
),
f.productLoading && !f.products.length
? (openBlock(),
createBlock(R, {
key: 0,
height: `110px`,
}))
: f.error && f.errorSource === `products`
? (openBlock(),
createBlock(
B,
{
key: 1,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(f.error),
1,
),
createVNode(
z,
{
label: `重新加载商品`,
severity: `secondary`,
text: ``,
disabled: f.disabled,
onClick: (_[1] ||= (e) =>
I(`retry-products`)),
},
null,
8,
[`disabled`],
),
]),
_: 1,
},
))
: !f.products.length && !f.error
? (openBlock(),
createElementBlock(`div`, j, [
...(_[7] ||= [
createElementVNode(
`p`,
null,
`该专区暂无可售产品`,
-1,
),
]),
]))
: (openBlock(),
createElementBlock(`div`, M, [
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
f.products,
(t) => (
openBlock(),
createElementBlock(
`button`,
{
key: t.id,
type: `button`,
class: normalizeClass([
`product-option`,
{
active: f.selectedId === t.id,
},
]),
"aria-pressed":
f.selectedId === t.id,
disabled:
f.disabled || f.soldOut(t),
onClick: (e) =>
I(`choose-product`, t.id),
},
[
createElementVNode(
`strong`,
null,
toDisplayString(
unref(product_name_n)(t.name),
),
1,
),
f.soldOut(t)
? (openBlock(),
createElementBlock(
`span`,
P,
toDisplayString(
f.stockLabel(t),
),
1,
))
: (openBlock(),
createElementBlock(
`span`,
F,
toDisplayString(
f.selectedId === t.id
? `当前配置`
: `查看可选配置`,
),
1,
)),
],
10,
N,
)
),
),
128,
)),
])),
f.total > 24
? (openBlock(),
createBlock(
U,
{
key: 4,
first: (f.page - 1) * 24,
rows: 24,
totalRecords: f.total,
disabled: f.disabled,
onPage: (_[2] ||= (e) => I(`paginate`, e)),
},
null,
8,
[`first`, `totalRecords`, `disabled`],
))
: createCommentVNode(``, !0),
],
8,
k,
))
: createCommentVNode(``, !0),
])
); );
}; const currentRegion = computed(
}, () => chosenRegion.value ?? activeGroupRegion.value ?? "all",
);
const visibleGroups = computed(() =>
currentRegion.value === "all"
? props.groups
: (regions.value.find((r) => r.name === currentRegion.value)?.groups ??
props.groups),
);
// 同一分组下的商品名称通常共享“地区 | 运营商(”前缀,只显示差异部分,完整名称放在 title 中。
const productLabels = computed(() => {
const names = props.products.map((p) => displayProductName(p.name));
const plain = { labels: names };
if (names.length < 2) return plain;
let prefix = names[0];
for (const name of names)
while (!name.startsWith(prefix)) prefix = prefix.slice(0, -1);
const boundary = prefix.match(/^.*[((\s||·\-_/]/);
prefix = boundary ? boundary[0] : "";
if (prefix.length < 2 || names.some((n) => n.length <= prefix.length))
return plain;
const opensParen = /[((]$/.test(prefix);
return {
labels: names.map((name) => {
const rest = name.slice(prefix.length).trim();
return opensParen ? rest.replace(/[))]$/, "") : rest;
}), }),
[[`__scopeId`, `data-v-a438862d`]], };
), });
{
__name: `CloudProductPicker`,
},
);
export {};
export default I;
</script> </script>
<template>
<section class="cloud-product-picker" :aria-labelledby="`${id}-title`">
<h2 :id="`${id}-title`" class="section-title">选择产品</h2>
<div class="picker-row">
<span :id="`${id}-lines-title`" class="picker-label">线路</span>
<div class="picker-control">
<Skeleton v-if="loading" height="88px" />
<Message
v-else-if="error && errorSource === 'groups'"
severity="error"
:closable="false"
>
{{ error }}
<Button
label="重新加载线路"
severity="secondary"
text
:disabled="disabled"
@click="emit('retry-groups')"
/>
</Message>
<template v-else-if="groups.length">
<div
v-if="regions.length > 1"
class="choice-tabs"
role="group"
aria-label="地区"
>
<button
v-for="region in regions"
:key="region.name"
type="button"
class="choice-tab"
:class="{ active: currentRegion === region.name }"
:aria-pressed="currentRegion === region.name"
@click="chosenRegion = region.name"
>
{{ region.name }}<small>{{ region.groups.length }}</small>
</button>
<button
type="button"
class="choice-tab"
:class="{ active: currentRegion === 'all' }"
:aria-pressed="currentRegion === 'all'"
@click="chosenRegion = 'all'"
>
全部<small>{{ groups.length }}</small>
</button>
</div>
<div
class="choice-chips"
role="group"
:aria-labelledby="`${id}-lines-title`"
>
<button
v-for="group in visibleGroups"
:key="group.id"
type="button"
class="choice-chip"
:class="{ active: activeGroup === group.id }"
:aria-pressed="activeGroup === group.id"
:disabled="disabled"
@click="emit('choose-group', group.id)"
>
<ProductGroupLabel :name="group.name" />
</button>
</div>
</template>
<div v-else class="catalog-empty">
<i class="pi pi-box" aria-hidden="true" />
<p>暂未提供可选的云服务器线路</p>
<NuxtLink to="/products">浏览产品</NuxtLink>
</div>
</div>
</div>
<div v-if="groups.length" class="picker-row">
<span :id="`${id}-products-title`" class="picker-label">型号</span>
<div class="picker-control">
<Skeleton v-if="productLoading && !products.length" height="34px" />
<Message
v-else-if="error && errorSource === 'products'"
severity="error"
:closable="false"
>
{{ error }}
<Button
label="重新加载商品"
severity="secondary"
text
:disabled="disabled"
@click="emit('retry-products')"
/>
</Message>
<p v-else-if="!products.length && !error" class="picker-empty">
该专区暂无可售产品
</p>
<div
v-else
class="choice-chips"
role="group"
:aria-labelledby="`${id}-products-title`"
>
<button
v-for="(product, index) in products"
:key="product.id"
type="button"
class="choice-chip"
:class="{ active: selectedId === product.id }"
:aria-pressed="selectedId === product.id"
:title="displayProductName(product.name)"
:disabled="disabled || soldOut(product)"
@click="emit('choose-product', product.id)"
>
{{ productLabels.labels[index] }}
<small v-if="soldOut(product)">{{ stockLabel(product) }}</small>
</button>
</div>
<Paginator
v-if="total > 24"
:first="(page - 1) * 24"
:rows="24"
:total-records="total"
:disabled="disabled"
@page="emit('paginate', $event)"
/>
</div>
</div>
</section>
</template>
<style scoped>
.cloud-product-picker {
display: flex;
flex-direction: column;
gap: 16px;
min-width: 0;
}
.cloud-product-picker > .section-title {
margin-bottom: 6px;
}
.picker-row {
display: grid;
grid-template-columns: 88px minmax(0, 1fr);
align-items: start;
gap: 8px 16px;
}
.picker-label {
color: var(--p-text-muted-color);
font-size: 0.9375rem;
line-height: 34px;
}
.picker-control {
display: flex;
flex-direction: column;
gap: 10px;
min-width: 0;
}
.picker-empty {
margin: 0;
color: var(--p-text-muted-color);
font-size: 0.875rem;
line-height: 34px;
}
/* 统一选择控件:与物理服务器筛选、商品目录分类使用同一套标签页与胶囊按钮规格。 */
.choice-tabs {
display: flex;
gap: 2px;
overflow-x: auto;
scrollbar-width: none;
border-bottom: 1px solid var(--p-content-border-color);
}
.choice-tabs::-webkit-scrollbar {
display: none;
}
.choice-tab {
flex: none;
display: inline-flex;
align-items: baseline;
gap: 4px;
margin-bottom: -1px;
padding: 6px 12px 8px;
border: 0;
border-bottom: 2px solid transparent;
background: none;
color: var(--p-text-muted-color);
font: inherit;
font-size: 0.9375rem;
cursor: pointer;
}
.choice-tab small {
font-size: 0.75rem;
opacity: 0.6;
}
.choice-tab:hover {
color: var(--p-text-color);
}
.choice-tab.active {
border-bottom-color: var(--p-primary-color);
color: var(--p-primary-color);
font-weight: 500;
}
.choice-chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.choice-chip {
display: inline-flex;
align-items: center;
gap: 6px;
min-width: 0;
min-height: 34px;
padding: 5px 12px;
border: 1px solid var(--p-content-border-color);
border-radius: var(--radius-md);
background: var(--p-content-background);
color: var(--p-text-color);
font: inherit;
font-size: 0.875rem;
line-height: 1.4;
text-align: left;
overflow-wrap: anywhere;
cursor: pointer;
}
.choice-chip small {
color: var(--p-text-muted-color);
font-size: 0.75rem;
}
.choice-chip:hover:not(:disabled) {
border-color: var(--p-primary-color);
}
.choice-chip.active {
border-color: var(--p-primary-color);
box-shadow: inset 0 0 0 1px var(--p-primary-color);
background: var(--p-highlight-background);
color: var(--p-highlight-color);
font-weight: 500;
}
.choice-chip:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.choice-chip:focus-visible,
.choice-tab:focus-visible {
outline: 2px solid var(--p-primary-color);
outline-offset: 2px;
}
.choice-chip :deep(.product-group-badge) {
padding: 0;
border: 0;
background: none;
color: inherit;
opacity: 0.6;
font-size: 0.75rem;
font-weight: 400;
}
.catalog-empty {
padding: 28px;
border: 1px solid var(--p-content-border-color);
border-radius: var(--radius-md);
background: var(--p-content-background);
color: var(--p-text-muted-color);
text-align: center;
}
.catalog-empty i {
font-size: 1.75rem;
}
.catalog-empty a {
color: var(--p-primary-color);
text-decoration: none;
}
@media (width < 640px) {
.picker-row {
grid-template-columns: minmax(0, 1fr);
gap: 6px;
}
.picker-label {
line-height: 1.5;
}
}
</style>
+12 -2
View File
@@ -596,7 +596,7 @@ var N = {
}, },
he = { he = {
key: 0, key: 0,
class: `panel`, class: `panel storage-panel`,
}, },
ge = { ge = {
key: 0, key: 0,
@@ -648,6 +648,9 @@ var N = {
Oe = { Oe = {
class: `form-row`, class: `form-row`,
}, },
Pe1 = {
class: `form-inline`,
},
ke = Object.assign( ke = Object.assign(
_plugin_vue_export_helper_t( _plugin_vue_export_helper_t(
defineComponent({ defineComponent({
@@ -1018,6 +1021,7 @@ var N = {
`基础配置`, `基础配置`,
-1, -1,
)), )),
createElementVNode(`div`, Pe1, [
createElementVNode(`div`, L, [ createElementVNode(`div`, L, [
(r[18] ||= createElementVNode( (r[18] ||= createElementVNode(
`label`, `label`,
@@ -1029,6 +1033,7 @@ var N = {
b, b,
{ {
label: `区域`, label: `区域`,
compact: ``,
disabled: unref(W), disabled: unref(W),
modelValue: unref(A).line_id, modelValue: unref(A).line_id,
"onUpdate:modelValue": (r[1] ||= (e) => "onUpdate:modelValue": (r[1] ||= (e) =>
@@ -1057,6 +1062,7 @@ var N = {
b, b,
{ {
label: `线路`, label: `线路`,
compact: ``,
disabled: unref(W), disabled: unref(W),
modelValue: unref(A).network_id, modelValue: unref(A).network_id,
"onUpdate:modelValue": (r[2] ||= (e) => "onUpdate:modelValue": (r[2] ||= (e) =>
@@ -1074,6 +1080,8 @@ var N = {
[`disabled`, `modelValue`, `options`], [`disabled`, `modelValue`, `options`],
), ),
]), ]),
]),
createElementVNode(`div`, Pe1, [
createElementVNode(`div`, z, [ createElementVNode(`div`, z, [
(r[20] ||= createElementVNode( (r[20] ||= createElementVNode(
`label`, `label`,
@@ -1093,7 +1101,8 @@ var N = {
value: e, value: e,
})), })),
compact: ``, compact: ``,
"onUpdate:modelValue": unref(S).selectCpu, "onUpdate:modelValue":
unref(S).selectCpu,
}, },
null, null,
8, 8,
@@ -1138,6 +1147,7 @@ var N = {
), ),
]), ]),
]), ]),
]),
unref(X) || unref(H) unref(X) || unref(H)
? (openBlock(), ? (openBlock(),
createElementBlock(`div`, he, [ createElementBlock(`div`, he, [
+12 -7
View File
@@ -15,7 +15,6 @@ import "~/assets/styles/PromoCodeField.qFEkB6Hm.css";
import "~/assets/styles/ProductCustomFields.B2QMnPil.css"; import "~/assets/styles/ProductCustomFields.B2QMnPil.css";
import "~/assets/styles/OsIcon.CAi4Iz2S.css"; import "~/assets/styles/OsIcon.CAi4Iz2S.css";
import "~/assets/styles/CommonConfig.Cfik-oMe.css"; import "~/assets/styles/CommonConfig.Cfik-oMe.css";
import "~/assets/styles/CloudProductPicker.CAm9rmUl.css";
import "~/assets/styles/ProductGroupLabel.B1kWpFA-.css"; import "~/assets/styles/ProductGroupLabel.B1kWpFA-.css";
import "~/assets/styles/PaymentDialog.BNFKQF1U.css"; import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
import "~/assets/styles/cloud-server.CcwlOXJ0.css"; import "~/assets/styles/cloud-server.CcwlOXJ0.css";
@@ -42,6 +41,7 @@ import { unref } from "vue";
import { defineComponent } from "vue"; import { defineComponent } from "vue";
import { createVNode } from "vue"; import { createVNode } from "vue";
import { openBlock } from "vue"; import { openBlock } from "vue";
import { NuxtLink } from "#components";
import { useRoute } from "#app"; import { useRoute } from "#app";
import { useRouter } from "#app"; import { useRouter } from "#app";
import { useHead } from "#app"; import { useHead } from "#app";
@@ -323,12 +323,17 @@ var ge = {
return ( return (
openBlock(), openBlock(),
createElementBlock(`div`, ge, [ createElementBlock(`div`, ge, [
(r[4] ||= createElementVNode( createElementVNode(`header`, { class: `order-head` }, [
`h2`, createElementVNode(`h2`, null, `创建云服务器实例`),
null, createVNode(
`创建云服务器实例`, NuxtLink,
-1, { to: `/products` },
)), {
default: withCtx(() => [createTextVNode(`查看全部产品`)]),
_: 1,
},
),
]),
unref(D) && unref(B) unref(D) && unref(B)
? (openBlock(), ? (openBlock(),
createBlock( createBlock(
@@ -3,7 +3,6 @@
* 物理服务器定制入口:只接受有效商品 ID 且模块为 mf_dcim/remf_dcim,避免把其他类型商品交给物理服务器配置器。 * 物理服务器定制入口:只接受有效商品 ID 且模块为 mf_dcim/remf_dcim,避免把其他类型商品交给物理服务器配置器。
*/ */
import { definePageMeta } from "#imports"; import { definePageMeta } from "#imports";
import "~/assets/styles/dedicated-server.CiJfMYtk.css";
import "~/assets/styles/ConfigDiskCard.Dx9IqqQ_.css"; import "~/assets/styles/ConfigDiskCard.Dx9IqqQ_.css";
import "~/assets/styles/PromoCodeField.qFEkB6Hm.css"; import "~/assets/styles/PromoCodeField.qFEkB6Hm.css";
import "~/assets/styles/ProductCustomFields.B2QMnPil.css"; import "~/assets/styles/ProductCustomFields.B2QMnPil.css";
File diff suppressed because it is too large. Load diff