feat(home): 更新首页UI设计并重构组件结构
- 更新导航栏标题为WEBSOFT,移除自定义导航样式 - 重构Hero区域样式,替换渐变背景和品牌展示组件 - 新增快速导航栏支持产品、能力、流程等功能跳转 - 实现产品矩阵展示企业官网、小程序、电商系统等六大产品 - 添加核心能力介绍包括SaaS多租户、私有化部署、模板插件市场 - 集成联系咨询功能和电话呼叫能力 - 优化分享消息内容显示站点名称和网宿软件信息 - 重构页面布局结构提升移动端用户体验
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
export default definePageConfig({
|
export default definePageConfig({
|
||||||
navigationBarTitleText: 'shopLnk.cn - 数灵云店',
|
navigationBarTitleText: 'WEBSOFT',
|
||||||
navigationBarTextStyle: 'black',
|
navigationBarTextStyle: 'black'
|
||||||
navigationStyle: 'custom'
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,446 +1,486 @@
|
|||||||
page {
|
page {
|
||||||
//background: url('https://oss.wsdns.cn/20250621/33ca4ca532e647bc918a59d01f5d88a9.jpg?x-oss-process=image/resize,m_fixed,w_2000/quality,Q_90') no-repeat top center;
|
background: linear-gradient(to bottom, #f6f7fb, #ffffff);
|
||||||
//background-size: 100%;
|
|
||||||
background: linear-gradient(to bottom, #e9fff2, #ffffff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-page {
|
.home-page {
|
||||||
padding: 24rpx 24rpx calc(32rpx + env(safe-area-inset-bottom));
|
padding: 24rpx 24rpx calc(32rpx + env(safe-area-inset-bottom));
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero {
|
/* Hero */
|
||||||
|
.hero {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 28rpx;
|
border-radius: 28rpx;
|
||||||
background: linear-gradient(180deg, #bfefff 0%, #eafaff 40%, #fff7ec 100%);
|
padding: 26rpx 22rpx 28rpx;
|
||||||
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.06);
|
background: linear-gradient(180deg, #0b1220 0%, #0b0f1a 100%);
|
||||||
|
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.14);
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero__bg {
|
.hero__bgGlow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
left: 50%;
|
||||||
background:
|
top: -160rpx;
|
||||||
radial-gradient(360rpx 240rpx at 18% 16%, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0)),
|
width: 760rpx;
|
||||||
radial-gradient(320rpx 220rpx at 84% 18%, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0)),
|
height: 420rpx;
|
||||||
linear-gradient(180deg, rgba(0, 207, 255, 0.12), rgba(0, 0, 0, 0));
|
transform: translateX(-50%);
|
||||||
|
border-radius: 999rpx;
|
||||||
|
background: rgba(34, 214, 74, 0.22);
|
||||||
|
filter: blur(60rpx);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero__content {
|
.hero__inner {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 18rpx;
|
|
||||||
padding: 26rpx 24rpx 28rpx;
|
|
||||||
min-height: 320rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero__left {
|
.hero__tag {
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-hero__topRow {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 16rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-hero__brand {
|
|
||||||
flex: none;
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8rpx 14rpx;
|
padding: 10rpx 16rpx;
|
||||||
border-radius: 999rpx;
|
border-radius: 999rpx;
|
||||||
background: rgba(255, 214, 84, 0.92);
|
background: rgba(255, 150, 0, 0.16);
|
||||||
color: #2a2a2a;
|
border: 2rpx solid rgba(255, 150, 0, 0.35);
|
||||||
font-weight: 700;
|
}
|
||||||
|
|
||||||
|
.hero__tagText {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(255, 195, 115, 0.95);
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero__brandText {
|
.hero__title {
|
||||||
line-height: 1;
|
margin-top: 18rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero__tag {
|
.hero__titleText {
|
||||||
flex: none;
|
display: block;
|
||||||
display: inline-flex;
|
font-size: 40rpx;
|
||||||
align-items: center;
|
|
||||||
padding: 10rpx 18rpx;
|
|
||||||
border-radius: 18rpx;
|
|
||||||
background: linear-gradient(90deg, #22d64a 0%, #7df4b0 100%);
|
|
||||||
box-shadow: 0 14rpx 24rpx rgba(36, 202, 148, 0.22);
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-hero__tagText {
|
|
||||||
font-size: 56rpx;
|
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
line-height: 1;
|
line-height: 1.18;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero__date {
|
.hero__desc {
|
||||||
flex: 1;
|
margin-top: 14rpx;
|
||||||
min-width: 0;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 10rpx 14rpx;
|
|
||||||
border-radius: 999rpx;
|
|
||||||
background: rgba(255, 255, 255, 0.75);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero__dateText {
|
.hero__descText {
|
||||||
|
display: block;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
font-weight: 700;
|
color: rgba(255, 255, 255, 0.78);
|
||||||
color: #1a1a1a;
|
line-height: 1.6;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-hero__headline {
|
.hero__actions {
|
||||||
margin-top: 22rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-hero__headlineText {
|
|
||||||
display: block;
|
|
||||||
font-size: 42rpx;
|
|
||||||
font-weight: 900;
|
|
||||||
color: #0b0b0b;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
line-height: 1.15;
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-hero__right {
|
|
||||||
width: 200rpx;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-hero__bottle {
|
|
||||||
position: relative;
|
|
||||||
width: 190rpx;
|
|
||||||
height: 250rpx;
|
|
||||||
border-radius: 28rpx;
|
|
||||||
background:
|
|
||||||
radial-gradient(240rpx 360rpx at 60% 30%, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.18)),
|
|
||||||
linear-gradient(180deg, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.1));
|
|
||||||
border: 2rpx solid rgba(255, 255, 255, 0.65);
|
|
||||||
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-hero__bottleCap {
|
|
||||||
position: absolute;
|
|
||||||
top: 14rpx;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
width: 88rpx;
|
|
||||||
height: 26rpx;
|
|
||||||
border-radius: 999rpx;
|
|
||||||
background: linear-gradient(180deg, #d7e6f3, #b0cadd);
|
|
||||||
box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-hero__bottleLabel {
|
|
||||||
position: absolute;
|
|
||||||
left: 18rpx;
|
|
||||||
right: 18rpx;
|
|
||||||
bottom: 30rpx;
|
|
||||||
padding: 12rpx 12rpx;
|
|
||||||
border-radius: 18rpx;
|
|
||||||
background: linear-gradient(90deg, rgba(0, 150, 255, 0.18), rgba(0, 255, 210, 0.18));
|
|
||||||
border: 2rpx solid rgba(255, 255, 255, 0.45);
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-hero__bottleLabelText {
|
|
||||||
font-size: 30rpx;
|
|
||||||
font-weight: 800;
|
|
||||||
color: rgba(0, 80, 140, 0.95);
|
|
||||||
text-align: center;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ticket-card {
|
|
||||||
margin-top: 18rpx;
|
|
||||||
border-radius: 22rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
background: #ffffff;
|
|
||||||
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.06);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ticket-card__head {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 18rpx 20rpx;
|
|
||||||
background: linear-gradient(90deg, #22d64a 0%, #7df4b0 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ticket-card__title {
|
|
||||||
color: #ffffff;
|
|
||||||
font-weight: 800;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ticket-card__count {
|
|
||||||
color: rgba(255, 255, 255, 0.92);
|
|
||||||
font-size: 24rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ticket-card__countNum {
|
|
||||||
color: #ffffff;
|
|
||||||
font-weight: 900;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ticket-card__body {
|
|
||||||
padding: 20rpx 10rpx 22rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shortcut-grid {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 12rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shortcut-grid__item {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shortcut-grid__icon {
|
|
||||||
width: 88rpx;
|
|
||||||
height: 88rpx;
|
|
||||||
border-radius: 18rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: transparent;
|
|
||||||
color: #20c26a;
|
|
||||||
border: 2rpx solid rgba(32, 194, 106, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
.shortcut-grid__text {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-tabs {
|
|
||||||
margin-top: 18rpx;
|
margin-top: 18rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-tabs__inner {
|
.hero__btn {
|
||||||
display: flex;
|
border-radius: 18rpx !important;
|
||||||
gap: 18rpx;
|
|
||||||
padding: 0 4rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-tabs__item {
|
.hero__btn--primary {
|
||||||
display: inline-flex;
|
background: linear-gradient(90deg, #24d34c 0%, #6df09a 100%) !important;
|
||||||
align-items: center;
|
border: none !important;
|
||||||
justify-content: center;
|
|
||||||
padding: 10rpx 18rpx;
|
|
||||||
border-radius: 999rpx;
|
|
||||||
background: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-tabs__item--active {
|
.hero__btnRow {
|
||||||
background: rgba(32, 194, 106, 0.16);
|
margin-top: 12rpx;
|
||||||
}
|
|
||||||
|
|
||||||
.home-tabs__itemText {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #2a2a2a;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-tabs__item--active .home-tabs__itemText {
|
|
||||||
color: #16b65a;
|
|
||||||
font-weight: 800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-grid {
|
|
||||||
margin-top: 18rpx;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 18rpx;
|
gap: 12rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-card {
|
/* Quick nav */
|
||||||
border-radius: 22rpx;
|
.quickNav {
|
||||||
overflow: hidden;
|
margin-top: 14rpx;
|
||||||
background: #ffffff;
|
padding: 12rpx 10rpx;
|
||||||
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.06);
|
border-radius: 20rpx;
|
||||||
}
|
background: rgba(255, 255, 255, 0.92);
|
||||||
|
box-shadow: 0 14rpx 26rpx rgba(0, 0, 0, 0.06);
|
||||||
.goods-card__imgWrap {
|
display: grid;
|
||||||
padding: 18rpx 18rpx 0;
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
}
|
|
||||||
|
|
||||||
.goods-card__img {
|
|
||||||
width: 100%;
|
|
||||||
height: 280rpx;
|
|
||||||
border-radius: 18rpx;
|
|
||||||
background: #f4f4f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-card__body {
|
|
||||||
padding: 18rpx 18rpx 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-card__title {
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 26rpx;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #1c1c1c;
|
|
||||||
min-height: 72rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-card__meta {
|
|
||||||
margin-top: 10rpx;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-end;
|
|
||||||
gap: 10rpx;
|
gap: 10rpx;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-card__sold {
|
.quickNav__item {
|
||||||
font-size: 22rpx;
|
height: 66rpx;
|
||||||
color: #9a9a9a;
|
border-radius: 16rpx;
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-card__price {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: center;
|
||||||
gap: 4rpx;
|
justify-content: center;
|
||||||
color: #27c86b;
|
background: rgba(22, 182, 90, 0.08);
|
||||||
white-space: nowrap;
|
border: 2rpx solid rgba(22, 182, 90, 0.14);
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-card__priceUnit {
|
.quickNav__text {
|
||||||
font-size: 22rpx;
|
font-size: 24rpx;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
|
color: #168c49;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-card__priceValue {
|
/* Sections */
|
||||||
font-size: 36rpx;
|
.section {
|
||||||
|
margin-top: 22rpx;
|
||||||
|
padding: 20rpx 18rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 16rpx 32rpx rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section--alt {
|
||||||
|
background: #f7f9fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section__head {
|
||||||
|
margin-bottom: 14rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section__title {
|
||||||
|
display: block;
|
||||||
|
font-size: 34rpx;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
|
color: #111111;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-card__actions {
|
.section__desc {
|
||||||
|
display: block;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section__foot {
|
||||||
margin-top: 16rpx;
|
margin-top: 16rpx;
|
||||||
display: flex;
|
}
|
||||||
|
|
||||||
|
/* Generic card */
|
||||||
|
.card {
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 18rpx 16rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 14rpx 26rpx rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card__title {
|
||||||
|
display: block;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #111111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card__desc {
|
||||||
|
display: block;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Product grid */
|
||||||
|
.productGrid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 14rpx;
|
gap: 14rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-card__btn {
|
.productCard {
|
||||||
flex: 1;
|
padding: 16rpx 14rpx;
|
||||||
height: 64rpx;
|
}
|
||||||
|
|
||||||
|
.productCard__top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCard__title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #111111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCard__desc {
|
||||||
|
display: block;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCard__actions {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagRow {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
padding: 6rpx 10rpx;
|
||||||
border-radius: 999rpx;
|
border-radius: 999rpx;
|
||||||
|
background: rgba(22, 182, 90, 0.12);
|
||||||
|
border: 2rpx solid rgba(22, 182, 90, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag__text {
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #16b65a;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Badges */
|
||||||
|
.badge {
|
||||||
|
padding: 6rpx 10rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge__text {
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge--green {
|
||||||
|
background: rgba(22, 182, 90, 0.14);
|
||||||
|
border: 2rpx solid rgba(22, 182, 90, 0.24);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge--green .badge__text {
|
||||||
|
color: #16b65a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge--soft {
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge--soft .badge__text {
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Capabilities */
|
||||||
|
.capGrid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 14rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.capCard__top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.capCard__title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #111111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.capCard__desc {
|
||||||
|
display: block;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flow */
|
||||||
|
.flowCard {
|
||||||
|
padding: 6rpx 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowStep {
|
||||||
|
display: flex;
|
||||||
|
gap: 14rpx;
|
||||||
|
padding: 14rpx 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowStep + .flowStep {
|
||||||
|
border-top: 2rpx solid rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowStep__dot {
|
||||||
|
flex: none;
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
background: rgba(22, 182, 90, 0.14);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-card__btn--ghost {
|
.flowStep__dotText {
|
||||||
border: 2rpx solid rgba(32, 194, 106, 0.7);
|
font-size: 22rpx;
|
||||||
background: #ffffff;
|
font-weight: 900;
|
||||||
|
color: #16b65a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-card__btn--primary {
|
.flowStep__body {
|
||||||
background: linear-gradient(90deg, #24d34c 0%, #6df09a 100%);
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-card__btnText {
|
.flowStep__title {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #111111;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowStep__desc {
|
||||||
|
display: block;
|
||||||
|
margin-top: 8rpx;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
font-weight: 700;
|
color: #666666;
|
||||||
color: #18b85a;
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Segmented tabs */
|
||||||
|
.segTabs {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 12rpx;
|
||||||
|
padding: 10rpx;
|
||||||
|
border-radius: 18rpx;
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.segTabs__item {
|
||||||
|
height: 70rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segTabs__item--active {
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 10rpx 22rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.segTabs__text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.twoCard {
|
||||||
|
margin-top: 14rpx;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 14rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* List */
|
||||||
|
.list {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list__item {
|
||||||
|
display: flex;
|
||||||
|
gap: 12rpx;
|
||||||
|
padding: 10rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list__dot {
|
||||||
|
flex: none;
|
||||||
|
width: 10rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
margin-top: 12rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
background: rgba(22, 182, 90, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list__text {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactMeta {
|
||||||
|
margin-top: 14rpx;
|
||||||
|
padding-top: 14rpx;
|
||||||
|
border-top: 2rpx solid rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactMeta__row {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12rpx;
|
||||||
|
padding: 10rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactMeta__label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #888888;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-card__btnText--primary {
|
.contactMeta__value {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #111111;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactActions {
|
||||||
|
margin-top: 14rpx;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bottom CTA */
|
||||||
|
.bottomCta {
|
||||||
|
margin-top: 22rpx;
|
||||||
|
padding: 22rpx 18rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
background: linear-gradient(180deg, #0b1220 0%, #0b0f1a 100%);
|
||||||
|
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.14);
|
||||||
|
}
|
||||||
|
.bottom__view{
|
||||||
|
background: linear-gradient(90deg, #24d34c 0%, #6df09a 100%) !important;
|
||||||
|
//background: linear-gradient(180deg, #2dd022 0%, #9be52b 100%);
|
||||||
|
}
|
||||||
|
.bottomCta__title {
|
||||||
|
display: block;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 900;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buy-btn{
|
.bottomCta__desc {
|
||||||
height: 70px;
|
display: block;
|
||||||
background: linear-gradient(to bottom, #1cd98a, #24ca94);
|
margin-top: 10rpx;
|
||||||
border-radius: 100px;
|
font-size: 26rpx;
|
||||||
color: #ffffff;
|
color: rgba(255, 255, 255, 0.75);
|
||||||
display: flex;
|
line-height: 1.55;
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
.cart-icon{
|
|
||||||
background: linear-gradient(to bottom, #bbe094, #4ee265);
|
|
||||||
border-radius: 100px 0 0 100px;
|
|
||||||
height: 70px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 轮播图容器样式,确保支持两种滑动操作 */
|
.bottomCta__actions {
|
||||||
.banner-swiper-container {
|
margin-top: 16rpx;
|
||||||
touch-action: pan-y !important; /* 允许垂直滑动 */
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
.nut-swiper {
|
gap: 12rpx;
|
||||||
touch-action: pan-y !important; /* 允许垂直滑动 */
|
|
||||||
|
|
||||||
.nut-swiper-item {
|
|
||||||
touch-action: pan-x pan-y !important; /* 允许水平和垂直滑动 */
|
|
||||||
|
|
||||||
image {
|
|
||||||
pointer-events: auto; /* 确保图片点击事件正常 */
|
|
||||||
touch-action: manipulation; /* 优化触摸操作 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 为Swiper容器添加特殊处理 */
|
|
||||||
.nut-swiper--horizontal {
|
|
||||||
touch-action: pan-y !important; /* 允许垂直滑动 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 吸顶状态下的样式 */
|
|
||||||
.nutui-sticky--fixed {
|
|
||||||
.header-bg {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 为Swiper添加更精确的触摸控制 */
|
|
||||||
.nut-swiper {
|
|
||||||
touch-action: pan-y !important;
|
|
||||||
|
|
||||||
.nut-swiper-inner {
|
|
||||||
touch-action: pan-x pan-y !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 自定义Swiper样式 */
|
|
||||||
.custom-swiper {
|
|
||||||
touch-action: pan-y !important;
|
|
||||||
|
|
||||||
.nut-swiper-item {
|
|
||||||
touch-action: pan-x pan-y !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保Swiper内部元素不会阻止页面滚动 */
|
|
||||||
.banner-swiper-container,
|
|
||||||
.custom-swiper,
|
|
||||||
.nut-swiper,
|
|
||||||
.nut-swiper-item {
|
|
||||||
-webkit-overflow-scrolling: touch; /* iOS平台启用硬件加速滚动 */
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,291 +1,469 @@
|
|||||||
import Header from './Header'
|
import Taro, { getCurrentInstance, useShareAppMessage } from '@tarojs/taro'
|
||||||
import Banner from './Banner'
|
import { View, Text } from '@tarojs/components'
|
||||||
import Taro, { useShareAppMessage } from '@tarojs/taro'
|
import { useMemo, useState } from 'react'
|
||||||
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
import { Button } from '@nutui/nutui-react-taro'
|
||||||
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
import { useConfig } from '@/hooks/useConfig'
|
||||||
import { Cart, Coupon, Gift, Ticket } from '@nutui/icons-react-taro'
|
import { copyText } from '@/utils/common'
|
||||||
import { getShopInfo } from '@/api/layout'
|
|
||||||
import { checkAndHandleInviteRelation, hasPendingInvite } from '@/utils/invite'
|
|
||||||
import { pageShopGoods } from '@/api/shop/shopGoods'
|
|
||||||
import type { ShopGoods } from '@/api/shop/shopGoods/model'
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
const [activeTab, setActiveTab] = useState('推荐')
|
const [ecoTab, setEcoTab] = useState<'template' | 'plugin'>('template')
|
||||||
const [goodsList, setGoodsList] = useState<ShopGoods[]>([])
|
const { config } = useConfig()
|
||||||
|
|
||||||
useShareAppMessage(() => {
|
useShareAppMessage(() => {
|
||||||
// 获取当前用户ID,用于生成邀请链接
|
// 获取当前用户ID,用于生成邀请链接
|
||||||
const userId = Taro.getStorageSync('UserId');
|
const userId = Taro.getStorageSync('UserId')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: '🏠 首页 🏠',
|
title: config?.siteName
|
||||||
path: userId ? `/pages/index/index?inviter=${userId}&source=share&t=${Date.now()}` : `/pages/index/index`,
|
? `${config.siteName} - 网宿软件`
|
||||||
|
: '软件开发平台:SaaS + 私有化 + 模板/插件生态',
|
||||||
|
path: userId
|
||||||
|
? `/pages/index/index?inviter=${userId}&source=share&t=${Date.now()}`
|
||||||
|
: `/pages/index/index`,
|
||||||
success: function () {
|
success: function () {
|
||||||
console.log('首页分享成功');
|
console.log('首页分享成功')
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '分享成功',
|
title: '分享成功',
|
||||||
icon: 'success',
|
icon: 'success',
|
||||||
duration: 2000
|
duration: 2000
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
fail: function () {
|
fail: function () {
|
||||||
console.log('首页分享失败');
|
console.log('首页分享失败')
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '分享失败',
|
title: '分享失败',
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
duration: 2000
|
duration: 2000
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// const reloadMore = async () => {
|
|
||||||
// setPage(page + 1)
|
|
||||||
// }
|
|
||||||
|
|
||||||
const showAuthModal = () => {
|
|
||||||
Taro.showModal({
|
|
||||||
title: '授权提示',
|
|
||||||
content: '需要获取您的用户信息',
|
|
||||||
confirmText: '去授权',
|
|
||||||
cancelText: '取消',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
// 用户点击确认,打开授权设置页面
|
|
||||||
openSetting();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const openSetting = () => {
|
|
||||||
// Taro.openSetting:调起客户端小程序设置界面,返回用户设置的操作结果。设置界面只会出现小程序已经向用户请求过的权限。
|
|
||||||
Taro.openSetting({
|
|
||||||
success: (res) => {
|
|
||||||
if (res.authSetting['scope.userInfo']) {
|
|
||||||
// 用户授权成功,可以获取用户信息
|
|
||||||
reload();
|
|
||||||
} else {
|
|
||||||
// 用户拒绝授权,提示授权失败
|
|
||||||
Taro.showToast({
|
|
||||||
title: '授权失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// const onSticky = (item: IArguments) => {
|
|
||||||
// if(item){
|
|
||||||
// setStickyStatus(!stickyStatus)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 处理Tabs粘性状态变化
|
|
||||||
// const handleTabsStickyChange = (isSticky: boolean) => {}
|
|
||||||
|
|
||||||
const reload = () => {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// 获取站点信息
|
|
||||||
getShopInfo().then(() => {
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
pageShopGoods({}).then(res => {
|
|
||||||
setGoodsList(res?.list || [])
|
|
||||||
})
|
|
||||||
|
|
||||||
// 检查是否有待处理的邀请关系 - 异步处理,不阻塞页面加载
|
|
||||||
if (hasPendingInvite()) {
|
|
||||||
console.log('检测到待处理的邀请关系')
|
|
||||||
// 延迟处理,确保用户信息已加载,并设置超时保护
|
|
||||||
setTimeout(async () => {
|
|
||||||
try {
|
|
||||||
// 设置超时保护,避免长时间等待
|
|
||||||
const timeoutPromise = new Promise((_, reject) =>
|
|
||||||
setTimeout(() => reject(new Error('邀请关系处理超时')), 8000)
|
|
||||||
);
|
|
||||||
|
|
||||||
const invitePromise = checkAndHandleInviteRelation();
|
|
||||||
|
|
||||||
const success = await Promise.race([invitePromise, timeoutPromise]);
|
|
||||||
if (success) {
|
|
||||||
console.log('首页邀请关系处理成功')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('首页邀请关系处理失败:', error)
|
|
||||||
// 邀请关系处理失败不应该影响页面正常显示
|
|
||||||
// 可以选择清除邀请参数,避免重复尝试
|
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
||||||
if (errorMessage?.includes('超时')) {
|
|
||||||
console.log('邀请关系处理超时,清除邀请参数')
|
|
||||||
// 可以选择清除邀请参数或稍后重试
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 2000)
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Taro.getSetting:获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
|
const scrollToSection = (id: string) => {
|
||||||
Taro.getSetting({
|
const query = Taro.createSelectorQuery()
|
||||||
success: (res) => {
|
const inst = getCurrentInstance()
|
||||||
if (res.authSetting['scope.userInfo']) {
|
// 兼容 React 运行时:尽量将查询限定在当前页面作用域内
|
||||||
// 用户已经授权过,可以直接获取用户信息
|
if (inst?.page && typeof query.in === 'function') query.in(inst.page)
|
||||||
console.log('用户已经授权过,可以直接获取用户信息')
|
query.selectViewport().scrollOffset()
|
||||||
reload();
|
query.select(`#${id}`).boundingClientRect()
|
||||||
} else {
|
query.exec((res: any[]) => {
|
||||||
// 用户未授权,需要弹出授权窗口
|
const viewport = res?.[0]
|
||||||
console.log('用户未授权,需要弹出授权窗口')
|
const rect = res?.[1]
|
||||||
showAuthModal();
|
const scrollTop = (viewport?.scrollTop || 0) + (rect?.top || 0) - 50
|
||||||
}
|
if (Number.isFinite(scrollTop)) {
|
||||||
|
Taro.pageScrollTo({ scrollTop: Math.max(scrollTop, 0), duration: 260 })
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
// 获取用户信息
|
}
|
||||||
Taro.getUserInfo({
|
|
||||||
success: (res) => {
|
|
||||||
const avatar = res.userInfo.avatarUrl;
|
|
||||||
console.log(avatar, 'avatarUrl')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const tabs = useMemo(() => ['推荐', '桶装水', '优惠组合', '购机套餐', '饮水设备'], [])
|
const toAbsoluteMaybe = (url: string) => {
|
||||||
|
if (/^https?:\/\//i.test(url)) return url
|
||||||
|
const base = (config?.domain || '').trim()
|
||||||
|
if (!base) return url
|
||||||
|
const withProto = /^https?:\/\//i.test(base) ? base : `https://${base}`
|
||||||
|
// 处理 domain 末尾/ 与 url 开头/ 的组合
|
||||||
|
const normalizedBase = withProto.replace(/\/+$/, '')
|
||||||
|
const normalizedPath = url.startsWith('/') ? url : `/${url}`
|
||||||
|
return `${normalizedBase}${normalizedPath}`
|
||||||
|
}
|
||||||
|
|
||||||
const shortcuts = useMemo<
|
const openMaybeLink = (url?: string) => {
|
||||||
Array<{ key: string; title: string; icon: ReactNode; onClick: () => void }>
|
if (!url) return
|
||||||
>(
|
// 小程序内无法直接打开外链,这里统一“复制链接”降低认知成本
|
||||||
|
const abs = toAbsoluteMaybe(url)
|
||||||
|
if (/^https?:\/\//i.test(abs)) {
|
||||||
|
copyText(abs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Taro.showToast({ title: '请在 PC 端访问该入口', icon: 'none', duration: 1600 })
|
||||||
|
copyText(abs)
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyConsultTemplate = () => {
|
||||||
|
const tel = config?.tel ? `\n联系电话:${config.tel}` : ''
|
||||||
|
const text =
|
||||||
|
'【需求咨询】\n' +
|
||||||
|
'1) 想开通哪些产品:企业官网 / 电商 / 小程序 / 其他\n' +
|
||||||
|
'2) 是否需要模板/插件市场:是 / 否\n' +
|
||||||
|
'3) 是否需要“支付即开通”:是 / 否\n' +
|
||||||
|
'4) 交付方式:SaaS / 私有化 / 混合\n' +
|
||||||
|
'5) 合规/部署要求:\n' +
|
||||||
|
'6) 期望上线时间:\n' +
|
||||||
|
tel
|
||||||
|
copyText(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
const callPhone = () => {
|
||||||
|
const tel = (config?.tel || '').trim()
|
||||||
|
if (!tel) {
|
||||||
|
Taro.showToast({ title: '暂无联系电话', icon: 'none', duration: 1600 })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Taro.makePhoneCall({ phoneNumber: tel })
|
||||||
|
}
|
||||||
|
|
||||||
|
const products = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
key: 'ticket',
|
title: '企业官网',
|
||||||
title: '我的水票',
|
recommend: true,
|
||||||
icon: <Ticket size={30} />,
|
desc: '品牌展示与获客转化,支持多模板、多语言、SEO 与可视化配置。',
|
||||||
onClick: () => Taro.navigateTo({ url: '/user/gift/index' }),
|
tags: ['模板', 'SEO', '多语言', '私有化'],
|
||||||
|
adminUrl: 'https://site.websoft.top',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'order',
|
title: '小程序/公众号',
|
||||||
title: '立即订水',
|
recommend: false,
|
||||||
icon: <Cart size={30} />,
|
desc: '多端渠道接入与统一管理,适配常见内容与电商场景。',
|
||||||
onClick: () => Taro.navigateTo({ url: '/shop/goodsDetail/index?id=10072' }),
|
tags: ['多端', '渠道', '可扩展'],
|
||||||
|
adminUrl: 'https://mp.websoft.top',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'invite',
|
title: '电商系统',
|
||||||
title: '邀请有礼',
|
recommend: true,
|
||||||
icon: <Gift size={30} />,
|
desc: '商品/订单/支付/营销基础能力,插件化扩展,支持多端触达。',
|
||||||
onClick: () => Taro.navigateTo({ url: '/dealer/qrcode/index' }),
|
tags: ['支付', '插件', '营销', '多租户'],
|
||||||
|
adminUrl: 'https://shop.websoft.top',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'coupon',
|
title: '管理后台',
|
||||||
title: '领券中心',
|
recommend: false,
|
||||||
icon: <Coupon size={30} />,
|
desc: '多租户管理、角色权限、组织架构与可扩展菜单体系。',
|
||||||
onClick: () => Taro.navigateTo({ url: '/coupon/index' }),
|
tags: ['权限', '多租户', '审计'],
|
||||||
|
adminUrl: 'https://oa.websoft.top',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '开发者中心',
|
||||||
|
recommend: false,
|
||||||
|
desc: '应用开发与交付入口:应用中心、源码仓库、Git 账号绑定、权限申请与教程文档。',
|
||||||
|
tags: ['应用', '源码', 'Git', '教程'],
|
||||||
|
adminUrl: '/developer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '模板/插件市场',
|
||||||
|
recommend: false,
|
||||||
|
desc: '支持模板与插件购买、授权与更新,形成生态与增值体系。',
|
||||||
|
tags: ['市场', '授权', '更新', '变现'],
|
||||||
|
adminUrl: '/market',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
const visibleGoods = useMemo(() => {
|
const capabilities = useMemo(
|
||||||
// 先按效果图展示两列卡片,数据不够时也保持布局稳定
|
() => [
|
||||||
const list = goodsList || []
|
{
|
||||||
if (list.length <= 6) return list
|
title: 'SaaS 多租户平台',
|
||||||
return list.slice(0, 6)
|
badge: '核心',
|
||||||
}, [goodsList])
|
desc: '租户隔离、组织与权限体系、配置中心与审计能力,为多业务线统一底座。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '私有化部署',
|
||||||
|
badge: '可选',
|
||||||
|
desc: '支持本地/专有云部署,提供部署文档、验收清单与升级策略,满足安全合规。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '模板市场',
|
||||||
|
badge: '生态',
|
||||||
|
desc: '行业模板一键套用,默认配置与初始化脚本配套,交付更标准、上线更快。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '插件市场',
|
||||||
|
badge: '扩展',
|
||||||
|
desc: '支付、会员、营销、工单等能力按需加购;支持授权、更新与版本管理。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '自动开通链路',
|
||||||
|
badge: '交付',
|
||||||
|
desc: '选品支付后自动创建租户、初始化模块/菜单/基础数据,并交付访问入口。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '模块化与可扩展',
|
||||||
|
badge: '开发',
|
||||||
|
desc: '支持按模块组合产品能力,插件化扩展点让二开与生态合作更高效。',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Header区域 - 现在由Header组件内部处理吸顶逻辑 */}
|
|
||||||
<Header />
|
|
||||||
|
|
||||||
<View className="home-page">
|
<View className="home-page">
|
||||||
{/* 顶部活动主视觉:使用 Banner 组件 */}
|
{/* 首屏:价值主张 + 关键动作 */}
|
||||||
<Banner />
|
<View className="hero">
|
||||||
|
<View className="hero__bgGlow" />
|
||||||
|
<View className="hero__inner">
|
||||||
|
<View className="hero__tag">
|
||||||
|
<Text className="hero__tagText">v3.0 版本发布</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* 电子水票 */}
|
<View className="hero__title">
|
||||||
<View className="ticket-card">
|
<Text className="hero__titleText">SaaS + 私有化 + 模板/插件生态</Text>
|
||||||
<View className="ticket-card__head">
|
</View>
|
||||||
<Text className="ticket-card__title">电子水票</Text>
|
|
||||||
<Text className="ticket-card__count">
|
|
||||||
您还有 <Text className="ticket-card__countNum">0</Text> 张水票
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className="ticket-card__body">
|
<View className="hero__desc">
|
||||||
<View className="shortcut-grid">
|
<Text className="hero__descText">
|
||||||
{shortcuts.map((item) => (
|
面向企业官网、公众号/小程序、电商系统、企业管理后台等业务场景,提供多租户架构与模块化能力;客户下单后自动创建租户、初始化模块与基础数据,实现“支付即开通”。
|
||||||
<View
|
</Text>
|
||||||
key={item.key}
|
</View>
|
||||||
className="shortcut-grid__item"
|
|
||||||
onClick={item.onClick}
|
<View className="hero__actions">
|
||||||
>
|
<Button
|
||||||
<View className="shortcut-grid__icon">{item.icon}</View>
|
type="primary"
|
||||||
<Text className="shortcut-grid__text">{item.title}</Text>
|
block
|
||||||
</View>
|
className="hero__btn hero__btn--primary"
|
||||||
))}
|
onClick={() => scrollToSection('contact')}
|
||||||
|
>
|
||||||
|
预约演示
|
||||||
|
</Button>
|
||||||
|
<View className="hero__btnRow">
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 分类Tabs */}
|
{/* 快捷导航:减少滚动成本 */}
|
||||||
<ScrollView className="home-tabs" scrollX enableFlex>
|
<View className="quickNav">
|
||||||
<View className="home-tabs__inner">
|
{[
|
||||||
{tabs.map((tab) => {
|
{ key: 'products', title: '产品矩阵' },
|
||||||
const active = tab === activeTab
|
{ key: 'capabilities', title: '核心能力' },
|
||||||
return (
|
{ key: 'flow', title: '开通流程' },
|
||||||
<View
|
{ key: 'ecosystem', title: '生态' },
|
||||||
key={tab}
|
].map((x) => (
|
||||||
className={`home-tabs__item ${active ? 'home-tabs__item--active' : ''}`}
|
<View key={x.key} className="quickNav__item" onClick={() => scrollToSection(x.key)}>
|
||||||
onClick={() => setActiveTab(tab)}
|
<Text className="quickNav__text">{x.title}</Text>
|
||||||
>
|
|
||||||
<Text className="home-tabs__itemText">{tab}</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
{/* 商品列表 */}
|
|
||||||
<View className="goods-grid">
|
|
||||||
{visibleGoods.map((item) => (
|
|
||||||
<View key={item.goodsId} className="goods-card">
|
|
||||||
<View className="goods-card__imgWrap">
|
|
||||||
<Image
|
|
||||||
className="goods-card__img"
|
|
||||||
src={item.image || ''}
|
|
||||||
mode="aspectFill"
|
|
||||||
lazyLoad={false}
|
|
||||||
onClick={() =>
|
|
||||||
Taro.navigateTo({ url: `/shop/goodsDetail/index?id=${item.goodsId}` })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className="goods-card__body">
|
|
||||||
<Text className="goods-card__title">{item.name}</Text>
|
|
||||||
<View className="goods-card__meta">
|
|
||||||
<Text className="goods-card__sold">已购:{item.sales || 0}人</Text>
|
|
||||||
<View className="goods-card__price">
|
|
||||||
<Text className="goods-card__priceUnit">¥</Text>
|
|
||||||
<Text className="goods-card__priceValue">{item.price}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className="goods-card__actions">
|
|
||||||
<View
|
|
||||||
className="goods-card__btn goods-card__btn--primary"
|
|
||||||
onClick={() =>
|
|
||||||
Taro.navigateTo({ url: `/shop/goodsDetail/index?id=${item.goodsId}` })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text className="goods-card__btnText goods-card__btnText--primary">立即购买</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* 产品矩阵 */}
|
||||||
|
<View className="section" id="products">
|
||||||
|
<View className="section__head">
|
||||||
|
<Text className="section__title">产品矩阵</Text>
|
||||||
|
<Text className="section__desc">
|
||||||
|
面向不同业务场景的可售卖产品,支持套餐化售卖、支付即开通、模板/插件加购与私有化交付。
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="productGrid">
|
||||||
|
{products.map((p) => (
|
||||||
|
<View key={p.title} className="card productCard">
|
||||||
|
<View className="productCard__top">
|
||||||
|
<Text className="productCard__title">{p.title}</Text>
|
||||||
|
{p.recommend ? (
|
||||||
|
<View className="badge badge--green">
|
||||||
|
<Text className="badge__text">推荐</Text>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
<Text className="productCard__desc">{p.desc}</Text>
|
||||||
|
<View className="tagRow">
|
||||||
|
{p.tags.map((t) => (
|
||||||
|
<View key={t} className="tag">
|
||||||
|
<Text className="tag__text">{t}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
<View className="productCard__actions">
|
||||||
|
<Button size="small" type="primary" onClick={() => openMaybeLink(p.adminUrl)}>
|
||||||
|
立即开通
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="section__foot">
|
||||||
|
<Button type="default" block onClick={() => scrollToSection('ecosystem')}>
|
||||||
|
了解模板/插件生态
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 核心能力 */}
|
||||||
|
<View className="section section--alt" id="capabilities">
|
||||||
|
<View className="section__head">
|
||||||
|
<Text className="section__title">核心能力</Text>
|
||||||
|
<Text className="section__desc">用一套平台能力,覆盖产品售卖、交付开通、运营升级与生态变现。</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="capGrid">
|
||||||
|
{capabilities.map((c) => (
|
||||||
|
<View key={c.title} className="card capCard">
|
||||||
|
<View className="capCard__top">
|
||||||
|
<Text className="capCard__title">{c.title}</Text>
|
||||||
|
<View className="badge badge--soft">
|
||||||
|
<Text className="badge__text">{c.badge}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<Text className="capCard__desc">{c.desc}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 支付即开通 */}
|
||||||
|
<View className="section" id="flow">
|
||||||
|
<View className="section__head">
|
||||||
|
<Text className="section__title">支付即开通</Text>
|
||||||
|
<Text className="section__desc">
|
||||||
|
客户选择产品并支付后,平台自动完成:创建租户、初始化模块、写入默认配置与基础数据、生成管理员账号并交付访问入口。
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="card flowCard">
|
||||||
|
{[
|
||||||
|
{ title: '选择产品/套餐', desc: '支持产品矩阵、模板/插件加购、增值项' },
|
||||||
|
{ title: '下单支付', desc: '支付成功触发开通任务编排' },
|
||||||
|
{ title: '创建租户', desc: '租户隔离、域名/应用信息绑定、管理员生成' },
|
||||||
|
{ title: '模块初始化', desc: '按所购产品加载模块与菜单权限,写入基础数据/示例数据' },
|
||||||
|
{ title: '交付上线', desc: 'SaaS 直接可用;私有化交付镜像/部署文档/验收清单' },
|
||||||
|
].map((s, idx) => (
|
||||||
|
<View key={s.title} className="flowStep">
|
||||||
|
<View className="flowStep__dot">
|
||||||
|
<Text className="flowStep__dotText">{idx + 1}</Text>
|
||||||
|
</View>
|
||||||
|
<View className="flowStep__body">
|
||||||
|
<Text className="flowStep__title">{s.title}</Text>
|
||||||
|
<Text className="flowStep__desc">{s.desc}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="section__foot">
|
||||||
|
<Button type="primary" block onClick={() => scrollToSection('products')}>
|
||||||
|
选择产品并开始规划
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 模板与插件生态 */}
|
||||||
|
<View className="section section--alt" id="ecosystem">
|
||||||
|
<View className="section__head">
|
||||||
|
<Text className="section__title">模板与插件生态</Text>
|
||||||
|
<Text className="section__desc">通过模板加速交付,通过插件扩展能力;支持购买、授权、更新与版本管理。</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="segTabs">
|
||||||
|
<View
|
||||||
|
className={`segTabs__item ${ecoTab === 'template' ? 'segTabs__item--active' : ''}`}
|
||||||
|
onClick={() => setEcoTab('template')}
|
||||||
|
>
|
||||||
|
<Text className="segTabs__text">模板</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
className={`segTabs__item ${ecoTab === 'plugin' ? 'segTabs__item--active' : ''}`}
|
||||||
|
onClick={() => setEcoTab('plugin')}
|
||||||
|
>
|
||||||
|
<Text className="segTabs__text">插件</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{ecoTab === 'template' ? (
|
||||||
|
<View className="twoCard">
|
||||||
|
<View className="card">
|
||||||
|
<Text className="card__title">行业模板</Text>
|
||||||
|
<Text className="card__desc">按行业/场景提供成套页面与配置,支持一键套用、二次编辑与多版本管理。</Text>
|
||||||
|
</View>
|
||||||
|
<View className="card">
|
||||||
|
<Text className="card__title">交付标准化</Text>
|
||||||
|
<Text className="card__desc">模板与初始化脚本配套,让“开通后的默认站点”可直接验收。</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<View className="twoCard">
|
||||||
|
<View className="card">
|
||||||
|
<Text className="card__title">能力扩展</Text>
|
||||||
|
<Text className="card__desc">支付、会员、营销、工单、数据统计等能力按需加购,随买随用。</Text>
|
||||||
|
</View>
|
||||||
|
<View className="card">
|
||||||
|
<Text className="card__title">升级与授权</Text>
|
||||||
|
<Text className="card__desc">支持版本升级、授权校验、到期续费与灰度发布。</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View className="section__foot">
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
block
|
||||||
|
onClick={() => {
|
||||||
|
Taro.showToast({ title: '该入口可对接到模板/插件市场', icon: 'none', duration: 1600 })
|
||||||
|
openMaybeLink('/market')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
了解模板/插件市场
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 底部 CTA */}
|
||||||
|
<View className="section" id="contact">
|
||||||
|
<View className="section__head">
|
||||||
|
<Text className="section__title">联系我们</Text>
|
||||||
|
<Text className="section__desc">
|
||||||
|
复制咨询模板或直接电话沟通,我们会按你的业务场景给出产品组合、开通链路与部署方案(SaaS/私有化)。
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="card">
|
||||||
|
<Text className="card__title">咨询内容建议</Text>
|
||||||
|
<View className="list">
|
||||||
|
{[
|
||||||
|
'你希望售卖哪些产品(官网/电商/小程序/门户等)?',
|
||||||
|
'是否需要模板/插件市场(购买、授权、更新)?',
|
||||||
|
'是否需要“支付即开通”(自动创建租户/初始化模块与数据)?',
|
||||||
|
'交付方式:SaaS 或私有化部署?是否有合规要求?',
|
||||||
|
].map((t) => (
|
||||||
|
<View key={t} className="list__item">
|
||||||
|
<View className="list__dot" />
|
||||||
|
<Text className="list__text">{t}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="contactMeta">
|
||||||
|
{config?.tel ? (
|
||||||
|
<View className="contactMeta__row">
|
||||||
|
<Text className="contactMeta__label">电话</Text>
|
||||||
|
<Text className="contactMeta__value">{config.tel}</Text>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
{config?.workDay ? (
|
||||||
|
<View className="contactMeta__row">
|
||||||
|
<Text className="contactMeta__label">工作日</Text>
|
||||||
|
<Text className="contactMeta__value">{config.workDay}</Text>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="contactActions">
|
||||||
|
<Button type="default" block onClick={copyConsultTemplate}>
|
||||||
|
复制咨询模板
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" block onClick={callPhone}>
|
||||||
|
电话咨询
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="bottomCta">
|
||||||
|
<View className="bottomCta__text">
|
||||||
|
<Text className="bottomCta__title">想快速搭建并交付一个可运营的产品?</Text>
|
||||||
|
<Text className="bottomCta__desc">预约演示,我们将按你的业务场景给出方案与报价。</Text>
|
||||||
|
</View>
|
||||||
|
<View className="bottomCta__actions">
|
||||||
|
<Button type="default" block className={'bottom__view'} onClick={() => scrollToSection('products')}>
|
||||||
|
看产品矩阵
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" block onClick={() => scrollToSection('contact')}>
|
||||||
|
马上联系
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const IsDealer = () => {
|
|||||||
title={
|
title={
|
||||||
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
<Reward className={'text-orange-100 '} size={16}/>
|
<Reward className={'text-orange-100 '} size={16}/>
|
||||||
<Text style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>{config?.vipText || '门店入驻'}</Text>
|
<Text style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>{config?.vipText || '开发者中心'}</Text>
|
||||||
<Text className={'text-white opacity-80 pl-3'}>{config?.vipComments || ''}</Text>
|
<Text className={'text-white opacity-80 pl-3'}>{config?.vipComments || ''}</Text>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ const UserCell = () => {
|
|||||||
</Grid.Item>
|
</Grid.Item>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hasRole('rider') && (
|
{hasRole('developer') && (
|
||||||
<Grid.Item text="配送中心" onClick={() => navTo('/rider/index', true)}>
|
<Grid.Item text="开发者中心" onClick={() => navTo('/rider/index', true)}>
|
||||||
<View className="text-center">
|
<View className="text-center">
|
||||||
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||||
<Jdl color="#3b82f6" size="20"/>
|
<Jdl color="#3b82f6" size="20"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user