首次提交:初始化项目代码
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h3 class="font-semibold text-slate-800 flex items-center gap-2">
|
||||
<i class="fas fa-folder-open text-purple-500"></i> {{ if .User }}{{.User.Name}}{{ else }}未知用户{{ end }} 的作品集管理
|
||||
</h3>
|
||||
<p class="text-sm text-slate-500 mt-1">管理用户的作品集项目</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<a href="{{.AdminPath}}/portfolio/add" class="btn btn-primary">
|
||||
<i class="fas fa-plus"></i> 添加项目
|
||||
</a>
|
||||
<a href="{{.AdminPath}}/users" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> 返回列表
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{{ range .PortfolioItems }}
|
||||
<div class="p-4 bg-slate-50 rounded-lg border border-slate-200 hover:border-primary-300 transition-all {{ if .Hidden }}opacity-50{{ end }}">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<h4 class="font-medium text-slate-800">{{.Name}}</h4>
|
||||
{{ if .Hidden }}
|
||||
<span class="px-2 py-0.5 bg-gray-200 text-gray-600 rounded text-xs">已隐藏</span>
|
||||
{{ end }}
|
||||
{{ if .Password }}
|
||||
<span class="px-2 py-0.5 bg-amber-100 text-amber-700 rounded text-xs"><i class="fas fa-lock"></i></span>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a href="{{$.AdminPath}}/portfolio/{{.ID}}/edit" class="btn btn-warning">
|
||||
<i class="fas fa-edit"></i> 编辑
|
||||
</a>
|
||||
<button onclick="togglePortfolioHidden('{{.ID}}', {{.Hidden}})" class="btn btn-{{ if .Hidden }}success{{ else }}secondary{{ end }}">
|
||||
<i class="fas {{ if .Hidden }}fa-eye{{ else }}fa-eye-slash{{ end }}"></i> {{ if .Hidden }}显示{{ else }}隐藏{{ end }}
|
||||
</button>
|
||||
<button onclick="deletePortfolio('{{.ID}}')" class="btn btn-danger">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-slate-600 mb-3">{{.Description}}</p>
|
||||
<div class="flex flex-wrap gap-1 mb-3">
|
||||
{{ range .TechStack }}
|
||||
<span class="px-2 py-0.5 bg-slate-200 text-slate-600 rounded text-xs">{{.}}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="flex items-center justify-between text-xs text-slate-500">
|
||||
<span>{{.StartDate}} ~ {{.EndDate}}</span>
|
||||
{{ if .URL }}
|
||||
<a href="{{.URL}}" target="_blank" class="text-primary-600 hover:text-primary-700">
|
||||
<i class="fas fa-external-link-alt"></i> 查看
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="col-span-full text-center py-12">
|
||||
<div class="w-16 h-16 mx-auto mb-4 rounded-full bg-slate-100 flex items-center justify-center">
|
||||
<i class="fas fa-folder-open text-slate-400 text-2xl"></i>
|
||||
</div>
|
||||
<h4 class="text-lg font-semibold text-slate-700 mb-2">暂无作品集项目</h4>
|
||||
<p class="text-slate-500">该用户还没有添加作品集项目</p>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal hidden fixed inset-0 bg-black/50 flex items-center justify-center z-50" id="portfolioModal">
|
||||
<div class="modal-content bg-white rounded-xl shadow-xl w-full max-w-lg mx-4">
|
||||
<div class="flex items-center justify-between p-4 border-b border-slate-200">
|
||||
<h3 class="font-semibold text-slate-800" id="portfolioModalTitle">添加项目</h3>
|
||||
<button onclick="closePortfolioModal()" class="btn btn-secondary btn-sm">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<form id="portfolioForm" onsubmit="savePortfolio(event)">
|
||||
<input type="hidden" id="portfolioId">
|
||||
<input type="hidden" id="portfolioUserId" value="{{ with .User }}{{.ID}}{{ end }}">
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">项目名称</label>
|
||||
<input type="text" id="portfolioName" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">项目描述</label>
|
||||
<textarea id="portfolioDescription" rows="3" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">技术栈 (每行一个)</label>
|
||||
<textarea id="portfolioTechStack" rows="3" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500" placeholder="Go React MySQL"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">项目链接</label>
|
||||
<input type="url" id="portfolioURL" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">开始日期</label>
|
||||
<input type="text" id="portfolioStartDate" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500" placeholder="2024-01">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">结束日期</label>
|
||||
<input type="text" id="portfolioEndDate" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500" placeholder="2024-12">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">关联简历项目</label>
|
||||
<select id="portfolioProjectID" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
|
||||
<option value="0">不关联简历项目</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" id="portfolioShowInResume" class="w-4 h-4 text-primary-600 rounded focus:ring-primary-500">
|
||||
<span class="text-sm font-medium text-slate-700">在简历中显示</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" id="portfolioHidden" class="w-4 h-4 text-primary-600 rounded focus:ring-primary-500">
|
||||
<span class="text-sm font-medium text-slate-700">隐藏项目</span>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">访问密码(留空则不设密码)</label>
|
||||
<input type="password" id="portfolioPassword" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500" placeholder="输入访问密码">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">项目文档 (Markdown格式)</label>
|
||||
<textarea id="portfolioDoc" rows="8" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500 font-mono text-sm" placeholder="# 项目概述 这里编写项目详细说明..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center gap-3 mt-6">
|
||||
<button type="button" onclick="closePortfolioModal()" class="btn btn-secondary">
|
||||
<i class="fas fa-times"></i> 取消
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> 保存
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function openAddPortfolioModal() {
|
||||
document.getElementById('portfolioModalTitle').textContent = '添加项目';
|
||||
document.getElementById('portfolioId').value = '';
|
||||
document.getElementById('portfolioName').value = '';
|
||||
document.getElementById('portfolioDescription').value = '';
|
||||
document.getElementById('portfolioTechStack').value = '';
|
||||
document.getElementById('portfolioURL').value = '';
|
||||
document.getElementById('portfolioStartDate').value = '';
|
||||
document.getElementById('portfolioEndDate').value = '';
|
||||
document.getElementById('portfolioProjectID').value = '0';
|
||||
document.getElementById('portfolioShowInResume').checked = false;
|
||||
document.getElementById('portfolioHidden').checked = false;
|
||||
document.getElementById('portfolioPassword').value = '';
|
||||
document.getElementById('portfolioDoc').value = '';
|
||||
document.getElementById('portfolioModal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
function closePortfolioModal() {
|
||||
document.getElementById('portfolioModal').classList.add('hidden');
|
||||
}
|
||||
|
||||
function editPortfolio(id) {
|
||||
fetch('/dashboard/api/portfolio/' + id)
|
||||
.then(response => response.json())
|
||||
.then(result => {
|
||||
if (!result.success || !result.data) {
|
||||
showCustomAlert('error', '错误', '获取项目信息失败');
|
||||
return;
|
||||
}
|
||||
const item = result.data;
|
||||
document.getElementById('portfolioModalTitle').textContent = '编辑项目';
|
||||
document.getElementById('portfolioId').value = item.id;
|
||||
document.getElementById('portfolioName').value = item.name;
|
||||
document.getElementById('portfolioDescription').value = item.description || '';
|
||||
document.getElementById('portfolioTechStack').value = (item.tech_stack || []).join('\n');
|
||||
document.getElementById('portfolioURL').value = item.url || '';
|
||||
document.getElementById('portfolioStartDate').value = item.start_date || '';
|
||||
document.getElementById('portfolioEndDate').value = item.end_date || '';
|
||||
document.getElementById('portfolioProjectID').value = item.project_id || '0';
|
||||
document.getElementById('portfolioShowInResume').checked = item.show_in_resume || false;
|
||||
document.getElementById('portfolioHidden').checked = item.hidden || false;
|
||||
document.getElementById('portfolioPassword').value = item.password || '';
|
||||
document.getElementById('portfolioDoc').value = item.doc || '';
|
||||
document.getElementById('portfolioModal').classList.remove('hidden');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showCustomAlert('error', '错误', '获取项目信息失败');
|
||||
});
|
||||
}
|
||||
|
||||
function deletePortfolio(id) {
|
||||
showCustomConfirm('确认删除', '确定要删除该项目吗?', function(confirmed) {
|
||||
if (!confirmed) return;
|
||||
fetch('/dashboard/api/portfolio/' + id, { method: 'DELETE' })
|
||||
.then(response => response.json())
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
showCustomAlert('error', '错误', '删除失败:' + (result.message || '未知错误'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showCustomAlert('error', '错误', '删除失败');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function savePortfolio(event) {
|
||||
event.preventDefault();
|
||||
const id = document.getElementById('portfolioId').value;
|
||||
const userId = document.getElementById('portfolioUserId').value;
|
||||
const data = {
|
||||
user_id: userId,
|
||||
name: document.getElementById('portfolioName').value,
|
||||
description: document.getElementById('portfolioDescription').value,
|
||||
tech_stack: document.getElementById('portfolioTechStack').value.split('\n').map(s => s.trim()).filter(s => s),
|
||||
url: document.getElementById('portfolioURL').value,
|
||||
start_date: document.getElementById('portfolioStartDate').value,
|
||||
end_date: document.getElementById('portfolioEndDate').value,
|
||||
project_id: parseInt(document.getElementById('portfolioProjectID').value) || 0,
|
||||
show_in_resume: document.getElementById('portfolioShowInResume').checked,
|
||||
hidden: document.getElementById('portfolioHidden').checked,
|
||||
password: document.getElementById('portfolioPassword').value,
|
||||
doc: document.getElementById('portfolioDoc').value
|
||||
};
|
||||
|
||||
const url = id ? '/dashboard/api/portfolio/' + id : '/dashboard/api/portfolio';
|
||||
const method = id ? 'PUT' : 'POST';
|
||||
|
||||
fetch(url, {
|
||||
method: method,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
showCustomAlert('success', '成功', '保存成功');
|
||||
closePortfolioModal();
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
showCustomAlert('error', '错误', '保存失败:' + (result.message || '未知错误'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showCustomAlert('error', '错误', '保存失败');
|
||||
});
|
||||
}
|
||||
|
||||
function togglePortfolioHidden(id, isHidden) {
|
||||
fetch('/dashboard/api/portfolio/' + id, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ hidden: !isHidden })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
showCustomAlert('error', '错误', '操作失败:' + (result.message || '未知错误'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showCustomAlert('error', '错误', '操作失败');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user