first commit
This commit is contained in:
115
apps/miniprogram/pages/about-app/index.js
Normal file
115
apps/miniprogram/pages/about-app/index.js
Normal file
@@ -0,0 +1,115 @@
|
||||
/* global Page, wx, require */
|
||||
|
||||
const { getSettings } = require("../../utils/storage");
|
||||
const { buildThemeStyle, applyNavigationBarTheme } = require("../../utils/themeStyle");
|
||||
const {
|
||||
getAboutBrand,
|
||||
getAboutDetailContent,
|
||||
getAboutFooterLinks,
|
||||
getAboutUiCopy
|
||||
} = require("../../utils/aboutContent");
|
||||
const { normalizeUiLanguage } = require("../../utils/i18n");
|
||||
const { buildButtonIconThemeMaps } = require("../../utils/themedIcons");
|
||||
const { buildSvgButtonPressData, createSvgButtonPressMethods } = require("../../utils/svgButtonFeedback");
|
||||
|
||||
// “关于”页分享出去后应回到小程序首页,而不是再次落到 about 详情页。
|
||||
const ABOUT_APP_SHARE_HOME_PATH = "/pages/connect/index";
|
||||
|
||||
/**
|
||||
* 将“关于”详情页按 Figma Frame 2223 落地:
|
||||
* 1. 保留关于首页的 5 个入口结构不变;
|
||||
* 2. 当前页只重排品牌区、信息卡、分享按钮和底部跳转;
|
||||
* 3. 中间信息继续复用统一数据源,避免文案在多个页面分叉。
|
||||
*/
|
||||
function buildInfoRows(section) {
|
||||
const bullets = Array.isArray(section && section.bullets) ? section.bullets : [];
|
||||
return bullets.map((line, index) => {
|
||||
const text = String(line || "").trim();
|
||||
const matched = text.match(/^([^::]+)[::]\s*(.+)$/);
|
||||
if (!matched) {
|
||||
return {
|
||||
key: `row-${index}`,
|
||||
label: "",
|
||||
value: text
|
||||
};
|
||||
}
|
||||
return {
|
||||
key: `row-${index}`,
|
||||
label: `${matched[1]}:`,
|
||||
value: matched[2]
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
...buildSvgButtonPressData(),
|
||||
brand: getAboutBrand("zh-Hans"),
|
||||
pageContent: getAboutDetailContent("app", "zh-Hans"),
|
||||
infoRows: [],
|
||||
versionLine: "",
|
||||
themeStyle: "",
|
||||
footerLinks: getAboutFooterLinks("zh-Hans"),
|
||||
uiCopy: getAboutUiCopy("zh-Hans"),
|
||||
icons: {},
|
||||
accentIcons: {}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const brand = getAboutBrand("zh-Hans");
|
||||
const pageContent = getAboutDetailContent("app", "zh-Hans");
|
||||
const primarySection = Array.isArray(pageContent.sections) ? pageContent.sections[0] : null;
|
||||
wx.setNavigationBarTitle({ title: pageContent.title || "关于" });
|
||||
this.setData({
|
||||
brand,
|
||||
pageContent,
|
||||
infoRows: buildInfoRows(primarySection),
|
||||
versionLine: `${brand.version}·wechat·${brand.updatedAtCompact}`,
|
||||
footerLinks: getAboutFooterLinks("zh-Hans"),
|
||||
uiCopy: getAboutUiCopy("zh-Hans")
|
||||
});
|
||||
this.applyThemeStyle();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.applyThemeStyle();
|
||||
},
|
||||
|
||||
applyThemeStyle() {
|
||||
const settings = getSettings();
|
||||
const language = normalizeUiLanguage(settings.uiLanguage);
|
||||
const brand = getAboutBrand(language);
|
||||
const pageContent = getAboutDetailContent("app", language);
|
||||
const primarySection = Array.isArray(pageContent.sections) ? pageContent.sections[0] : null;
|
||||
const { icons, accentIcons } = buildButtonIconThemeMaps(settings);
|
||||
applyNavigationBarTheme(settings);
|
||||
wx.setNavigationBarTitle({ title: pageContent.title || "关于" });
|
||||
this.setData({
|
||||
brand,
|
||||
pageContent,
|
||||
infoRows: buildInfoRows(primarySection),
|
||||
versionLine: `${brand.version}·wechat·${brand.updatedAtCompact}`,
|
||||
footerLinks: getAboutFooterLinks(language),
|
||||
uiCopy: getAboutUiCopy(language),
|
||||
icons,
|
||||
accentIcons,
|
||||
themeStyle: buildThemeStyle(settings)
|
||||
});
|
||||
},
|
||||
|
||||
onOpenLink(event) {
|
||||
const path = String(event.currentTarget.dataset.path || "").trim();
|
||||
if (!path) return;
|
||||
wx.navigateTo({ url: path });
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
const brand = this.data.brand || getAboutBrand("zh-Hans");
|
||||
return {
|
||||
title: `${brand.productName} ${brand.version}`,
|
||||
path: ABOUT_APP_SHARE_HOME_PATH
|
||||
};
|
||||
},
|
||||
|
||||
...createSvgButtonPressMethods()
|
||||
});
|
||||
9
apps/miniprogram/pages/about-app/index.json
Normal file
9
apps/miniprogram/pages/about-app/index.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"navigationBarTitleText": "关于",
|
||||
"navigationBarBackgroundColor": "#f4f3ef",
|
||||
"navigationBarTextStyle": "black",
|
||||
"disableScroll": true,
|
||||
"usingComponents": {
|
||||
"bottom-nav": "/components/bottom-nav/index"
|
||||
}
|
||||
}
|
||||
59
apps/miniprogram/pages/about-app/index.test.ts
Normal file
59
apps/miniprogram/pages/about-app/index.test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
type SharePayload = {
|
||||
path: string;
|
||||
title: string;
|
||||
};
|
||||
|
||||
type AboutAppPageOptions = {
|
||||
onShareAppMessage?: () => SharePayload;
|
||||
};
|
||||
|
||||
type MiniprogramGlobals = typeof globalThis & {
|
||||
Page?: (options: AboutAppPageOptions) => void;
|
||||
wx?: {
|
||||
setNavigationBarTitle?: (options: { title: string }) => void;
|
||||
};
|
||||
};
|
||||
|
||||
describe("about-app page", () => {
|
||||
const globalState = globalThis as MiniprogramGlobals;
|
||||
const originalPage = globalState.Page;
|
||||
const originalWx = globalState.wx;
|
||||
let capturedPageOptions: AboutAppPageOptions | null = null;
|
||||
|
||||
beforeEach(() => {
|
||||
capturedPageOptions = null;
|
||||
vi.resetModules();
|
||||
globalState.Page = vi.fn((options: AboutAppPageOptions) => {
|
||||
capturedPageOptions = options;
|
||||
});
|
||||
globalState.wx = {
|
||||
setNavigationBarTitle: vi.fn()
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (originalPage) {
|
||||
globalState.Page = originalPage;
|
||||
} else {
|
||||
delete globalState.Page;
|
||||
}
|
||||
if (originalWx) {
|
||||
globalState.wx = originalWx;
|
||||
} else {
|
||||
delete globalState.wx;
|
||||
}
|
||||
});
|
||||
|
||||
it("分享后应落到首页而不是 about 详情页", () => {
|
||||
require("./index.js");
|
||||
|
||||
expect(capturedPageOptions).toBeTruthy();
|
||||
expect(capturedPageOptions?.onShareAppMessage).toBeTypeOf("function");
|
||||
expect(capturedPageOptions?.onShareAppMessage?.()).toEqual({
|
||||
title: "RemoteConn v3.0.0",
|
||||
path: "/pages/connect/index"
|
||||
});
|
||||
});
|
||||
});
|
||||
61
apps/miniprogram/pages/about-app/index.wxml
Normal file
61
apps/miniprogram/pages/about-app/index.wxml
Normal file
@@ -0,0 +1,61 @@
|
||||
<view class="about-app-page" style="{{themeStyle}}">
|
||||
<scroll-view class="about-app-scroll" scroll-y="true">
|
||||
<view class="about-app-shell">
|
||||
<view class="about-bg-orb about-bg-orb-left"></view>
|
||||
<view class="about-bg-orb about-bg-orb-right"></view>
|
||||
|
||||
<view class="about-app-brand">
|
||||
<image class="about-app-logo" src="/assets/icons/logo.svg" mode="aspectFit" />
|
||||
<image class="about-app-wordmark" src="/assets/icons/remoteconn.svg" mode="widthFix" />
|
||||
<image class="about-app-submark" src="/assets/icons/ai矩连.svg" mode="widthFix" />
|
||||
<text class="about-app-version">{{versionLine}}</text>
|
||||
</view>
|
||||
|
||||
<view class="about-app-card">
|
||||
<view class="about-app-card-inner">
|
||||
<text class="about-app-card-title">{{pageContent.sections[0].title}}</text>
|
||||
<text wx:if="{{pageContent.lead}}" class="about-app-card-lead">{{pageContent.lead}}</text>
|
||||
|
||||
<view class="about-app-info-list">
|
||||
<view wx:for="{{infoRows}}" wx:key="key" class="about-app-info-row">
|
||||
<text wx:if="{{item.label}}" class="about-app-info-label">{{item.label}}</text>
|
||||
<text class="about-app-info-value">{{item.value}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button
|
||||
class="about-app-share svg-press-btn"
|
||||
hover-class="svg-press-btn-hover"
|
||||
hover-start-time="0"
|
||||
hover-stay-time="80"
|
||||
open-type="share"
|
||||
data-press-key="about-app:share"
|
||||
bindtouchstart="onSvgButtonTouchStart"
|
||||
bindtouchend="onSvgButtonTouchEnd"
|
||||
bindtouchcancel="onSvgButtonTouchEnd"
|
||||
>
|
||||
<image
|
||||
class="about-app-share-icon svg-press-icon"
|
||||
src="{{pressedSvgButtonKey === 'about-app:share' ? (accentIcons.share || icons.share || '/assets/icons/share.svg') : (icons.share || '/assets/icons/share.svg')}}"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="about-app-share-text">{{uiCopy.shareButton}}</text>
|
||||
</button>
|
||||
|
||||
<view class="about-app-footer">
|
||||
<text
|
||||
wx:for="{{footerLinks}}"
|
||||
wx:key="key"
|
||||
class="about-app-footer-link"
|
||||
data-path="{{item.path}}"
|
||||
bindtap="onOpenLink"
|
||||
>
|
||||
{{item.title}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<bottom-nav page="about-app" />
|
||||
</view>
|
||||
185
apps/miniprogram/pages/about-app/index.wxss
Normal file
185
apps/miniprogram/pages/about-app/index.wxss
Normal file
@@ -0,0 +1,185 @@
|
||||
@import "../about/common.wxss";
|
||||
|
||||
/**
|
||||
* 当前页沿用 about 共用色板,只保留“关于”详情页自身的布局差异,
|
||||
* 避免再维护第二套颜色常量。
|
||||
*/
|
||||
|
||||
.about-app-page {
|
||||
height: 100vh;
|
||||
background: var(--bg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.about-app-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.about-app-shell {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
padding: 40rpx 28rpx 136rpx;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.about-app-brand {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 0 0 34rpx 16rpx;
|
||||
}
|
||||
|
||||
.about-app-logo {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.about-app-wordmark {
|
||||
width: 563rpx;
|
||||
display: block;
|
||||
margin-top: -96rpx;
|
||||
margin-left: 112rpx;
|
||||
}
|
||||
|
||||
.about-app-submark {
|
||||
width: 88rpx;
|
||||
display: block;
|
||||
margin-top: 14rpx;
|
||||
margin-left: 112rpx;
|
||||
}
|
||||
|
||||
.about-app-version {
|
||||
display: block;
|
||||
margin-top: 14rpx;
|
||||
margin-left: 112rpx;
|
||||
font-size: 20rpx;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
color: var(--about-text);
|
||||
}
|
||||
|
||||
.about-app-card {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
min-height: 622rpx;
|
||||
border-radius: 28rpx;
|
||||
background: var(--about-surface);
|
||||
border: 1rpx solid var(--about-surface-border);
|
||||
box-shadow: 0 16rpx 38rpx var(--about-glow);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.about-app-card-inner {
|
||||
padding: 30rpx 28rpx 34rpx;
|
||||
}
|
||||
|
||||
.about-app-card-title {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
color: var(--about-text-strong);
|
||||
}
|
||||
|
||||
.about-app-card-lead {
|
||||
display: block;
|
||||
margin-top: 14rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.65;
|
||||
color: var(--about-text);
|
||||
}
|
||||
|
||||
.about-app-info-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
margin-top: 26rpx;
|
||||
}
|
||||
|
||||
.about-app-info-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.about-app-info-label {
|
||||
flex: 0 0 auto;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.6;
|
||||
font-weight: 700;
|
||||
color: var(--about-text-strong);
|
||||
}
|
||||
|
||||
.about-app-info-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.6;
|
||||
color: var(--about-text);
|
||||
}
|
||||
|
||||
.about-app-share {
|
||||
width: auto !important;
|
||||
min-width: 0 !important;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
align-self: flex-start;
|
||||
margin: 30rpx 0 0 16rpx !important;
|
||||
padding: 18rpx 24rpx !important;
|
||||
border: 1rpx solid var(--about-action-border) !important;
|
||||
border-radius: 999rpx !important;
|
||||
background: var(--about-action-bg) !important;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
box-shadow: 0 10rpx 24rpx var(--about-glow);
|
||||
--svg-press-active-radius: 999rpx;
|
||||
--svg-press-active-bg: var(--about-action-bg);
|
||||
--svg-press-active-shadow:
|
||||
0 14rpx 28rpx var(--about-glow),
|
||||
inset 0 0 0 1rpx var(--about-action-border);
|
||||
--svg-press-active-scale: 0.96;
|
||||
--svg-press-icon-opacity: 0.94;
|
||||
--svg-press-icon-active-opacity: 1;
|
||||
--svg-press-icon-active-scale: 1.08;
|
||||
}
|
||||
|
||||
.about-app-share::after {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.about-app-share-icon {
|
||||
width: 31rpx;
|
||||
height: 31rpx;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.about-app-share-text {
|
||||
font-size: 24rpx;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
color: var(--about-action-text);
|
||||
}
|
||||
|
||||
.about-app-footer {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-top: 34rpx;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 24rpx;
|
||||
padding-right: 10rpx;
|
||||
}
|
||||
|
||||
.about-app-footer-link {
|
||||
padding: 10rpx 0;
|
||||
font-size: 20rpx;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
color: var(--about-accent);
|
||||
}
|
||||
Reference in New Issue
Block a user