513 lines
13 KiB
Plaintext
513 lines
13 KiB
Plaintext
|
|
<template>
|
|||
|
|
<cl-page title="提交周报">
|
|||
|
|
<view class="p-4">
|
|||
|
|
<!-- 周范围选择 -->
|
|||
|
|
<view class="mb-4">
|
|||
|
|
<text class="text-base font-bold mb-2">周报周期</text>
|
|||
|
|
<view class="flex gap-2 items-center">
|
|||
|
|
<cl-input
|
|||
|
|
v-model="form.weekStartDate"
|
|||
|
|
type="date"
|
|||
|
|
placeholder="周开始日期"
|
|||
|
|
:max="todayDate"
|
|||
|
|
/>
|
|||
|
|
<text>至</text>
|
|||
|
|
<cl-input
|
|||
|
|
v-model="form.weekEndDate"
|
|||
|
|
type="date"
|
|||
|
|
placeholder="周结束日期"
|
|||
|
|
:max="todayDate"
|
|||
|
|
/>
|
|||
|
|
</view>
|
|||
|
|
<view class="mt-2">
|
|||
|
|
<cl-button
|
|||
|
|
type="primary"
|
|||
|
|
size="small"
|
|||
|
|
@tap="setCurrentWeek"
|
|||
|
|
>
|
|||
|
|
设置为本周
|
|||
|
|
</cl-button>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 输入方式选择 -->
|
|||
|
|
<view class="mb-4">
|
|||
|
|
<text class="text-base font-bold mb-2">输入方式</text>
|
|||
|
|
<view class="flex gap-2">
|
|||
|
|
<cl-button
|
|||
|
|
:type="inputType === 0 ? 'primary' : 'default'"
|
|||
|
|
size="small"
|
|||
|
|
@tap="inputType = 0"
|
|||
|
|
>
|
|||
|
|
文字输入
|
|||
|
|
</cl-button>
|
|||
|
|
<cl-button
|
|||
|
|
:type="inputType === 1 ? 'primary' : 'default'"
|
|||
|
|
size="small"
|
|||
|
|
@tap="inputType = 1"
|
|||
|
|
>
|
|||
|
|
语音输入
|
|||
|
|
</cl-button>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 语音输入区域 -->
|
|||
|
|
<view v-if="inputType === 1" class="mb-4">
|
|||
|
|
<text class="text-base font-bold mb-2">语音录制</text>
|
|||
|
|
<view class="flex flex-col items-center p-6 bg-gray-50 rounded-lg">
|
|||
|
|
<text class="text-gray-500 mb-4">语音录制功能暂未在H5环境实现</text>
|
|||
|
|
<text class="text-gray-500 mb-4">请切换到文字输入</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 文字输入区域 -->
|
|||
|
|
<view v-if="inputType === 0" class="mb-4">
|
|||
|
|
<view class="p-4 bg-white rounded-2xl">
|
|||
|
|
<cl-form>
|
|||
|
|
<cl-form-item label="工作内容" required>
|
|||
|
|
<cl-input
|
|||
|
|
v-model="originalText"
|
|||
|
|
placeholder="请输入本周的工作内容"
|
|||
|
|
:maxlength="2000"
|
|||
|
|
/>
|
|||
|
|
</cl-form-item>
|
|||
|
|
</cl-form>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- AI格式化按钮 -->
|
|||
|
|
<view v-if="originalText" class="mb-4">
|
|||
|
|
<cl-button
|
|||
|
|
type="success"
|
|||
|
|
size="large"
|
|||
|
|
block
|
|||
|
|
@tap="formatWithAI"
|
|||
|
|
:loading="isFormatting"
|
|||
|
|
>
|
|||
|
|
{{ isFormatting ? 'AI生成中...' : '🤖 AI生成周报' }}
|
|||
|
|
</cl-button>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- AI生成的周报内容 -->
|
|||
|
|
<view v-if="aiFormattedContent" class="mb-4">
|
|||
|
|
<text class="text-base font-bold mb-2">AI生成的周报</text>
|
|||
|
|
<view class="p-3 bg-green-50 rounded-lg mb-2">
|
|||
|
|
<cl-markdown :content="aiFormattedContent" />
|
|||
|
|
</view>
|
|||
|
|
<text class="text-sm text-gray-500">您可以在下方编辑最终内容</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 最终编辑区域 -->
|
|||
|
|
<view v-if="aiFormattedContent" class="mb-4">
|
|||
|
|
<view class="p-4 bg-white rounded-2xl">
|
|||
|
|
<cl-form>
|
|||
|
|
<cl-form-item label="最终周报内容" required>
|
|||
|
|
<cl-input
|
|||
|
|
v-model="userEditedContent"
|
|||
|
|
placeholder="请编辑最终的周报内容"
|
|||
|
|
:maxlength="3000"
|
|||
|
|
/>
|
|||
|
|
</cl-form-item>
|
|||
|
|
</cl-form>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 从日报自动生成 -->
|
|||
|
|
<view class="mb-4">
|
|||
|
|
<cl-button
|
|||
|
|
type="primary"
|
|||
|
|
size="large"
|
|||
|
|
block
|
|||
|
|
@tap="generateFromDaily"
|
|||
|
|
:loading="isGenerating"
|
|||
|
|
>
|
|||
|
|
{{ isGenerating ? '生成中..' : '📝 从我的日报生成' }}
|
|||
|
|
</cl-button>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 操作按钮 -->
|
|||
|
|
<view v-if="userEditedContent" class="flex gap-2 mt-6">
|
|||
|
|
<cl-button
|
|||
|
|
type="default"
|
|||
|
|
size="large"
|
|||
|
|
:flex="1"
|
|||
|
|
@tap="saveDraft"
|
|||
|
|
:loading="isSavingDraft"
|
|||
|
|
>
|
|||
|
|
{{ isSavingDraft ? '保存中...' : '保存草稿' }}
|
|||
|
|
</cl-button>
|
|||
|
|
<cl-button
|
|||
|
|
type="primary"
|
|||
|
|
size="large"
|
|||
|
|
:flex="1"
|
|||
|
|
@tap="submitReport"
|
|||
|
|
:loading="isSubmitting"
|
|||
|
|
>
|
|||
|
|
{{ isSubmitting ? '提交中...' : '提交周报' }}
|
|||
|
|
</cl-button>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</cl-page>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { ref, computed, onMounted } from "vue";
|
|||
|
|
import { request, router, useStore } from "@/cool";
|
|||
|
|
import { useUi } from "@/uni_modules/cool-ui";
|
|||
|
|
|
|||
|
|
const ui = useUi();
|
|||
|
|
const { user } = useStore();
|
|||
|
|
|
|||
|
|
// 表单数据
|
|||
|
|
const form = ref({
|
|||
|
|
weekStartDate: "",
|
|||
|
|
weekEndDate: "",
|
|||
|
|
userId: 0
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 输入方式:0-文字,1-语音
|
|||
|
|
const inputType = ref<number>(0);
|
|||
|
|
|
|||
|
|
// 文字输入
|
|||
|
|
const originalText = ref("");
|
|||
|
|
|
|||
|
|
// AI格式化相关
|
|||
|
|
const isFormatting = ref(false);
|
|||
|
|
const aiFormattedContent = ref("");
|
|||
|
|
const userEditedContent = ref("");
|
|||
|
|
|
|||
|
|
// 按日报生成相关
|
|||
|
|
const isGenerating = ref(false);
|
|||
|
|
|
|||
|
|
// 提交相关
|
|||
|
|
const isSavingDraft = ref(false);
|
|||
|
|
const isSubmitting = ref(false);
|
|||
|
|
|
|||
|
|
// 今天的日期
|
|||
|
|
const todayDate = computed(() => {
|
|||
|
|
const today = new Date();
|
|||
|
|
return `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
onMounted(async () => {
|
|||
|
|
console.log("【周报提交】页面加载, user.info:", user.info.value);
|
|||
|
|
|
|||
|
|
// 获取用户信息
|
|||
|
|
if (user.token) {
|
|||
|
|
try {
|
|||
|
|
await user.get();
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error("【周报提交】获取用户信息失败:", e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log("【周报提交】获取用户信息后, user.info:", user.info.value);
|
|||
|
|
|
|||
|
|
// 获取当前登录用户ID
|
|||
|
|
if (user.info.value && user.info.value.id) {
|
|||
|
|
form.value.userId = user.info.value.id;
|
|||
|
|
console.log("【周报提交】设置userId:", form.value.userId);
|
|||
|
|
} else {
|
|||
|
|
console.error("【周报提交】用户未登录或用户信息为空");
|
|||
|
|
ui.showToast({
|
|||
|
|
message: "请先登录",
|
|||
|
|
type: "error"
|
|||
|
|
});
|
|||
|
|
setTimeout(() => {
|
|||
|
|
router.to("/pages/user/login");
|
|||
|
|
}, 1000);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置默认为本周
|
|||
|
|
setCurrentWeek();
|
|||
|
|
|
|||
|
|
// 检查本周是否已有周报
|
|||
|
|
checkCurrentWeekReport();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 设置为本周
|
|||
|
|
function setCurrentWeek() {
|
|||
|
|
const today = new Date();
|
|||
|
|
const dayOfWeek = today.getDay();
|
|||
|
|
|
|||
|
|
// 计算周一(如果今天是周日,dayOfWeek=0,需要特殊处理)
|
|||
|
|
const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
|
|||
|
|
const monday = new Date(today);
|
|||
|
|
monday.setDate(today.getDate() + diff);
|
|||
|
|
|
|||
|
|
// 计算周日
|
|||
|
|
const sunday = new Date(monday);
|
|||
|
|
sunday.setDate(monday.getDate() + 6);
|
|||
|
|
|
|||
|
|
form.value.weekStartDate = formatDateToString(monday);
|
|||
|
|
form.value.weekEndDate = formatDateToString(sunday);
|
|||
|
|
|
|||
|
|
console.log("【周报提交】设置本周范围:", form.value.weekStartDate, "-", form.value.weekEndDate);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 格式化日期为字符串
|
|||
|
|
function formatDateToString(date: Date) {
|
|||
|
|
const year = date.getFullYear();
|
|||
|
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|||
|
|
const day = String(date.getDate()).padStart(2, "0");
|
|||
|
|
return `${year}-${month}-${day}`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查本周是否已有周报
|
|||
|
|
async function checkCurrentWeekReport() {
|
|||
|
|
try {
|
|||
|
|
const res = await request({
|
|||
|
|
url: "/app/weeklyreport/report/currentWeekReport",
|
|||
|
|
method: "GET",
|
|||
|
|
params: { userId: form.value.userId }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (res && res.id) {
|
|||
|
|
// 本周已有周报,询问是否继续编辑
|
|||
|
|
uni.showModal({
|
|||
|
|
title: "提示",
|
|||
|
|
content: `本周已经${res.status === 1 ? "提交" : "保存"}了周报,是否继续编辑?`,
|
|||
|
|
success: (modalRes) => {
|
|||
|
|
if (modalRes.confirm) {
|
|||
|
|
// 加载已有周报
|
|||
|
|
loadExistingReport(res);
|
|||
|
|
} else {
|
|||
|
|
// 跳转到列表页
|
|||
|
|
router.back();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.log("本周还没有周报,可以新建");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 加载已有周报
|
|||
|
|
function loadExistingReport(report: any) {
|
|||
|
|
originalText.value = report.originalText || "";
|
|||
|
|
aiFormattedContent.value = report.aiFormattedContent || "";
|
|||
|
|
userEditedContent.value = report.userEditedContent || "";
|
|||
|
|
inputType.value = report.inputType || 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// AI格式化
|
|||
|
|
async function formatWithAI() {
|
|||
|
|
const text = originalText.value;
|
|||
|
|
if (!text) {
|
|||
|
|
return ui.showToast({
|
|||
|
|
message: "请先输入内容",
|
|||
|
|
type: "error"
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
isFormatting.value = true;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const res = await request({
|
|||
|
|
url: "/app/weeklyreport/report/aiFormat",
|
|||
|
|
method: "POST",
|
|||
|
|
data: {
|
|||
|
|
originalText: text,
|
|||
|
|
weekStartDate: form.value.weekStartDate,
|
|||
|
|
weekEndDate: form.value.weekEndDate
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 后端返回的是对象 {formattedContent: "内容", length: 数字}
|
|||
|
|
const formattedContent = res.formattedContent || res;
|
|||
|
|
aiFormattedContent.value = formattedContent;
|
|||
|
|
userEditedContent.value = formattedContent;
|
|||
|
|
|
|||
|
|
ui.showToast({
|
|||
|
|
message: "AI生成成功",
|
|||
|
|
type: "success"
|
|||
|
|
});
|
|||
|
|
} catch (error: any) {
|
|||
|
|
console.error("AI格式化失败:", error);
|
|||
|
|
ui.showToast({
|
|||
|
|
message: "AI格式化失败: " + (error.message || "未知错误"),
|
|||
|
|
type: "error"
|
|||
|
|
});
|
|||
|
|
} finally {
|
|||
|
|
isFormatting.value = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 从日报自动生成周报
|
|||
|
|
async function generateFromDaily() {
|
|||
|
|
if (!form.value.weekStartDate || !form.value.weekEndDate) {
|
|||
|
|
return ui.showToast({ message: "请先选择周范围", type: "error" });
|
|||
|
|
}
|
|||
|
|
isGenerating.value = true;
|
|||
|
|
try {
|
|||
|
|
// 优先调用后端聚合接口
|
|||
|
|
try {
|
|||
|
|
const res = await request({
|
|||
|
|
url: "/app/weeklyreport/report/generateFromDaily",
|
|||
|
|
method: "POST",
|
|||
|
|
data: {
|
|||
|
|
userId: form.value.userId,
|
|||
|
|
weekStartDate: form.value.weekStartDate,
|
|||
|
|
weekEndDate: form.value.weekEndDate
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const formatted = res.formattedContent || res;
|
|||
|
|
aiFormattedContent.value = formatted || "";
|
|||
|
|
userEditedContent.value = formatted || "";
|
|||
|
|
ui.showToast({ message: "已从日报生成", type: "success" });
|
|||
|
|
return;
|
|||
|
|
} catch (e1: any) {
|
|||
|
|
console.warn("后端聚合接口不可用,回退到前端聚合:", e1?.message || e1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 回退:前端聚合本周已提交日报,再调用现有AI格式化
|
|||
|
|
const dailyList = await request({
|
|||
|
|
url: "/app/dailyreport/report/myReports",
|
|||
|
|
method: "GET",
|
|||
|
|
params: {
|
|||
|
|
userId: form.value.userId,
|
|||
|
|
startDate: form.value.weekStartDate,
|
|||
|
|
endDate: form.value.weekEndDate,
|
|||
|
|
status: 1
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!Array.isArray(dailyList) || dailyList.length === 0) {
|
|||
|
|
throw new Error("本周没有已提交的日报");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按日期升序拼接内容
|
|||
|
|
dailyList.sort((a: any, b: any) => new Date(a.reportDate).getTime() - new Date(b.reportDate).getTime());
|
|||
|
|
const source = dailyList.map((r: any) => {
|
|||
|
|
const content = r.userEditedContent || r.aiFormattedContent || r.originalText || "";
|
|||
|
|
if (!content) return "";
|
|||
|
|
return `# ${r.reportDate}\n${content}`;
|
|||
|
|
}).filter(Boolean).join("\n\n");
|
|||
|
|
|
|||
|
|
if (!source) {
|
|||
|
|
throw new Error("本周日报内容为空,无法生成周报");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const res2 = await request({
|
|||
|
|
url: "/app/weeklyreport/report/aiFormat",
|
|||
|
|
method: "POST",
|
|||
|
|
data: {
|
|||
|
|
originalText: source,
|
|||
|
|
weekStartDate: form.value.weekStartDate,
|
|||
|
|
weekEndDate: form.value.weekEndDate
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const formatted2 = res2.formattedContent || res2;
|
|||
|
|
aiFormattedContent.value = formatted2 || "";
|
|||
|
|
userEditedContent.value = formatted2 || "";
|
|||
|
|
ui.showToast({ message: "已从日报生成", type: "success" });
|
|||
|
|
} catch (error: any) {
|
|||
|
|
console.error("按日报生成失败:", error);
|
|||
|
|
ui.showToast({ message: "生成失败: " + (error.message || "未知错误"), type: "error" });
|
|||
|
|
} finally {
|
|||
|
|
isGenerating.value = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 保存草稿
|
|||
|
|
async function saveDraft() {
|
|||
|
|
if (!userEditedContent.value) {
|
|||
|
|
return ui.showToast({
|
|||
|
|
message: "请先生成周报内容",
|
|||
|
|
type: "error"
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
isSavingDraft.value = true;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await request({
|
|||
|
|
url: "/app/weeklyreport/report/saveDraft",
|
|||
|
|
method: "POST",
|
|||
|
|
data: {
|
|||
|
|
userId: form.value.userId,
|
|||
|
|
weekStartDate: form.value.weekStartDate,
|
|||
|
|
weekEndDate: form.value.weekEndDate,
|
|||
|
|
originalText: originalText.value,
|
|||
|
|
aiFormattedContent: aiFormattedContent.value,
|
|||
|
|
userEditedContent: userEditedContent.value,
|
|||
|
|
inputType: inputType.value
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
ui.showToast({
|
|||
|
|
message: "草稿保存成功",
|
|||
|
|
type: "success"
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 延迟跳转
|
|||
|
|
setTimeout(() => {
|
|||
|
|
router.back();
|
|||
|
|
}, 1000);
|
|||
|
|
} catch (error: any) {
|
|||
|
|
console.error("保存草稿失败:", error);
|
|||
|
|
ui.showToast({
|
|||
|
|
message: "保存草稿失败: " + (error.message || "未知错误"),
|
|||
|
|
type: "error"
|
|||
|
|
});
|
|||
|
|
} finally {
|
|||
|
|
isSavingDraft.value = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 提交周报
|
|||
|
|
async function submitReport() {
|
|||
|
|
if (!userEditedContent.value) {
|
|||
|
|
return ui.showToast({
|
|||
|
|
message: "请先生成周报内容",
|
|||
|
|
type: "error"
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
isSubmitting.value = true;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await request({
|
|||
|
|
url: "/app/weeklyreport/report/submit",
|
|||
|
|
method: "POST",
|
|||
|
|
data: {
|
|||
|
|
userId: form.value.userId,
|
|||
|
|
weekStartDate: form.value.weekStartDate,
|
|||
|
|
weekEndDate: form.value.weekEndDate,
|
|||
|
|
originalText: originalText.value,
|
|||
|
|
aiFormattedContent: aiFormattedContent.value,
|
|||
|
|
userEditedContent: userEditedContent.value,
|
|||
|
|
inputType: inputType.value
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
ui.showToast({
|
|||
|
|
message: "周报提交成功",
|
|||
|
|
type: "success"
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 延迟跳转
|
|||
|
|
setTimeout(() => {
|
|||
|
|
router.back();
|
|||
|
|
}, 1000);
|
|||
|
|
} catch (error: any) {
|
|||
|
|
console.error("提交周报失败:", error);
|
|||
|
|
ui.showToast({
|
|||
|
|
message: "提交周报失败: " + (error.message || "未知错误"),
|
|||
|
|
type: "error"
|
|||
|
|
});
|
|||
|
|
} finally {
|
|||
|
|
isSubmitting.value = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.gap-2 {
|
|||
|
|
gap: 0.5rem;
|
|||
|
|
}
|
|||
|
|
</style>
|