菜品规划
Some checks failed
Flask 提示词大师 - CI/CD 流水线 / 代码质量检查 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 单元测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 集成测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 构建Docker镜像 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到测试环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到生产环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署监控系统 (push) Has been cancelled
Some checks failed
Flask 提示词大师 - CI/CD 流水线 / 代码质量检查 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 单元测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 集成测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 构建Docker镜像 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到测试环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到生产环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署监控系统 (push) Has been cancelled
This commit is contained in:
@@ -26,6 +26,10 @@
|
||||
android:name=".HistoryActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".MealPlanningActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
|
||||
@@ -48,6 +48,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (binding.btnHistory != null) {
|
||||
binding.btnHistory.setOnClickListener(v -> openHistory());
|
||||
}
|
||||
if (binding.btnMealPlanning != null) {
|
||||
binding.btnMealPlanning.setOnClickListener(v -> startActivity(new Intent(this, MealPlanningActivity.class)));
|
||||
}
|
||||
observeViewModel();
|
||||
showApiKeyHintIfNeeded();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.ruilaizi.example;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.ruilaizi.example.data.meal.MealPlanGenerator;
|
||||
import com.ruilaizi.example.databinding.ActivityMealPlanningBinding;
|
||||
import com.ruilaizi.example.ui.MealPlanningViewModel;
|
||||
|
||||
/**
|
||||
* 智能饭菜规划:本地调用 DeepSeek 生成饭菜清单,与 example 提示词优化同构。
|
||||
*/
|
||||
public class MealPlanningActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityMealPlanningBinding binding;
|
||||
private MealPlanningViewModel viewModel;
|
||||
|
||||
private static final String[] BUDGET_VALUES = {"50", "100", "150", "200", "300", "500", "1000"};
|
||||
private static final String[] DINER_VALUES = {"1", "2", "3", "4", "5", "6", "8", "10"};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityMealPlanningBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MealPlanningViewModel.class);
|
||||
|
||||
setupSpinners();
|
||||
binding.btnBack.setOnClickListener(v -> finish());
|
||||
binding.btnGenerate.setOnClickListener(v -> doGenerate());
|
||||
binding.btnCopy.setOnClickListener(v -> copyResult());
|
||||
observeViewModel();
|
||||
}
|
||||
|
||||
private void setupSpinners() {
|
||||
ArrayAdapter<CharSequence> regionAdapter = ArrayAdapter.createFromResource(this, R.array.meal_region_types, android.R.layout.simple_spinner_item);
|
||||
regionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
binding.spinnerRegion.setAdapter(regionAdapter);
|
||||
|
||||
ArrayAdapter<CharSequence> dinerAdapter = ArrayAdapter.createFromResource(this, R.array.meal_diner_counts, android.R.layout.simple_spinner_item);
|
||||
dinerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
binding.spinnerDiner.setAdapter(dinerAdapter);
|
||||
binding.spinnerDiner.setSelection(1); // 2人
|
||||
|
||||
ArrayAdapter<CharSequence> mealTypeAdapter = ArrayAdapter.createFromResource(this, R.array.meal_types, android.R.layout.simple_spinner_item);
|
||||
mealTypeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
binding.spinnerMealType.setAdapter(mealTypeAdapter);
|
||||
binding.spinnerMealType.setSelection(1); // 午餐
|
||||
|
||||
ArrayAdapter<CharSequence> budgetAdapter = ArrayAdapter.createFromResource(this, R.array.meal_budgets, android.R.layout.simple_spinner_item);
|
||||
budgetAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
binding.spinnerBudget.setAdapter(budgetAdapter);
|
||||
binding.spinnerBudget.setSelection(1); // 50-100元
|
||||
}
|
||||
|
||||
private void doGenerate() {
|
||||
String hometown = getText(binding.etHometown);
|
||||
if (hometown.isEmpty()) {
|
||||
Toast.makeText(this, "请输入用餐者家乡", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
MealPlanGenerator.MealPlanParams params = new MealPlanGenerator.MealPlanParams();
|
||||
params.setRegionType(getSpinnerValue(binding.spinnerRegion, getResources().getStringArray(R.array.meal_region_types)));
|
||||
params.setDinerCount(DINER_VALUES[binding.spinnerDiner.getSelectedItemPosition()]);
|
||||
params.setMealType(getSpinnerValue(binding.spinnerMealType, getResources().getStringArray(R.array.meal_types)));
|
||||
params.setHometown(hometown);
|
||||
params.setPreferences(getText(binding.etPreferences));
|
||||
params.setDietaryRestrictions(getText(binding.etDietary));
|
||||
params.setBudget(BUDGET_VALUES[binding.spinnerBudget.getSelectedItemPosition()]);
|
||||
|
||||
viewModel.generate(params);
|
||||
}
|
||||
|
||||
private static String getSpinnerValue(Spinner spinner, String[] displayItems) {
|
||||
int pos = spinner.getSelectedItemPosition();
|
||||
if (pos >= 0 && pos < displayItems.length) {
|
||||
return displayItems[pos];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String getText(TextInputEditText et) {
|
||||
return et.getText() != null ? et.getText().toString().trim() : "";
|
||||
}
|
||||
|
||||
private void copyResult() {
|
||||
String text = binding.tvResult.getText() != null ? binding.tvResult.getText().toString() : "";
|
||||
if (text.isEmpty() || getString(R.string.meal_planning_result_placeholder).equals(text)) {
|
||||
Snackbar.make(binding.getRoot(), "暂无内容可复制", Snackbar.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
if (cm != null) {
|
||||
cm.setPrimaryClip(ClipData.newPlainText("饭菜规划", text));
|
||||
Snackbar.make(binding.getRoot(), "已复制到剪贴板", Snackbar.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getResultLiveData().observe(this, text -> {
|
||||
if (text != null) {
|
||||
binding.tvResult.setText(text);
|
||||
binding.tvResult.setTextColor(getResources().getColor(R.color.colorTextDark, getTheme()));
|
||||
}
|
||||
});
|
||||
viewModel.getLoadingLiveData().observe(this, loading -> {
|
||||
binding.progressBar.setVisibility(loading != null && loading ? View.VISIBLE : View.GONE);
|
||||
binding.btnGenerate.setEnabled(loading == null || !loading);
|
||||
});
|
||||
viewModel.getErrorLiveData().observe(this, error -> {
|
||||
if (error != null && !error.isEmpty()) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage(error)
|
||||
.setPositiveButton(android.R.string.ok, (d, w) -> viewModel.clearError())
|
||||
.show();
|
||||
}
|
||||
});
|
||||
viewModel.getSnackbarLiveData().observe(this, msg -> {
|
||||
if (msg != null && !msg.isEmpty()) {
|
||||
Snackbar.make(binding.getRoot(), msg, Snackbar.LENGTH_SHORT).show();
|
||||
viewModel.clearSnackbar();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.ruilaizi.example.data.meal;
|
||||
|
||||
import com.ruilaizi.example.data.api.ApiKeyProvider;
|
||||
import com.ruilaizi.example.data.api.OpenAICompletionService;
|
||||
|
||||
/**
|
||||
* 智能饭菜规划生成器。本地调用 DeepSeek,与 Flask 端 meal_planning 逻辑一致。
|
||||
*/
|
||||
public class MealPlanGenerator {
|
||||
|
||||
private static final float TEMPERATURE = 0.7f;
|
||||
private static final int MAX_TOKENS = 2000;
|
||||
|
||||
private final OpenAICompletionService completionService;
|
||||
private final ApiKeyProvider apiKeyProvider;
|
||||
|
||||
public MealPlanGenerator(OpenAICompletionService completionService, ApiKeyProvider apiKeyProvider) {
|
||||
this.completionService = completionService;
|
||||
this.apiKeyProvider = apiKeyProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成饭菜规划。需在后台线程调用。
|
||||
*/
|
||||
public String generate(MealPlanParams params) throws Exception {
|
||||
String apiKey = apiKeyProvider.getApiKey();
|
||||
if (apiKey == null || apiKey.isEmpty()) {
|
||||
throw new Exception("请先在设置中配置 API Key");
|
||||
}
|
||||
String regionType = params.getRegionType() != null ? params.getRegionType() : "全国";
|
||||
String systemPrompt = "你是一位专业的" + regionType + "智能饭菜清单规划师和营养搭配专家。\n\n"
|
||||
+ "请按以下**固定结构**输出 Markdown,便于用户查阅:\n\n"
|
||||
+ "**一、菜品推荐与搭配思路**\n"
|
||||
+ "用一段话说明本餐的搭配思路(结合家乡风味、人数、预算等)。\n\n"
|
||||
+ "**二、推荐菜品详情**\n"
|
||||
+ "每道菜单独成块,格式如下:\n"
|
||||
+ "- 用 **加粗标题** 写菜品名(如:**1. 肉夹馍(经典主食)**)\n"
|
||||
+ "- **菜品特色**:一句话或要点\n"
|
||||
+ "- **食材清单**:列表写出食材与用量、预估价格(如:五花肉 300g (18元))\n"
|
||||
+ "- **制作步骤**:编号步骤\n"
|
||||
+ "- **营养信息**:简要说明\n"
|
||||
+ "- **预算估算**:单道菜金额(如:29元)\n\n"
|
||||
+ "**三、总预算与营养总结**\n"
|
||||
+ "- **总预算**:总金额与简要说明\n"
|
||||
+ "- **碳水化合物/蛋白质/维生素与纤维/脂肪**:各一句话\n"
|
||||
+ "- **过敏信息**:如有需提示\n\n"
|
||||
+ "约束:营养均衡、符合个人喜好与禁忌、控制预算、体现家乡特色。";
|
||||
|
||||
String hometown = params.getHometown() != null ? params.getHometown().trim() : "";
|
||||
if (hometown.isEmpty()) {
|
||||
throw new Exception("请输入用餐者家乡");
|
||||
}
|
||||
String dinerCount = params.getDinerCount() != null ? params.getDinerCount() : "2";
|
||||
String mealType = params.getMealType() != null ? params.getMealType() : "午餐";
|
||||
String preferences = params.getPreferences() != null ? params.getPreferences().trim() : "无";
|
||||
String dietaryRestrictions = params.getDietaryRestrictions() != null ? params.getDietaryRestrictions().trim() : "无";
|
||||
String budget = params.getBudget() != null ? params.getBudget() : "100";
|
||||
|
||||
String userPrompt = "为" + dinerCount + "人制定" + mealType + "饭菜清单。家乡:" + hometown
|
||||
+ "。喜好:" + preferences + "。禁忌:" + dietaryRestrictions + "。预算:" + budget
|
||||
+ "元。请按「一、二、三」结构输出,每道菜包含菜品特色、食材清单、制作步骤、营养信息、预算估算。";
|
||||
|
||||
return completionService.chat(systemPrompt, userPrompt, TEMPERATURE, MAX_TOKENS, apiKey);
|
||||
}
|
||||
|
||||
/** 饭菜规划参数,与 Web 端一致 */
|
||||
public static class MealPlanParams {
|
||||
private String regionType;
|
||||
private String dinerCount;
|
||||
private String mealType;
|
||||
private String hometown;
|
||||
private String preferences;
|
||||
private String dietaryRestrictions;
|
||||
private String budget;
|
||||
|
||||
public String getRegionType() { return regionType; }
|
||||
public void setRegionType(String regionType) { this.regionType = regionType; }
|
||||
public String getDinerCount() { return dinerCount; }
|
||||
public void setDinerCount(String dinerCount) { this.dinerCount = dinerCount; }
|
||||
public String getMealType() { return mealType; }
|
||||
public void setMealType(String mealType) { this.mealType = mealType; }
|
||||
public String getHometown() { return hometown; }
|
||||
public void setHometown(String hometown) { this.hometown = hometown; }
|
||||
public String getPreferences() { return preferences; }
|
||||
public void setPreferences(String preferences) { this.preferences = preferences; }
|
||||
public String getDietaryRestrictions() { return dietaryRestrictions; }
|
||||
public void setDietaryRestrictions(String dietaryRestrictions) { this.dietaryRestrictions = dietaryRestrictions; }
|
||||
public String getBudget() { return budget; }
|
||||
public void setBudget(String budget) { this.budget = budget; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.ruilaizi.example.data.meal;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.ruilaizi.example.data.api.ApiKeyProvider;
|
||||
import com.ruilaizi.example.data.api.OpenAICompletionService;
|
||||
|
||||
/**
|
||||
* 饭菜规划仓库:本地调用 DeepSeek 生成饭菜清单,与 example 项目提示词优化/生成同构。
|
||||
*/
|
||||
public class MealPlanningRepository {
|
||||
|
||||
private final ApiKeyProvider apiKeyProvider;
|
||||
private final MealPlanGenerator generator;
|
||||
|
||||
public MealPlanningRepository(Context context) {
|
||||
apiKeyProvider = new ApiKeyProvider(context);
|
||||
OpenAICompletionService completionService = new OpenAICompletionService(null);
|
||||
generator = new MealPlanGenerator(completionService, apiKeyProvider);
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKeyProvider.getApiKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成饭菜规划。需在后台线程调用。
|
||||
*/
|
||||
public String generateMealPlan(MealPlanGenerator.MealPlanParams params) throws Exception {
|
||||
return generator.generate(params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.ruilaizi.example.ui;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.ruilaizi.example.data.meal.MealPlanGenerator;
|
||||
import com.ruilaizi.example.data.meal.MealPlanningRepository;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* 饭菜规划页 ViewModel:参数、生成、结果、加载与错误。
|
||||
*/
|
||||
public class MealPlanningViewModel extends AndroidViewModel {
|
||||
|
||||
private final MealPlanningRepository repository;
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final MutableLiveData<String> resultLiveData = new MutableLiveData<>("");
|
||||
private final MutableLiveData<Boolean> loadingLiveData = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();
|
||||
private final MutableLiveData<String> snackbarLiveData = new MutableLiveData<>();
|
||||
|
||||
public MealPlanningViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
repository = new MealPlanningRepository(application);
|
||||
}
|
||||
|
||||
public LiveData<String> getResultLiveData() { return resultLiveData; }
|
||||
public LiveData<Boolean> getLoadingLiveData() { return loadingLiveData; }
|
||||
public LiveData<String> getErrorLiveData() { return errorLiveData; }
|
||||
public LiveData<String> getSnackbarLiveData() { return snackbarLiveData; }
|
||||
|
||||
/**
|
||||
* 生成饭菜规划。参数由调用方从界面读取并传入。
|
||||
*/
|
||||
public void generate(MealPlanGenerator.MealPlanParams params) {
|
||||
if (repository.getApiKey() == null || repository.getApiKey().isEmpty()) {
|
||||
errorLiveData.setValue("请先在主界面设置中配置 API Key");
|
||||
return;
|
||||
}
|
||||
loadingLiveData.setValue(true);
|
||||
resultLiveData.setValue("");
|
||||
errorLiveData.setValue(null);
|
||||
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
final String plan = repository.generateMealPlan(params);
|
||||
resultLiveData.postValue(plan);
|
||||
snackbarLiveData.postValue("饭菜规划生成完成");
|
||||
} catch (Exception e) {
|
||||
errorLiveData.postValue(e != null ? e.getMessage() : "生成失败");
|
||||
} finally {
|
||||
loadingLiveData.postValue(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void clearError() {
|
||||
errorLiveData.setValue(null);
|
||||
}
|
||||
|
||||
public void clearSnackbar() {
|
||||
snackbarLiveData.setValue(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
executor.shutdown();
|
||||
super.onCleared();
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,13 @@
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_meal_planning"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/btn_meal_planning"
|
||||
android:layout_marginEnd="8dp" />
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_history"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
|
||||
165
example/app/src/main/res/layout/activity_meal_planning.xml
Normal file
165
example/app/src/main/res/layout/activity_meal_planning.xml
Normal file
@@ -0,0 +1,165 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/colorSurface"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- 标题栏:返回 + 标题 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="16dp">
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_back"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="返回" />
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/meal_planning_title"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:gravity="center" />
|
||||
<View android:layout_width="48dp" android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 规划参数卡片 -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardBackgroundColor="@color/colorCard"
|
||||
app:cardElevation="2dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="规划参数"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<!-- 地区类型 -->
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="地区类型" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||
<Spinner android:id="@+id/spinner_region" android:layout_width="match_parent" android:layout_height="48dp" android:layout_marginBottom="12dp" />
|
||||
|
||||
<!-- 就餐人数 -->
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="就餐人数" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||
<Spinner android:id="@+id/spinner_diner" android:layout_width="match_parent" android:layout_height="48dp" android:layout_marginBottom="12dp" />
|
||||
|
||||
<!-- 用餐类型 -->
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="用餐类型" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||
<Spinner android:id="@+id/spinner_meal_type" android:layout_width="match_parent" android:layout_height="48dp" android:layout_marginBottom="12dp" />
|
||||
|
||||
<!-- 用餐者家乡 * -->
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="用餐者家乡 *" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_hometown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="如:四川成都"
|
||||
android:inputType="text"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<!-- 个人喜好 -->
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="个人喜好" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_preferences"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minLines="2"
|
||||
android:hint="如:喜欢辣味、偏爱素食"
|
||||
android:inputType="textMultiLine"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<!-- 饮食禁忌 -->
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="饮食禁忌" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_dietary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minLines="2"
|
||||
android:hint="如:不吃猪肉、对花生过敏"
|
||||
android:inputType="textMultiLine"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<!-- 预算 -->
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="预算范围(元)" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||
<Spinner android:id="@+id/spinner_budget" android:layout_width="match_parent" android:layout_height="48dp" android:layout_marginBottom="16dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_generate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:text="@string/meal_planning_btn_generate"
|
||||
app:backgroundTint="@color/colorPrimary"
|
||||
android:textColor="@color/colorWhite" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="规划结果" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="8dp" />
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardBackgroundColor="@color/colorCard"
|
||||
app:cardElevation="2dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
android:minHeight="100dp"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true"
|
||||
android:padding="16dp">
|
||||
<TextView
|
||||
android:id="@+id/tv_result"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/meal_planning_result_placeholder"
|
||||
android:textColor="@color/colorText"
|
||||
android:textSize="14sp"
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
tools:text="一、菜品推荐与搭配思路\n…" />
|
||||
</ScrollView>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_copy"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/btn_copy" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -1,5 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- 饭菜规划:地区类型 -->
|
||||
<string-array name="meal_region_types">
|
||||
<item>全国</item><item>北方</item><item>南方</item><item>川菜</item><item>粤菜</item>
|
||||
<item>鲁菜</item><item>苏菜</item><item>浙菜</item><item>闽菜</item><item>湘菜</item><item>徽菜</item>
|
||||
</string-array>
|
||||
<!-- 就餐人数 -->
|
||||
<string-array name="meal_diner_counts">
|
||||
<item>1人</item><item>2人</item><item>3人</item><item>4人</item><item>5人</item>
|
||||
<item>6人</item><item>8人</item><item>10人</item>
|
||||
</string-array>
|
||||
<!-- 用餐类型 -->
|
||||
<string-array name="meal_types">
|
||||
<item>早餐</item><item>午餐</item><item>晚餐</item><item>全天</item>
|
||||
</string-array>
|
||||
<!-- 预算(元) -->
|
||||
<string-array name="meal_budgets">
|
||||
<item>50元以下</item><item>50-100元</item><item>100-150元</item><item>150-200元</item>
|
||||
<item>200-300元</item><item>300-500元</item><item>500元以上</item>
|
||||
</string-array>
|
||||
<declare-styleable name="CircleImageView">
|
||||
<attr name="border_width" format="dimension" />
|
||||
<attr name="border_color" format="color" />
|
||||
|
||||
@@ -32,4 +32,8 @@
|
||||
<string name="history_empty_generation">暂无生成记录</string>
|
||||
<string name="history_empty_direct_answer">暂无直答记录</string>
|
||||
<string name="history_use">使用</string>
|
||||
<string name="meal_planning_title">智能饭菜规划</string>
|
||||
<string name="meal_planning_btn_generate">生成饭菜规划</string>
|
||||
<string name="meal_planning_result_placeholder">填写左侧参数后点击「生成饭菜规划」</string>
|
||||
<string name="btn_meal_planning">饭菜规划</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user