first commit

This commit is contained in:
douboer
2026-03-21 18:57:10 +08:00
commit c49aa1a5e9
570 changed files with 107167 additions and 0 deletions

View 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()
});

View File

@@ -0,0 +1,9 @@
{
"navigationBarTitleText": "关于",
"navigationBarBackgroundColor": "#f4f3ef",
"navigationBarTextStyle": "black",
"disableScroll": true,
"usingComponents": {
"bottom-nav": "/components/bottom-nav/index"
}
}

View 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"
});
});
});

View 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>

View 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);
}