将待办截图改为任务内画廊 #672
@ -83,10 +83,12 @@
|
||||
.sys-todo-badge { display:inline-flex; align-items:center; padding:.1rem .45rem; border:1px solid #dfd6c5; border-radius:999px; background:#faf6ee; color:#51483c; white-space:nowrap; }
|
||||
.sys-todo-badge--incomplete { border-color:#dbbf7a; background:#fff7df; color:#935f07; }
|
||||
.sys-todo-badge--completed { border-color:#bad0ac; background:#f2f8ed; color:#53793e; }
|
||||
.sys-todo-shots { display:grid; grid-template-columns:repeat(auto-fit,minmax(7rem,1fr)); gap:.4rem; max-width:none; }
|
||||
.sys-todo-shots button { display:block; width:100%; height:5.5rem; padding:0; border:1px solid #ddd4c4; border-radius:.4rem; overflow:hidden; background:#fff; cursor:zoom-in; }
|
||||
.sys-todo-shots img { width:100%; height:100%; padding:.2rem; object-fit:contain; background:#fff; }
|
||||
.sys-todo-shots-caption { display:-webkit-box; margin-top:.35rem; overflow:hidden; color:#6d5630; font-size:.68rem; line-height:1.45; overflow-wrap:anywhere; -webkit-box-orient:vertical; -webkit-line-clamp:2; }
|
||||
.sys-todo-table__title-wrap { display:flex; flex-wrap:wrap; align-items:flex-start; gap:.35rem .55rem; }
|
||||
.sys-todo-table__title-text { min-width:0; flex:1 1 12rem; }
|
||||
.sys-todo-shot-trigger { display:inline-flex; flex:0 0 auto; align-items:center; gap:.25rem; min-height:1.8rem; padding:0 .5rem; border:1px solid #d8c9aa; border-radius:.4rem; background:#fffaf0; color:#6d5630; font:inherit; font-size:.7rem; font-weight:500; white-space:nowrap; cursor:pointer; }
|
||||
.sys-todo-shot-trigger:hover { border-color:var(--sys-gold); background:var(--sys-gold-soft); color:#6a5129; }
|
||||
.sys-todo-shot-trigger:focus-visible { outline:2px solid var(--sys-gold); outline-offset:2px; }
|
||||
.sys-todo-shot-trigger .material-icons-outlined { font-size:1rem; }
|
||||
.sys-todo-table__add { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:.5rem .75rem; margin-top:1rem; padding:.55rem .7rem; border:1px dashed #d8cdb8; border-radius:.55rem; background:#fcfaf5; color:#666; font-size:.72rem; }
|
||||
.sys-todo-table__add-copy { min-width:16rem; flex:1 1 30rem; }
|
||||
.sys-todo-table__reset-widths { min-height:1.8rem; padding:0 .55rem; border:1px solid var(--sys-border); border-radius:.4rem; background:#fff; color:#51483c; font:inherit; font-size:.7rem; cursor:pointer; }
|
||||
@ -182,8 +184,11 @@
|
||||
.sys-todo-image-dialog__head { display:flex; align-items:center; gap:1rem; padding:.65rem .85rem; background:#1d1d1f; }
|
||||
.sys-todo-image-dialog__head h2 { margin:0; min-width:0; overflow:hidden; font-size:.82rem; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.sys-todo-image-dialog__head button { margin-left:auto; width:2rem; height:2rem; border:1px solid #5a554d; border-radius:999px; background:#2a2927; color:#fff; cursor:pointer; }
|
||||
.sys-todo-image-dialog__body { display:grid; place-items:center; height:calc(92vh - 7rem); padding:.75rem; background:#111; overflow:auto; }
|
||||
.sys-todo-image-dialog__body { display:grid; grid-template-columns:2.25rem minmax(0,1fr) 2.25rem; align-items:center; gap:.6rem; height:calc(92vh - 7rem); padding:.75rem; background:#111; overflow:auto; }
|
||||
.sys-todo-image-dialog__body img { max-width:100%; max-height:100%; object-fit:contain; }
|
||||
.sys-todo-image-dialog__body button { display:inline-flex; align-items:center; justify-content:center; width:2.25rem; height:2.75rem; padding:0; border:1px solid #5a554d; border-radius:999px; background:#2a2927; color:#fff; font-size:1.5rem; cursor:pointer; }
|
||||
.sys-todo-image-dialog__body button:hover { border-color:#f0e4bf; color:#f0e4bf; }
|
||||
.sys-todo-image-dialog__body button[hidden] { visibility:hidden; }
|
||||
.sys-todo-image-dialog__foot { display:flex; justify-content:flex-end; padding:.55rem .85rem; background:#1d1d1f; }
|
||||
.sys-todo-image-dialog__foot a { color:#f0e4bf; font-size:.75rem; }
|
||||
@media (max-width:1300px) { .sys-todo-summary { grid-template-columns:repeat(2,minmax(0,1fr)); } }
|
||||
@ -371,7 +376,6 @@
|
||||
<col data-sys-todo-col="reminder" data-default-width="116" data-min-width="96" style="width:116px">
|
||||
<col data-sys-todo-col="status" data-default-width="92" data-min-width="80" style="width:92px">
|
||||
<col data-sys-todo-col="progress_notes" data-default-width="260" data-min-width="180" style="width:260px">
|
||||
<col data-sys-todo-col="screenshots" data-default-width="180" data-min-width="140" style="width:180px">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
@ -386,7 +390,6 @@
|
||||
<th scope="col" data-sys-todo-column="reminder">提醒<button type="button" class="sys-todo-table__col-resizer" data-sys-todo-col-resizer="reminder" aria-label="调整提醒列宽" title="拖动调整,双击恢复"></button></th>
|
||||
<th scope="col" data-sys-todo-column="status">状态<button type="button" class="sys-todo-table__col-resizer" data-sys-todo-col-resizer="status" aria-label="调整状态列宽" title="拖动调整,双击恢复"></button></th>
|
||||
<th scope="col" data-sys-todo-column="progress_notes">完成情况 / 备注<button type="button" class="sys-todo-table__col-resizer" data-sys-todo-col-resizer="progress_notes" aria-label="调整完成情况和备注列宽" title="拖动调整,双击恢复"></button></th>
|
||||
<th scope="col" data-sys-todo-column="screenshots">完成截图<button type="button" class="sys-todo-table__col-resizer" data-sys-todo-col-resizer="screenshots" aria-label="调整完成截图列宽" title="拖动调整,双击恢复"></button></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -491,7 +494,19 @@
|
||||
<td class="sys-todo-table__title"
|
||||
tabindex="0"
|
||||
title="点击编辑待办"
|
||||
data-sys-todo-row-edit="{{ $todoPayloadEncoded }}">{{ $titleLabels[$loop->index] }}</td>
|
||||
data-sys-todo-row-edit="{{ $todoPayloadEncoded }}">
|
||||
<div class="sys-todo-table__title-wrap">
|
||||
<span class="sys-todo-table__title-text">{{ $titleLabels[$loop->index] }}</span>
|
||||
@if ($item->attachments->isNotEmpty())
|
||||
<button type="button" class="sys-todo-shot-trigger"
|
||||
data-sys-todo-image-gallery="{{ $todoPayloadEncoded }}"
|
||||
title="查看这条待办的 {{ $item->attachments->count() }} 张截图">
|
||||
<span class="material-icons-outlined" aria-hidden="true">photo_library</span>
|
||||
<span>截图 {{ $item->attachments->count() }}</span>
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ $item->owner ?: '—' }}</td>
|
||||
<td>{{ $item->assignee ?: '—' }}</td>
|
||||
<td>{{ $item->start_date?->format('Y-m-d') ?: ($item->start_note ?: '—') }}</td>
|
||||
@ -516,30 +531,9 @@
|
||||
—
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($item->attachments->isNotEmpty())
|
||||
<div class="sys-todo-shots">
|
||||
@foreach ($item->attachments as $attachment)
|
||||
@php
|
||||
$attachmentUrl = route('business.systemization-todos.attachments.show', [
|
||||
'todo' => $item->id,
|
||||
'attachment' => $attachment->id,
|
||||
]);
|
||||
@endphp
|
||||
<button type="button" data-sys-todo-image-url="{{ $attachmentUrl }}" data-sys-todo-image-name="{{ $attachment->original_name }}" title="点击预览:{{ $attachment->original_name }}">
|
||||
<img src="{{ route('business.systemization-todos.attachments.show', ['todo' => $item->id, 'attachment' => $attachment->id]) }}"
|
||||
alt="{{ $attachment->original_name }}" loading="lazy">
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
<span class="sys-todo-shots-caption" title="{{ $titleLabels[$loop->index] ?: '未填写事项' }}">所属待办:{{ $titleLabels[$loop->index] ?: '未填写事项' }}</span>
|
||||
@else
|
||||
—
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="12"><div class="sys-todo-empty">当前筛选条件下没有待办事项。</div></td></tr>
|
||||
<tr><td colspan="11"><div class="sys-todo-empty">当前筛选条件下没有待办事项。</div></td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
<tfoot>
|
||||
@ -549,7 +543,7 @@
|
||||
data-sys-todo-inline-add
|
||||
data-sys-todo-add-mode="type">+ 类型</button>
|
||||
</td>
|
||||
<td colspan="11" class="sys-todo-table__footer-add sys-todo-table__footer-note">也可在现有类型下方新增内容,在内容下方新增待办。</td>
|
||||
<td colspan="10" class="sys-todo-table__footer-add sys-todo-table__footer-note">也可在现有类型下方新增内容,在内容下方新增待办。</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
@ -712,7 +706,9 @@
|
||||
<button type="button" data-sys-todo-image-close aria-label="关闭截图预览">×</button>
|
||||
</div>
|
||||
<div class="sys-todo-image-dialog__body">
|
||||
<button type="button" data-sys-todo-image-prev aria-label="上一张截图">‹</button>
|
||||
<img src="" alt="" data-sys-todo-image-preview>
|
||||
<button type="button" data-sys-todo-image-next aria-label="下一张截图">›</button>
|
||||
</div>
|
||||
<div class="sys-todo-image-dialog__foot">
|
||||
<a href="" target="_blank" rel="noopener" data-sys-todo-image-open>在新页面打开原图</a>
|
||||
@ -735,6 +731,10 @@
|
||||
var imagePreview = imageViewer.querySelector('[data-sys-todo-image-preview]');
|
||||
var imageTitle = imageViewer.querySelector('[data-sys-todo-image-title]');
|
||||
var imageOpen = imageViewer.querySelector('[data-sys-todo-image-open]');
|
||||
var imagePrevious = imageViewer.querySelector('[data-sys-todo-image-prev]');
|
||||
var imageNext = imageViewer.querySelector('[data-sys-todo-image-next]');
|
||||
var imageGalleryItems = [];
|
||||
var imageGalleryIndex = 0;
|
||||
var categorySelect = dialog.querySelector('[data-sys-todo-category-select]');
|
||||
var categoryValue = dialog.querySelector('[data-sys-todo-category-value]');
|
||||
var newCategoryWrap = dialog.querySelector('[data-sys-todo-new-category]');
|
||||
@ -1193,19 +1193,58 @@
|
||||
}
|
||||
}
|
||||
|
||||
function openImageViewer(url, name) {
|
||||
imageTitle.textContent = name || '完成截图预览';
|
||||
imagePreview.src = url;
|
||||
imagePreview.alt = name || '完成截图';
|
||||
imageOpen.href = url;
|
||||
function showImageViewerItem() {
|
||||
var item = imageGalleryItems[imageGalleryIndex];
|
||||
if (!item) return;
|
||||
|
||||
var position = imageGalleryItems.length > 1
|
||||
? (imageGalleryIndex + 1) + ' / ' + imageGalleryItems.length + ' · '
|
||||
: '';
|
||||
imageTitle.textContent = position + (item.name || '完成截图预览');
|
||||
imagePreview.src = item.url;
|
||||
imagePreview.alt = item.name || '完成截图';
|
||||
imageOpen.href = item.url;
|
||||
imagePrevious.hidden = imageGalleryItems.length <= 1;
|
||||
imageNext.hidden = imageGalleryItems.length <= 1;
|
||||
}
|
||||
|
||||
function openImageViewerItems(items) {
|
||||
imageGalleryItems = Array.isArray(items) && items.length
|
||||
? items
|
||||
: [];
|
||||
imageGalleryIndex = 0;
|
||||
if (!imageGalleryItems.length) return;
|
||||
|
||||
showImageViewerItem();
|
||||
if (typeof imageViewer.showModal === 'function') imageViewer.showModal();
|
||||
else imageViewer.setAttribute('open', '');
|
||||
}
|
||||
|
||||
function openImageViewer(url, name) {
|
||||
openImageViewerItems([{ url: url, name: name }]);
|
||||
}
|
||||
|
||||
function showPreviousImage() {
|
||||
if (imageGalleryItems.length < 2) return;
|
||||
imageGalleryIndex = (imageGalleryIndex - 1 + imageGalleryItems.length) % imageGalleryItems.length;
|
||||
showImageViewerItem();
|
||||
}
|
||||
|
||||
function showNextImage() {
|
||||
if (imageGalleryItems.length < 2) return;
|
||||
imageGalleryIndex = (imageGalleryIndex + 1) % imageGalleryItems.length;
|
||||
showImageViewerItem();
|
||||
}
|
||||
|
||||
function closeImageViewer() {
|
||||
if (typeof imageViewer.close === 'function') imageViewer.close();
|
||||
else imageViewer.removeAttribute('open');
|
||||
imagePreview.removeAttribute('src');
|
||||
imageTitle.textContent = '完成截图预览';
|
||||
imageGalleryItems = [];
|
||||
imageGalleryIndex = 0;
|
||||
imagePrevious.hidden = true;
|
||||
imageNext.hidden = true;
|
||||
}
|
||||
|
||||
function openGroupDialog(type, value, count) {
|
||||
@ -1331,6 +1370,14 @@
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (event) {
|
||||
var gallery = event.target.closest('[data-sys-todo-image-gallery]');
|
||||
if (gallery) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var galleryPayload = decodePayload(gallery.getAttribute('data-sys-todo-image-gallery'));
|
||||
openImageViewerItems(galleryPayload.attachments || []);
|
||||
return;
|
||||
}
|
||||
var image = event.target.closest('[data-sys-todo-image-url]');
|
||||
if (image) {
|
||||
event.preventDefault();
|
||||
@ -1397,6 +1444,8 @@
|
||||
if (event.target.closest('[data-sys-todo-group-close]')) closeGroupDialog();
|
||||
if (event.target.closest('[data-sys-todo-delete-close]')) closeDeleteConfirm();
|
||||
if (event.target.closest('[data-sys-todo-image-close]')) closeImageViewer();
|
||||
if (event.target.closest('[data-sys-todo-image-prev]')) showPreviousImage();
|
||||
if (event.target.closest('[data-sys-todo-image-next]')) showNextImage();
|
||||
if (event.target.closest('[data-sys-todo-choose-files]')) screenshotInput.click();
|
||||
});
|
||||
dialog.addEventListener('click', function (event) {
|
||||
@ -1487,6 +1536,16 @@
|
||||
closeImageViewer();
|
||||
return;
|
||||
}
|
||||
if (imageViewer.open && event.key === 'ArrowLeft') {
|
||||
event.preventDefault();
|
||||
showPreviousImage();
|
||||
return;
|
||||
}
|
||||
if (imageViewer.open && event.key === 'ArrowRight') {
|
||||
event.preventDefault();
|
||||
showNextImage();
|
||||
return;
|
||||
}
|
||||
var groupCell = event.target.closest('[data-sys-todo-group-edit]');
|
||||
if (groupCell && event.target === groupCell && (event.key === 'Enter' || event.key === ' ')) {
|
||||
event.preventDefault();
|
||||
|
||||
@ -68,8 +68,8 @@ class SystemizationTodosTest extends FeatureTestCase
|
||||
->assertSee('id="sysTodoDeleteConfirm"', false)
|
||||
->assertSee('data-sys-todo-table-wrap', false)
|
||||
->assertSee('data-sys-todo-scrollrail', false)
|
||||
->assertSee('object-fit:contain', false)
|
||||
->assertSee('data-sys-todo-col="screenshots" data-default-width="180" data-min-width="140"', false)
|
||||
->assertSee('data-sys-todo-image-prev', false)
|
||||
->assertSee('data-sys-todo-image-next', false)
|
||||
->assertDontSee('playlist_add', false)
|
||||
->assertDontSee('<th scope="col">操作</th>', false)
|
||||
->assertDontSeeText('下一页')
|
||||
@ -113,8 +113,8 @@ class SystemizationTodosTest extends FeatureTestCase
|
||||
]))
|
||||
->assertOk()
|
||||
->assertSee('data-sys-todo-row-edit', false)
|
||||
->assertSee('data-sys-todo-image-url', false)
|
||||
->assertSeeText('所属待办:核对淘系收入与平台账单差异');
|
||||
->assertSee('data-sys-todo-image-gallery', false)
|
||||
->assertSeeText('截图 2');
|
||||
$firstAttachment = $todo->attachments()->firstOrFail();
|
||||
$existingAttachment = $todo->attachments()->reorder()->orderByDesc('id')->firstOrFail();
|
||||
Storage::disk('uploads')->assertExists($firstAttachment->path);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user