542 lines
20 KiB
PHP
542 lines
20 KiB
PHP
<?php
|
|
|
|
use yii\helpers\Html;
|
|
use yii\helpers\Url;
|
|
use yii\bootstrap4\ActiveForm;
|
|
use yii\bootstrap4\Modal;
|
|
|
|
// Safety check for variables passed from controller
|
|
$currentPath = isset($currentPath) ? $currentPath : '';
|
|
$dirs = isset($dirs) ? $dirs : [];
|
|
$files = isset($files) ? $files : [];
|
|
|
|
$this->title = 'คลังสื่อและเอกสาร - ' . Yii::$app->params['org_short'];
|
|
$this->params['breadcrumbs'][] = ['label' => 'Administrator', 'url' => ['/administrator/default/index']];
|
|
$this->params['breadcrumbs'][] = 'File Manager';
|
|
|
|
$baseUrl = Yii::$app->request->baseUrl . '/_uploads/';
|
|
?>
|
|
|
|
<style>
|
|
:root {
|
|
--or: #FFC068; /* Warm Orange */
|
|
--or2: #FFDBBB; /* Soft Peach */
|
|
--bd: #2c1810; /* Deep Brown */
|
|
--rd: #8e1d1d; /* Thai Red */
|
|
--light: #fdfbf9;
|
|
}
|
|
|
|
.fm-wrapper {
|
|
background: var(--light);
|
|
border-radius: 20px;
|
|
box-shadow: 0 10px 40px rgba(0,0,0,0.05);
|
|
min-height: 80vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Header Gradient */
|
|
.fm-header-main {
|
|
background: linear-gradient(135deg, var(--bd) 0%, var(--rd) 100%);
|
|
padding: 2rem;
|
|
color: white;
|
|
position: relative;
|
|
}
|
|
.fm-header-main h3 {
|
|
font-weight: 700;
|
|
letter-spacing: -0.5px;
|
|
margin-bottom: 5px;
|
|
}
|
|
.fm-stat-badge {
|
|
background: rgba(255,255,255,0.15);
|
|
backdrop-filter: blur(10px);
|
|
padding: 8px 15px;
|
|
border-radius: 30px;
|
|
font-size: 0.85rem;
|
|
border: 1px solid rgba(255,255,255,0.1);
|
|
}
|
|
|
|
/* Toolbar Area */
|
|
.fm-toolbar {
|
|
background: white;
|
|
padding: 15px 25px;
|
|
border-bottom: 1px solid #eee;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 15px;
|
|
}
|
|
|
|
.search-group {
|
|
position: relative;
|
|
flex-grow: 1;
|
|
max-width: 400px;
|
|
}
|
|
.search-group i {
|
|
position: absolute;
|
|
left: 15px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: #bbb;
|
|
}
|
|
.fm-search-input {
|
|
padding-left: 40px;
|
|
height: 45px;
|
|
border-radius: 12px;
|
|
border: 1.5px solid #eee;
|
|
background: #fcfcfc;
|
|
transition: all 0.3s;
|
|
}
|
|
.fm-search-input:focus {
|
|
border-color: var(--or);
|
|
background: white;
|
|
box-shadow: 0 0 0 4px rgba(255, 192, 104, 0.1);
|
|
}
|
|
|
|
/* Breadcrumbs Modern */
|
|
.fm-nav-bar {
|
|
background: #fdfdfd;
|
|
padding: 10px 25px;
|
|
border-bottom: 1px solid #f5f5f5;
|
|
}
|
|
.fm-bc {
|
|
display: flex;
|
|
align-items: center;
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
font-size: 0.9rem;
|
|
}
|
|
.fm-bc-item a {
|
|
color: #888;
|
|
padding: 5px 10px;
|
|
border-radius: 8px;
|
|
transition: all 0.2s;
|
|
}
|
|
.fm-bc-item a:hover {
|
|
background: var(--or2);
|
|
color: var(--bd);
|
|
text-decoration: none;
|
|
}
|
|
.fm-bc-item.active {
|
|
font-weight: 700;
|
|
color: var(--bd);
|
|
}
|
|
.fm-bc-sep {
|
|
color: #ddd;
|
|
margin: 0 5px;
|
|
}
|
|
|
|
/* Grid Area */
|
|
.fm-content {
|
|
padding: 25px;
|
|
flex-grow: 1;
|
|
}
|
|
.section-title {
|
|
font-size: 0.8rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1.5px;
|
|
color: #aaa;
|
|
font-weight: 700;
|
|
margin-bottom: 20px;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.section-title::after {
|
|
content: '';
|
|
height: 1px;
|
|
background: #eee;
|
|
flex-grow: 1;
|
|
margin-left: 15px;
|
|
}
|
|
|
|
/* Folder Card */
|
|
.folder-box {
|
|
background: white;
|
|
border: 1.5px solid #f0f0f0;
|
|
border-radius: 16px;
|
|
padding: 15px;
|
|
display: flex;
|
|
align-items: center;
|
|
transition: all 0.25s cubic-bezier(.25,.8,.25,1);
|
|
cursor: pointer;
|
|
position: relative;
|
|
margin-bottom: 20px;
|
|
}
|
|
.folder-box:hover {
|
|
border-color: var(--or);
|
|
box-shadow: 0 10px 25px rgba(255, 192, 104, 0.15);
|
|
transform: translateY(-3px);
|
|
}
|
|
.folder-icon-wrap {
|
|
width: 45px;
|
|
height: 45px;
|
|
background: var(--or2);
|
|
border-radius: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 15px;
|
|
}
|
|
.folder-icon-wrap i {
|
|
font-size: 1.5rem;
|
|
color: #d35400;
|
|
}
|
|
.folder-name {
|
|
font-weight: 600;
|
|
color: var(--bd);
|
|
font-size: 0.95rem;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
flex-grow: 1;
|
|
}
|
|
|
|
/* File Card */
|
|
.file-box {
|
|
background: white;
|
|
border: 1.5px solid #f0f0f0;
|
|
border-radius: 18px;
|
|
overflow: hidden;
|
|
transition: all 0.3s ease;
|
|
height: 100%;
|
|
position: relative;
|
|
}
|
|
.file-box:hover {
|
|
border-color: var(--or);
|
|
box-shadow: 0 15px 35px rgba(0,0,0,0.08);
|
|
transform: translateY(-5px);
|
|
}
|
|
.file-thumb {
|
|
height: 140px;
|
|
background: #fafafa;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.file-thumb img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
.file-thumb i {
|
|
font-size: 3.5rem;
|
|
opacity: 0.8;
|
|
}
|
|
.file-info {
|
|
padding: 12px 15px;
|
|
}
|
|
.file-name {
|
|
font-size: 0.85rem;
|
|
font-weight: 600;
|
|
color: var(--bd);
|
|
margin-bottom: 4px;
|
|
display: block;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.file-meta {
|
|
font-size: 0.75rem;
|
|
color: #999;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
/* Action Buttons Overlay */
|
|
.fm-actions-overlay {
|
|
position: absolute;
|
|
top: 8px;
|
|
right: 8px;
|
|
display: flex;
|
|
gap: 5px;
|
|
opacity: 0;
|
|
transition: opacity 0.2s;
|
|
z-index: 5;
|
|
}
|
|
.folder-box:hover .fm-actions-overlay,
|
|
.file-box:hover .fm-actions-overlay {
|
|
opacity: 1;
|
|
}
|
|
.btn-action-sm {
|
|
width: 32px;
|
|
height: 32px;
|
|
padding: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 10px;
|
|
background: rgba(255,255,255,0.9);
|
|
backdrop-filter: blur(5px);
|
|
border: 1px solid #eee;
|
|
color: #555;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
}
|
|
.btn-action-sm:hover {
|
|
background: white;
|
|
color: var(--rd);
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
/* Empty State */
|
|
.empty-state-wrap {
|
|
text-align: center;
|
|
padding: 80px 20px;
|
|
}
|
|
.empty-icon {
|
|
font-size: 5rem;
|
|
color: var(--or2);
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
/* Custom Buttons */
|
|
.btn-fm-primary {
|
|
background: var(--or);
|
|
color: var(--bd);
|
|
border: none;
|
|
font-weight: 700;
|
|
padding: 10px 25px;
|
|
border-radius: 12px;
|
|
transition: all 0.3s;
|
|
box-shadow: 0 4px 15px rgba(255, 192, 104, 0.3);
|
|
}
|
|
.btn-fm-primary:hover {
|
|
background: var(--bd);
|
|
color: white;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 20px rgba(44, 24, 16, 0.2);
|
|
}
|
|
</style>
|
|
|
|
<div class="fm-wrapper">
|
|
<!-- Header -->
|
|
<div class="fm-header-main">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-8">
|
|
<h3><i class="fas fa-th-large mr-2"></i> File Explorer</h3>
|
|
<p class="mb-0 opacity-75">จัดการสื่อ รูปภาพ และเอกสารทั้งหมดในโฟลเดอร์ _uploads</p>
|
|
</div>
|
|
<div class="col-md-4 text-right">
|
|
<div class="fm-stat-badge d-inline-block">
|
|
<i class="fas fa-database mr-2"></i>
|
|
<strong><?= count($dirs) ?></strong> โฟลเดอร์ / <strong><?= count($files) ?></strong> ไฟล์
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toolbar -->
|
|
<div class="fm-toolbar">
|
|
<div class="search-group">
|
|
<i class="fas fa-search"></i>
|
|
<input type="text" id="fileSearch" class="form-control fm-search-input" placeholder="ค้นหาตามชื่อไฟล์หรือโฟลเดอร์...">
|
|
</div>
|
|
|
|
<div class="action-group d-flex align-items-center">
|
|
<button type="button" class="btn btn-outline-secondary mr-3 rounded-pill px-3" data-toggle="modal" data-target="#newFolderModal">
|
|
<i class="fas fa-folder-plus mr-1 text-warning"></i> โฟลเดอร์ใหม่
|
|
</button>
|
|
|
|
<?php $form = ActiveForm::begin([
|
|
'action' => ['upload', 'path' => $currentPath],
|
|
'options' => ['enctype' => 'multipart/form-data', 'id' => 'upload-form', 'class' => 'd-flex align-items-center']
|
|
]); ?>
|
|
<div class="input-group">
|
|
<div class="input-group-prepend">
|
|
<span class="input-group-text bg-light border-0 small" style="border-radius: 12px 0 0 12px;">ย่อ:</span>
|
|
</div>
|
|
<select name="resize_width" class="form-control border-0 bg-light" style="width: 100px; font-size: 0.85rem;">
|
|
<option value="original">ต้นฉบับ</option>
|
|
<option value="1200">1200px</option>
|
|
<option value="1024" selected>1024px</option>
|
|
<option value="512">512px</option>
|
|
<option value="384">384px</option>
|
|
</select>
|
|
<div class="input-group-append">
|
|
<button type="button" class="btn btn-fm-primary" onclick="document.getElementById('file-input').click()">
|
|
<i class="fas fa-cloud-upload-alt mr-1"></i> อัปโหลด
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<input type="file" name="files[]" id="file-input" multiple style="display: none;" onchange="document.getElementById('upload-form').submit()">
|
|
<?php ActiveForm::end(); ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Breadcrumbs -->
|
|
<div class="fm-nav-bar">
|
|
<ul class="fm-bc">
|
|
<li class="fm-bc-item">
|
|
<?= Html::a('<i class="fas fa-hdd"></i> root', ['index', 'path' => '']) ?>
|
|
</li>
|
|
<?php
|
|
$parts = array_filter(explode('/', $currentPath));
|
|
$accumulatedPath = '';
|
|
foreach ($parts as $part):
|
|
$accumulatedPath .= ($accumulatedPath ? '/' : '') . $part;
|
|
?>
|
|
<li class="fm-bc-sep">/</li>
|
|
<li class="fm-bc-item <?= ($accumulatedPath == $currentPath) ? 'active' : '' ?>">
|
|
<?= Html::a(Html::encode($part), ['index', 'path' => $accumulatedPath]) ?>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Main Content -->
|
|
<div class="fm-content">
|
|
<div id="fileGridArea">
|
|
|
|
<!-- Folders Section -->
|
|
<?php if (!empty($dirs)): ?>
|
|
<div class="section-title">โฟลเดอร์ย่อย</div>
|
|
<div class="row">
|
|
<?php foreach ($dirs as $dir): ?>
|
|
<div class="col-xl-3 col-lg-4 col-md-6 mb-3 card-item" data-name="<?= strtolower($dir['name']) ?>">
|
|
<div class="folder-box">
|
|
<div class="fm-actions-overlay">
|
|
<?= Html::beginForm(['delete', 'relPath' => $dir['relPath']], 'post') ?>
|
|
<button type="submit" class="btn-action-sm text-danger" title="ลบ" data-confirm="ต้องการลบโฟลเดอร์นี้หรือไม่? (ต้องเป็นโฟลเดอร์ว่าง)">
|
|
<i class="far fa-trash-alt"></i>
|
|
</button>
|
|
<?= Html::endForm() ?>
|
|
</div>
|
|
<a href="<?= Url::to(['index', 'path' => $dir['relPath']]) ?>" class="d-flex align-items-center text-decoration-none w-100">
|
|
<div class="folder-icon-wrap">
|
|
<i class="fas fa-folder"></i>
|
|
</div>
|
|
<div class="folder-name"><?= Html::encode($dir['name']) ?></div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Files Section -->
|
|
<?php if (!empty($files)): ?>
|
|
<div class="section-title mt-4">ไฟล์ทั้งหมด</div>
|
|
<div class="row">
|
|
<?php foreach ($files as $file): ?>
|
|
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 mb-4 card-item" data-name="<?= strtolower($file['name']) ?>">
|
|
<div class="file-box">
|
|
<!-- Actions -->
|
|
<div class="fm-actions-overlay">
|
|
<button class="btn-action-sm copy-link text-info" data-url="<?= Yii::$app->request->hostInfo . '/_uploads/' . $file['relPath'] ?>" title="คัดลอกลิงก์">
|
|
<i class="fas fa-link"></i>
|
|
</button>
|
|
<?= Html::beginForm(['delete', 'relPath' => $file['relPath']], 'post') ?>
|
|
<button type="submit" class="btn-action-sm text-danger" title="ลบ" data-confirm="ยืนยันการลบไฟล์นี้?">
|
|
<i class="far fa-trash-alt"></i>
|
|
</button>
|
|
<?= Html::endForm() ?>
|
|
</div>
|
|
|
|
<div class="file-thumb">
|
|
<?php
|
|
$ext = strtolower($file['ext']);
|
|
$isImage = in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg']);
|
|
if ($isImage): ?>
|
|
<?= Html::img($baseUrl . $file['relPath'], ['alt' => $file['name']]) ?>
|
|
<?php else:
|
|
$icon = 'far fa-file';
|
|
$color = '#95a5a6';
|
|
if ($ext == 'pdf') { $icon = 'far fa-file-pdf'; $color = '#e74c3c'; }
|
|
elseif (in_array($ext, ['doc', 'docx'])) { $icon = 'far fa-file-word'; $color = '#3498db'; }
|
|
elseif (in_array($ext, ['xls', 'xlsx'])) { $icon = 'far fa-file-excel'; $color = '#27ae60'; }
|
|
elseif (in_array($ext, ['zip', 'rar', '7z'])) { $icon = 'far fa-file-archive'; $color = '#f39c12'; }
|
|
?>
|
|
<i class="<?= $icon ?>" style="color: <?= $color ?>"></i>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="file-info">
|
|
<span class="file-name" title="<?= Html::encode($file['name']) ?>"><?= Html::encode($file['name']) ?></span>
|
|
<div class="file-meta">
|
|
<span><?= $file['size'] ?></span>
|
|
<?= Html::a('<i class="fas fa-download"></i>', ['download', 'relPath' => $file['relPath']], ['class' => 'text-primary']) ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Empty State -->
|
|
<?php if (empty($dirs) && empty($files)): ?>
|
|
<div class="empty-state-wrap">
|
|
<i class="fas fa-cloud-upload-alt empty-icon"></i>
|
|
<h4>ยังไม่มีไฟล์ในโฟลเดอร์นี้</h4>
|
|
<p class="text-muted">ลากไฟล์มาวางที่นี่เพื่ออัปโหลด หรือกดปุ่ม "โฟลเดอร์ใหม่" เพื่อจัดระเบียบ</p>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal: New Folder -->
|
|
<div class="modal fade" id="newFolderModal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
|
<div class="modal-content" style="border-radius: 20px; border: none; overflow: hidden;">
|
|
<div class="modal-header" style="background: var(--bd); color: white; border: none;">
|
|
<h5 class="modal-title font-weight-bold"><i class="fas fa-folder-plus mr-2"></i> สร้างโฟลเดอร์ใหม่</h5>
|
|
<button type="button" class="close text-white" data-dismiss="modal">×</button>
|
|
</div>
|
|
<?php $form = ActiveForm::begin(['action' => ['create-folder', 'path' => $currentPath]]); ?>
|
|
<div class="modal-body p-4">
|
|
<div class="form-group">
|
|
<label class="font-weight-bold text-muted">ชื่อโฟลเดอร์ที่คุณต้องการ</label>
|
|
<input type="text" name="folder_name" class="form-control form-control-lg" style="border-radius: 12px; border: 2px solid #eee;" placeholder="เช่น images, news, 2567..." required autofocus>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer border-0">
|
|
<button type="button" class="btn btn-light rounded-pill px-4" data-dismiss="modal">ยกเลิก</button>
|
|
<button type="submit" class="btn btn-fm-primary px-4">สร้างเดี๋ยวนี้</button>
|
|
</div>
|
|
<?php ActiveForm::end(); ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php
|
|
$js = <<<JS
|
|
// Real-time Search Filter
|
|
$("#fileSearch").on("keyup", function() {
|
|
var value = $(this).val().toLowerCase();
|
|
$("#fileGridArea .card-item").filter(function() {
|
|
$(this).toggle($(this).attr('data-name').indexOf(value) > -1)
|
|
});
|
|
});
|
|
|
|
// Copy Link Feedback
|
|
$('.copy-link').click(function(e) {
|
|
e.preventDefault();
|
|
var url = $(this).data('url');
|
|
var tempInput = document.createElement("input");
|
|
tempInput.style = "position: absolute; left: -1000px; top: -1000px";
|
|
tempInput.value = url;
|
|
document.body.appendChild(tempInput);
|
|
tempInput.select();
|
|
document.execCommand("copy");
|
|
document.body.removeChild(tempInput);
|
|
|
|
var btn = $(this);
|
|
var oldHtml = btn.html();
|
|
btn.html('<i class="fas fa-check"></i>').css('color', '#27ae60');
|
|
setTimeout(function(){ btn.html(oldHtml).css('color', ''); }, 2000);
|
|
});
|
|
|
|
// Drag and Drop Effects (Optional Visual Enhancement)
|
|
var dropZone = document.querySelector('.fm-content');
|
|
window.addEventListener('dragover', function(e) { e.preventDefault(); }, false);
|
|
window.addEventListener('drop', function(e) { e.preventDefault(); }, false);
|
|
JS;
|
|
$this->registerJs($js);
|
|
?>
|