Your commit message
8
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
15
.idea/deployment.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PublishConfigData" autoUpload="Always" remoteFilesAllowedToDisappearOnAutoupload="false">
|
||||
<serverData>
|
||||
<paths name="bt">
|
||||
<serverdata>
|
||||
<mappings>
|
||||
<mapping deploy="/www/wwwroot/unapp_admin" local="$PROJECT_DIR$" web="/" />
|
||||
</mappings>
|
||||
</serverdata>
|
||||
</paths>
|
||||
</serverData>
|
||||
<option name="myAutoUpload" value="ALWAYS" />
|
||||
</component>
|
||||
</project>
|
9
.idea/encodings.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/AI日志.log" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/MyApi/AiMssage.py" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/MyApi/urls.py" charset="UTF-8" />
|
||||
<file url="PROJECT" charset="GBK" />
|
||||
</component>
|
||||
</project>
|
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
7
.idea/misc.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.10 (website)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (website)" project-jdk-type="Python SDK" />
|
||||
</project>
|
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/website.iml" filepath="$PROJECT_DIR$/.idea/website.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
8
.idea/sshConfigs.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SshConfigs">
|
||||
<configs>
|
||||
<sshConfig authType="PASSWORD" host="81.70.187.27" id="1116324b-4de4-402e-88b4-7d91bcd3eeb2" port="22" nameFormat="DESCRIPTIVE" username="root" useOpenSSHConfig="true" />
|
||||
</configs>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
14
.idea/webServers.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebServers">
|
||||
<option name="servers">
|
||||
<webServer id="a6d64284-97a3-4cf2-989c-9e8c30a5aaa1" name="bt" url="http://81.70.187.27/">
|
||||
<fileTransfer accessType="SFTP" host="81.70.187.27" port="22" sshConfigId="1116324b-4de4-402e-88b4-7d91bcd3eeb2" sshConfig="root@81.70.187.27:22 password">
|
||||
<advancedOptions>
|
||||
<advancedOptions dataProtectionLevel="Private" keepAliveTimeout="0" passiveMode="true" shareSSLContext="true" isUseSudo="true" />
|
||||
</advancedOptions>
|
||||
</fileTransfer>
|
||||
</webServer>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
30
.idea/website.iml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="django" name="Django">
|
||||
<configuration>
|
||||
<option name="rootFolder" value="$MODULE_DIR$" />
|
||||
<option name="settingsModule" value="website/settings.py" />
|
||||
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
<option name="doNotUseTestRunner" value="false" />
|
||||
<option name="trackFilePattern" value="migrations" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
<option name="TEMPLATE_FOLDERS">
|
||||
<list>
|
||||
<option value="$MODULE_DIR$/../website\templates" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</module>
|
26
MyWeb/WXBizDataCrypt.py
Normal file
@ -0,0 +1,26 @@
|
||||
from base64 import b64decode
|
||||
from Crypto.Cipher import AES
|
||||
import json
|
||||
|
||||
class WXBizDataCrypt:
|
||||
def __init__(self, appid, session_key):
|
||||
self.appid = appid
|
||||
self.session_key = b64decode(session_key)
|
||||
|
||||
def decrypt(self, encrypted_data, iv):
|
||||
# AES解密
|
||||
try:
|
||||
cipher = AES.new(self.session_key, AES.MODE_CBC, b64decode(iv))
|
||||
decrypted = json.loads(self._unpad(cipher.decrypt(b64decode(encrypted_data))))
|
||||
if decrypted['watermark']['appid'] != self.appid:
|
||||
raise Exception('Invalid Buffer')
|
||||
return decrypted
|
||||
except Exception as e:
|
||||
raise Exception('Failed to decrypt data')
|
||||
|
||||
def _unpad(self, s):
|
||||
try:
|
||||
return s[:-ord(s[len(s) - 1:])]
|
||||
except Exception as e:
|
||||
print("Error during unpadding:", e)
|
||||
return s # 返回原始数据以便进一步分析错误
|
0
MyWeb/__init__.py
Normal file
3
MyWeb/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
6
MyWeb/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MywebConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'MyWeb'
|
13
MyWeb/decorators.py
Normal file
@ -0,0 +1,13 @@
|
||||
from django.shortcuts import redirect
|
||||
from functools import wraps
|
||||
|
||||
def admin_login_required(view_func):
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(request, *args, **kwargs):
|
||||
# 这里使用 'admin_id' 作为登录状态的标记
|
||||
if 'admin_id' not in request.session:
|
||||
# 用户未登录,重定向到登录页面
|
||||
return redirect('admin_login')
|
||||
# 用户已登录,执行原始视图函数
|
||||
return view_func(request, *args, **kwargs)
|
||||
return _wrapped_view
|
3
MyWeb/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
27
MyWeb/urls.py
Normal file
@ -0,0 +1,27 @@
|
||||
# myapp/urls.py
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.admin_login, name='admin_login'),
|
||||
path('admin_dashboard/', views.admin_dashboard, name='admin_dashboard'),
|
||||
path('admin_logout/', views.admin_logout, name='admin_logout'),
|
||||
path('main/', views.main, name='main'),
|
||||
path('miniapp_users/', views.miniapp_users, name='miniapp_users'),
|
||||
path('wechat_users/', views.wechat_users, name='wechat_users'),
|
||||
path('other_sources/', views.other_sources, name='other_sources'),
|
||||
path('site_admins/', views.site_admins, name='site_admins'),
|
||||
path('video_extraction_records/', views.video_extraction_records, name='video_extraction_records'),
|
||||
path('copy_extraction_records/', views.copy_extraction_records, name='copy_extraction_records'),
|
||||
path('ai_api_usage/', views.ai_api_usage, name='ai_api_usage'),
|
||||
path('website_settings/', views.website_settings, name='website_settings'),
|
||||
path('miniapp_settings/', views.miniapp_settings, name='miniapp_settings'),
|
||||
path('copywriting_library/', views.copywriting_library, name='copywriting_library'),
|
||||
path('article_records/', views.article_records, name='article_records'),
|
||||
path('material_library/', views.material_library, name='material_library'),
|
||||
path('delete-similar-texts/', views.delete_similar_texts_view, name='delete_similar_texts'),
|
||||
path('redemption-cards/', views.redemption_card_list, name='redemption_card_list'),
|
||||
path('membership-types/', views.membership_type_list, name='membership_type_list'),
|
||||
path('transaction-logs/', views.transaction_log_list, name='transaction_log_list'),
|
||||
|
||||
]
|
609
MyWeb/views.py
Normal file
@ -0,0 +1,609 @@
|
||||
import hashlib
|
||||
import json
|
||||
import random
|
||||
import time
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from django.db.models import Q
|
||||
from django.http import JsonResponse, HttpResponseRedirect, HttpResponse
|
||||
from django.contrib.auth.hashers import check_password
|
||||
from MyApi.models import Administrator, Copywriting, User, FriendRequest, ApiCallLog, MembershipType, RedemptionCard, \
|
||||
TransactionLog
|
||||
from django.contrib.auth import login as django_login, logout as django_logout, login
|
||||
from django.shortcuts import render, redirect
|
||||
from .decorators import admin_login_required
|
||||
from django.contrib.auth import logout
|
||||
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
|
||||
from django.db import transaction
|
||||
import Levenshtein
|
||||
from urllib.parse import unquote, quote
|
||||
|
||||
|
||||
def admin_login(request):
|
||||
# 检查是否已登录
|
||||
if 'admin_id' in request.session:
|
||||
return redirect('admin_dashboard') # 使用视图的名称执行重定向
|
||||
|
||||
if request.method == 'POST':
|
||||
data = json.loads(request.body)
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
try:
|
||||
user = Administrator.objects.get(username=username)
|
||||
except Administrator.DoesNotExist:
|
||||
return JsonResponse({'status': 'error', 'message': '用户不存在'})
|
||||
|
||||
# 检查密码是否正确
|
||||
if user and check_password(password, user.password):
|
||||
if user.is_active:
|
||||
# 登录成功,设置会话
|
||||
request.session['role'] = user.role
|
||||
request.session['admin_id'] = user.id
|
||||
return JsonResponse({'status': 'success', 'message': '登录成功'})
|
||||
else:
|
||||
return JsonResponse({'status': 'error', 'message': '用户已被禁用'})
|
||||
else:
|
||||
return JsonResponse({'status': 'error', 'message': '密码错误'})
|
||||
else:
|
||||
# 非POST请求,返回登录页面
|
||||
return render(request, 'login.html')
|
||||
|
||||
|
||||
@admin_login_required
|
||||
def admin_dashboard(request):
|
||||
# 从session获取管理员ID
|
||||
admin_id = request.session.get('admin_id')
|
||||
|
||||
# 使用管理员ID查询管理员信息
|
||||
try:
|
||||
admin = Administrator.objects.get(id=admin_id)
|
||||
except Administrator.DoesNotExist:
|
||||
# 如果没有找到管理员,可以重定向到登录页面或显示错误信息
|
||||
return HttpResponse('管理员不存在', status=404)
|
||||
|
||||
# 将管理员信息添加到上下文中
|
||||
# context = {
|
||||
# 'admin_username': admin.username,
|
||||
# 'admin_role': admin.role,
|
||||
# # 添加更多管理员信息到上下文中,根据需要
|
||||
# }
|
||||
|
||||
return render(request, 'index.html', {'username':admin.username})
|
||||
|
||||
|
||||
def admin_logout(request):
|
||||
# 登出用户
|
||||
logout(request)
|
||||
# 重定向到登录页面,或者你希望用户登出后看到的页面
|
||||
return redirect('admin_login')
|
||||
|
||||
|
||||
@admin_login_required
|
||||
# 小程序用户管理页面
|
||||
def miniapp_users(request):
|
||||
if request.method == "POST":
|
||||
data = json.loads(request.body)
|
||||
action = data.get('action')
|
||||
if action == 'fetch':
|
||||
# 分页获取小程序用户数据
|
||||
users_list = User.objects.all().order_by('-created_at')
|
||||
page = data.get('page', 1)
|
||||
paginator = Paginator(users_list, 10) # 每页显示10条记录
|
||||
|
||||
try:
|
||||
users = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
users = paginator.page(1)
|
||||
except EmptyPage:
|
||||
users = paginator.page(paginator.num_pages)
|
||||
|
||||
users_data = list(users.object_list.values(
|
||||
'openid', 'wxid','wechat_number', 'nickname', 'gender', 'region', 'email',
|
||||
'phone', 'scene', 'is_member', 'member_start_time',
|
||||
'member_end_time', 'is_active', 'usage_count', 'created_at'
|
||||
))
|
||||
|
||||
return JsonResponse({
|
||||
'data': users_data,
|
||||
'code': 0,
|
||||
'count': paginator.count,
|
||||
'msg': ''
|
||||
})
|
||||
|
||||
elif action == 'add':
|
||||
print('添加')
|
||||
elif action == 'edit':
|
||||
# 编辑用户信息
|
||||
user_id = data.get('id')
|
||||
try:
|
||||
user = User.objects.get(id=user_id)
|
||||
user.wxid = data.get('wxid', user.wxid)
|
||||
user.nickname = data.get('nickname', user.nickname)
|
||||
user.gender = data.get('gender', user.gender)
|
||||
user.region = data.get('region', user.region)
|
||||
user.email = data.get('email', user.email)
|
||||
user.phone = data.get('phone', user.phone)
|
||||
user.scene = data.get('scene', user.scene)
|
||||
user.is_member = data.get('is_member', user.is_member)
|
||||
user.member_start_time = data.get('member_start_time', user.member_start_time)
|
||||
user.member_end_time = data.get('member_end_time', user.member_end_time)
|
||||
user.is_active = data.get('is_active', user.is_active)
|
||||
user.usage_count = data.get('usage_count', user.usage_count)
|
||||
user.save()
|
||||
return JsonResponse({'status': 'success', 'msg': '用户信息更新成功'})
|
||||
except User.DoesNotExist:
|
||||
return JsonResponse({'status': 'error', 'msg': '用户不存在'})
|
||||
elif action == 'delete':
|
||||
# 删除用户
|
||||
user_id = data.get('id')
|
||||
User.objects.filter(id=user_id).delete()
|
||||
return JsonResponse({'status': 'success', 'msg': '用户删除成功'})
|
||||
|
||||
return render(request, 'miniapp_users.html')
|
||||
|
||||
@admin_login_required
|
||||
# 微信用户管理页面
|
||||
def wechat_users(request):
|
||||
if request.method == 'POST':
|
||||
data = json.loads(request.body)
|
||||
action = data.get('action')
|
||||
|
||||
if action == 'fetch':
|
||||
# 处理获取好友请求记录的逻辑
|
||||
page_number = data.get('page', 1)
|
||||
limit = data.get('limit', 10)
|
||||
search_keyword = data.get('search', '')
|
||||
|
||||
friend_requests_query = FriendRequest.objects.filter(
|
||||
Q(fromnickname__icontains=search_keyword) |
|
||||
Q(alias__icontains=search_keyword) |
|
||||
Q(fromusername__icontains=search_keyword)
|
||||
).order_by('-time')
|
||||
paginator = Paginator(friend_requests_query, limit)
|
||||
try:
|
||||
friend_requests_page = paginator.page(page_number)
|
||||
except PageNotAnInteger:
|
||||
friend_requests_page = paginator.page(1)
|
||||
except EmptyPage:
|
||||
friend_requests_page = paginator.page(paginator.num_pages)
|
||||
|
||||
friend_requests_list = list(friend_requests_page.object_list.values(
|
||||
'id', 'fromusername', 'alias', 'fromnickname', 'country',
|
||||
'province', 'city', 'sex', 'scene', 'time', 'towxid'
|
||||
))
|
||||
|
||||
return JsonResponse({
|
||||
'code': 0,
|
||||
'msg': '',
|
||||
'count': paginator.count,
|
||||
'data': friend_requests_list
|
||||
})
|
||||
|
||||
elif action == 'add':
|
||||
# 处理添加好友请求记录的逻辑
|
||||
pass # 实现添加逻辑
|
||||
|
||||
elif action == 'edit':
|
||||
# 处理编辑好友请求记录的逻辑
|
||||
pass # 实现编辑逻辑
|
||||
|
||||
elif action == 'delete':
|
||||
# 处理删除好友请求记录的逻辑
|
||||
fr_id = data.get('id')
|
||||
FriendRequest.objects.filter(id=fr_id).delete()
|
||||
return JsonResponse({'status': 'success', 'msg': '记录删除成功'})
|
||||
return render(request, 'wechat_users.html')
|
||||
|
||||
@admin_login_required
|
||||
# 其他来源用户管理页面
|
||||
def other_sources(request):
|
||||
# 实现具体逻辑
|
||||
return render(request, 'other_sources.html')
|
||||
|
||||
@admin_login_required
|
||||
# 网站管理员管理页面
|
||||
def site_admins(request):
|
||||
# 实现具体逻辑
|
||||
return render(request, 'site_admins.html')
|
||||
|
||||
@admin_login_required
|
||||
# 视频提取记录管理页面
|
||||
def video_extraction_records(request):
|
||||
# 实现具体逻辑
|
||||
return render(request, 'video_extraction_records.html')
|
||||
|
||||
@admin_login_required
|
||||
# 文案提取记录管理页面
|
||||
def copy_extraction_records(request):
|
||||
# 实现具体逻辑
|
||||
return render(request, 'copy_extraction_records.html')
|
||||
|
||||
@admin_login_required
|
||||
# AI接口使用情况页面
|
||||
def ai_api_usage(request):
|
||||
# 实现具体逻辑
|
||||
return render(request, 'ai_api_usage.html')
|
||||
|
||||
@admin_login_required
|
||||
# 网站设置页面
|
||||
def website_settings(request):
|
||||
# 实现具体逻辑
|
||||
return render(request, 'website_settings.html')
|
||||
|
||||
@admin_login_required
|
||||
# 小程序设置页面
|
||||
def miniapp_settings(request):
|
||||
# 实现具体逻辑
|
||||
return render(request, 'miniapp_settings.html')
|
||||
|
||||
@admin_login_required
|
||||
# 后台主页
|
||||
def main(request):
|
||||
# 实现具体逻辑
|
||||
return render(request, 'main.html')
|
||||
|
||||
# 文案库页面
|
||||
@admin_login_required
|
||||
def copywriting_library(request):
|
||||
if request.method == "POST":
|
||||
print(request.body)
|
||||
data = json.loads(request.body)
|
||||
action = data.get('action', '')
|
||||
print(data)
|
||||
if action == 'fetch':
|
||||
# 分页参数
|
||||
page = data.get('page', 1)
|
||||
limit = data.get('limit', 10)
|
||||
search = data.get('search', '')
|
||||
search = quote(search)
|
||||
# 过滤和排序Q(text_content__icontains=search_query)
|
||||
queryset = Copywriting.objects.filter(Q(text_content__icontains=search)).order_by('-added_time')
|
||||
paginator = Paginator(queryset, limit)
|
||||
try:
|
||||
copywritings = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
copywritings = paginator.page(1)
|
||||
except EmptyPage:
|
||||
copywritings = paginator.page(paginator.num_pages)
|
||||
|
||||
copywriting_list = list(
|
||||
copywritings.object_list.values('id', 'text_content', 'source', 'popularity','tag','is_approved', 'added_time'))
|
||||
return JsonResponse({
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"count": paginator.count,
|
||||
"data": copywriting_list
|
||||
})
|
||||
|
||||
elif action == 'add':
|
||||
# 添加文案
|
||||
text_content = data.get('text_content', '')
|
||||
source = data.get('source', '')
|
||||
tag = data.get('tag', '')
|
||||
popularity = data.get('popularity', 0)
|
||||
copywriting = Copywriting(
|
||||
text_content=text_content,
|
||||
source=source,
|
||||
tag=tag,
|
||||
popularity=popularity
|
||||
)
|
||||
copywriting.save()
|
||||
return JsonResponse({"status": "success", "msg": "文案添加成功"})
|
||||
|
||||
elif action == 'batch-delete':
|
||||
# 批量删除文案
|
||||
ids = data.get('ids', [])
|
||||
Copywriting.objects.filter(id__in=ids).delete()
|
||||
return JsonResponse({"status": "success", "msg": "文案批量删除成功"})
|
||||
elif action == 'delete':
|
||||
# 批量删除文案
|
||||
id = data.get('id')
|
||||
Copywriting.objects.filter(id=id).delete()
|
||||
return JsonResponse({"status": "success", "msg": "文案批量删除成功"})
|
||||
elif action == 'edit':
|
||||
# 获取请求中的数据
|
||||
copywriting_id = data.get('id')
|
||||
text_content = data.get('text_content')
|
||||
source = data.get('source')
|
||||
tag = data.get('tag')
|
||||
|
||||
try:
|
||||
# 根据ID查找要编辑的文案实例
|
||||
copywriting = Copywriting.objects.get(id=copywriting_id)
|
||||
# 更新文案实例的字段
|
||||
copywriting.text_content = text_content
|
||||
copywriting.source = source
|
||||
copywriting.tag = tag
|
||||
copywriting.save() # 保存更改到数据库
|
||||
return JsonResponse({'status': 'success', 'message': '文案更新成功'})
|
||||
except Copywriting.DoesNotExist:
|
||||
return JsonResponse({'status': 'error', 'message': '文案未找到'})
|
||||
elif action == 'approve':
|
||||
try:
|
||||
copywriting_id = data.get('id')
|
||||
copywriting = Copywriting.objects.get(id=copywriting_id)
|
||||
copywriting.is_approved = 1
|
||||
copywriting.save() # 保存更改到数据库
|
||||
return JsonResponse({'status': 'success', 'message': '文案审核成功'})
|
||||
except Copywriting.DoesNotExist:
|
||||
return JsonResponse({'status': 'error', 'message': '文案未找到'})
|
||||
|
||||
# 对于非POST请求或者没有指定操作的POST请求,返回页面
|
||||
return render(request, 'copywriting_library.html')
|
||||
@admin_login_required
|
||||
# 文章记录页面
|
||||
def article_records(request):
|
||||
# 这里实现具体的视图逻辑
|
||||
return render(request, 'article_records.html')
|
||||
|
||||
@admin_login_required
|
||||
# 素材库页面
|
||||
def material_library(request):
|
||||
# 这里实现具体的视图逻辑
|
||||
return render(request, 'material_library.html')
|
||||
|
||||
#删除重复文案
|
||||
def get_similar_texts(threshold=0.8):
|
||||
# 假设threshold为相似度阈值
|
||||
all_texts = Copywriting.objects.filter(is_approved=0) # 仅考虑已审核的文案
|
||||
print(len(all_texts))
|
||||
to_delete = []
|
||||
|
||||
# 将查询到的文案内容解码
|
||||
decoded_texts = [(text, unquote(text.text_content)) for text in all_texts]
|
||||
|
||||
for i, (text1, decoded_content1) in enumerate(decoded_texts):
|
||||
for text2, decoded_content2 in decoded_texts[i + 1:]:
|
||||
# 计算解码后的文本之间的相似度
|
||||
if Levenshtein.ratio(decoded_content1, decoded_content2) > threshold:
|
||||
# 如果相似度大于阈值,选择热度较低的文案删除
|
||||
if text1.popularity <= text2.popularity:
|
||||
to_delete.append(text1)
|
||||
else:
|
||||
to_delete.append(text2)
|
||||
|
||||
return set(to_delete) # 使用集合避免重复
|
||||
|
||||
@transaction.atomic
|
||||
def delete_similar_texts():
|
||||
to_delete = get_similar_texts()
|
||||
deleted_count = len(to_delete) # 计算删除对象的数量
|
||||
for text in to_delete:
|
||||
text.delete() # 删除操作
|
||||
return deleted_count # 返回删除的行数
|
||||
|
||||
def delete_similar_texts_view(request):
|
||||
deleted_count = delete_similar_texts()
|
||||
print(f"Deleted {deleted_count} similar texts.")
|
||||
# 返回结果
|
||||
return JsonResponse({'message': f'Deleted {deleted_count} similar texts.'})
|
||||
|
||||
|
||||
|
||||
|
||||
def generate_redemption_code(prefix):
|
||||
# 生成唯一的 UUID
|
||||
unique_id = uuid.uuid4()
|
||||
|
||||
# 获取当前时间戳
|
||||
current_timestamp = int(time.time())
|
||||
|
||||
# 使用 MD5 算法生成散列值
|
||||
hash_object = hashlib.md5(unique_id.bytes + str(current_timestamp).encode())
|
||||
hash_hex = hash_object.hexdigest()
|
||||
|
||||
# 将前缀和散列值的前8位拼接起来作为卡密
|
||||
redemption_code = f"{prefix}{hash_hex[:8]}"
|
||||
return redemption_code
|
||||
#卡密管理
|
||||
@admin_login_required
|
||||
def redemption_card_list(request):
|
||||
if request.method == "POST":
|
||||
data = json.loads(request.body)
|
||||
action = data.get('action', '')
|
||||
if action == 'fetch':
|
||||
# 获取分页参数
|
||||
page = int(data.get('page', 1))
|
||||
limit = int(data.get('limit', 10))
|
||||
search = data.get('search', '')
|
||||
search = quote(search)
|
||||
|
||||
# 过滤和排序
|
||||
queryset = RedemptionCard.objects.filter().order_by('-card_creation_time')
|
||||
paginator = Paginator(queryset, limit)
|
||||
|
||||
try:
|
||||
redemptionCard = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
redemptionCard = paginator.page(1)
|
||||
except EmptyPage:
|
||||
redemptionCard = paginator.page(paginator.num_pages)
|
||||
|
||||
copywriting_list = list(
|
||||
redemptionCard.object_list.values('id', 'code', 'card_type', 'created_date', 'expiry_date', 'is_used',
|
||||
'used_by_openid','used_by_nickname','card_creation_time','validity_period'))
|
||||
|
||||
return JsonResponse({
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"count": paginator.count,
|
||||
"data": copywriting_list
|
||||
})
|
||||
elif action == 'edit':
|
||||
try:
|
||||
redemptionCard_id = data.get('id')
|
||||
redemptionCard = RedemptionCard.objects.get(id=redemptionCard_id)
|
||||
# 更新文案
|
||||
# 这里添加更新文案的逻辑
|
||||
return JsonResponse({'status': 'success', 'message': '更新成功'})
|
||||
except RedemptionCard.DoesNotExist:
|
||||
return JsonResponse({'status': 'error', 'message': '卡密未找到'})
|
||||
elif action == 'add':
|
||||
try:
|
||||
# 解析请求数据
|
||||
code_prefix = data.get('code_prefix', '')
|
||||
card_type = data.get('card_type', '')
|
||||
quantity = int(data.get('quantity', 0))
|
||||
expiry_date = data.get('expiry_date', '')
|
||||
validity_period = int(data.get('validity_period',0))
|
||||
|
||||
# 添加卡密逻辑
|
||||
for _ in range(quantity):
|
||||
card = RedemptionCard.objects.create(
|
||||
code=generate_redemption_code(code_prefix),
|
||||
card_type=card_type,
|
||||
expiry_date=expiry_date,
|
||||
validity_period=validity_period
|
||||
)
|
||||
|
||||
return JsonResponse({'status': 'success', 'message': '卡密添加成功'})
|
||||
except Exception as e:
|
||||
return JsonResponse({'status': 'error', 'message': str(e)})
|
||||
elif action == 'delete':
|
||||
try:
|
||||
redemptionCard_id = data.get('id')
|
||||
redemptionCard = RedemptionCard.objects.get(id=redemptionCard_id)
|
||||
redemptionCard.delete() # 删除单个卡密记录
|
||||
return JsonResponse({'status': 'success', 'message': '卡密删除成功'})
|
||||
except RedemptionCard.DoesNotExist:
|
||||
return JsonResponse({'status': 'error', 'message': '卡密未找到'})
|
||||
|
||||
elif action == 'batch_delete':
|
||||
try:
|
||||
ids = data.get('ids', [])
|
||||
RedemptionCard.objects.filter(id__in=ids).delete() # 批量删除卡密记录
|
||||
return JsonResponse({'status': 'success', 'message': '批量删除卡密成功'})
|
||||
except Exception as e:
|
||||
return JsonResponse({'status': 'error', 'message': str(e)})
|
||||
|
||||
else:
|
||||
return JsonResponse({
|
||||
"code": 400,
|
||||
"msg": "Invalid action parameter.",
|
||||
})
|
||||
|
||||
return render(request, 'redemption_card.html')
|
||||
|
||||
|
||||
# 会员配置
|
||||
@admin_login_required
|
||||
def membership_type_list(request):
|
||||
if request.method == 'POST':
|
||||
data = json.loads(request.body)
|
||||
action = data['action']
|
||||
if action == 'fetch': # 读取会员卡类型列表
|
||||
is_quota = data.get('is_quota', None)
|
||||
if is_quota is not None:
|
||||
membership_types = MembershipType.objects.filter(is_quota=is_quota)
|
||||
else:
|
||||
membership_types = MembershipType.objects.all()
|
||||
|
||||
# 将会员卡类型列表序列化为JSON格式
|
||||
membership_types_data = [
|
||||
{
|
||||
'id': m.id,
|
||||
'type': m.type,
|
||||
'title': m.title,
|
||||
'description': m.description,
|
||||
'price': m.price,
|
||||
'duration_days': m.duration_days,
|
||||
'coins': m.coins,
|
||||
'is_quota': m.is_quota
|
||||
} for m in membership_types
|
||||
]
|
||||
return JsonResponse({"code": 0, 'data': membership_types_data})
|
||||
elif action == 'add': # 添加会员卡类型
|
||||
# 获取前端传递过来的数据
|
||||
membership_type = MembershipType(
|
||||
type=data.get('type'),
|
||||
title=data.get('title'),
|
||||
description=data.get('description'),
|
||||
price=data.get('price'),
|
||||
duration_days=data.get('duration_days'),
|
||||
coins=data.get('coins'),
|
||||
is_quota=data.get('is_quota')
|
||||
)
|
||||
membership_type.save()
|
||||
return JsonResponse({'status': 'success', 'message': '会员卡类型添加成功'})
|
||||
elif action == 'edit': # 编辑会员卡类型
|
||||
id = int(data['id'])
|
||||
# 根据id获取要修改的MembershipType对象
|
||||
membership_type = MembershipType.objects.get(id=id)
|
||||
# 获取前端传递过来的数据
|
||||
membership_type.title = data.get('title')
|
||||
membership_type.description = data.get('description')
|
||||
membership_type.price = data.get('price')
|
||||
membership_type.duration_days = data.get('duration_days')
|
||||
membership_type.coins = data.get('coins')
|
||||
membership_type.is_quota = data.get('is_quota')
|
||||
membership_type.save()
|
||||
return JsonResponse({'status': 'success', 'message': '会员卡类型修改成功'})
|
||||
elif action == 'delete': # 删除会员卡类型
|
||||
id = int(data['id'])
|
||||
# 根据id删除对应的MembershipType对象
|
||||
MembershipType.objects.filter(id=id).delete()
|
||||
return JsonResponse({'status': 'success', 'message': '会员卡类型删除成功'})
|
||||
return render(request, 'membership-types.html')
|
||||
|
||||
|
||||
# 订单记录
|
||||
|
||||
@admin_login_required
|
||||
def transaction_log_list(request):
|
||||
if request.method == "POST":
|
||||
data = json.loads(request.body)
|
||||
action = data.get('action', '')
|
||||
|
||||
if action == 'fetch':
|
||||
page = data.get('page', 1)
|
||||
limit = data.get('limit', 10)
|
||||
queryset = TransactionLog.objects.all().order_by('-created_at')
|
||||
paginator = Paginator(queryset, limit)
|
||||
|
||||
try:
|
||||
transaction_logs = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
transaction_logs = paginator.page(1)
|
||||
except EmptyPage:
|
||||
transaction_logs = paginator.page(paginator.num_pages)
|
||||
|
||||
transaction_log_list = list(
|
||||
transaction_logs.object_list.values(
|
||||
'id',
|
||||
'transaction_no',
|
||||
'transaction_status',
|
||||
'user_openid',
|
||||
'transaction_type',
|
||||
'transaction_amount',
|
||||
'remark',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
)
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"count": paginator.count,
|
||||
"data": transaction_log_list
|
||||
})
|
||||
|
||||
elif action == 'delete':
|
||||
try:
|
||||
id = data.get('id')
|
||||
transaction_log = TransactionLog.objects.get(id=id)
|
||||
transaction_log.delete()
|
||||
return JsonResponse({'status': 'success', 'message': '订单记录删除成功'})
|
||||
except TransactionLog.DoesNotExist:
|
||||
return JsonResponse({'status': 'error', 'message': '订单记录未找到'})
|
||||
|
||||
elif action == 'batch_delete':
|
||||
try:
|
||||
ids = data.get('ids', [])
|
||||
TransactionLog.objects.filter(id__in=ids).delete()
|
||||
return JsonResponse({'status': 'success', 'message': '批量删除订单记录成功'})
|
||||
except Exception as e:
|
||||
return JsonResponse({'status': 'error', 'message': str(e)})
|
||||
|
||||
return render(request, 'transaction-logs.html')
|
22
manage.py
Normal file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'website.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
242
static/css/style.css
Normal file
@ -0,0 +1,242 @@
|
||||
/* 清除浏览器默认边距,
|
||||
使边框和内边距的值包含在元素的width和height内 */
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 使用flex布局,让内容垂直和水平居中 */
|
||||
|
||||
section {
|
||||
/* 相对定位 */
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
/* linear-gradient() 函数用于创建一个表示两种或多种颜色线性渐变的图片 */
|
||||
background: linear-gradient(to bottom, #f1f4f9, #dff1ff);
|
||||
}
|
||||
|
||||
/* 背景颜色 */
|
||||
|
||||
section .color {
|
||||
/* 绝对定位 */
|
||||
position: absolute;
|
||||
/* 使用filter(滤镜) 属性,给图像设置高斯模糊*/
|
||||
filter: blur(200px);
|
||||
}
|
||||
|
||||
/* :nth-child(n) 选择器匹配父元素中的第 n 个子元素 */
|
||||
|
||||
section .color:nth-child(1) {
|
||||
top: -350px;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background: #ff359b;
|
||||
}
|
||||
|
||||
section .color:nth-child(2) {
|
||||
bottom: -150px;
|
||||
left: 100px;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: #fffd87;
|
||||
}
|
||||
|
||||
section .color:nth-child(3) {
|
||||
bottom: 50px;
|
||||
right: 100px;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: #00d2ff;
|
||||
}
|
||||
|
||||
.box {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 背景圆样式 */
|
||||
|
||||
.box .circle {
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
/* backdrop-filter属性为一个元素后面区域添加模糊效果 */
|
||||
backdrop-filter: blur(5px);
|
||||
box-shadow: 0 25px 45px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
/* 使用filter(滤镜) 属性,改变颜色。
|
||||
hue-rotate(deg) 给图像应用色相旋转
|
||||
calc() 函数用于动态计算长度值
|
||||
var() 函数调用自定义的CSS属性值x*/
|
||||
filter: hue-rotate(calc(var(--x) * 70deg));
|
||||
/* 调用动画animate,需要10s完成动画,
|
||||
linear表示动画从头到尾的速度是相同的,
|
||||
infinite指定动画应该循环播放无限次*/
|
||||
animation: animate 10s linear infinite;
|
||||
/* 动态计算动画延迟几秒播放 */
|
||||
animation-delay: calc(var(--x) * -1s);
|
||||
}
|
||||
|
||||
/* 背景圆动画 */
|
||||
|
||||
@keyframes animate {
|
||||
0%, 100%{
|
||||
transform: translateY(-50px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(50px);
|
||||
}
|
||||
}
|
||||
|
||||
.box .circle:nth-child(1) {
|
||||
top: -50px;
|
||||
right: -60px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.box .circle:nth-child(2) {
|
||||
top: 150px;
|
||||
left: -100px;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.box .circle:nth-child(3) {
|
||||
bottom: 50px;
|
||||
right: -60px;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.box .circle:nth-child(4) {
|
||||
bottom: -80px;
|
||||
left: 100px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.box .circle:nth-child(5) {
|
||||
top: -80px;
|
||||
left: 140px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
/* 登录框样式 */
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
width: 400px;
|
||||
min-height: 400px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
backdrop-filter: blur(5px);
|
||||
box-shadow: 0 25px 45px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.form {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
/* 登录标题样式 */
|
||||
|
||||
.form h2 {
|
||||
position: relative;
|
||||
color: #333;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 5px;
|
||||
margin-bottom: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 登录标题的下划线样式 */
|
||||
|
||||
.form h2::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -10px;
|
||||
width: 0px;
|
||||
height: 3px;
|
||||
background: #fff;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.form h2:hover:before {
|
||||
width: 53px;
|
||||
}
|
||||
|
||||
.form .inputBox {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* 输入框样式 */
|
||||
|
||||
.form .inputBox input {
|
||||
width: 100%;
|
||||
padding: 10px 20px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 30px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
font-size: 16px;
|
||||
letter-spacing: 1px;
|
||||
color: #666;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.form .inputBox input::placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 登录按钮样式 */
|
||||
|
||||
.form .inputBox input[type="submit"] {
|
||||
background: #fff;
|
||||
color: #666;
|
||||
max-width: 100px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.forget {
|
||||
margin-top: 6px;
|
||||
color: #333;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.forget a {
|
||||
color: #666;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.radio input{
|
||||
margin-top:15px;
|
||||
|
||||
}
|
||||
|
BIN
static/ding.mp3
Normal file
BIN
static/img/bg.png
Normal file
After Width: | Height: | Size: 110 KiB |
3
static/js/axios.js
Normal file
1589
static/js/china.js
Normal file
8
static/js/echarts-wordcloud.min.js
vendored
Normal file
98075
static/js/echarts.js
Normal file
45
static/js/echarts.min.js
vendored
Normal file
6
static/js/jquery-2.1.0.min.js
vendored
Normal file
2
static/js/jquery.js
vendored
Normal file
1
static/js/layui/css/layui.css
Normal file
2
static/js/layui/css/layui.mobile.css
Normal file
2
static/js/layui/css/modules/code.css
Normal file
@ -0,0 +1,2 @@
|
||||
/** layui-v2.4.5 MIT License By https://www.layui.com */
|
||||
html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none}
|
2
static/js/layui/css/modules/laydate/default/laydate.css
Normal file
BIN
static/js/layui/css/modules/layer/default/icon-ext.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
static/js/layui/css/modules/layer/default/icon.png
Normal file
After Width: | Height: | Size: 11 KiB |
2
static/js/layui/css/modules/layer/default/layer.css
Normal file
BIN
static/js/layui/css/modules/layer/default/loading-0.gif
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
static/js/layui/css/modules/layer/default/loading-1.gif
Normal file
After Width: | Height: | Size: 701 B |
BIN
static/js/layui/css/modules/layer/default/loading-2.gif
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
static/js/layui/font/iconfont.eot
Normal file
473
static/js/layui/font/iconfont.svg
Normal file
After Width: | Height: | Size: 274 KiB |
BIN
static/js/layui/font/iconfont.ttf
Normal file
BIN
static/js/layui/font/iconfont.woff
Normal file
BIN
static/js/layui/font/iconfont.woff2
Normal file
BIN
static/js/layui/images/face/0.gif
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
static/js/layui/images/face/1.gif
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
static/js/layui/images/face/10.gif
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
static/js/layui/images/face/11.gif
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
static/js/layui/images/face/12.gif
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
static/js/layui/images/face/13.gif
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
static/js/layui/images/face/14.gif
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
static/js/layui/images/face/15.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
static/js/layui/images/face/16.gif
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
static/js/layui/images/face/17.gif
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
static/js/layui/images/face/18.gif
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
static/js/layui/images/face/19.gif
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
static/js/layui/images/face/2.gif
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
static/js/layui/images/face/20.gif
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
static/js/layui/images/face/21.gif
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
static/js/layui/images/face/22.gif
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
static/js/layui/images/face/23.gif
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
static/js/layui/images/face/24.gif
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
static/js/layui/images/face/25.gif
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
static/js/layui/images/face/26.gif
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
static/js/layui/images/face/27.gif
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
static/js/layui/images/face/28.gif
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
static/js/layui/images/face/29.gif
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
static/js/layui/images/face/3.gif
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
static/js/layui/images/face/30.gif
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
static/js/layui/images/face/31.gif
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
static/js/layui/images/face/32.gif
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
static/js/layui/images/face/33.gif
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
static/js/layui/images/face/34.gif
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
static/js/layui/images/face/35.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
static/js/layui/images/face/36.gif
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
static/js/layui/images/face/37.gif
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
static/js/layui/images/face/38.gif
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
static/js/layui/images/face/39.gif
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
static/js/layui/images/face/4.gif
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
static/js/layui/images/face/40.gif
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
static/js/layui/images/face/41.gif
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
static/js/layui/images/face/42.gif
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
static/js/layui/images/face/43.gif
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
static/js/layui/images/face/44.gif
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
static/js/layui/images/face/45.gif
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
static/js/layui/images/face/46.gif
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
static/js/layui/images/face/47.gif
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
static/js/layui/images/face/48.gif
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
static/js/layui/images/face/49.gif
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
static/js/layui/images/face/5.gif
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
static/js/layui/images/face/50.gif
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
static/js/layui/images/face/51.gif
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
static/js/layui/images/face/52.gif
Normal file
After Width: | Height: | Size: 777 B |
BIN
static/js/layui/images/face/53.gif
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
static/js/layui/images/face/54.gif
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
static/js/layui/images/face/55.gif
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
static/js/layui/images/face/56.gif
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
static/js/layui/images/face/57.gif
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
static/js/layui/images/face/58.gif
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
static/js/layui/images/face/59.gif
Normal file
After Width: | Height: | Size: 10 KiB |