diff --git a/data/db.sqlite3 b/data/db.sqlite3 index ab521e1..bcd0367 100644 Binary files a/data/db.sqlite3 and b/data/db.sqlite3 differ diff --git a/links/__pycache__/views.cpython-312.pyc b/links/__pycache__/views.cpython-312.pyc index 4c90855..635f9ff 100644 Binary files a/links/__pycache__/views.cpython-312.pyc and b/links/__pycache__/views.cpython-312.pyc differ diff --git a/links/templates/links/link_detail.html b/links/templates/links/link_detail.html index 539b2bb..8b005e7 100644 --- a/links/templates/links/link_detail.html +++ b/links/templates/links/link_detail.html @@ -20,11 +20,11 @@

{% trans "Created At" %}:

-

{{ link.created_at|date:"Y-m-d H:i" }}

+

{{ link.created_at|date:"Y-m-d H:i" }}

{% trans "Last Updated" %}:

-

{{ link.updated_at|date:"Y-m-d H:i" }}

+

{{ link.updated_at|date:"Y-m-d H:i" }}

@@ -85,3 +85,16 @@ {% endblock %} + +{% block extra_js %} + +{% endblock %} diff --git a/links/templates/links/link_list.html b/links/templates/links/link_list.html index aa18f2d..62cb13d 100644 --- a/links/templates/links/link_list.html +++ b/links/templates/links/link_list.html @@ -2,6 +2,25 @@ {% load i18n %} {% block content %} + +
+

{% trans "Popular Links" %}

+
+ {% for item in popular_links %} +
+
+ + {{ item.link.alias }} + +

{% trans "Clicks" %}: {{ item.link.click_count }}

+
+
+ {% empty %} +

{% trans "No popular links available" %}

+ {% endfor %} +
+
+

{% trans "Link List" %}

@@ -81,8 +100,12 @@
{{ link.click_count }} - {{ link.created_at|date:"Y-m-d H:i" }} - {{ link.updated_at|date:"Y-m-d H:i" }} + + {{ link.created_at|date:"Y-m-d H:i" }} + + + {{ link.updated_at|date:"Y-m-d H:i" }} + {% trans "Edit" %} {% trans "Delete" %} @@ -111,4 +134,17 @@ } }); + + {% block extra_js %} + + {% endblock %} {% endblock %} diff --git a/links/templates/links/tools.html b/links/templates/links/tools.html index c0252ed..5b11715 100644 --- a/links/templates/links/tools.html +++ b/links/templates/links/tools.html @@ -17,6 +17,32 @@

{% trans "Import Aliases" %}

+
+

{% trans "Import aliases from a JSON file. The file should contain a list of objects with the following fields:" %}

+ +
+
+

{% trans "Example JSON file content:" %}

+
+[
+  {
+    "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"
+  }
+]
+        
+
{% csrf_token %}
diff --git a/links/views.py b/links/views.py index 7601980..23529ea 100644 --- a/links/views.py +++ b/links/views.py @@ -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 diff --git a/locale/zh_Hans/LC_MESSAGES/django.mo b/locale/zh_Hans/LC_MESSAGES/django.mo index 079e0c2..d53930d 100644 Binary files a/locale/zh_Hans/LC_MESSAGES/django.mo and b/locale/zh_Hans/LC_MESSAGES/django.mo differ diff --git a/locale/zh_Hans/LC_MESSAGES/django.po b/locale/zh_Hans/LC_MESSAGES/django.po index a224fc7..8cf3054 100644 --- a/locale/zh_Hans/LC_MESSAGES/django.po +++ b/locale/zh_Hans/LC_MESSAGES/django.po @@ -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 "暂无热门链接" diff --git a/new_theme/static/css/dist/styles.css b/new_theme/static/css/dist/styles.css index 2373b97..5f6c690 100644 --- a/new_theme/static/css/dist/styles.css +++ b/new_theme/static/css/dist/styles.css @@ -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)); + } } diff --git a/templates/base.html b/templates/base.html index 6175cd4..304a724 100644 --- a/templates/base.html +++ b/templates/base.html @@ -5,7 +5,7 @@ - {% trans "URL Manager" %} + {% trans "GoLinks" %}