HarmonyOS APP<<古今职鉴定>>开源教程第26篇:【完整案例】职业性格测试开发
本篇开发职业性格测试模块,包括古代官场测试和现代职业测试

图:【完整案例】职业性格测试开发 的关键流程与实现要点。
学习目标
- ✅ 设计测试题目与选项
- ✅ 实现答题流程与进度
- ✅ 开发结果计算算法
- ✅ 设计结果展示页面
预计学习时间
约 120 分钟
实战一:设计测试数据结构
第一步:定义题目接口
在 entry/src/main/ets/common/Types.ets 中添加测试相关类型:
// 测试题目接口
interface TestQuestion {
id: number;
question: string;
options: TestOption[];
}
// 测试选项接口
interface TestOption {
value: string; // 选项值,用于计算结果
text: string; // 选项主文本
subtext: string; // 选项副文本(解释说明)
}
// 测试记录接口
interface TestRecord {
testType: 'ancient' | 'modern';
resultType: string;
matchScore: number;
timestamp: number;
}
原理解释:
value用于结果计算,如 MBTI 测试中的 E/I、S/N、T/F、J/Psubtext提供选项的补充说明,帮助用户理解选项含义
第二步:设计古代官场测试题目
创建 10 道情景选择题,每题 4 个选项:
const ANCIENT_QUESTIONS: TestQuestion[] = [
{
id: 1,
question: '假如初入官场,上司命你处理一件涉及权贵的棘手税务案件,你会如何应对?',
options: [
{ value: 'A', text: '秉公执法,严查到底', subtext: '不畏强权,只求无愧于心' },
{ value: 'B', text: '圆滑处理,暗中请示', subtext: '两不得罪,以求自保' },
{ value: 'C', text: '请教官场前辈,依照旧例', subtext: '萧规曹随,稳妥为上' },
{ value: 'D', text: '称病推脱,避开是非', subtext: '明哲保身,不接烫手山芋' }
]
},
{
id: 2,
question: '朝廷派你去边疆赈灾,当地官员贪腐严重,你会如何处理?',
options: [
{ value: 'A', text: '直接弹劾,上报朝廷', subtext: '铁面无私,为民请命' },
{ value: 'B', text: '先稳住局面,暗中收集证据', subtext: '谋定后动,步步为营' },
{ value: 'C', text: '与当地官员协商,各退一步', subtext: '以和为贵,共谋发展' },
{ value: 'D', text: '专注赈灾,不管闲事', subtext: '各司其职,完成使命' }
]
},
// ... 更多题目
];
第三步:设计现代职业测试题目(MBTI 风格)
const MODERN_QUESTIONS: TestQuestion[] = [
{
id: 1,
question: '在团队会议中,你通常会?',
options: [
{ value: 'E', text: '积极发言,分享想法', subtext: '喜欢在讨论中表达观点' },
{ value: 'I', text: '倾听为主,深思熟虑后发言', subtext: '更愿意先思考再表达' }
]
},
{
id: 2,
question: '面对一个新项目,你更倾向于?',
options: [
{ value: 'S', text: '关注具体细节和实际操作', subtext: '脚踏实地,注重可行性' },
{ value: 'N', text: '思考整体愿景和创新可能', subtext: '着眼未来,追求突破' }
]
},
{
id: 3,
question: '做决策时,你更看重?',
options: [
{ value: 'T', text: '逻辑分析和客观数据', subtext: '理性判断,追求效率' },
{ value: 'F', text: '团队感受和人际和谐', subtext: '重视关系,关注影响' }
]
},
{
id: 4,
question: '对于工作计划,你更喜欢?',
options: [
{ value: 'J', text: '提前规划,按部就班', subtext: '有条不紊,掌控全局' },
{ value: 'P', text: '灵活应变,随机调整', subtext: '保持弹性,适应变化' }
]
}
// ... 共12道题,每个维度3道
];
预期效果:题目设计完成,古代测试 10 题,现代测试 12 题。
预期效果:题目设计完成,古代测试 10 题,现代测试 12 题。
案例效果:测试主页展示如下:
┌──────────────────────────────────────┐
│ ← 性格测试 │
├──────────────────────────────────────┤
│ │
│ 探索你的职业性格 │
│ │
│ ┌══════════════════════════════┐ │
│ ║ 🏛️ 古代官场测试 ║ │
│ ║ ║ │
│ ║ 10道情景题,测测你在古代 ║ │
│ ║ 官场中属于哪种类型 ║ │
│ ║ ║ │
│ ║ 上次结果: 谋臣智士 90分 ║ │
│ └══════════════════════════════┘ │
│ │
│ ┌══════════════════════════════┐ │
│ ║ 💼 现代职业测试 (MBTI) ║ │
│ ║ ║ │
│ ║ 12道选择题,发现你的 ║ │
│ ║ 职业性格类型 ║ │
│ ║ ║ │
│ ║ 上次结果: INTJ 策略家 88分 ║ │
│ └══════════════════════════════┘ │
│ │
│ ── 测试历史 ── │
│ 谋臣智士 90分 2024-01-15 │
│ INTJ 88分 2024-01-14 │
└──────────────────────────────────────┘
效果说明: - 两张测试入口卡片,分别对应古代和现代测试 - 卡片底部显示上次测试结果(如有) - 下方展示测试历史记录列表
实战二:实现答题页面
第一步:创建答题页面基础结构
@Component
struct AncientTestPage {
@Consume('mainNavPathStack') mainNavPathStack: NavPathStack;
@StorageLink('isDarkMode') isDarkMode: boolean = true;
@State currentQuestion: number = 0; // 当前题目索引
@State selectedAnswer: string = ''; // 当前选中答案
@State answers: string[] = []; // 所有答案记录
private questions: TestQuestion[] = ANCIENT_QUESTIONS;
build() {
NavDestination() {
Stack() {
// 背景
Column()
.width('100%')
.height('100%')
.backgroundColor(this.isDarkMode ? '#221210' : '#f8f6f5')
Column() {
// 顶部栏
this.HeaderBar()
// 进度条
this.ProgressSection()
// 题目和选项
Scroll() {
Column() {
this.QuestionSection()
this.OptionsSection()
}
.padding({ left: 20, right: 20, bottom: 100 })
}
.layoutWeight(1)
.scrollBar(BarState.Off)
}
.width('100%')
.height('100%')
// 底部按钮
this.BottomButton()
}
}
.hideTitleBar(true)
}
}
第二步:实现进度条组件
@Builder
ProgressSection() {
Column({ space: 8 }) {
Row() {
Text(`QUESTION ${(this.currentQuestion + 1).toString().padStart(2, '0')}`)
.fontSize(12)
.fontWeight(FontWeight.Bold)
.fontColor('#c41e3a')
.letterSpacing(2)
Blank()
Text(`${this.currentQuestion + 1} / ${this.questions.length}`)
.fontSize(12)
.fontColor(this.isDarkMode ? 'rgba(255,255,255,0.4)' : '#9ca3af')
}
.width('100%')
// 进度条
Stack({ alignContent: Alignment.Start }) {
// 背景
Column()
.width('100%')
.height(6)
.borderRadius(3)
.backgroundColor(this.isDarkMode ? '#2d1f1d' : '#e5e7eb')
// 进度
Column()
.width(`${((this.currentQuestion + 1) / this.questions.length) * 100}%`)
.height(6)
.borderRadius(3)
.backgroundColor('#c41e3a')
.shadow({ radius: 8, color: 'rgba(196, 30, 58, 0.5)', offsetY: 0 })
}
.width('100%')
}
.width('100%')
.padding({ left: 20, right: 20, top: 16, bottom: 8 })
}
原理解释:
- 使用
Stack叠加背景和进度条 - 进度宽度通过百分比动态计算
shadow添加发光效果增强视觉
第三步:实现选项组件
@Builder
OptionItem(option: TestOption) {
Row({ space: 16 }) {
// 单选指示器
Column() {
if (this.selectedAnswer === option.value) {
Column()
.width(12)
.height(12)
.borderRadius(6)
.backgroundColor('#c41e3a')
}
}
.width(24)
.height(24)
.borderRadius(12)
.border({
width: 2,
color: this.selectedAnswer === option.value ? '#c41e3a' :
(this.isDarkMode ? 'rgba(255,255,255,0.3)' : '#d1d5db')
})
.justifyContent(FlexAlign.Center)
// 选项内容
Column({ space: 4 }) {
Text(option.text)
.fontSize(15)
.fontWeight(FontWeight.Medium)
.fontColor(this.isDarkMode ? 'rgba(255,255,255,0.9)' : '#1e293b')
Text(option.subtext)
.fontSize(12)
.fontColor(this.isDarkMode ? 'rgba(255,255,255,0.4)' : '#9ca3af')
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
// 选中图标
if (this.selectedAnswer === option.value) {
Image($r('app.media.ic_check'))
.width(20)
.height(20)
.fillColor('#c41e3a')
}
}
.width('100%')
.padding(16)
.borderRadius(12)
.backgroundColor(this.selectedAnswer === option.value ?
'rgba(196, 30, 58, 0.1)' :
(this.isDarkMode ? 'rgba(52, 28, 24, 0.5)' : Color.White))
.border({
width: 1,
color: this.selectedAnswer === option.value ? '#c41e3a' :
(this.isDarkMode ? 'rgba(255,255,255,0.1)' : '#e5e7eb')
})
.onClick(() => {
this.selectedAnswer = option.value;
})
}
第四步:实现答题流程控制
// 下一题或提交
goToNext() {
// 保存当前答案
this.answers[this.currentQuestion] = this.selectedAnswer;
if (this.currentQuestion < this.questions.length - 1) {
// 下一题
this.currentQuestion++;
// 恢复之前的答案(如果有)
this.selectedAnswer = this.answers[this.currentQuestion] || '';
} else {
// 完成测试,跳转到结果页
this.mainNavPathStack.pushPathByName('AncientTestResultPage', {
answers: this.answers
});
}
}
// 底部按钮
@Builder
BottomButton() {
Column() {
Button() {
Row({ space: 8 }) {
Text(this.currentQuestion < this.questions.length - 1 ? '下一题' : '查看结果')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
Image($r('app.media.ic_arrow_right'))
.width(20)
.height(20)
.fillColor(Color.White)
}
}
.width('100%')
.height(52)
.backgroundColor(this.selectedAnswer ? '#c41e3a' : 'rgba(196, 30, 58, 0.5)')
.borderRadius(12)
.enabled(this.selectedAnswer !== '')
.onClick(() => this.goToNext())
}
.width('100%')
.padding({ left: 16, right: 16, bottom: 32 })
.position({ x: 0, y: '100%' })
.translate({ y: -100 })
}
预期效果:点击选项选中,点击按钮进入下一题,最后一题显示"查看结果"。
预期效果:点击选项选中,点击按钮进入下一题,最后一题显示"查看结果"。
案例效果:答题页面展示如下:
┌──────────────────────────────────────┐
│ ← 古代官场测试 │
├──────────────────────────────────────┤
│ QUESTION 03 3 / 10 │
│ ▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░ │ ← 红色进度条 + 发光效果
├──────────────────────────────────────┤
│ │
│ 朝廷派你去边疆赈灾,当地官员 │
│ 贪腐严重,你会如何处理? │
│ │
│ ┌──────────────────────────────┐ │
│ │ ○ 直接弹劾,上报朝廷 │ │
│ │ 铁面无私,为民请命 │ │ ← 灰色副文本
│ └──────────────────────────────┘ │
│ ┌══════════════════════════════┐ │
│ ║ ◉ 先稳住局面,暗中收集 ✓ ║ │ ← 选中: 红色边框
│ ║ 谋定后动,步步为营 ║ │ 红色圆点 + ✓
│ └══════════════════════════════┘ │
│ ┌──────────────────────────────┐ │
│ │ ○ 与当地官员协商,各退一步 │ │
│ │ 以和为贵,共谋发展 │ │
│ └──────────────────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ ○ 专注赈灾,不管闲事 │ │
│ │ 各司其职,完成使命 │ │
│ └──────────────────────────────┘ │
│ │
│ ┌══════════════════════════════┐ │
│ ║ 下一题 → ║ │ ← 最后一题显示「查看结果」
│ └══════════════════════════════┘ │
└──────────────────────────────────────┘
效果说明: - 顶部
QUESTION 03红色大写字母标识,右侧显示进度分数 - 进度条带阴影发光效果(shadow) - 选项卡片:未选为白底灰边框,选中为浅红底红边框 - 选中的单选圆点变红色实心,右侧出现红色 ✓ 图标 - 副文本(subtext)用小号灰色字体提供选项解释 - 未选择答案时底部按钮置灰不可点击
实战三:实现结果计算
第一步:古代官场结果计算
根据答案统计各类型得分:
// 结果类型定义
interface AncientResult {
type: string;
title: string;
description: string;
traits: string[];
historicalFigure: string;
matchScore: number;
}
// 计算古代官场测试结果
function calculateAncientResult(answers: string[]): AncientResult {
// 统计各选项数量
let countA = 0, countB = 0, countC = 0, countD = 0;
answers.forEach(answer => {
switch (answer) {
case 'A': countA++; break;
case 'B': countB++; break;
case 'C': countC++; break;
case 'D': countD++; break;
}
});
// 根据最多的选项确定类型
const maxCount = Math.max(countA, countB, countC, countD);
if (countA === maxCount) {
return {
type: 'upright',
title: '清官廉吏',
description: '你刚正不阿,秉公执法,是百姓心中的青天大老爷。',
traits: ['正直', '勇敢', '为民请命'],
historicalFigure: '包拯',
matchScore: 85 + Math.floor(Math.random() * 10)
};
} else if (countB === maxCount) {
return {
type: 'strategist',
title: '谋臣智士',
description: '你深谋远虑,善于权衡,是帝王身边的智囊。',
traits: ['智慧', '谨慎', '善于谋划'],
historicalFigure: '诸葛亮',
matchScore: 80 + Math.floor(Math.random() * 15)
};
} else if (countC === maxCount) {
return {
type: 'diplomat',
title: '圆滑老臣',
description: '你八面玲珑,左右逢源,是官场中的不倒翁。',
traits: ['圆滑', '人脉广', '善于协调'],
historicalFigure: '和珅',
matchScore: 75 + Math.floor(Math.random() * 15)
};
} else {
return {
type: 'hermit',
title: '隐逸之士',
description: '你淡泊名利,明哲保身,向往归隐田园。',
traits: ['淡泊', '低调', '知足常乐'],
historicalFigure: '陶渊明',
matchScore: 70 + Math.floor(Math.random() * 20)
};
}
}
第二步:现代职业结果计算(MBTI 风格)
// MBTI 类型结果
interface MBTIResult {
type: string; // 如 'INTJ'
title: string; // 如 '策略家'
careers: string[]; // 推荐职业
strengths: string[]; // 优势
matchScore: number;
}
// 计算 MBTI 类型
function calculateMBTIResult(answers: string[]): MBTIResult {
// 统计各维度
let E = 0, I = 0, S = 0, N = 0, T = 0, F = 0, J = 0, P = 0;
answers.forEach(answer => {
switch (answer) {
case 'E': E++; break;
case 'I': I++; break;
case 'S': S++; break;
case 'N': N++; break;
case 'T': T++; break;
case 'F': F++; break;
case 'J': J++; break;
case 'P': P++; break;
}
});
// 组合 MBTI 类型
const type = (E >= I ? 'E' : 'I') +
(S >= N ? 'S' : 'N') +
(T >= F ? 'T' : 'F') +
(J >= P ? 'J' : 'P');
// 根据类型返回结果
return getMBTIDescription(type);
}
// MBTI 类型描述映射
function getMBTIDescription(type: string): MBTIResult {
const descriptions: Record<string, MBTIResult> = {
'INTJ': {
type: 'INTJ',
title: '策略家',
careers: ['架构师', '数据分析师', '战略顾问'],
strengths: ['战略思维', '独立自主', '追求卓越'],
matchScore: 88
},
'ENFP': {
type: 'ENFP',
title: '探险家',
careers: ['产品经理', '市场营销', '创意总监'],
strengths: ['创造力', '热情洋溢', '善于沟通'],
matchScore: 85
}
// ... 其他 16 种类型
};
return descriptions[type] || descriptions['INTJ'];
}
实战四:实现结果展示页
第一步:创建结果页面
@Component
struct AncientTestResultPage {
@Consume('mainNavPathStack') mainNavPathStack: NavPathStack;
@StorageLink('isDarkMode') isDarkMode: boolean = true;
@State result: AncientResult | null = null;
@State showAnimation: boolean = false;
aboutToAppear() {
// 获取答案并计算结果
const params = this.mainNavPathStack.getParamByName('AncientTestResultPage');
if (params && params.length > 0) {
const answers = (params[0] as { answers: string[] }).answers;
this.result = calculateAncientResult(answers);
// 保存测试记录
this.saveTestRecord();
// 延迟显示动画
setTimeout(() => {
this.showAnimation = true;
}, 100);
}
}
saveTestRecord() {
if (this.result) {
const record: TestRecord = {
testType: 'ancient',
resultType: this.result.title,
matchScore: this.result.matchScore,
timestamp: Date.now()
};
saveTestRecord(record);
}
}
build() {
NavDestination() {
Stack() {
// 背景渐变
Column()
.width('100%')
.height('100%')
.linearGradient({
direction: GradientDirection.Bottom,
colors: [['#1a0a0a', 0], ['#3d1515', 0.5], ['#8b0000', 1]]
})
if (this.result) {
Scroll() {
Column({ space: 24 }) {
// 结果卡片
this.ResultCard()
// 性格特点
this.TraitsSection()
// 历史名人对照
this.HistoricalFigureSection()
// 操作按钮
this.ActionButtons()
}
.padding(20)
}
.layoutWeight(1)
}
}
}
.hideTitleBar(true)
}
}
第二步:实现结果卡片动画
@Builder
ResultCard() {
Column({ space: 16 }) {
// 匹配度圆环
Stack() {
// 背景圆
Circle()
.width(120)
.height(120)
.fill(Color.Transparent)
.stroke('rgba(255,255,255,0.2)')
.strokeWidth(8)
// 进度圆(动画)
Circle()
.width(120)
.height(120)
.fill(Color.Transparent)
.stroke('#ffd700')
.strokeWidth(8)
.strokeDashArray([this.showAnimation ? (this.result!.matchScore / 100) * 377 : 0, 377])
.animation({ duration: 1000, curve: Curve.EaseOut })
// 分数文字
Column({ space: 4 }) {
Text(`${this.result!.matchScore}`)
.fontSize(36)
.fontWeight(FontWeight.Bold)
.fontColor('#ffd700')
Text('匹配度')
.fontSize(12)
.fontColor('rgba(255,255,255,0.7)')
}
}
// 结果类型
Text(this.result!.title)
.fontSize(32)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.opacity(this.showAnimation ? 1 : 0)
.animation({ duration: 500, delay: 300 })
// 描述
Text(this.result!.description)
.fontSize(14)
.fontColor('rgba(255,255,255,0.8)')
.textAlign(TextAlign.Center)
.lineHeight(22)
.opacity(this.showAnimation ? 1 : 0)
.animation({ duration: 500, delay: 500 })
}
.width('100%')
.padding(24)
.backgroundColor('rgba(255,255,255,0.1)')
.borderRadius(20)
}
第三步:实现分享功能
@Builder
ActionButtons() {
Column({ space: 12 }) {
// 分享按钮
Button() {
Row({ space: 8 }) {
Image($r('app.media.ic_share'))
.width(20)
.height(20)
.fillColor(Color.White)
Text('分享结果')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
}
}
.width('100%')
.height(52)
.backgroundColor('#c41e3a')
.borderRadius(12)
.onClick(() => this.shareResult())
// 重新测试
Button('重新测试')
.width('100%')
.height(48)
.backgroundColor('rgba(255,255,255,0.15)')
.fontColor(Color.White)
.borderRadius(12)
.onClick(() => {
this.mainNavPathStack.pop();
})
// 返回首页
Button('返回首页')
.width('100%')
.height(48)
.backgroundColor(Color.Transparent)
.fontColor('rgba(255,255,255,0.7)')
.onClick(() => {
this.mainNavPathStack.clear();
})
}
.width('100%')
.padding({ top: 16 })
}
案例效果:结果页面展示如下(渐变深红背景):
┌──────────── 深红渐变背景 ────────────┐
│ │
│ ┌──────────────┐ │
│ │ ╭────╮ │ │
│ │ │ 90 │ │ │ ← 金色圆环动画
│ │ │匹配度│ │ │ 从0到90动画填充
│ │ ╰────╯ │ │
│ │ │ │
│ │ 谋臣智士 │ │ ← 白色大字,淡入动画
│ │ │ │
│ │ 你深谋远虑,善│ │
│ │ 于权衡,是帝王│ │ ← 渐显描述文字
│ │ 身边的智囊。 │ │
│ └──────────────┘ │
│ (半透明白色卡片背景) │
│ │
│ ┌──────────────────────────────┐ │
│ │ 性格特点 │ │
│ │ ┌────┐ ┌────┐ ┌──────┐ │ │
│ │ │智慧│ │谨慎│ │善于谋划│ │ │
│ │ └────┘ └────┘ └──────┘ │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ 历史名人对照 │ │
│ │ 你像:诸葛亮 │ │
│ └──────────────────────────────┘ │
│ │
│ ┌══════════════════════════════┐ │
│ ║ 📤 分享结果 ║ │ ← 红色按钮
│ └══════════════════════════════┘ │
│ ┌──────────────────────────────┐ │
│ │ 重新测试 │ │ ← 半透明白色按钮
│ └──────────────────────────────┘ │
│ │ 返回首页 │ │ ← 透明按钮
└──────────────────────────────────────┘
效果说明: - 整体页面使用从深黑到暗红的渐变背景 - 匹配度圆环:背景为半透明白色圆环,进度为金色(#ffd700) - 圆环动画:页面打开后从 0% 动画填充到实际分数,持续1秒 - 结果标题(如「谋臣智士」)延迟 300ms 淡入 - 描述文字延迟 500ms 淡入,营造逐步揭晓效果 - 性格特点以胶囊标签展示 - 三个操作按钮层次分明:主要→次要→文字
完整代码汇总
测试主页 PersonalityTestPage.ets
@Component
struct PersonalityTestPage {
@Consume('mainNavPathStack') mainNavPathStack: NavPathStack;
@StorageLink('isDarkMode') isDarkMode: boolean = true;
@State ancientRecord: TestRecord | null = null;
@State modernRecord: TestRecord | null = null;
aboutToAppear() {
this.loadRecords();
}
loadRecords() {
this.ancientRecord = getLatestTestRecord('ancient');
this.modernRecord = getLatestTestRecord('modern');
}
build() {
NavDestination() {
Column() {
this.HeaderBar()
Scroll() {
Column({ space: 24 }) {
this.HeadlineSection()
this.AncientTestCard()
this.ModernTestCard()
this.TestHistorySection()
}
.padding({ left: 16, right: 16, bottom: 32 })
}
.layoutWeight(1)
}
}
.hideTitleBar(true)
}
@Builder
AncientTestCard() {
// 古代官场测试入口卡片
Column() {
// 图片 + 内容
}
.onClick(() => {
this.mainNavPathStack.pushPathByName('AncientTestPage', null);
})
}
@Builder
ModernTestCard() {
// 现代职业测试入口卡片
Column() {
// 图片 + 内容
}
.onClick(() => {
this.mainNavPathStack.pushPathByName('ModernTestPage', null);
})
}
}
@Builder
export function PersonalityTestPageBuilder() {
PersonalityTestPage()
}
本课小结
| 功能 | 实现方式 |
|---|---|
| 题目设计 | 情景选择题 + 选项说明 |
| 答题流程 | 状态管理 + 索引控制 |
| 结果计算 | 统计选项 + 类型映射 |
| 结果展示 | 动画效果 + 分享功能 |
课后练习
- 为测试结果添加历史名人头像展示
- 实现测试结果的图片生成与分享
- 添加测试历史记录的删除功能
下一课预告
第27课开发新年习俗专题,包括八朝年俗数据、列表展示、详情页面。
项目开源地址
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)