鸿蒙开发-步骤管理

0 阅读5分钟

锻造步骤怎么管理?HarmonyOS preferences存储制作步骤

如果你对金属锻造感兴趣,可以去鸿蒙应用市场搜一下**「锻艺册」**,下载下来体验体验。从退火到抛光,每个锻造步骤都有详细的温度、时间和技巧说明。体验完了再回来看这篇文章,你会更清楚这些步骤数据是怎么存储和管理的。


写在前面

大家好,我是一名写了十多年Web前端的老兵。从jQuery时代一路走到React/Vue,CSS3动画、requestAnimationFrame、Web Animation API这些都算是看家本领。去年开始转战鸿蒙生态,用ArkTS开发App,这一路踩了不少坑,也积累了不少心得。

很多人觉得"前端转鸿蒙"应该很容易——都是写UI嘛,组件化、状态管理、生命周期,概念都差不多。但真正上手之后你会发现,相似的地方让你觉得亲切,不同的地方让你抓狂

比如:

  • 步骤序列化:锻造步骤有固定的顺序,需要正确管理步骤的先后关系。
  • 状态追踪:每个步骤有自己的状态(未开始、进行中、已完成),需要正确更新。

别担心,接下来这篇文章,我会用"锻艺册"的步骤管理功能,带你看看怎么在HarmonyOS里管理锻造步骤数据。


这篇文章聊什么

锻艺册的步骤管理功能,核心要解决:

  1. 步骤定义:每个锻造步骤的详细信息
  2. 步骤排序:正确的制作顺序
  3. 状态追踪:记录每个步骤的完成状态
  4. 进度显示:显示整体完成进度

第一步:设计步骤数据结构

// 锻造步骤
interface ForgingStep {
  id: string;
  name: string;
  description: string;
  temperature: string;    // 温度要求
  duration: string;       // 时间要求
  tip: string;            // 技巧提示
  status: 'pending' | 'in_progress' | 'completed';
  completedAt: string;    // 完成时间
}

// 项目中的步骤记录
interface ProjectStep {
  stepId: string;
  status: 'pending' | 'in_progress' | 'completed';
  startedAt: string;
  completedAt: string;
  notes: string;
}

// 锻造项目
interface ForgingProject {
  id: string;
  name: string;
  category: string;       // 项目分类
  metalType: string;      // 金属类型
  steps: ProjectStep[];   // 步骤记录
  status: string;
  notes: string;
  createdAt: string;
}

// 锻造步骤定义
const FORGING_STEPS = [
  { id: 'annealing', name: '退火', description: '加热金属使其软化便于加工', temperature: '600-700°C', duration: '2-5分钟', tip: '观察金属颜色变为暗红即可' },
  { id: 'pickling', name: '酸洗', description: '去除退火产生的氧化层', temperature: '常温', duration: '1-3分钟', tip: '使用稀硫酸溶液,注意安全' },
  { id: 'forming', name: '成型', description: '锤打、弯曲塑造基本形状', temperature: '常温', duration: '视作品而定', tip: '力度均匀,逐步成型' },
  { id: 'soldering', name: '焊接', description: '用焊料连接金属部件', temperature: '600-800°C', duration: '10-30秒', tip: '焊药要均匀涂抹' },
  { id: 'filing', name: '锉修', description: '锉平焊接点和不平整处', temperature: '常温', duration: '10-30分钟', tip: '从粗锉到细锉逐步进行' },
  { id: 'sanding', name: '打磨', description: '砂纸打磨表面至光滑', temperature: '常温', duration: '15-45分钟', tip: '逐级提高砂纸目数' },
  { id: 'polishing', name: '抛光', description: '抛光至镜面或哑光效果', temperature: '常温', duration: '10-30分钟', tip: '根据需要选择抛光膏' },
  { id: 'texturing', name: '做纹理', description: '锤纹、拉丝、喷砂等表面处理', temperature: '常温', duration: '5-20分钟', tip: '先在废料上试验效果' },
  { id: 'setting', name: '镶嵌', description: '将宝石固定在金属座上', temperature: '常温', duration: '20-60分钟', tip: '爪镶、包镶、钉镶等技法' },
  { id: 'finishing', name: '收尾', description: '最终检查与调整', temperature: '常温', duration: '5-15分钟', tip: '检查每个连接点是否牢固' },
];

第二步:实现步骤管理功能

import { preferences } from '@kit.ArkData';

let prefInstance: preferences.Preferences | null = null;

async function getPreferences(context: Context): Promise<preferences.Preferences> {
  if (!prefInstance) {
    prefInstance = await preferences.getPreferences(context, 'duanyice_data');
  }
  return prefInstance;
}

async function setItem(context: Context, key: string, value: unknown): Promise<boolean> {
  try {
    const pref = await getPreferences(context);
    await pref.put(key, JSON.stringify(value));
    await pref.flush();
    return true;
  } catch (err) {
    console.error('存储失败:', err);
    return false;
  }
}

async function getItem<T>(context: Context, key: string, defaultValue: T): Promise<T> {
  try {
    const pref = await getPreferences(context);
    const value = await pref.get(key, '');
    if (typeof value === 'string' && value.length > 0) {
      return JSON.parse(value) as T;
    }
    return defaultValue;
  } catch (err) {
    console.error('读取失败:', err);
    return defaultValue;
  }
}

// 添加项目
async function addProject(context: Context, project: ForgingProject): Promise<boolean> {
  const projects = await getItem<ForgingProject[]>(context, 'projects', []);

  const newProject: ForgingProject = {
    ...project,
    id: `project_${Date.now()}`,
    createdAt: new Date().toISOString().slice(0, 10)
  };

  projects.push(newProject);
  return await setItem(context, 'projects', projects);
}

// 更新步骤状态
async function updateStepStatus(
  context: Context,
  projectId: string,
  stepId: string,
  status: 'pending' | 'in_progress' | 'completed',
  notes: string = ''
): Promise<boolean> {
  const projects = await getItem<ForgingProject[]>(context, 'projects', []);
  const projectIndex = projects.findIndex(p => p.id === projectId);
  if (projectIndex === -1) return false;

  const project = projects[projectIndex];
  const stepIndex = project.steps.findIndex(s => s.stepId === stepId);

  if (stepIndex > -1) {
    // 更新现有步骤
    project.steps[stepIndex].status = status;
    project.steps[stepIndex].notes = notes;
    if (status === 'completed') {
      project.steps[stepIndex].completedAt = new Date().toISOString().slice(0, 10);
    }
  } else {
    // 添加新步骤
    project.steps.push({
      stepId,
      status,
      startedAt: status === 'in_progress' ? new Date().toISOString().slice(0, 10) : '',
      completedAt: status === 'completed' ? new Date().toISOString().slice(0, 10) : '',
      notes
    });
  }

  // 更新项目状态
  const completedCount = project.steps.filter(s => s.status === 'completed').length;
  const totalSteps = FORGING_STEPS.length;

  if (completedCount === totalSteps) {
    project.status = 'completed';
  } else if (completedCount > 0) {
    project.status = 'in_progress';
  }

  return await setItem(context, 'projects', projects);
}

// 计算进度
function calculateProgress(project: ForgingProject): number {
  if (project.steps.length === 0) return 0;
  const completed = project.steps.filter(s => s.status === 'completed').length;
  return Math.round((completed / FORGING_STEPS.length) * 100);
}

第三步:实现步骤列表页面

@Entry
@Component
struct StepListPage {
  @State project: ForgingProject | null = null

  async aboutToAppear() {
    const projects = await getProjects(getContext(this) as Context);
    this.project = projects[0]; // 示例:显示第一个项目
  }

  private getStepStatus(stepId: string): ProjectStep | null {
    return this.project?.steps.find(s => s.stepId === stepId) || null;
  }

  private getStepColor(stepId: string): string {
    const status = this.getStepStatus(stepId);
    if (!status) return '#e5e7eb';
    if (status.status === 'completed') return '#22c55e';
    if (status.status === 'in_progress') return '#f97316';
    return '#e5e7eb';
  }

  build() {
    Column() {
      if (this.project) {
        Text(this.project.name)
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 8)

        Text(`进度: ${calculateProgress(this.project)}%`)
          .fontSize(14)
          .fontColor('#6b7280')
          .margin({ bottom: 16)

        // 进度条
        Stack({ alignContent: Alignment.Start }) {
          Row()
            .width('100%')
            .height(8)
            .backgroundColor('#e5e7eb')
            .borderRadius(4)
          Row()
            .width(`${calculateProgress(this.project)}%`)
            .height(8)
            .backgroundColor('#22c55e')
            .borderRadius(4)
        }
        .width('100%')
        .margin({ bottom: 20 })

        // 步骤列表
        ForEach(FORGING_STEPS, (step, index) => {
          Row() {
            // 步骤图标
            Circle({ width: 32, height: 32 })
              .fill(this.getStepColor(step.id))
              .overlay(
                Text(this.getStepStatus(step.id)?.status === 'completed' ? '✓' : `${index + 1}`)
                  .fontSize(14)
                  .fontColor('#ffffff')
              )

            // 步骤信息
            Column() {
              Text(step.name)
                .fontSize(16)
                .fontWeight(FontWeight.Medium)

              Text(step.description)
                .fontSize(12)
                .fontColor('#9ca3af')

              Row() {
                Text(step.temperature)
                  .fontSize(10)
                  .fontColor('#f97316')
                  .padding({ left: 4, right: 4, top: 2, bottom: 2 })
                  .backgroundColor('#fff7ed')
                  .borderRadius(4)

                Text(step.duration)
                  .fontSize(10)
                  .fontColor('#3b82f6')
                  .padding({ left: 4, right: 4, top: 2, bottom: 2 })
                  .backgroundColor('#eff6ff')
                  .borderRadius(4)
                  .margin({ left: 4 })
              }
              .margin({ top: 4 })
            }
            .layoutWeight(1)
            .margin({ left: 12 })
            .alignItems(HorizontalAlign.Start)

            // 操作按钮
            if (this.getStepStatus(step.id)?.status !== 'completed') {
              Button(this.getStepStatus(step.id)?.status === 'in_progress' ? '完成' : '开始')
                .fontSize(12)
                .height(32)
                .backgroundColor(this.getStepStatus(step.id)?.status === 'in_progress' ? '#22c55e' : '#f97316')
                .borderRadius(8)
                .onClick(async () => {
                  const newStatus = this.getStepStatus(step.id)?.status === 'in_progress' ? 'completed' : 'in_progress';
                  await updateStepStatus(
                    getContext(this) as Context,
                    this.project!.id,
                    step.id,
                    newStatus
                  );
                  // 刷新数据
                  const projects = await getProjects(getContext(this) as Context);
                  this.project = projects.find(p => p.id === this.project?.id) || null;
                })
            }
          }
          .width('100%')
          .padding(12)
          .backgroundColor('#ffffff')
          .borderRadius(12)
          .margin({ bottom: 8 })
        })
      }
    }
    .padding(16)
  }
}

第四步:常见问题

4.1 步骤顺序管理

问题:用户可能跳过某些步骤。

解决:提示用户按顺序完成,或者允许跳过但标记警告。

4.2 步骤时间记录

问题:需要记录每个步骤的实际耗时。

解决:在步骤开始时记录时间戳,完成时计算差值。


总结

这篇文章围绕"锻艺册"的步骤管理功能,讲解了:

步骤数据管理

  • 步骤定义和状态追踪
  • 进度计算和显示
  • 状态流转管理

UI设计

  • 步骤列表和状态图标
  • 进度条可视化
  • 操作按钮

如果你对"锻艺册"感兴趣,欢迎去鸿蒙应用市场搜索下载体验。