小程序初始提交
This commit is contained in:
159
cool-unix/pages/weeklyreport/detail.uvue
Normal file
159
cool-unix/pages/weeklyreport/detail.uvue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<cl-page :title="'\u5468\u62a5\u8BE6\u60C5'">
|
||||
<view v-if="loading" class="flex justify-center items-center py-20">
|
||||
<cl-loading />
|
||||
</view>
|
||||
|
||||
<view v-else-if="report" class="p-4">
|
||||
<!-- header -->
|
||||
<view class="mb-6">
|
||||
<view class="flex justify-between items-center mb-2">
|
||||
<text class="text-2xl font-bold">{{ formatWeekRange(report.weekStartDate, report.weekEndDate) }}</text>
|
||||
<cl-tag :type="report.status === 1 ? 'success' : 'warning'">{{ report.status === 1 ? '\u5DF2\u63D0\u4EA4' : '\u8349\u7A3F' }}</cl-tag>
|
||||
</view>
|
||||
<text class="text-sm text-gray-500">{{ report.submitTime ? ('\u63D0\u4EA4\u4E8E ' + formatDateTime(report.submitTime)) : ('\u521B\u5EFA\u4E8E ' + formatDateTime(report.createTime)) }}</text>
|
||||
</view>
|
||||
|
||||
<!-- body content -->
|
||||
<view class="mb-6">
|
||||
<text class="text-base font-bold mb-3 block">{{ '\u5468\u62A5\u5185\u5BB9' }}</text>
|
||||
<view class="p-4 bg-white rounded-lg shadow-sm">
|
||||
<text class="text-sm text-gray-800">{{ report.userEditedContent || '\u6682\u65E0\u5185\u5BB9' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- original and ai sections (no cl-collapse) -->
|
||||
<view v-if="report.originalText" class="mb-4">
|
||||
<text class="text-base font-bold mb-2">{{ '\u539F\u59CB\u8F93\u5165\u5185\u5BB9' }}</text>
|
||||
<view class="p-3 bg-gray-50 rounded">
|
||||
<text class="text-sm text-gray-700">{{ report.originalText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="report.aiFormattedContent" class="mb-4">
|
||||
<text class="text-base font-bold mb-2">{{ 'AI\u751F\u6210\u5185\u5BB9' }}</text>
|
||||
<view class="p-3 bg-blue-50 rounded">
|
||||
<text class="text-sm text-gray-800">{{ report.aiFormattedContent }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- actions -->
|
||||
<view class="flex gap-3 mt-6">
|
||||
<cl-button type="primary" size="large" :flex="1" @tap="editReport">{{ '\u7F16\u8F91\u5468\u62A5' }}</cl-button>
|
||||
<cl-button v-if="report.status === 0" type="success" size="large" :flex="1" @tap="submitReport" :loading="isSubmitting">{{ isSubmitting ? '\u63D0\u4EA4\u4E2D...' : '\u63D0\u4EA4\u5468\u62A5' }}</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="flex flex-col items-center justify-center py-20">
|
||||
<text class="text-6xl mb-4">{{ '\u270F\uFE0F' }}</text>
|
||||
<text class="text-gray-400 text-base">{{ '\u5468\u62A5\u4E0D\u5B58\u5728' }}</text>
|
||||
<cl-button type="primary" size="small" class="mt-4" @tap="router.back()">{{ '\u8FD4\u56DE\u5217\u8868' }}</cl-button>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { request, router, useStore } from '@/cool';
|
||||
import { useUi } from '@/uni_modules/cool-ui';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
const report = ref<any>(null);
|
||||
const loading = ref(true);
|
||||
const isSubmitting = ref(false);
|
||||
const userId = ref(0);
|
||||
const reportId = ref('');
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
reportId.value = (options?.id ?? '').toString();
|
||||
if (user.token) {
|
||||
try { await user.get(); } catch {}
|
||||
}
|
||||
if (user.info.value && user.info.value.id) {
|
||||
userId.value = user.info.value.id;
|
||||
} else {
|
||||
ui.showToast({ message: '\u8BF7\u5148\u767B\u5F55', type: 'error' });
|
||||
setTimeout(() => router.to('/pages/user/login'), 800);
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
if (reportId.value) {
|
||||
await loadReportDetail();
|
||||
} else {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
async function loadReportDetail() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request({ url: '/app/weeklyreport/report/detail', method: 'GET', params: { id: reportId.value, userId: userId.value } });
|
||||
if (res) report.value = res;
|
||||
} catch (e: any) {
|
||||
ui.showToast({ message: '\u52A0\u8F7D\u5931\u8D25: ' + (e.message || '\u672A\u77E5\u9519\u8BEF'), type: 'error' });
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function editReport() {
|
||||
router.to(`/pages/weeklyreport/submit?id=${reportId.value}`);
|
||||
}
|
||||
|
||||
async function submitReport() {
|
||||
if (!report.value) return;
|
||||
isSubmitting.value = true;
|
||||
try {
|
||||
await request({
|
||||
url: '/app/weeklyreport/report/submit',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: userId.value,
|
||||
weekStartDate: report.value.weekStartDate,
|
||||
weekEndDate: report.value.weekEndDate,
|
||||
originalText: report.value.originalText,
|
||||
aiFormattedContent: report.value.aiFormattedContent,
|
||||
userEditedContent: report.value.userEditedContent,
|
||||
inputType: report.value.inputType
|
||||
}
|
||||
});
|
||||
ui.showToast({ message: '\u5468\u62A5\u63D0\u4EA4\u6210\u529F', type: 'success' });
|
||||
setTimeout(() => { loadReportDetail(); }, 700);
|
||||
} catch (e: any) {
|
||||
ui.showToast({ message: '\u63D0\u4EA4\u5931\u8D25: ' + (e.message || '\u672A\u77E5\u9519\u8BEF'), type: 'error' });
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function formatWeekRange(startDate: string, endDate: string) {
|
||||
if (!startDate || !endDate) return '';
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
const sy = start.getFullYear();
|
||||
const sm = String(start.getMonth() + 1).padStart(2, '0');
|
||||
const sd = String(start.getDate()).padStart(2, '0');
|
||||
const ey = end.getFullYear();
|
||||
const em = String(end.getMonth() + 1).padStart(2, '0');
|
||||
const ed = String(end.getDate()).padStart(2, '0');
|
||||
return sy === ey ? `${sy}-${sm}-${sd} ~ ${em}-${ed}` : `${sy}-${sm}-${sd} ~ ${ey}-${em}-${ed}`;
|
||||
}
|
||||
|
||||
function formatDateTime(timeStr: string) {
|
||||
if (!timeStr) return '';
|
||||
const d = new Date(timeStr);
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(d.getDate()).padStart(2, '0');
|
||||
const hh = String(d.getHours()).padStart(2, '0');
|
||||
const mm = String(d.getMinutes()).padStart(2, '0');
|
||||
return `${y}-${m}-${dd} ${hh}:${mm}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.gap-3 { gap: 0.75rem; }
|
||||
</style>
|
||||
323
cool-unix/pages/weeklyreport/list.uvue
Normal file
323
cool-unix/pages/weeklyreport/list.uvue
Normal file
@@ -0,0 +1,323 @@
|
||||
<template>
|
||||
<cl-page title="我的周报">
|
||||
<!-- 筛选栏 -->
|
||||
<view class="px-4 py-3 bg-white border-b">
|
||||
<view class="flex gap-2">
|
||||
<cl-button
|
||||
:type="filterType === 'all' ? 'primary' : 'default'"
|
||||
size="small"
|
||||
@tap="changeFilter('all')"
|
||||
>
|
||||
全部
|
||||
</cl-button>
|
||||
<cl-button
|
||||
:type="filterType === 'draft' ? 'primary' : 'default'"
|
||||
size="small"
|
||||
@tap="changeFilter('draft')"
|
||||
>
|
||||
草稿
|
||||
</cl-button>
|
||||
<cl-button
|
||||
:type="filterType === 'submitted' ? 'primary' : 'default'"
|
||||
size="small"
|
||||
@tap="changeFilter('submitted')"
|
||||
>
|
||||
已提交
|
||||
</cl-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 周报列表 -->
|
||||
<scroll-view scroll-y class="flex-1">
|
||||
<!-- 加载中 -->
|
||||
<view v-if="loading && list.length === 0" class="flex items-center justify-center py-20">
|
||||
<text class="text-gray-400">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<view v-else-if="list.length > 0">
|
||||
<view
|
||||
v-for="(item, index) in list"
|
||||
:key="item.id || index"
|
||||
class="mx-4 my-2 p-4 bg-white rounded-lg shadow-sm"
|
||||
@tap="toDetail(item)"
|
||||
>
|
||||
<!-- 头部:周范围和状态 -->
|
||||
<view class="flex justify-between items-center mb-3">
|
||||
<view class="flex items-center gap-2">
|
||||
<text class="text-lg font-bold">
|
||||
{{ formatWeekRange(item.weekStartDate, item.weekEndDate) }}
|
||||
</text>
|
||||
</view>
|
||||
<view
|
||||
:class="[
|
||||
'px-2 py-1 rounded text-xs',
|
||||
item.status === 1 ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700'
|
||||
]"
|
||||
>
|
||||
{{ item.status === 1 ? "已提交" : "草稿" }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 内容摘要 -->
|
||||
<view class="mb-3">
|
||||
<text class="text-sm text-gray-700">
|
||||
{{ getContentPreview(item.userEditedContent) }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<view class="flex justify-between items-center text-xs text-gray-500">
|
||||
<view class="flex items-center gap-2">
|
||||
<text>{{ item.inputType === 1 ? "🎤 语音输入" : "⌨️ 文字输入" }}</text>
|
||||
</view>
|
||||
<text v-if="item.submitTime">
|
||||
提交于 {{ formatTime(item.submitTime) }}
|
||||
</text>
|
||||
<text v-else-if="item.createTime">
|
||||
创建于 {{ formatTime(item.createTime) }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多提示 -->
|
||||
<view v-if="loading" class="flex items-center justify-center py-4">
|
||||
<text class="text-gray-400 text-sm">加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="finished" class="flex items-center justify-center py-4">
|
||||
<text class="text-gray-400 text-sm">没有更多了</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else class="flex flex-col items-center justify-center py-20">
|
||||
<text class="text-6xl mb-4">📝</text>
|
||||
<text class="text-gray-400 text-base">暂无周报</text>
|
||||
<view
|
||||
class="mt-4 px-4 py-2 bg-blue-500 text-white rounded"
|
||||
@tap="toSubmit"
|
||||
>
|
||||
<text>去提交周报</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 悬浮提交按钮 -->
|
||||
<view class="fixed bottom-20 right-4">
|
||||
<cl-button
|
||||
type="primary"
|
||||
size="large"
|
||||
round
|
||||
@tap="toSubmit"
|
||||
>
|
||||
<text class="text-2xl">✏️</text>
|
||||
</cl-button>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { request, router, useStore } from "@/cool";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
// 列表相关
|
||||
const list = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
const finished = ref(false);
|
||||
const page = ref(1);
|
||||
const pageSize = ref(20);
|
||||
|
||||
// 筛选类型
|
||||
const filterType = ref<"all" | "draft" | "submitted">("all");
|
||||
|
||||
// 用户ID(从登录状态获取)
|
||||
const userId = ref(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);
|
||||
|
||||
if (user.info.value && user.info.value.id) {
|
||||
userId.value = user.info.value.id;
|
||||
console.log("【周报列表】设置userId:", userId.value);
|
||||
loadReports();
|
||||
} else {
|
||||
console.error("【周报列表】用户未登录或用户信息为空");
|
||||
ui.showToast({
|
||||
message: "请先登录",
|
||||
type: "error"
|
||||
});
|
||||
setTimeout(() => {
|
||||
router.to("/pages/user/login");
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
// 加载周报列表
|
||||
async function loadReports() {
|
||||
if (loading.value || finished.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
console.log("【周报列表】开始加载, userId:", userId.value);
|
||||
|
||||
if (!userId.value || userId.value === 0) {
|
||||
console.error("【周报列表】userId无效:", userId.value);
|
||||
throw new Error("用户ID无效,请重新登录");
|
||||
}
|
||||
|
||||
const params: any = {
|
||||
userId: userId.value,
|
||||
page: page.value,
|
||||
size: pageSize.value
|
||||
};
|
||||
|
||||
// 添加状态筛选
|
||||
if (filterType.value === "draft") {
|
||||
params.status = 0;
|
||||
} else if (filterType.value === "submitted") {
|
||||
params.status = 1;
|
||||
}
|
||||
|
||||
console.log("【周报列表】请求参数:", params);
|
||||
|
||||
const res = await request({
|
||||
url: "/app/weeklyreport/report/myReports",
|
||||
method: "GET",
|
||||
params
|
||||
});
|
||||
|
||||
console.log("【周报列表】响应数据:", res);
|
||||
console.log("【周报列表】响应类型:", typeof res);
|
||||
console.log("【周报列表】是否为数组:", Array.isArray(res));
|
||||
console.log("【周报列表】数据长度:", res ? res.length : 0);
|
||||
|
||||
if (res && res.length > 0) {
|
||||
console.log("【周报列表】开始填充列表数据");
|
||||
if (page.value === 1) {
|
||||
list.value = res;
|
||||
} else {
|
||||
list.value.push(...res);
|
||||
}
|
||||
page.value++;
|
||||
|
||||
console.log("【周报列表】列表已更新, 当前list长度:", list.value.length);
|
||||
console.log("【周报列表】列表第一项数据:", list.value[0]);
|
||||
|
||||
// 如果返回数据少于每页大小,说明已经到底了
|
||||
if (res.length < pageSize.value) {
|
||||
finished.value = true;
|
||||
}
|
||||
} else {
|
||||
console.log("【周报列表】无数据或数据为空");
|
||||
// 即使没有数据,也不应该标记为finished,可能只是筛选条件没有匹配的数据
|
||||
if (page.value === 1) {
|
||||
list.value = [];
|
||||
}
|
||||
finished.value = true;
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("加载周报列表失败:", error);
|
||||
ui.showToast({
|
||||
message: "加载失败: " + (error.message || "未知错误"),
|
||||
type: "error"
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
function onRefresh() {
|
||||
console.log("【周报列表】下拉刷新");
|
||||
page.value = 1;
|
||||
finished.value = false;
|
||||
list.value = [];
|
||||
loadReports();
|
||||
}
|
||||
|
||||
// 切换筛选条件
|
||||
function changeFilter(type: "all" | "draft" | "submitted") {
|
||||
console.log("【周报列表】切换筛选:", type);
|
||||
filterType.value = type;
|
||||
page.value = 1;
|
||||
finished.value = false;
|
||||
list.value = [];
|
||||
loadReports();
|
||||
}
|
||||
|
||||
// 跳转到详情
|
||||
function toDetail(item: any) {
|
||||
router.to(`/pages/weeklyreport/detail?id=${item.id}`);
|
||||
}
|
||||
|
||||
// 跳转到提交页面
|
||||
function toSubmit() {
|
||||
router.to("/pages/weeklyreport/submit");
|
||||
}
|
||||
|
||||
// 格式化周范围
|
||||
function formatWeekRange(startDate: string, endDate: string) {
|
||||
if (!startDate || !endDate) return "";
|
||||
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
|
||||
const startMonth = String(start.getMonth() + 1).padStart(2, "0");
|
||||
const startDay = String(start.getDate()).padStart(2, "0");
|
||||
const endMonth = String(end.getMonth() + 1).padStart(2, "0");
|
||||
const endDay = String(end.getDate()).padStart(2, "0");
|
||||
|
||||
return `${startMonth}月${startDay}日 - ${endMonth}月${endDay}日`;
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
function formatTime(timeStr: string) {
|
||||
if (!timeStr) return "";
|
||||
const date = new Date(timeStr);
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
const hours = String(date.getHours()).padStart(2, "0");
|
||||
const minutes = String(date.getMinutes()).padStart(2, "0");
|
||||
return `${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
// 获取内容摘要
|
||||
function getContentPreview(content: string) {
|
||||
if (!content) return "(无内容)";
|
||||
// 移除 Markdown 标记
|
||||
const plain = content.replace(/[#*`\[\]()]/g, "").trim();
|
||||
// 限制长度
|
||||
return plain.length > 100 ? plain.substring(0, 100) + "..." : plain;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.line-clamp-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
512
cool-unix/pages/weeklyreport/submit.uvue
Normal file
512
cool-unix/pages/weeklyreport/submit.uvue
Normal file
@@ -0,0 +1,512 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user