更新日报相关功能

This commit is contained in:
hero920103 committed 2026-10-22 17:55:50 +08:00
1 parent 51fd90fc51
commit 68cdaf38fa
23 files changed
+2313 -1056

No files matched your search

@@ -69,15 +69,51 @@
</div>
<section class="detail-section">
<h4>今日完成</h4>
<h4 class="section-title">
<span>今日完成</span>
<el-button
link
type="primary"
size="small"
:icon="CopyDocument"
:disabled="!detail.report.today_done?.trim()"
@click="copySection('今日完成', detail.report.today_done)"
>
复制
</el-button>
</h4>
<div class="section-text">{{ detail.report.today_done || '—' }}</div>
</section>
<section class="detail-section">
<h4>明日计划</h4>
<h4 class="section-title">
<span>明日计划</span>
<el-button
link
type="primary"
size="small"
:icon="CopyDocument"
:disabled="!detail.report.tomorrow_plan?.trim()"
@click="copySection('明日计划', detail.report.tomorrow_plan)"
>
复制
</el-button>
</h4>
<div class="section-text">{{ detail.report.tomorrow_plan || '—' }}</div>
</section>
<section class="detail-section">
<h4>问题与建议</h4>
<h4 class="section-title">
<span>问题与建议</span>
<el-button
link
type="primary"
size="small"
:icon="CopyDocument"
:disabled="!detail.report.issues?.trim()"
@click="copySection('问题与建议', detail.report.issues)"
>
复制
</el-button>
</h4>
<div class="section-text">{{ detail.report.issues || '—' }}</div>
</section>
@@ -200,7 +236,7 @@
<script setup>
import { ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Paperclip } from '@element-plus/icons-vue'
import { CopyDocument, Paperclip } from '@element-plus/icons-vue'
import {
getDailyReportDetail,
saveDailyReport,
@@ -373,6 +409,32 @@ async function handleDeleteReport() {
}
}
// 一键复制单个板块的内容,非 HTTPS 环境回退到 textarea 复制。
async function copySection(label, text) {
const content = (text || '').trim()
if (!content) {
ElMessage.warning(`「${label}」暂无内容可复制`)
return
}
try {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(content)
} else {
const textarea = document.createElement('textarea')
textarea.value = content
textarea.style.position = 'fixed'
textarea.style.top = '-9999px'
document.body.appendChild(textarea)
textarea.select()
document.execCommand('copy')
document.body.removeChild(textarea)
}
ElMessage.success(`「${label}」已复制到剪贴板`)
} catch {
ElMessage.error('复制失败,请手动选择内容复制')
}
}
function formatSize(size) {
const n = Number(size) || 0
if (n < 1024) return `${n} B`
@@ -484,6 +546,13 @@ function formatSize(size) {
margin: 0 0 8px;
font-size: 14px;
color: #606266;
&.section-title {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
}
}
@@ -547,9 +547,11 @@ function cellClass(cell) {
return cell.isFuture ? 'is-future' : 'is-none'
}
// 补写窗口内、尚未写日报的日期:格子上标注「补」,提示可直接补写
// 补写窗口内、往日尚未写日报的日期:格子上标注「补」,提示可直接补写
// 今天属于正常填写,未写时不标「补」
function cellBackfill(cell) {
if (!cell || cell.isFuture || cell.record) return false
if (cell.date === todayStr()) return false
return inWindow(cell.date)
}
@@ -557,6 +559,7 @@ function cellTooltip(cell) {
if (!cell) return ''
if (!cell.record) {
if (cell.isFuture) return `${cell.date}(未到)`
if (cell.date === todayStr()) return `${cell.date}(今日未填写,点击填写)`
if (cellBackfill(cell)) return `${cell.date}(可补写,点击填写)`
return `${cell.date}(未填写)`
}
+44 -41
View File
@@ -1,29 +1,27 @@
<template>
<div class="mp-login">
<div class="box">
<!-- 企业选择(一人多企业) -->
<template v-if="choosing">
<p class="title">选择要进入的企业</p>
<ul class="tenant-list">
<li v-for="t in tenants" :key="t.tid" class="tenant-item" @click="chooseTenant(t)">
<span class="t-name">{{ t.tenant_name }}</span>
<span v-if="t.name" class="t-meta">{{ t.name }}</span>
</li>
</ul>
<p v-if="chooseErr" class="msg err">{{ chooseErr }}</p>
</template>
<!-- 企业选择(一人多企业) -->
<div v-if="choosing" class="box">
<p class="title">选择要进入的企业</p>
<ul class="tenant-list">
<li v-for="t in tenants" :key="t.tid" class="tenant-item" @click="chooseTenant(t)">
<span class="t-name">{{ t.tenant_name }}</span>
<span v-if="t.name" class="t-meta">{{ t.name }}</span>
</li>
</ul>
<p v-if="chooseErr" class="msg err">{{ chooseErr }}</p>
</div>
<template v-else>
<div v-if="error" class="error">
<p class="title">免登录失败</p>
<p class="msg">{{ error }}</p>
<button class="btn" @click="goHome">返回首页</button>
</div>
<div v-else class="loading">
<div class="spinner"></div>
<p>正在同步登录状态,请稍候…</p>
</div>
</template>
<!-- 正在同步登录态:白底 + 居中 loading -->
<div v-else-if="!error" class="loading-wrap">
<img class="loading-gif" src="/loading.gif" alt="登录中,请稍后…" />
<p class="loading-text">登录中,请稍后…</p>
</div>
<div v-else class="box">
<p class="title">免登录失败</p>
<p class="msg">{{ error }}</p>
<button class="btn" @click="goHome">返回首页</button>
</div>
</div>
</template>
@@ -148,29 +146,34 @@ function goHome() {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: #fff;
}
.loading-wrap {
display: flex;
flex-direction: column;
align-items: center;
}
.loading-gif {
display: block;
width: 160px;
max-width: 45vw;
height: auto;
}
.loading-text {
margin: 6px 0 0;
font-size: 14px;
color: #8a8f9c;
letter-spacing: 0.5px;
}
.box {
background: #fff;
border: 1px solid #eef0f5;
border-radius: 12px;
padding: 40px 36px;
padding: 36px 32px;
width: 360px;
max-width: 90vw;
text-align: center;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
}
.spinner {
width: 36px;
height: 36px;
margin: 0 auto 16px;
border: 3px solid #e4e7ee;
border-top-color: #667eea;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
box-shadow: 0 12px 32px rgba(31, 43, 72, 0.08);
}
.title {
font-size: 16px;
@@ -225,7 +228,7 @@ function goHome() {
border: none;
border-radius: 8px;
cursor: pointer;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: linear-gradient(135deg, #3973ff 0%, #52a8ff 100%);
color: #fff;
font-size: 14px;
}
+31 -28
View File
@@ -1,15 +1,13 @@
<template>
<div class="auth-callback">
<div class="box">
<div v-if="error" class="error">
<p class="title">登录失败</p>
<p class="msg">{{ error }}</p>
<button class="btn" @click="retry">重新登录</button>
</div>
<div v-else class="loading">
<div class="spinner"></div>
<p>正在完成登录,请稍候…</p>
</div>
<div v-if="!error" class="loading-wrap">
<img class="loading-gif" src="/loading.gif" alt="登录中,请稍后…" />
<p class="loading-text">登录中,请稍后…</p>
</div>
<div v-else class="box">
<p class="title">登录失败</p>
<p class="msg">{{ error }}</p>
<button class="btn" @click="retry">重新登录</button>
</div>
</div>
</template>
@@ -83,29 +81,34 @@ onMounted(finish);
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: #fff;
}
.loading-wrap {
display: flex;
flex-direction: column;
align-items: center;
}
.loading-gif {
display: block;
width: 160px;
max-width: 45vw;
height: auto;
}
.loading-text {
margin: 6px 0 0;
font-size: 14px;
color: #8a8f9c;
letter-spacing: 0.5px;
}
.box {
background: #fff;
border: 1px solid #eef0f5;
border-radius: 12px;
padding: 40px 36px;
padding: 36px 32px;
width: 360px;
max-width: 90vw;
text-align: center;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
}
.spinner {
width: 36px;
height: 36px;
margin: 0 auto 16px;
border: 3px solid #e4e7ee;
border-top-color: #667eea;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
box-shadow: 0 12px 32px rgba(31, 43, 72, 0.08);
}
.title {
font-size: 16px;
@@ -124,7 +127,7 @@ onMounted(finish);
border: none;
border-radius: 8px;
cursor: pointer;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: linear-gradient(135deg, #3973ff 0%, #52a8ff 100%);
color: #fff;
font-size: 14px;
}
+44 -41
View File
@@ -1,29 +1,27 @@
<template>
<div class="mp-login">
<div class="box">
<!-- 企业选择 -->
<template v-if="choosing">
<p class="title">选择要进入的企业</p>
<ul class="tenant-list">
<li v-for="t in tenants" :key="t.tid" class="tenant-item" @click="chooseTenant(t)">
<span class="t-name">{{ t.tenant_name }}</span>
<span v-if="t.name" class="t-meta">{{ t.name }}</span>
</li>
</ul>
<p v-if="chooseErr" class="msg err">{{ chooseErr }}</p>
</template>
<!-- 企业选择 -->
<div v-if="choosing" class="box">
<p class="title">选择要进入的企业</p>
<ul class="tenant-list">
<li v-for="t in tenants" :key="t.tid" class="tenant-item" @click="chooseTenant(t)">
<span class="t-name">{{ t.tenant_name }}</span>
<span v-if="t.name" class="t-meta">{{ t.name }}</span>
</li>
</ul>
<p v-if="chooseErr" class="msg err">{{ chooseErr }}</p>
</div>
<template v-else>
<div v-if="error" class="error">
<p class="title">微信登录失败</p>
<p class="msg">{{ error }}</p>
<button class="btn" @click="retry">返回重试</button>
</div>
<div v-else class="loading">
<div class="spinner"></div>
<p>正在通过微信登录,请稍候…</p>
</div>
</template>
<!-- 正在完成登录:白底 + 居中 loading -->
<div v-else-if="!error" class="loading-wrap">
<img class="loading-gif" src="/loading.gif" alt="登录中,请稍后…" />
<p class="loading-text">登录中,请稍后…</p>
</div>
<div v-else class="box">
<p class="title">微信登录失败</p>
<p class="msg">{{ error }}</p>
<button class="btn" @click="retry">返回重试</button>
</div>
</div>
</template>
@@ -132,29 +130,34 @@ onMounted(finish);
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: #fff;
}
.loading-wrap {
display: flex;
flex-direction: column;
align-items: center;
}
.loading-gif {
display: block;
width: 160px;
max-width: 45vw;
height: auto;
}
.loading-text {
margin: 6px 0 0;
font-size: 14px;
color: #8a8f9c;
letter-spacing: 0.5px;
}
.box {
background: #fff;
border: 1px solid #eef0f5;
border-radius: 12px;
padding: 40px 36px;
padding: 36px 32px;
width: 360px;
max-width: 90vw;
text-align: center;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
}
.spinner {
width: 36px;
height: 36px;
margin: 0 auto 16px;
border: 3px solid #e4e7ee;
border-top-color: #667eea;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
box-shadow: 0 12px 32px rgba(31, 43, 72, 0.08);
}
.title {
font-size: 16px;
@@ -209,7 +212,7 @@ onMounted(finish);
border: none;
border-radius: 8px;
cursor: pointer;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: linear-gradient(135deg, #3973ff 0%, #52a8ff 100%);
color: #fff;
font-size: 14px;
}
File diff suppressed because it is too large. Load diff