parent
872bb5fe69
commit
20d289918d
File diff suppressed because it is too large
Load Diff
@ -1,260 +0,0 @@
|
||||
package com.smartlibrary.android
|
||||
|
||||
import com.smartlibrary.R
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import com.smartlibrary.android.ui.UMLViewerActivity
|
||||
|
||||
/**
|
||||
* 智能图书管理系统 - Android 主界面
|
||||
*/
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var emptyView: TextView
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var bookAdapter: BookAdapter
|
||||
|
||||
private val books = mutableListOf<Book>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
setupViews()
|
||||
loadMockData()
|
||||
}
|
||||
|
||||
private fun setupViews() {
|
||||
recyclerView = findViewById(R.id.recyclerView)
|
||||
emptyView = findViewById(R.id.emptyView)
|
||||
progressBar = findViewById(R.id.progressBar)
|
||||
|
||||
bookAdapter = BookAdapter(books) { book ->
|
||||
showBookDetail(book)
|
||||
}
|
||||
|
||||
recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(this@MainActivity)
|
||||
adapter = bookAdapter
|
||||
}
|
||||
|
||||
findViewById<Button>(R.id.btnAddBook).setOnClickListener {
|
||||
showAddBookDialog()
|
||||
}
|
||||
|
||||
findViewById<Button>(R.id.btnAiAssistant).setOnClickListener {
|
||||
showAiAssistant()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadMockData() {
|
||||
progressBar.visibility = View.VISIBLE
|
||||
|
||||
// 模拟数据
|
||||
books.clear()
|
||||
books.addAll(listOf(
|
||||
Book("1", "设计模式", "Gang of Four", "978-0-201-63361-0", "机械工业出版社", "计算机", true),
|
||||
Book("2", "重构", "Martin Fowler", "978-0-13-475759-9", "人民邮电出版社", "计算机", true),
|
||||
Book("3", "代码整洁之道", "Robert C. Martin", "978-0-13-235088-4", "人民邮电出版社", "计算机", false),
|
||||
Book("4", "算法导论", "Thomas H. Cormen", "978-0-262-03384-8", "机械工业出版社", "计算机", true),
|
||||
Book("5", "深入理解Java虚拟机", "周志明", "978-7-111-64124-1", "机械工业出版社", "计算机", true)
|
||||
))
|
||||
|
||||
progressBar.visibility = View.GONE
|
||||
updateUI()
|
||||
}
|
||||
|
||||
private fun updateUI() {
|
||||
if (books.isEmpty()) {
|
||||
recyclerView.visibility = View.GONE
|
||||
emptyView.visibility = View.VISIBLE
|
||||
} else {
|
||||
recyclerView.visibility = View.VISIBLE
|
||||
emptyView.visibility = View.GONE
|
||||
bookAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showBookDetail(book: Book) {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(book.title)
|
||||
.setMessage("""
|
||||
作者: ${book.author}
|
||||
ISBN: ${book.isbn}
|
||||
出版社: ${book.publisher}
|
||||
分类: ${book.category}
|
||||
状态: ${if (book.available) "可借阅" else "已借出"}
|
||||
""".trimIndent())
|
||||
.setPositiveButton(if (book.available) "借阅" else "归还") { _, _ ->
|
||||
book.available = !book.available
|
||||
bookAdapter.notifyDataSetChanged()
|
||||
Toast.makeText(this, if (book.available) "归还成功" else "借阅成功", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
.setNegativeButton("关闭", null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showAddBookDialog() {
|
||||
val dialogView: View = layoutInflater.inflate(R.layout.dialog_add_book, null)
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("添加图书")
|
||||
.setView(dialogView)
|
||||
.setPositiveButton("添加") { _: android.content.DialogInterface, _: Int ->
|
||||
val title = dialogView.findViewById<EditText>(R.id.etTitle).text.toString()
|
||||
val author = dialogView.findViewById<EditText>(R.id.etAuthor).text.toString()
|
||||
val isbn = dialogView.findViewById<EditText>(R.id.etIsbn).text.toString()
|
||||
|
||||
if (title.isNotBlank() && author.isNotBlank()) {
|
||||
val newBook = Book(
|
||||
id = (books.size + 1).toString(),
|
||||
title = title,
|
||||
author = author,
|
||||
isbn = isbn,
|
||||
publisher = "未知",
|
||||
category = "未分类",
|
||||
available = true
|
||||
)
|
||||
books.add(0, newBook)
|
||||
updateUI()
|
||||
Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
.setNegativeButton("取消", null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showAiAssistant() {
|
||||
val input = EditText(this).apply {
|
||||
hint = "请输入您的问题..."
|
||||
}
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("🤖 AI智能助手")
|
||||
.setView(input)
|
||||
.setPositiveButton("发送") { _, _ ->
|
||||
val question = input.text.toString()
|
||||
if (question.isNotBlank()) {
|
||||
// 模拟AI回复
|
||||
val response = when {
|
||||
question.contains("推荐") -> "根据您的借阅历史,推荐您阅读《设计模式》和《重构》"
|
||||
question.contains("搜索") || question.contains("查找") -> "已为您找到相关图书,请查看列表"
|
||||
else -> "感谢您的提问!如需帮助,可以问我:推荐图书、搜索图书等"
|
||||
}
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("AI回复")
|
||||
.setMessage(response)
|
||||
.setPositiveButton("确定", null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
.setNegativeButton("取消", null)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.main_menu, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.action_uml -> {
|
||||
startActivity(Intent(this, UMLViewerActivity::class.java))
|
||||
true
|
||||
}
|
||||
R.id.action_web -> {
|
||||
showSwitchToWebDialog()
|
||||
true
|
||||
}
|
||||
R.id.action_about -> {
|
||||
showAboutDialog()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSwitchToWebDialog() {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("切换到Web端")
|
||||
.setMessage("Web服务地址:\nhttp://localhost:8082\n\n请确保后端服务已启动")
|
||||
.setPositiveButton("打开浏览器") { _, _ ->
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("http://localhost:8082"))
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "无法打开浏览器", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
.setNegativeButton("取消", null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showAboutDialog() {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("关于")
|
||||
.setMessage("智能图书管理系统 (SLMS)\n版本: 1.0.0\n\n四端统一架构:\n• CLI 命令行\n• GUI 桌面应用\n• WUI Web应用\n• App 移动端")
|
||||
.setPositiveButton("确定", null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图书数据类
|
||||
*/
|
||||
data class Book(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val author: String,
|
||||
val isbn: String,
|
||||
val publisher: String,
|
||||
val category: String,
|
||||
var available: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* 图书列表适配器
|
||||
*/
|
||||
class BookAdapter(
|
||||
private val books: List<Book>,
|
||||
private val onClick: (Book) -> Unit
|
||||
) : RecyclerView.Adapter<BookAdapter.ViewHolder>() {
|
||||
|
||||
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val tvTitle: TextView = view.findViewById(R.id.tvTitle)
|
||||
val tvAuthor: TextView = view.findViewById(R.id.tvAuthor)
|
||||
val tvStatus: TextView = view.findViewById(R.id.tvStatus)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: android.view.ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = android.view.LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_book, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val book = books[position]
|
||||
holder.tvTitle.text = book.title
|
||||
holder.tvAuthor.text = "作者: ${book.author}"
|
||||
holder.tvStatus.text = if (book.available) "✅ 可借阅" else "❌ 已借出"
|
||||
holder.itemView.setOnClickListener { onClick(book) }
|
||||
}
|
||||
|
||||
override fun getItemCount() = books.size
|
||||
}
|
||||
@ -0,0 +1,171 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>用户管理 - MCSLMS</title>
|
||||
<th:block th:replace="~{fragments/layout :: styles}"/>
|
||||
</head>
|
||||
<body>
|
||||
<nav th:replace="~{fragments/layout :: navbar('users')}"/>
|
||||
|
||||
<main class="container mt-4">
|
||||
<div th:if="${param.approved}" class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
用户审核通过成功!<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<div th:if="${param.rejected}" class="alert alert-warning alert-dismissible fade show" role="alert">
|
||||
用户已被标记为拒绝!<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<div th:if="${param.roleUpdated}" class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
用户角色更新成功!<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<div th:if="${param.roleError}" class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
角色更新失败:无效角色类型。<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>👥 用户管理</h1>
|
||||
<span class="badge bg-info" th:text="'待审核用户: ' + ${pendingCount}"></span>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<form th:action="@{/users}" method="get" class="row g-3 align-items-end">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">关键字</label>
|
||||
<input type="text" class="form-control" name="keyword" placeholder="姓名 / 邮箱 / 电话 / 院系 / 专业"
|
||||
th:value="${keyword}">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">角色</label>
|
||||
<select class="form-select" name="role">
|
||||
<option value="ALL" th:selected="${roleFilter == 'ALL'}">全部角色</option>
|
||||
<option value="READER" th:selected="${roleFilter == 'READER'}">读者</option>
|
||||
<option value="LIBRARIAN" th:selected="${roleFilter == 'LIBRARIAN'}">馆员</option>
|
||||
<option value="ADMIN" th:selected="${roleFilter == 'ADMIN'}">管理员</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">状态</label>
|
||||
<select class="form-select" name="status">
|
||||
<option value="ALL" th:selected="${statusFilter == 'ALL'}">全部状态</option>
|
||||
<option value="PENDING" th:selected="${statusFilter == 'PENDING'}">待审核</option>
|
||||
<option value="APPROVED" th:selected="${statusFilter == 'APPROVED'}">已通过</option>
|
||||
<option value="REJECTED" th:selected="${statusFilter == 'REJECTED'}">已拒绝</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary w-100">🔍 筛选</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">全部用户</h5>
|
||||
<span class="badge bg-primary" th:text="'共 ' + ${users.size()} + ' 个用户'"></span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover align-middle">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>姓名</th>
|
||||
<th>邮箱</th>
|
||||
<th>类型</th>
|
||||
<th>角色</th>
|
||||
<th>状态</th>
|
||||
<th>院系/专业</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:if="${users.empty}">
|
||||
<td colspan="8" class="text-center text-muted py-4">暂无用户记录</td>
|
||||
</tr>
|
||||
<tr th:each="u : ${users}">
|
||||
<td th:text="${u.id}"></td>
|
||||
<td th:text="${u.name}"></td>
|
||||
<td th:text="${u.email}"></td>
|
||||
<td th:text="${u.userType}"></td>
|
||||
<td>
|
||||
<span th:if="${u.role.name() == 'ADMIN'}" class="badge bg-danger">管理员</span>
|
||||
<span th:if="${u.role.name() == 'LIBRARIAN'}" class="badge bg-warning text-dark">馆员</span>
|
||||
<span th:if="${u.role.name() == 'READER'}" class="badge bg-secondary">读者</span>
|
||||
</td>
|
||||
<td>
|
||||
<span th:if="${u.status.name() == 'PENDING'}" class="badge bg-warning text-dark">待审核</span>
|
||||
<span th:if="${u.status.name() == 'APPROVED'}" class="badge bg-success">已通过</span>
|
||||
<span th:if="${u.status.name() == 'REJECTED'}" class="badge bg-secondary">已拒绝</span>
|
||||
</td>
|
||||
<td>
|
||||
<div th:text="${u.department}"></div>
|
||||
<div class="text-muted small" th:text="${u.major}"></div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column gap-1">
|
||||
<form th:action="@{/users/approve/{id}(id=${u.id})}" method="post" th:if="${u.status.name() == 'PENDING'}">
|
||||
<button type="submit" class="btn btn-sm btn-success w-100">审核通过</button>
|
||||
</form>
|
||||
<form th:action="@{/users/reject/{id}(id=${u.id})}" method="post" th:if="${u.status.name() == 'PENDING'}">
|
||||
<button type="submit" class="btn btn-sm btn-outline-secondary w-100">拒绝</button>
|
||||
</form>
|
||||
<form th:action="@{/users/role/{id}(id=${u.id})}" method="post">
|
||||
<div class="input-group input-group-sm">
|
||||
<select class="form-select" name="role">
|
||||
<option value="READER" th:selected="${u.role.name() == 'READER'}">读者</option>
|
||||
<option value="LIBRARIAN" th:selected="${u.role.name() == 'LIBRARIAN'}">馆员</option>
|
||||
<option value="ADMIN" th:selected="${u.role.name() == 'ADMIN'}">管理员</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-outline-primary">更新</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">待审核用户</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="list-group" th:if="${!pendingUsers.empty}">
|
||||
<div class="list-group-item list-group-item-action" th:each="u : ${pendingUsers}">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h6 class="mb-1" th:text="${u.name}"></h6>
|
||||
<small th:text="${u.department}"></small>
|
||||
</div>
|
||||
<p class="mb-1" th:text="${u.email}"></p>
|
||||
<small th:text="${u.userType}"></small>
|
||||
<div class="mt-2 d-flex gap-2">
|
||||
<form th:action="@{/users/approve/{id}(id=${u.id})}" method="post">
|
||||
<button type="submit" class="btn btn-sm btn-success">通过</button>
|
||||
</form>
|
||||
<form th:action="@{/users/reject/{id}(id=${u.id})}" method="post">
|
||||
<button type="submit" class="btn btn-sm btn-outline-secondary">拒绝</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-muted" th:if="${pendingUsers.empty}">当前没有待审核用户。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer th:replace="~{fragments/layout :: footer}"/>
|
||||
<th:block th:replace="~{fragments/layout :: scripts}"/>
|
||||
</body>
|
||||
</html>
|
||||
@ -1 +1,61 @@
|
||||
所有端的功能点实现状态
|
||||
## MCSLMS 功能点与四端实现状态
|
||||
|
||||
> 基于 `core` 模块中 `SystemSettingsService#getAllFeatures()` 的功能模块定义,结合 CLI / GUI / Web / Android 四端的实际代码实现情况整理。
|
||||
|
||||
状态说明:
|
||||
|
||||
- ✅ 已实现:该端有完整的功能入口并接入核心服务
|
||||
- ⚠️ 部分实现:仅实现简化版或演示版,或缺少部分子功能
|
||||
- ❌ 未实现:当前端没有对应的功能入口或实现
|
||||
|
||||
### 1. 核心业务与读者互动功能
|
||||
|
||||
| 功能ID | 功能名称 | 描述 | CLI | GUI | Web | Android |
|
||||
|--------|----------|------|-----|-----|-----|---------|
|
||||
| books | 图书管理 | 图书的添加、删除、修改、查询 | ✅ | ✅ | ✅ | ⚠️ |
|
||||
| loans | 借阅管理 | 图书借阅、归还、续借 | ✅ | ✅ | ✅ | ⚠️ |
|
||||
| users | 用户管理 | 用户注册、信息管理 | ✅ | ✅ | ❌ | ❌ |
|
||||
| notifications | 通知中心 | 多渠道通知推送 | ✅ | ✅ | ✅ | ❌ |
|
||||
| reservations | 预约管理 | 图书预约、排队 | ✅ | ✅ | ✅ | ✅ |
|
||||
| history | 借阅历史 | 借阅记录、罚款 | ✅ | ✅ | ✅ | ❌ |
|
||||
| notes | 读书笔记 | 记录阅读心得 | ✅ | ✅ | ✅ | ❌ |
|
||||
| favorites | 我的收藏 | 收藏喜欢的图书 | ✅ | ✅ | ✅ | ❌ |
|
||||
| comments | 图书评论 | 评价与评分 | ✅ | ✅ | ✅ | ❌ |
|
||||
| feedback | 意见反馈 | 问题反馈与建议 | ✅ | ❌ | ✅ | ❌ |
|
||||
| statistics | 数据统计 | 图表分析与报表 | ✅ | ✅ | ✅ | ❌ |
|
||||
|
||||
说明:
|
||||
|
||||
- **CLI**:通过命令 `notes` / `favorites` / `comments` / `feedback` / `notifications` / `reservations` / `history` / `stats` 等,已接入全部业务与读者互动模块。
|
||||
- **GUI**:已实现图书管理、借阅归还、预约管理、读者中心(我的借阅 / 借阅历史 / 收藏 / 笔记 / 通知中心)以及基础统计;暂未提供反馈界面。
|
||||
- **Web**:基于 `WebController` 和 Thymeleaf 页面,已覆盖图书管理、借阅管理、通知中心、预约、历史、笔记、收藏、评论、反馈以及可视化统计。
|
||||
- **Android**:当前通过 REST API 从后端加载图书列表,并接入统一数据库下的登录与预约管理;借阅/归还和添加图书仍以本地演示逻辑为主,尚未接入通知、历史、笔记、收藏、评论、反馈与统计等业务模块。
|
||||
|
||||
### 2. AI 与系统设置功能
|
||||
|
||||
| 功能ID | 功能名称 | 描述 | CLI | GUI | Web | Android |
|
||||
|--------|----------|------|-----|-----|-----|---------|
|
||||
| ai_recommend | 智能推荐 | AI 图书推荐 | ✅ | ✅ | ❌ | ✅ |
|
||||
| ai_qa | 智能问答 | AI 助手问答 | ✅ | ✅ | ❌ | ✅ |
|
||||
| ai_analysis | 智能分析 | 阅读行为分析 / 图书分析 | ❌ | ✅ | ❌ | ❌ |
|
||||
| settings | 系统设置 | 配置与数据源管理 | ✅ | ❌ | ✅ | ❌ |
|
||||
|
||||
说明:
|
||||
|
||||
- **CLI**:提供 `ai` / `recommend` 命令进行 AI 问答和推荐,并通过 `settings` 命令查看系统状态;暂未提供独立的 AI 行为分析界面。
|
||||
- **GUI**:右侧集成 AI 聊天面板,支持聊天问答、智能推荐、图书分析、阅读计划与图书馆报告;当前未接入 `SystemSettingsService` 做统一系统设置界面。
|
||||
- **Web**:主要通过 `/stats`、`/settings` 访问统计分析与系统配置,尚未集成前端 AI 问答 / 推荐 / 分析界面。
|
||||
- **Android**:通过 `AIAssistantFragment`、`AIRecommendFragment` 等实现本地/远程 AI 问答与推荐,以及 UML 相关 AI 能力;暂未实现与核心借阅/历史数据联动的阅读行为分析和系统设置功能。
|
||||
|
||||
### 3. 四端统一与端切换(补充)
|
||||
|
||||
| 功能 | 说明 | CLI | GUI | Web | Android |
|
||||
|------|------|-----|-----|-----|---------|
|
||||
| 端切换 | CLI / GUI / Web / App 之间的说明与跳转 | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
- CLI 通过 `gui` / `web` / `app` 命令提示切换方式。
|
||||
- GUI 在“切换”菜单中提供 CLI / Web / App 二维码的说明与链接。
|
||||
- Web 提供 `/switch` 页面说明四端架构与切换方式。
|
||||
- Android 在菜单中提供“切换到 Web 端”的说明与浏览器跳转。
|
||||
|
||||
> 本文档仅标记 **功能是否在各端有入口与实现**,不区分 UI 复杂度和交互细节。后续如果对某端补充了新的功能入口或与核心服务的集成,请同步更新本表。
|
||||
Loading…
Reference in new issue