feat(update): 添加 API Key 管理和 WebSocket 实时通知功能

- 新增 API Key 管理功能,支持外部工具认证调用平台 API
- 实现 WebSocket 实时通知,版本发布时推送轻量通知给客户端
- 添加 APK 文件哈希校验,支持已下载检测和直接安装
- 支持外部 APK 上传使用 API Key 认证
- 优化私有化部署自动注入 nginx WebSocket 代理配置
- 扩展 SDK 功能包括已下载检测、直接安装和实时通知监听
这个提交包含在:
XuqmGroup 2026-06-11 12:25:16 +08:00
父节点 cd39c7fb30
当前提交 cbc29cf255
共有 2 个文件被更改,包括 189 次插入130 次删除

查看文件

@ -3,7 +3,7 @@
<el-page-header @back="$router.back()" :content="`应用配置指引 — ${appKey}`" /> <el-page-header @back="$router.back()" :content="`应用配置指引 — ${appKey}`" />
<el-alert <el-alert
title="这里是应用商店配置的独立指引页。建议先完成凭据配置和市场跳转页,再回到版本管理页提交审核。Harmony 应用仅跳转应用市场,不做本地安装。" title="各应用商店的凭据配置说明。配置完成后回到版本管理页提交审核。"
type="info" type="info"
show-icon show-icon
:closable="false" :closable="false"
@ -13,15 +13,17 @@
<div class="guide-layout"> <div class="guide-layout">
<!-- 左侧厂商列表 --> <!-- 左侧厂商列表 -->
<div class="guide-sidebar"> <div class="guide-sidebar">
<div <div class="guide-sidebar-group" v-for="group in GUIDE_GROUPS" :key="group.label">
v-for="store in STORE_GUIDES" <div class="guide-sidebar-group-label">{{ group.label }}</div>
:key="store.type" <div
class="guide-sidebar-item" v-for="store in group.stores"
:class="{ active: activeStoreType === store.type }" :key="store.type"
@click="activeStoreType = store.type" class="guide-sidebar-item"
> :class="{ active: activeStoreType === store.type }"
<span class="guide-sidebar-label">{{ store.label }}</span> @click="activeStoreType = store.type"
<el-tag size="small" type="success" v-if="store.enabled">已接入</el-tag> >
<span class="guide-sidebar-label">{{ store.label }}</span>
</div>
</div> </div>
</div> </div>
@ -30,17 +32,12 @@
<div class="guide-content-header"> <div class="guide-content-header">
<div class="guide-content-title"> <div class="guide-content-title">
<span>{{ activeStore.label }}</span> <span>{{ activeStore.label }}</span>
<el-tag size="small" type="success" v-if="activeStore.enabled">已接入</el-tag>
</div> </div>
<p class="guide-subtitle">{{ activeStore.subtitle }}</p> <p class="guide-subtitle">{{ activeStore.subtitle }}</p>
<el-link :href="activeStore.url" target="_blank" type="primary">
{{ activeStore.urlLabel }}
</el-link>
</div> </div>
<div class="guide-content-body"> <div class="guide-content-body">
<!-- 左侧步骤 --> <div class="guide-steps-wrap">
<div class="guide-steps">
<el-steps direction="vertical" :active="activeStore.steps.length" finish-status="success"> <el-steps direction="vertical" :active="activeStore.steps.length" finish-status="success">
<el-step <el-step
v-for="step in activeStore.steps" v-for="step in activeStore.steps"
@ -52,11 +49,16 @@
<p class="guide-hint">{{ activeStore.hint }}</p> <p class="guide-hint">{{ activeStore.hint }}</p>
</div> </div>
<!-- 右侧图片 -->
<div class="guide-image-wrap" v-if="activeStore.image"> <div class="guide-image-wrap" v-if="activeStore.image">
<img :src="activeStore.image" :alt="activeStore.label + ' 配置截图'" class="guide-image" /> <img :src="activeStore.image" :alt="activeStore.label + ' 配置截图'" class="guide-image" />
</div> </div>
</div> </div>
<div class="guide-footer">
<el-link :href="activeStore.url" target="_blank" type="primary">
{{ activeStore.urlLabel }}
</el-link>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -83,39 +85,36 @@ interface StoreGuide {
steps: { title: string; description: string }[] steps: { title: string; description: string }[]
hint: string hint: string
image?: string image?: string
enabled: boolean
} }
const STORE_GUIDES: StoreGuide[] = [ const STORE_GUIDES: StoreGuide[] = [
{ {
type: 'HUAWEI', type: 'HUAWEI',
label: '华为应用市场', label: '华为应用市场',
subtitle: 'AppGallery Connect Connect API 凭据', subtitle: 'AppGallery Connect API 凭据',
url: 'https://developer.huawei.com/consumer/cn/doc/AppGallery-connect-Guides/agcapi-getstarted-0000001111845114', url: 'https://developer.huawei.com/consumer/cn/doc/AppGallery-connect-Guides/agcapi-getstarted-0000001111845114',
urlLabel: '查看华为官方文档', urlLabel: '查看华为官方文档',
steps: [ steps: [
{ title: '创建应用', description: '在 AppGallery Connect 中打开目标应用。' }, { title: '进入 AppGallery Connect', description: '在华为开发者平台找到目标应用。' },
{ title: '创建 Connect API 凭据', description: '保存 Client ID 和 Client Secret。' }, { title: '创建 Connect API 凭据', description: '进入开发工具 → Connect API,创建服务端凭据并选择 APP 管理员角色。' },
{ title: '回到版本管理页保存', description: '配置凭据后即可提交审核。' }, { title: '填写 Client ID / Client Secret', description: '复制凭据信息保存到租户平台的凭据配置中。' },
], ],
hint: '适合上传 APK 并提交审核;支持回调跟踪审核状态。', hint: '配置后可自动上传 APK 并提交审核,支持回调跟踪审核状态。',
image: huaweiGuideImage, image: huaweiGuideImage,
enabled: true,
}, },
{ {
type: 'MI', type: 'MI',
label: '小米应用商店', label: '小米应用商店',
subtitle: '使用自动发布接口的账号与私钥', subtitle: '自动发布接口的账号与私钥',
url: 'https://dev.mi.com/distribute/doc/details?pId=1134', url: 'https://dev.mi.com/distribute/doc/details?pId=1134',
urlLabel: '查看小米官方文档', urlLabel: '查看小米官方文档',
steps: [ steps: [
{ title: '进入应用管理', description: '定位到目标应用。' }, { title: '进入应用游戏管理', description: '在控制台选择目标应用。' },
{ title: '准备自动发布接口密钥', description: '记录用户名、公钥证书和 RSA 私钥。' }, { title: '打开自动发布接口', description: '下载公钥文件并准备 RSA 私钥。' },
{ title: '保存到租户平台', description: '完成后可提交审核。' }, { title: '填写用户名 / 公钥 / 私钥', description: '保存的是服务端上传所需凭据。' },
], ],
hint: '字段按 username / publicKey / privateKey 保存。', hint: '字段为 username / publicKey / privateKey,与后端服务一致。',
image: miGuideImage, image: miGuideImage,
enabled: true,
}, },
{ {
type: 'OPPO', type: 'OPPO',
@ -124,13 +123,12 @@ const STORE_GUIDES: StoreGuide[] = [
url: 'https://open.oppomobile.com/new/developmentDoc/info?id=11119', url: 'https://open.oppomobile.com/new/developmentDoc/info?id=11119',
urlLabel: '查看 OPPO 官方文档', urlLabel: '查看 OPPO 官方文档',
steps: [ steps: [
{ title: '进入我的 API', description: '确认当前应用可创建服务端应用。' }, { title: '进入"我的 API"', description: '确认当前应用拥有服务端应用能力。' },
{ title: '创建 client_id / client_secret', description: '完成服务端凭据申请。' }, { title: '新建服务端应用', description: '按平台要求创建接口凭据。' },
{ title: '保存并提交审核', description: '配置完成后返回版本管理页。' }, { title: '填写 Client ID / Client Secret', description: '对应凭据配置中的两个字段。' },
], ],
hint: '与后端 submitToOppo 的字段一致。', hint: '字段与后端 submitToOppo 读取逻辑一致。',
image: oppoGuideImage, image: oppoGuideImage,
enabled: true,
}, },
{ {
type: 'VIVO', type: 'VIVO',
@ -139,13 +137,12 @@ const STORE_GUIDES: StoreGuide[] = [
url: 'https://dev.vivo.com.cn/documentCenter/doc/326', url: 'https://dev.vivo.com.cn/documentCenter/doc/326',
urlLabel: '查看 vivo 官方文档', urlLabel: '查看 vivo 官方文档',
steps: [ steps: [
{ title: '进入 API 管理', description: '找到当前应用对应入口。' }, { title: '进入 API 管理', description: '找到当前应用对应的接口管理入口。' },
{ title: '复制 access key 和 secret', description: '按服务端要求保存。' }, { title: '激活后再读取密钥', description: '首次启用后可能需要刷新页面。' },
{ title: '保存后提交审核', description: '审核状态会通过 Webhook 回传。' }, { title: '填写 Access Key / Access Secret', description: '与后端提交服务字段保持一致。' },
], ],
hint: '注意请求频率限制,状态拉取需做节流。', hint: '注意请求频率限制,状态拉取需做节流。',
image: vivoGuideImage, image: vivoGuideImage,
enabled: true,
}, },
{ {
type: 'HONOR', type: 'HONOR',
@ -154,27 +151,12 @@ const STORE_GUIDES: StoreGuide[] = [
url: 'https://developer.honor.com/cn', url: 'https://developer.honor.com/cn',
urlLabel: '查看荣耀官方文档', urlLabel: '查看荣耀官方文档',
steps: [ steps: [
{ title: '进入管理中心', description: '打开荣耀开发者后台。' }, { title: '进入管理中心', description: '打开荣耀开发者后台并进入凭证页。' },
{ title: '申请服务端凭据', description: '保存 Client ID / Client Secret。' }, { title: '申请凭证', description: '创建用于服务端上传的 API 凭据。' },
{ title: '提交审核', description: '与华为同类流程接入。' }, { title: '填写 Client ID / Client Secret', description: '与华为同类流程接入。' },
], ],
hint: 'Harmony 这条线仅跳转应用市场,不提供本地安装包。', hint: '与后端 Honor 提交流程完全一致。',
image: honorGuideImage, image: honorGuideImage,
enabled: true,
},
{
type: 'HARMONY_APP',
label: '鸿蒙应用',
subtitle: '鸿蒙应用市场独立跳转页',
url: 'https://developer.huawei.com/consumer/cn/',
urlLabel: '查看鸿蒙官方文档',
steps: [
{ title: '确认鸿蒙应用页', description: '准备独立的鸿蒙应用市场详情页。' },
{ title: '复制跳转链接', description: '如需市场跳转,再把详情页链接填写到应用商店配置。' },
{ title: '回到版本管理页', description: '鸿蒙版本仅记录版本号和跳转页,不填也可继续。' },
],
hint: '鸿蒙应用配置只维护市场跳转页,不参与 Android 审核上传。',
enabled: true,
}, },
{ {
type: 'APP_STORE', type: 'APP_STORE',
@ -183,12 +165,24 @@ const STORE_GUIDES: StoreGuide[] = [
url: 'https://developer.apple.com/documentation/appstoreconnectapi', url: 'https://developer.apple.com/documentation/appstoreconnectapi',
urlLabel: '查看 Apple 官方文档', urlLabel: '查看 Apple 官方文档',
steps: [ steps: [
{ title: '创建 API Key', description: '保存 Team ID / Key ID / p8 私钥。' }, { title: '创建 API Key', description: '进入 App Store Connect → 用户和访问 → 整合 → 点击 "+" 创建密钥。' },
{ title: '补充 Bundle ID', description: '回到版本管理页保存包名;链接可选。' }, { title: '下载 .p8 私钥', description: '创建后立即下载私钥文件(只能下载一次),复制完整内容。' },
{ title: '提交审核', description: '支持审核后自动发布,链接不填也能继续走流程。' }, { title: '填写 Issuer ID / Key ID / 私钥', description: 'Issuer ID 在整合页面顶部,Key ID 在密钥列表中。' },
], ],
hint: 'iOS 的 App Store 链接可选填写,需要跳转时再补。', hint: '配置 API Key 后可自动查询审核状态。私钥为 .p8 文件的完整内容,包含 BEGIN/END 标记。',
enabled: true, },
{
type: 'HARMONY_APP',
label: '鸿蒙应用',
subtitle: 'AppGallery Connect API 凭据',
url: 'https://developer.huawei.com/consumer/cn/doc/AppGallery-connect-Guides/agcapi-getstarted-0000001111845114',
urlLabel: '查看鸿蒙官方文档',
steps: [
{ title: '进入 AppGallery Connect', description: '在华为开发者平台找到目标鸿蒙应用。' },
{ title: '创建 Connect API 凭据', description: '进入开发工具 → Connect API,创建服务端凭据。' },
{ title: '填写 Client ID / Client Secret', description: '复制凭据信息保存到凭据配置中。' },
],
hint: '配置 API 凭证后可自动查询审核状态。鸿蒙应用审核流程与华为应用市场共用 AppGallery Connect 平台。',
}, },
{ {
type: 'GOOGLE_PLAY', type: 'GOOGLE_PLAY',
@ -197,20 +191,30 @@ const STORE_GUIDES: StoreGuide[] = [
url: 'https://developer.android.com/google/play/developer-api', url: 'https://developer.android.com/google/play/developer-api',
urlLabel: '查看 Google Play 官方文档', urlLabel: '查看 Google Play 官方文档',
steps: [ steps: [
{ title: '创建服务账号', description: '从 Google Cloud 获取 JSON。' }, { title: '创建服务账号', description: '从 Google Cloud 控制台获取服务账号 JSON。' },
{ title: '绑定 Play 权限', description: '授予对应应用的发布权限。' }, { title: '授予 Play 管理权限', description: '把该服务账号绑定到目标应用的发布权限。' },
{ title: '保存服务账号 JSON', description: '回到版本管理页保存凭据。' }, { title: '粘贴 JSON 内容', description: '将完整的 JSON 内容保存到凭据配置中。' },
], ],
hint: '适合由服务端或脚本自动提交审核。', hint: '适合由服务端或脚本自动提交审核。',
enabled: true,
}, },
] ]
const GUIDE_GROUPS = [
{ label: 'Android', stores: STORE_GUIDES.filter(s => ['HUAWEI', 'MI', 'OPPO', 'VIVO', 'HONOR', 'GOOGLE_PLAY'].includes(s.type)) },
{ label: 'iOS / 鸿蒙', stores: STORE_GUIDES.filter(s => ['APP_STORE', 'HARMONY_APP'].includes(s.type)) },
]
const activeStoreType = ref(STORE_GUIDES[0].type) const activeStoreType = ref(STORE_GUIDES[0].type)
const activeStore = computed(() => STORE_GUIDES.find(s => s.type === activeStoreType.value) ?? STORE_GUIDES[0]) const activeStore = computed(() => STORE_GUIDES.find(s => s.type === activeStoreType.value) ?? STORE_GUIDES[0])
</script> </script>
<style scoped> <style scoped>
.store-guide-page {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.guide-layout { .guide-layout {
display: flex; display: flex;
gap: 16px; gap: 16px;
@ -226,11 +230,21 @@ const activeStore = computed(() => STORE_GUIDES.find(s => s.type === activeStore
background: var(--el-bg-color); background: var(--el-bg-color);
} }
.guide-sidebar-group-label {
font-size: 11px;
font-weight: 600;
color: var(--el-text-color-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 10px 16px 4px;
background: var(--el-fill-color-lighter);
}
.guide-sidebar-item { .guide-sidebar-item {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 12px 16px; padding: 10px 16px;
cursor: pointer; cursor: pointer;
border-bottom: 1px solid var(--el-border-color-lighter); border-bottom: 1px solid var(--el-border-color-lighter);
transition: background 0.2s; transition: background 0.2s;
@ -248,10 +262,11 @@ const activeStore = computed(() => STORE_GUIDES.find(s => s.type === activeStore
background: var(--el-color-primary-light-9); background: var(--el-color-primary-light-9);
color: var(--el-color-primary); color: var(--el-color-primary);
border-left: 3px solid var(--el-color-primary); border-left: 3px solid var(--el-color-primary);
font-weight: 500;
} }
.guide-sidebar-label { .guide-sidebar-label {
font-size: 14px; font-size: 13px;
} }
.guide-content { .guide-content {
@ -270,17 +285,15 @@ const activeStore = computed(() => STORE_GUIDES.find(s => s.type === activeStore
} }
.guide-content-title { .guide-content-title {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600; font-weight: 600;
font-size: 18px; font-size: 18px;
margin-bottom: 8px; margin-bottom: 4px;
} }
.guide-subtitle { .guide-subtitle {
margin: 0 0 8px; margin: 0;
color: var(--el-text-color-secondary); color: var(--el-text-color-secondary);
font-size: 14px;
} }
.guide-content-body { .guide-content-body {
@ -288,7 +301,7 @@ const activeStore = computed(() => STORE_GUIDES.find(s => s.type === activeStore
gap: 24px; gap: 24px;
} }
.guide-steps { .guide-steps-wrap {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
} }
@ -300,40 +313,55 @@ const activeStore = computed(() => STORE_GUIDES.find(s => s.type === activeStore
padding: 12px; padding: 12px;
background: var(--el-fill-color-light); background: var(--el-fill-color-light);
border-radius: 6px; border-radius: 6px;
line-height: 1.6;
} }
.guide-image-wrap { .guide-image-wrap {
width: 420px; width: 400px;
flex-shrink: 0; flex-shrink: 0;
} }
.guide-image { .guide-image {
width: 100%; width: 100%;
border-radius: 12px; border-radius: 8px;
border: 1px solid var(--el-border-color-light); border: 1px solid var(--el-border-color-light);
} }
.guide-footer {
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid var(--el-border-color-lighter);
}
@media (max-width: 992px) { @media (max-width: 992px) {
.guide-layout { .guide-layout {
flex-direction: column; flex-direction: column;
} }
.guide-sidebar { .guide-sidebar {
width: 100%; width: 100%;
display: flex;
flex-direction: column;
}
.guide-sidebar-group {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 4px;
border: none; padding: 8px 12px;
align-items: center;
}
.guide-sidebar-group-label {
padding: 0;
background: transparent; background: transparent;
margin-right: 8px;
} }
.guide-sidebar-item { .guide-sidebar-item {
border: 1px solid var(--el-border-color-light); border: 1px solid var(--el-border-color-light);
border-radius: 6px; border-radius: 6px;
flex: 1; padding: 6px 12px;
min-width: 120px; border-bottom: none;
} }
.guide-sidebar-item.active { .guide-sidebar-item.active {
border-left: none; border-left: 1px solid var(--el-color-primary);
border: 1px solid var(--el-color-primary);
} }
.guide-content-body { .guide-content-body {
flex-direction: column; flex-direction: column;

查看文件

@ -264,38 +264,36 @@
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="应用配置指引" name="guide"> <el-tab-pane label="应用配置指引" name="guide">
<el-alert <div v-for="group in GUIDE_GROUPS" :key="group.label" class="guide-group">
title="这里说明各商店配置项与跳转页的获取方式。App Store 和鸿蒙只需要跳转页;审核通知只配一次,所有市场共享。" <div class="guide-group-title">{{ group.label }}</div>
type="info" <div class="guide-grid">
:closable="false" <el-card v-for="store in group.stores" :key="store.type" class="guide-card" shadow="never">
show-icon <div class="guide-card-title-row">
style="margin-bottom:16px" <div>
/> <div class="guide-card-title">{{ store.label }}</div>
<div class="guide-grid"> <div class="guide-card-subtitle">{{ store.guideSubtitle }}</div>
<el-card v-for="store in STORE_DEFS" :key="store.type" class="guide-card" shadow="never"> </div>
<div class="guide-card-title-row"> <el-tag size="small" :type="isStoreEnabled(store.type) ? 'success' : 'info'">
<div> {{ isStoreEnabled(store.type) ? '已配置' : '未配置' }}
<div class="guide-card-title">{{ store.label }}</div> </el-tag>
<div class="guide-card-subtitle">{{ store.guideSubtitle }}</div>
</div> </div>
<el-tag size="small" type="info">{{ store.shortLabel }}</el-tag> <el-steps direction="vertical" :active="store.guideSteps.length" finish-status="success" style="margin:12px 0 8px">
</div> <el-step v-for="step in store.guideSteps" :key="step.title" :title="step.title" :description="step.description" />
<el-steps direction="vertical" :active="store.guideSteps.length" finish-status="success" style="margin:12px 0 8px"> </el-steps>
<el-step v-for="step in store.guideSteps" :key="step.title" :title="step.title" :description="step.description" /> <el-image
</el-steps> v-if="store.guideImage"
<el-image :src="store.guideImage"
v-if="store.guideImage" fit="cover"
:src="store.guideImage" class="guide-image"
fit="cover" preview-teleported
class="guide-image" />
preview-teleported <div class="guide-hint">{{ store.guideHint }}</div>
/> <el-divider />
<div class="guide-hint">{{ store.guideHint }}</div> <div class="guide-link-title">应用跳转链接</div>
<el-divider /> <div class="guide-link-hint">{{ store.jumpLinkHint }}</div>
<div class="guide-link-title">应用跳转链接的获取方式</div> <el-link :href="store.guideUrl" target="_blank" type="primary">查看官方文档</el-link>
<div class="guide-link-hint">{{ store.jumpLinkHint }}</div> </el-card>
<el-link :href="store.guideUrl" target="_blank" type="primary">查看官方文档</el-link> </div>
</el-card>
</div> </div>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -1249,17 +1247,20 @@ const STORE_DEFS: StoreDef[] = [
label: 'Apple App Store', label: 'Apple App Store',
shortLabel: 'App Store', shortLabel: 'App Store',
fields: [ fields: [
marketUrlField('App Store 详情页链接'), { key: 'issuerId', label: 'Issuer ID', placeholder: 'App Store Connect → 用户和访问 → 整合 → Issuer ID' },
{ key: 'keyId', label: 'Key ID', placeholder: 'API 密钥页面的 Key ID' },
{ key: 'privateKey', label: '私钥 (.p8)', type: 'textarea', placeholder: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----' },
marketUrlField('App Store 详情页链接(可选)'),
], ],
guideSubtitle: 'App Store 只保留应用商店跳转链接', guideSubtitle: 'App Store Connect API Key 凭证',
guideUrl: 'https://developer.apple.com/app-store/', guideUrl: 'https://developer.apple.com/documentation/appstoreconnectapi',
guideSteps: [ guideSteps: [
{ title: '进入 App Store Connect', description: '打开对应应用的详情页或 App Store 公开页面。' }, { title: '创建 API Key', description: '进入 App Store Connect → 用户和访问 → 整合 → 点击 "+" 创建密钥。' },
{ title: '复制 App Store 页面链接', description: '这里只需要保存跳转页,不需要再填密钥。' }, { title: '下载 .p8 私钥', description: '创建后立即下载私钥文件(只能下载一次),复制完整内容。' },
{ title: '保存配置', description: '配置完成后可在版本提醒中直接跳转。' }, { title: '填写 Issuer ID / Key ID / 私钥', description: 'Issuer ID 在整合页面顶部,Key ID 在密钥列表中。' },
], ],
jumpLinkHint: 'App Store 链接可选填写,通常以 apps.apple.com 开头,需要跳转时再补。', jumpLinkHint: 'App Store 链接可选填写,通常以 apps.apple.com 开头。可在 App Store Connect 的应用页面或 App Store 公开页面复制。',
guideHint: '不再配置 Team ID、Key ID 或私钥;发布侧只在需要跳转时使用该链接。', guideHint: '配置 API Key 后可自动查询审核状态。私钥为 .p8 文件的完整内容,包含 BEGIN/END 标记。',
}, },
{ {
type: 'GOOGLE_PLAY', type: 'GOOGLE_PLAY',
@ -1284,17 +1285,19 @@ const STORE_DEFS: StoreDef[] = [
label: '鸿蒙应用', label: '鸿蒙应用',
shortLabel: '鸿蒙', shortLabel: '鸿蒙',
fields: [ fields: [
marketUrlField('鸿蒙应用市场详情页链接'), { key: 'clientId', label: 'Client ID', placeholder: 'AppGallery Connect Client ID' },
{ key: 'clientSecret', label: 'Client Secret', type: 'password', placeholder: 'AppGallery Connect Client Secret' },
marketUrlField('鸿蒙应用市场详情页链接(可选)'),
], ],
guideSubtitle: '鸿蒙应用只保留市场跳转页', guideSubtitle: 'AppGallery Connect API 凭证',
guideUrl: 'https://developer.huawei.com/consumer/cn/', guideUrl: 'https://developer.huawei.com/consumer/cn/doc/AppGallery-connect-Guides/agcapi-getstarted-0000001111845114',
guideSteps: [ guideSteps: [
{ title: '打开鸿蒙应用市场', description: '确认目标应用已在鸿蒙市场上架或待上架。' }, { title: '进入 AppGallery Connect', description: '在华为开发者平台找到目标鸿蒙应用。' },
{ title: '复制详情页链接', description: '填写市场跳转页面,便于版本提醒直接跳转。' }, { title: '创建 Connect API 凭据', description: '进入开发工具 → Connect API,创建服务端凭据。' },
{ title: '保存配置', description: '配置完成后可在版本提醒里引用。' }, { title: '填写 Client ID / Client Secret', description: '复制凭据信息保存到此处。' },
], ],
jumpLinkHint: '鸿蒙应用市场详情页链接可选填写,通常使用 appgallery.huawei.com/app/detail?id=包名。', jumpLinkHint: '鸿蒙应用市场详情页链接可选填写,通常使用 appgallery.huawei.com/app/detail?id=包名。可在 AppGallery Connect 的应用页面复制。',
guideHint: '这里只保存鸿蒙应用市场的独立跳转页,不参与 Android 审核提交。', guideHint: '配置 API 凭证后可自动查询审核状态。鸿蒙应用审核流程与华为应用市场共用 AppGallery Connect 平台。',
}, },
{ {
type: 'REVIEW_WEBHOOK', type: 'REVIEW_WEBHOOK',
@ -1323,6 +1326,21 @@ const STORE_DEFS: StoreDef[] = [
}, },
] ]
const GUIDE_GROUPS = computed(() => [
{
label: 'Android 应用商店',
stores: STORE_DEFS.filter(s => ['HUAWEI', 'MI', 'OPPO', 'VIVO', 'HONOR', 'GOOGLE_PLAY'].includes(s.type)),
},
{
label: 'iOS / 鸿蒙',
stores: STORE_DEFS.filter(s => ['APP_STORE', 'HARMONY_APP'].includes(s.type)),
},
{
label: '通知配置',
stores: STORE_DEFS.filter(s => s.type === 'REVIEW_WEBHOOK'),
},
])
function getStoreConfig(type: StoreType): StoreConfig | undefined { function getStoreConfig(type: StoreType): StoreConfig | undefined {
return storeConfigs.value.find(c => c.storeType === type) return storeConfigs.value.find(c => c.storeType === type)
} }
@ -2649,6 +2667,19 @@ onBeforeUnmount(() => {
max-width: 920px; max-width: 920px;
} }
.guide-group {
margin-bottom: 24px;
}
.guide-group-title {
font-size: 15px;
font-weight: 600;
color: var(--el-text-color-primary);
margin-bottom: 12px;
padding-left: 10px;
border-left: 3px solid var(--el-color-primary);
}
.guide-grid { .guide-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));