美文网首页
Compose Desktop开发日报软件

Compose Desktop开发日报软件

作者: jianboo | 来源:发表于2025-04-10 14:30 被阅读0次

一、软件概述

1.1 简介

这款日报软件基于 Compose Desktop 开发,采用 Kotlin 语言构建桌面应用程序。它为用户提供了便捷的日报填写、历史查询功能,并且能够根据输入的时间区间自动导出周报,合并统计项目工时,极大地提升了工作记录与汇报的效率。

1.2 技术栈

项目使用MVI架构

1、依赖注入使用koin

2、图片加载使用coil

3、网络请求使用retrofit

4、缓存使用datastore

5、页面导航使用voyager

二、功能模块详解

2.1 日报新增功能

用户打开软件后,可在主界面点击 “填写日报” 进入填写页面。该页面提供文本输入框用于记录工作内容详情,以及专门的输入框用于输入项目名称和工时。在技术实现上,通过 Compose Desktop 的文本输入组件构建交互界面,获取用户输入内容并进行本地存储。同时,对输入数据进行初步校验,如项目名称不能为空、工时需为有效数字等,以保证数据的有效性。

package screen.submit

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import kotlinx.coroutines.delay
import org.koin.compose.viewmodel.koinViewModel
import theme.AppScaffoldWithBack

/**
 *    @Author : Cook
 *    @Date   : 2025/4/8
 *    @Desc   :
 *    @Version:
 */
object SubmitReportScreen : Screen {
    @Composable
    override fun Content() {
        Screen()

    }
}

@Composable
private fun Screen(viewModel: SubmitViewModel = koinViewModel<SubmitViewModel>()) {
    val viewState = viewModel.viewStates
    val navigator = LocalNavigator.currentOrThrow
    AppScaffoldWithBack(title = "填写日报", onBack = {
        navigator.pop()
    }) { padding ->
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(padding)
                .padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            TextField(
                value = viewState.content,
                onValueChange = { viewModel.onContentChanged(it) },
                modifier = Modifier.Companion.fillMaxWidth().height(260.dp),
                shape = RoundedCornerShape(12.dp),
                colors = TextFieldDefaults.textFieldColors(
                    backgroundColor = Color(0xFFF0F0F0),
                    focusedIndicatorColor = Color.Companion.Transparent,
                    unfocusedIndicatorColor = Color.Companion.Transparent
                )
            )
            AnimatedVisibility(visible = viewState.error != null) {
                Text(
                    text = viewState.error ?: "",
                    color = Color.Red,
                    fontSize = 13.sp,
                    modifier = Modifier.align(Alignment.Start)
                )
            }
            OutlinedTextField(
                value = viewState.project,
                onValueChange = { viewModel.onProjectChanged(it) },
                label = { Text("项目名称") },
                modifier = Modifier.fillMaxWidth(),
                shape = RoundedCornerShape(12.dp)
            )

            OutlinedTextField(
                value = viewState.workTime,
                onValueChange = { viewModel.onWorkTimeChanged(it) },
                label = { Text("工时(小时)") },
                modifier = Modifier.fillMaxWidth(),
                shape = RoundedCornerShape(12.dp),
                singleLine = true
            )
            Button(
                onClick = {
                    viewModel.submitReport()
                },
                modifier = Modifier.align(Alignment.CenterHorizontally)
                    .width(220.dp)
                    .height(48.dp),
                shape = RoundedCornerShape(12.dp),
                colors = ButtonDefaults.buttonColors(
                    backgroundColor = Color(0xFF2196F3),
                    contentColor = Color.White
                )
            ) {
                Text("提交")
            }

            if (viewState.success) {
                Text("✅ 提交成功!", color = Color(0xFF4CAF50), fontSize = 16.sp)
                LaunchedEffect(Unit) {
                    delay(1000)
                    navigator.pop()
                }
            }

        }
    }
}
填写日报.png

2.2 日报查询功能

点击主界面的 “查看历史” 按钮,用户可进入历史日报查看页面。在此页面,软件以列表形式展示历史日报记录,每条记录包含日期、项目名称、日报内容等信息。后台通过查询本地存储的数据(如采用 SQLite 等轻量级数据库进行存储管理),将数据按时间顺序排列并展示在界面上,方便用户快速浏览和查找过往工作记录。

列表.png
package screen.report

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import net.model.daily.DailyReport
import org.koin.compose.viewmodel.koinViewModel
import screen.export.ExportScreen
import theme.AppScaffoldMenu
import utils.AppUtils

/**
 *    @Author : Cook
 *    @Date   : 2025/4/8
 *    @Desc   :
 *    @Version:
 */
object ReportHistoryScreen : Screen {
    @Composable
    override fun Content() {
        Screen()
    }
}

@Composable
private fun Screen(viewModel: ReportHistoryViewModel = koinViewModel<ReportHistoryViewModel>()) {
    val navigator = LocalNavigator.currentOrThrow
    val viewState = viewModel.viewStates
    LaunchedEffect(Unit) {
        viewModel.load()
    }
    AppScaffoldMenu(title = "历史", "导出", onBack = {
        navigator.pop()
    }, onNext = {
        navigator.push(ExportScreen)
    }) { padding ->
        Column(
            modifier = Modifier.fillMaxSize().background(Color.White).padding(16.dp),
        ) {
            AnimatedVisibility(visible = viewState.error != null) {
                Text(
                    text = viewState.error ?: "",
                    color = Color.Red,
                    fontSize = 13.sp,
                    modifier = Modifier.align(Alignment.Start)
                )
            }
            LazyColumn(
                verticalArrangement = Arrangement.spacedBy(12.dp),
                modifier = Modifier.fillMaxSize().background(Color.White)
            ) {
                itemsIndexed(viewState.data) { index, report ->
                    ReportCardFlyStyle(report) {

                    }
                }
            }
        }
    }
}

@Composable
private fun ReportCardFlyStyle(
    report: DailyReport,
    onClick: () -> Unit = {}
) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .clickable { onClick() },
        shape = RoundedCornerShape(10.dp),
        elevation = 4.dp
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            // 顶部:日期 + 标签
            Row(
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.SpaceBetween,
                modifier = Modifier.fillMaxWidth()
            ) {
                Text(
                    text = AppUtils.formatDate(report.date),
                    fontSize = 13.sp,
                    color = Color.Gray
                )
                Surface(
                    color = Color(0xFFE3F2FD),
                    shape = RoundedCornerShape(6.dp)
                ) {
                    Text(
                        text = AppUtils.formateTime(report.updatedAt),
                        modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp),
                        fontSize = 12.sp,
                        color = Color(0xFF2196F3)
                    )
                }
            }

            Spacer(modifier = Modifier.height(10.dp))

            // 标题
            Text(
                text = "${report.project}【${report.workTime}】",
                fontSize = 18.sp,
                fontWeight = FontWeight.Bold,
                color = Color(0xFF212121)
            )

            Spacer(modifier = Modifier.height(6.dp))

            // 正文
            Text(
                text = report.content,
                fontSize = 15.sp,
                color = Color(0xFF424242),
                maxLines = 4,
                overflow = TextOverflow.Ellipsis,
                lineHeight = 22.sp
            )
        }
    }
}

image.gif

2.3 周报导出功能

用户在导出界面输入开始日期和结束日期后,点击 “导出 Excel” 按钮,软件会根据设定的时间区间,从本地存储中提取相应的日报数据。对提取到的日报数据进行整理,按照项目维度合并统计工时,并将日报内容按日期顺序整合。将整理好的数据写入 Excel 文件,实现周报的自动生成与导出。

相关文章

网友评论

      本文标题:Compose Desktop开发日报软件

      本文链接:https://www.haomeiwen.com/subject/ralqbjtx.html