mirror of
https://github.com/wahyd4/links.git
synced 2026-08-09 05:06:16 +10:00
UI change
This commit is contained in:
Binary file not shown.
@@ -22,14 +22,52 @@
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<input type="checkbox" id="select-all" class="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50">
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{% trans "Alias" %}</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<a href="?sort=alias&order={% if current_sort == 'alias' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
|
||||
{% trans "Alias" %}
|
||||
{% if current_sort == 'alias' %}
|
||||
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
|
||||
</svg>
|
||||
{% endif %}
|
||||
</a>
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{% trans "Original URL" %}</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{% trans "Clicks" %}</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<a href="?sort=clicks&order={% if current_sort == 'clicks' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
|
||||
{% trans "Clicks" %}
|
||||
{% if current_sort == 'clicks' %}
|
||||
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
|
||||
</svg>
|
||||
{% endif %}
|
||||
</a>
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<a href="?sort=created_at&order={% if current_sort == 'created_at' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
|
||||
{% trans "Created At" %}
|
||||
{% if current_sort == 'created_at' %}
|
||||
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
|
||||
</svg>
|
||||
{% endif %}
|
||||
</a>
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<a href="?sort=updated_at&order={% if current_sort == 'updated_at' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
|
||||
{% trans "Updated At" %}
|
||||
{% if current_sort == 'updated_at' %}
|
||||
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
|
||||
</svg>
|
||||
{% endif %}
|
||||
</a>
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{% for link in object_list %}
|
||||
{% for link in links %}
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<input type="checkbox" name="selected_links" value="{{ link.id }}" class="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50">
|
||||
@@ -41,6 +79,8 @@
|
||||
<a href="{{ link.original_url }}" target="_blank" class="text-blue-600 hover:text-blue-900">{{ link.original_url }}</a>
|
||||
</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 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>
|
||||
|
||||
@@ -16,6 +16,31 @@ from django.http import JsonResponse
|
||||
class LinkListView(ListView):
|
||||
model = Link
|
||||
template_name = 'links/link_list.html'
|
||||
context_object_name = 'links'
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
sort_by = self.request.GET.get('sort', 'alias')
|
||||
order = self.request.GET.get('order', 'asc')
|
||||
|
||||
if sort_by == 'alias':
|
||||
ordering = F('alias').asc(nulls_last=True) if order == 'asc' else F('alias').desc(nulls_last=True)
|
||||
elif sort_by == 'clicks':
|
||||
ordering = F('click_count').asc(nulls_last=True) if order == 'asc' else F('click_count').desc(nulls_last=True)
|
||||
elif sort_by == 'created_at':
|
||||
ordering = F('created_at').asc(nulls_last=True) if order == 'asc' else F('created_at').desc(nulls_last=True)
|
||||
elif sort_by == 'updated_at':
|
||||
ordering = F('updated_at').asc(nulls_last=True) if order == 'asc' else F('updated_at').desc(nulls_last=True)
|
||||
else:
|
||||
ordering = F('alias').asc(nulls_last=True)
|
||||
|
||||
return queryset.order_by(ordering)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['current_sort'] = self.request.GET.get('sort', 'alias')
|
||||
context['current_order'] = self.request.GET.get('order', 'asc')
|
||||
return context
|
||||
|
||||
class LinkCreateView(CreateView):
|
||||
model = Link
|
||||
|
||||
Binary file not shown.
@@ -78,8 +78,8 @@ msgstr "总点击次数"
|
||||
msgid "Created At"
|
||||
msgstr "创建时间"
|
||||
|
||||
msgid "Last Updated"
|
||||
msgstr "最后更新"
|
||||
msgid "Updated At"
|
||||
msgstr "更新时间"
|
||||
|
||||
msgid "Click Statistics"
|
||||
msgstr "点击统计"
|
||||
|
||||
Vendored
+22
@@ -900,6 +900,18 @@ select {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.ml-1 {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.mr-1 {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.mr-4 {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
@@ -952,6 +964,10 @@ select {
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.w-64 {
|
||||
width: 16rem;
|
||||
}
|
||||
|
||||
.min-w-full {
|
||||
min-width: 100%;
|
||||
}
|
||||
@@ -1020,6 +1036,12 @@ select {
|
||||
margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
|
||||
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-right: calc(1rem * var(--tw-space-x-reverse));
|
||||
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
|
||||
.divide-y > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-divide-y-reverse: 0;
|
||||
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
||||
|
||||
+49
-16
@@ -7,6 +7,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans "URL Manager" %}</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>
|
||||
/* ... other styles ... */
|
||||
#search-results {
|
||||
@@ -25,31 +26,63 @@
|
||||
#search-results div:hover {
|
||||
background-color: #f7fafc;
|
||||
}
|
||||
.language-selector {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.language-options {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
background-color: white;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
min-width: 150px; /* Increased width */
|
||||
}
|
||||
.language-selector:hover .language-options {
|
||||
display: block;
|
||||
}
|
||||
.language-option {
|
||||
padding: 0.5rem 1rem;
|
||||
color: #4a5568;
|
||||
}
|
||||
.language-option:hover {
|
||||
background-color: #f7fafc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<nav class="bg-blue-600 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>
|
||||
<div class="flex items-center">
|
||||
<div class="relative mr-2">
|
||||
<input type="text" id="search-input" class="bg-white text-gray-800 rounded px-2 py-1" placeholder="{% trans 'Search aliases...' %}">
|
||||
<div class="flex items-center space-x-4"> <!-- Added space-x-4 for more spacing -->
|
||||
<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...' %}"> <!-- Increased width and padding -->
|
||||
<div id="search-results" class="absolute top-full left-0 right-0 bg-white border border-gray-300 rounded mt-1 hidden"></div>
|
||||
</div>
|
||||
<form action="{% url 'set_language' %}" method="post" class="inline">
|
||||
{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ request.path }}">
|
||||
<select name="language" onchange="this.form.submit()" class="bg-white text-gray-800 rounded px-2 py-1">
|
||||
<div class="language-selector">
|
||||
<span class="flex items-center">
|
||||
<i class="fas fa-globe mr-2"></i> <!-- Increased margin -->
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
|
||||
{{ language.name_local }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
{% get_language_info for LANGUAGE_CODE as current_language %}
|
||||
{{ current_language.code|upper }}
|
||||
</span>
|
||||
<div class="language-options">
|
||||
<form action="{% url 'set_language' %}" method="post">
|
||||
{% 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="language-option w-full text-left">
|
||||
{{ language.name_local }} ({{ language.code|upper }})
|
||||
</button>
|
||||
{% endfor %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
Reference in New Issue
Block a user