mirror of
https://github.com/wahyd4/links.git
synced 2026-08-09 05:06:16 +10:00
Add hot links
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -20,11 +20,11 @@
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-600 mb-2">{% trans "Created At" %}:</p>
|
||||
<p>{{ link.created_at|date:"Y-m-d H:i" }}</p>
|
||||
<p class="utc-time" data-utc="{{ link.created_at|date:'c' }}">{{ link.created_at|date:"Y-m-d H:i" }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-600 mb-2">{% trans "Last Updated" %}:</p>
|
||||
<p>{{ link.updated_at|date:"Y-m-d H:i" }}</p>
|
||||
<p class="utc-time" data-utc="{{ link.updated_at|date:'c' }}">{{ link.updated_at|date:"Y-m-d H:i" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -85,3 +85,16 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const utcTimes = document.querySelectorAll('.utc-time');
|
||||
utcTimes.forEach(function(element) {
|
||||
const utcTime = new Date(element.dataset.utc);
|
||||
const localTime = utcTime.toLocaleString();
|
||||
element.textContent = localTime;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -2,6 +2,25 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Popular Links Section -->
|
||||
<div class="mb-8">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-4">{% trans "Popular Links" %}</h2>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
|
||||
{% for item in popular_links %}
|
||||
<div class="rounded-lg overflow-hidden shadow-md" style="background-color: {{ item.color }};">
|
||||
<div class="p-4">
|
||||
<a href="{% url 'redirect_to_original' item.link.alias %}" class="text-white hover:text-gray-200 font-semibold text-lg">
|
||||
{{ item.link.alias }}
|
||||
</a>
|
||||
<p class="text-white text-sm mt-2">{% trans "Clicks" %}: {{ item.link.click_count }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="text-gray-600 col-span-full">{% trans "No popular links available" %}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white shadow-md rounded-lg overflow-hidden">
|
||||
<div class="flex justify-between items-center p-6 bg-gray-50 border-b border-gray-200">
|
||||
<h1 class="text-2xl font-bold text-gray-800">{% trans "Link List" %}</h1>
|
||||
@@ -81,8 +100,12 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ link.click_count }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ link.created_at|date:"Y-m-d H:i" }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ link.updated_at|date:"Y-m-d H:i" }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span class="utc-time" data-utc="{{ link.created_at|date:'c' }}">{{ link.created_at|date:"Y-m-d H:i" }}</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span class="utc-time" data-utc="{{ link.updated_at|date:'c' }}">{{ link.updated_at|date:"Y-m-d H:i" }}</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<a href="{% url 'link_update' link.id %}" class="text-indigo-600 hover:text-indigo-900 mr-2">{% trans "Edit" %}</a>
|
||||
<a href="{% url 'link_delete' link.id %}" class="text-red-600 hover:text-red-900 mr-2">{% trans "Delete" %}</a>
|
||||
@@ -111,4 +134,17 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const utcTimes = document.querySelectorAll('.utc-time');
|
||||
utcTimes.forEach(function(element) {
|
||||
const utcTime = new Date(element.dataset.utc);
|
||||
const localTime = utcTime.toLocaleString();
|
||||
element.textContent = localTime;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -17,6 +17,32 @@
|
||||
</form>
|
||||
|
||||
<h2 class="text-xl font-semibold mt-8 mb-4">{% trans "Import Aliases" %}</h2>
|
||||
<div class="mb-4">
|
||||
<p class="text-gray-700">{% trans "Import aliases from a JSON file. The file should contain a list of objects with the following fields:" %}</p>
|
||||
<ul class="list-disc list-inside mt-2 ml-4 text-gray-600">
|
||||
<li>{% trans "alias (required): The short alias for the URL" %}</li>
|
||||
<li>{% trans "original_url (required): The original URL" %}</li>
|
||||
<li>{% trans "created_at (optional): Creation timestamp (ISO format)" %}</li>
|
||||
<li>{% trans "updated_at (optional): Last update timestamp (ISO format)" %}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-100 p-4 rounded-lg mb-4">
|
||||
<h3 class="text-lg font-semibold mb-2">{% trans "Example JSON file content:" %}</h3>
|
||||
<pre class="bg-white p-2 rounded text-sm overflow-x-auto">
|
||||
[
|
||||
{
|
||||
"alias": "example",
|
||||
"original_url": "https://example.com",
|
||||
"created_at": "2023-09-08T12:00:00Z",
|
||||
"updated_at": "2023-09-08T12:00:00Z"
|
||||
},
|
||||
{
|
||||
"alias": "google",
|
||||
"original_url": "https://www.google.com"
|
||||
}
|
||||
]
|
||||
</pre>
|
||||
</div>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="mb-4">
|
||||
|
||||
+18
-9
@@ -18,6 +18,8 @@ from django.http import JsonResponse, HttpResponse
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.utils.dateparse import parse_datetime
|
||||
|
||||
import random
|
||||
|
||||
class LinkListView(ListView):
|
||||
model = Link
|
||||
template_name = 'links/link_list.html'
|
||||
@@ -45,6 +47,20 @@ class LinkListView(ListView):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['current_sort'] = self.request.GET.get('sort', 'alias')
|
||||
context['current_order'] = self.request.GET.get('order', 'asc')
|
||||
|
||||
# Modern color palette
|
||||
colors = [
|
||||
'#3498db', '#2ecc71', '#e74c3c', '#f39c12', '#9b59b6',
|
||||
'#1abc9c', '#d35400', '#34495e', '#16a085', '#27ae60'
|
||||
]
|
||||
|
||||
# Add popular links to the context with random colors
|
||||
popular_links = Link.objects.order_by('-click_count')[:5]
|
||||
context['popular_links'] = [
|
||||
{'link': link, 'color': random.choice(colors)}
|
||||
for link in popular_links
|
||||
]
|
||||
|
||||
return context
|
||||
|
||||
class LinkCreateView(CreateView):
|
||||
@@ -115,15 +131,8 @@ def search_aliases(request):
|
||||
query = request.GET.get('q', '').strip()
|
||||
print(f"Search query: {query}") # Debug print
|
||||
|
||||
if not query:
|
||||
return JsonResponse([], safe=False)
|
||||
|
||||
if len(query) == 1:
|
||||
# For single-letter queries, only match the start of the alias
|
||||
links = Link.objects.filter(alias__istartswith=query)[:10]
|
||||
else:
|
||||
# For longer queries, keep the current behavior
|
||||
links = Link.objects.filter(Q(alias__icontains=query) | Q(original_url__icontains=query))[:10]
|
||||
# Use icontains for all query lengths
|
||||
links = Link.objects.filter(alias__icontains=query)[:10]
|
||||
|
||||
print(f"Found {links.count()} links") # Debug print
|
||||
|
||||
|
||||
Binary file not shown.
@@ -24,8 +24,8 @@ msgstr "英语"
|
||||
msgid "Simplified Chinese"
|
||||
msgstr "简体中文"
|
||||
|
||||
msgid "URL Manager"
|
||||
msgstr "URL管理器"
|
||||
msgid "GoLinks"
|
||||
msgstr "GoLinks"
|
||||
|
||||
msgid "Link List"
|
||||
msgstr "链接列表"
|
||||
@@ -121,7 +121,7 @@ msgid "Export Aliases"
|
||||
msgstr "导出别名"
|
||||
|
||||
msgid "Export as JSON"
|
||||
msgstr "导��为JSON"
|
||||
msgstr "导为JSON"
|
||||
|
||||
msgid "Import Aliases"
|
||||
msgstr "导入别名"
|
||||
@@ -129,6 +129,24 @@ msgstr "导入别名"
|
||||
msgid "Import from JSON"
|
||||
msgstr "从JSON导入"
|
||||
|
||||
msgid "Import aliases from a JSON file. The file should contain a list of objects with the following fields:"
|
||||
msgstr "从JSON文件导入别名。文件应包含具有以下字段的对象列表:"
|
||||
|
||||
msgid "alias (required): The short alias for the URL"
|
||||
msgstr "alias(必填):URL的短别名"
|
||||
|
||||
msgid "original_url (required): The original URL"
|
||||
msgstr "original_url(必填):原始URL"
|
||||
|
||||
msgid "created_at (optional): Creation timestamp (ISO format)"
|
||||
msgstr "created_at(可选):创建时间戳(ISO格式)"
|
||||
|
||||
msgid "updated_at (optional): Last update timestamp (ISO format)"
|
||||
msgstr "updated_at(可选):最后更新时间戳(ISO格式)"
|
||||
|
||||
msgid "Example JSON file content:"
|
||||
msgstr "示例JSON文件内容:"
|
||||
|
||||
msgid "{} aliases have been imported successfully."
|
||||
msgstr "{}个别名已成功导入。"
|
||||
|
||||
@@ -144,3 +162,9 @@ msgstr "JSON中存在无效项。每个项目必须是包含'alias'字段的对
|
||||
#: 3.12/lib/python3.12/site-packages/django/forms/fields.py:95
|
||||
msgid "This field is required."
|
||||
msgstr ""
|
||||
|
||||
msgid "Popular Links"
|
||||
msgstr "热门链接"
|
||||
|
||||
msgid "No popular links available"
|
||||
msgstr "暂无热门链接"
|
||||
|
||||
Vendored
+119
-39
@@ -843,6 +843,14 @@ select {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.z-20 {
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.col-span-full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.mx-auto {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
@@ -860,10 +868,18 @@ select {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mb-6 {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.ml-1 {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.ml-4 {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
@@ -876,6 +892,10 @@ select {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.mt-20 {
|
||||
margin-top: 5rem;
|
||||
}
|
||||
|
||||
.mt-6 {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
@@ -884,12 +904,8 @@ select {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.mt-20 {
|
||||
margin-top: 5rem;
|
||||
}
|
||||
|
||||
.mb-6 {
|
||||
margin-bottom: 1.5rem;
|
||||
.mb-8 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
@@ -932,22 +948,6 @@ select {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.w-4 {
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.w-64 {
|
||||
width: 16rem;
|
||||
}
|
||||
|
||||
.w-10 {
|
||||
width: 2.5rem;
|
||||
}
|
||||
|
||||
.w-1\/12 {
|
||||
width: 8.333333%;
|
||||
}
|
||||
@@ -960,6 +960,26 @@ select {
|
||||
width: 16.666667%;
|
||||
}
|
||||
|
||||
.w-10 {
|
||||
width: 2.5rem;
|
||||
}
|
||||
|
||||
.w-4 {
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.w-48 {
|
||||
width: 12rem;
|
||||
}
|
||||
|
||||
.w-64 {
|
||||
width: 16rem;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.min-w-full {
|
||||
min-width: 100%;
|
||||
}
|
||||
@@ -994,6 +1014,14 @@ select {
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
.list-inside {
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
.list-disc {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.grid-cols-1 {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
@@ -1131,6 +1159,11 @@ select {
|
||||
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-green-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
@@ -1141,23 +1174,22 @@ select {
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-green-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
|
||||
.bg-transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.p-6 {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.p-4 {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.p-6 {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.px-4 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
@@ -1183,6 +1215,16 @@ select {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.px-2 {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.py-1 {
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
@@ -1314,10 +1356,29 @@ select {
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.shadow-xl {
|
||||
--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.blur {
|
||||
--tw-blur: blur(8px);
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.filter {
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.transition {
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
.ease-in-out {
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
@@ -1327,21 +1388,26 @@ select {
|
||||
background-color: rgb(37 99 235 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-gray-100:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-gray-300:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(209 213 219 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-red-600:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-green-600:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-red-600:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:text-blue-800:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(30 64 175 / var(--tw-text-opacity));
|
||||
@@ -1352,6 +1418,11 @@ select {
|
||||
color: rgb(30 58 138 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:text-gray-200:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(229 231 235 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:text-green-900:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(20 83 45 / var(--tw-text-opacity));
|
||||
@@ -1367,11 +1438,6 @@ select {
|
||||
color: rgb(127 29 29 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:text-gray-200:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(229 231 235 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:underline:hover {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
@@ -1416,10 +1482,24 @@ select {
|
||||
.sm\:inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.sm\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.md\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.md\:grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.lg\:grid-cols-5 {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
+75
-8
@@ -5,7 +5,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans "URL Manager" %}</title>
|
||||
<title>{% trans "GoLinks" %}</title>
|
||||
<link href="{% static 'css/dist/styles.css' %}" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
@@ -28,26 +28,59 @@
|
||||
#search-results div:hover, #search-results div.active {
|
||||
background-color: #e2e8f0;
|
||||
}
|
||||
.search-container {
|
||||
position: relative;
|
||||
width: 40px;
|
||||
transition: width 0.3s ease-in-out;
|
||||
}
|
||||
.search-container.expanded {
|
||||
width: 200px;
|
||||
}
|
||||
#search-input {
|
||||
width: 100%;
|
||||
padding-left: 30px;
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="%23ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 8px center;
|
||||
background-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#search-input:focus {
|
||||
cursor: text;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<nav class="bg-custom-blue text-white p-4 fixed w-full top-0 z-10">
|
||||
<div class="container mx-auto flex justify-between items-center">
|
||||
<a href="{% url 'link_list' %}" class="text-xl font-bold">{% trans "URL Manager" %}</a>
|
||||
<a href="{% url 'link_list' %}" class="text-xl font-bold">{% trans "GoLinks" %}</a>
|
||||
<div class="flex items-center space-x-4">
|
||||
<a href="{% url 'search' %}" class="text-white hover:text-gray-200">{% trans "Advanced Search" %}</a>
|
||||
<a href="{% url 'tools' %}" class="text-white hover:text-gray-200">{% trans "Tools" %}</a>
|
||||
<div class="relative">
|
||||
<input type="text" id="search-input" class="bg-white text-gray-800 rounded px-4 py-2 w-64" placeholder="{% trans 'Search aliases...' %}">
|
||||
<div class="search-container">
|
||||
<input type="text" id="search-input" class="bg-transparent text-white rounded px-2 py-1" placeholder="">
|
||||
<div id="search-results" class="absolute top-full left-0 right-0 bg-white border border-gray-300 rounded mt-1 hidden"></div>
|
||||
</div>
|
||||
<div class="language-selector">
|
||||
<span class="flex items-center">
|
||||
<div class="language-selector relative">
|
||||
<button class="flex items-center text-white hover:text-gray-200">
|
||||
<i class="fas fa-globe mr-2"></i>
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_language_info for LANGUAGE_CODE as current_language %}
|
||||
{{ current_language.code|upper }}
|
||||
</span>
|
||||
</button>
|
||||
<div class="language-options absolute right-0 mt-2 py-2 w-48 bg-white rounded-md shadow-xl z-20 hidden">
|
||||
<form action="{% url 'set_language' %}" method="post" id="language-form">
|
||||
{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ request.path }}">
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<button type="submit" name="language" value="{{ language.code }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 w-full text-left {% if language.code == LANGUAGE_CODE %}font-bold{% endif %}">
|
||||
{{ language.name_local }} ({{ language.code|upper }})
|
||||
</button>
|
||||
{% endfor %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -63,17 +96,31 @@
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-2xl font-bold mb-4">Welcome to URL Manager</h1>
|
||||
<h1 class="text-2xl font-bold mb-4">Welcome to GoLinks</h1>
|
||||
<p>If you can see this message, the base template is working correctly.</p>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const searchContainer = document.querySelector('.search-container');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const searchResults = document.getElementById('search-results');
|
||||
let currentFocus = -1;
|
||||
|
||||
searchInput.addEventListener('focus', function() {
|
||||
searchContainer.classList.add('expanded');
|
||||
this.placeholder = "{% trans 'Search aliases...' %}";
|
||||
});
|
||||
|
||||
searchInput.addEventListener('blur', function() {
|
||||
if (this.value === '') {
|
||||
searchContainer.classList.remove('expanded');
|
||||
this.placeholder = "";
|
||||
}
|
||||
});
|
||||
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
@@ -169,6 +216,26 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Add this new script for language selector functionality
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const languageSelector = document.querySelector('.language-selector');
|
||||
const languageOptions = document.querySelector('.language-options');
|
||||
|
||||
languageSelector.addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
languageOptions.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
document.addEventListener('click', function() {
|
||||
languageOptions.classList.add('hidden');
|
||||
});
|
||||
|
||||
languageOptions.addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -23,7 +23,7 @@ ROOT_URLCONF = 'url_manager.urls'
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware', # 添加这行
|
||||
'django.middleware.locale.LocaleMiddleware', # Make sure this is here
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
|
||||
+2
-2
@@ -3,11 +3,11 @@ from django.urls import path, include
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
|
||||
urlpatterns = [
|
||||
path('search/', include('links.search_urls')), # Add this line
|
||||
path('i18n/', include('django.conf.urls.i18n')), # Move this outside of i18n_patterns
|
||||
path('search/', include('links.search_urls')),
|
||||
]
|
||||
|
||||
urlpatterns += i18n_patterns(
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('links.urls')),
|
||||
path('i18n/', include('django.conf.urls.i18n')),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user