feat: 添加多个字体图标、样式文件和配置文件
新增了多个字体图标文件(SVG格式),包括品牌图标和常规图标。添加了相关的样式文件(LESS和SCSS)用于管理图标样式。更新了配置文件如.gitignore、composer.json和.htaccess等。新增了开发者相关的PHP文件如logout.php。添加了项目规则文档和字体相关的样式文件。
This commit is contained in:
134
windows/console_app.py
Normal file
134
windows/console_app.py
Normal file
@@ -0,0 +1,134 @@
|
||||
import requests
|
||||
import json
|
||||
|
||||
def fetch_apps(search_term=None):
|
||||
url = 'http://localhost:3232/api.php?action=list'
|
||||
if search_term:
|
||||
url += f'&search={search_term}'
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
# 确保返回的是字典类型
|
||||
if isinstance(data, dict):
|
||||
return data
|
||||
else:
|
||||
print(f"获取公告失败: 无效的数据格式")
|
||||
return None
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"获取应用列表失败: {e}")
|
||||
return []
|
||||
|
||||
def get_app_details(app_id):
|
||||
url = f'http://localhost:3232/api.php?action=app&id={app_id}'
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if response.status_code == 404:
|
||||
try:
|
||||
error_data = response.json()
|
||||
print(f"获取应用详情失败: {error_data.get('error')}")
|
||||
print(f"执行的SQL: {error_data.get('sql')}")
|
||||
except ValueError:
|
||||
print(f"获取应用详情失败: {e}")
|
||||
else:
|
||||
print(f"获取应用详情失败: {e}")
|
||||
return None
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"获取应用详情失败: {e}")
|
||||
return None
|
||||
|
||||
def fetch_latest_announcement():
|
||||
url = 'http://localhost:3232/api.php?action=latest_announcement'
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"获取最新公告失败: {e}")
|
||||
return None
|
||||
|
||||
def download_app(version_id):
|
||||
url = f'http://localhost:3232/api.php?action=download&version_id={version_id}'
|
||||
try:
|
||||
response = requests.get(f'http://localhost:3232/api/download/{version_id}', stream=True)
|
||||
response.raise_for_status()
|
||||
|
||||
filename = response.headers.get('Content-Disposition', '').split('filename=')[-1].strip('"')
|
||||
if not filename:
|
||||
filename = f'app_version_{version_id}.apk'
|
||||
|
||||
with open(filename, 'wb') as f:
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
f.write(chunk)
|
||||
|
||||
print(f'下载成功: {filename}')
|
||||
return True
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f'下载失败: {e}')
|
||||
return False
|
||||
|
||||
def display_apps(apps):
|
||||
if not apps:
|
||||
print('没有找到应用程序')
|
||||
return
|
||||
|
||||
print('=== 应用商店 ===')
|
||||
for i, app in enumerate(apps, 1):
|
||||
print(f'[{i}] {app.get("name")}')
|
||||
print(f' 描述: {app.get("description", "无")}')
|
||||
print(f' 评分: {app.get("avg_rating", "暂无")}/5')
|
||||
print(f' 适用平台: {", ".join(app.get("platforms", []))}')
|
||||
|
||||
if __name__ == "__main__":
|
||||
while True:
|
||||
print("\n=== 应用商店控制台 ===")
|
||||
print("1. 浏览所有应用")
|
||||
print("2. 搜索应用")
|
||||
print("3. 查看应用详情")
|
||||
print("4. 下载应用")
|
||||
print("5. 查看最新公告")
|
||||
print("6. 退出")
|
||||
|
||||
choice = input("请选择操作 (1-6): ")
|
||||
|
||||
if choice == "1":
|
||||
apps = fetch_apps()
|
||||
display_apps(apps)
|
||||
elif choice == "2":
|
||||
search_term = input("请输入搜索关键词: ")
|
||||
apps = fetch_apps(search_term)
|
||||
display_apps(apps)
|
||||
elif choice == "3":
|
||||
app_id = input("请输入应用ID: ")
|
||||
app = get_app_details(app_id)
|
||||
if app:
|
||||
print("\n=== 应用详情 ===")
|
||||
print(f"名称: {app.get('name')}")
|
||||
print(f"描述: {app.get('description', '无')}")
|
||||
print(f"评分: {app.get('avg_rating', '暂无')}/5")
|
||||
print(f"适用平台: {', '.join(app.get('platforms', []))}")
|
||||
print("版本:")
|
||||
for version in app.get('versions', []):
|
||||
print(f" - {version.get('version_name')} (ID: {version.get('id')})")
|
||||
else:
|
||||
print("应用不存在或获取失败")
|
||||
elif choice == "4":
|
||||
version_id = input("请输入版本ID: ")
|
||||
download_app(version_id)
|
||||
elif choice == "5":
|
||||
announcement = fetch_latest_announcement()
|
||||
if announcement:
|
||||
print("\n=== 最新公告 ===")
|
||||
print(f"标题: {announcement.get('title')}")
|
||||
print(f"内容: {announcement.get('content')}")
|
||||
print(f"发布时间: {announcement.get('created_at')}")
|
||||
else:
|
||||
print("获取公告失败或暂无公告")
|
||||
elif choice == "6":
|
||||
print("谢谢使用,再见!")
|
||||
break
|
||||
else:
|
||||
print("无效的选择,请重试")
|
||||
271
windows/gui_app.py
Normal file
271
windows/gui_app.py
Normal file
@@ -0,0 +1,271 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
import requests
|
||||
import json
|
||||
import threading
|
||||
|
||||
class AppStoreGUI:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("应用商店")
|
||||
self.root.geometry("800x600")
|
||||
self.root.resizable(True, True)
|
||||
|
||||
# 创建搜索框架
|
||||
self.search_frame = ttk.Frame(root)
|
||||
self.search_frame.pack(fill=tk.X, padx=10, pady=5)
|
||||
|
||||
ttk.Label(self.search_frame, text="搜索应用: ").pack(side=tk.LEFT, padx=5)
|
||||
self.search_entry = ttk.Entry(self.search_frame, width=50)
|
||||
self.search_entry.pack(side=tk.LEFT, padx=5)
|
||||
ttk.Button(self.search_frame, text="搜索", command=self.on_search).pack(side=tk.LEFT, padx=5)
|
||||
ttk.Button(self.search_frame, text="查看最新公告", command=self.show_latest_announcement).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 创建主框架
|
||||
self.main_frame = ttk.Frame(root)
|
||||
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
|
||||
|
||||
# 创建滚动条
|
||||
self.canvas = tk.Canvas(self.main_frame)
|
||||
self.scrollbar = ttk.Scrollbar(self.main_frame, orient="vertical", command=self.canvas.yview)
|
||||
self.scrollable_frame = ttk.Frame(self.canvas)
|
||||
|
||||
self.scrollable_frame.bind(
|
||||
"<Configure>",
|
||||
lambda e: self.canvas.configure(
|
||||
scrollregion=self.canvas.bbox("all")
|
||||
)
|
||||
)
|
||||
|
||||
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
|
||||
self.canvas.configure(yscrollcommand=self.scrollbar.set)
|
||||
|
||||
self.canvas.pack(side="left", fill="both", expand=True)
|
||||
self.scrollbar.pack(side="right", fill="y")
|
||||
|
||||
# 加载应用列表
|
||||
self.load_apps()
|
||||
|
||||
# 创建公告对话框
|
||||
self.create_announcement_dialog()
|
||||
|
||||
def fetch_apps(self, search_term=None):
|
||||
url = 'http://localhost:3232/api.php?action=list'
|
||||
if search_term:
|
||||
url += f'&search={search_term}'
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
# 确保返回的是字典类型
|
||||
if isinstance(data, dict):
|
||||
return data
|
||||
else:
|
||||
messagebox.showerror("错误", "获取公告失败: 无效的数据格式")
|
||||
return None
|
||||
except requests.exceptions.RequestException as e:
|
||||
messagebox.showerror('错误', f'获取应用列表失败: {str(e)}')
|
||||
return []
|
||||
|
||||
def create_announcement_dialog(self):
|
||||
self.announcement_window = tk.Toplevel(self.root)
|
||||
self.announcement_window.title("最新公告")
|
||||
self.announcement_window.geometry("500x300")
|
||||
self.announcement_window.withdraw() # 初始隐藏
|
||||
|
||||
# 公告标题
|
||||
self.announcement_title = ttk.Label(self.announcement_window, text="", font=('SimHei', 14, 'bold'))
|
||||
self.announcement_title.pack(pady=10)
|
||||
|
||||
# 公告内容
|
||||
self.announcement_content = tk.Text(self.announcement_window, wrap=tk.WORD, width=60, height=10)
|
||||
self.announcement_content.pack(padx=10, pady=5)
|
||||
self.announcement_content.config(state=tk.DISABLED)
|
||||
|
||||
# 公告时间
|
||||
self.announcement_time = ttk.Label(self.announcement_window, text="")
|
||||
self.announcement_time.pack(pady=5)
|
||||
|
||||
# 关闭按钮
|
||||
ttk.Button(self.announcement_window, text="关闭", command=self.announcement_window.withdraw).pack(pady=10)
|
||||
|
||||
def fetch_app_details(self, app_id):
|
||||
url = f'http://localhost:3232/api.php?action=app&id={app_id}'
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.exceptions.RequestException as e:
|
||||
messagebox.showerror('错误', f'获取应用详情失败: {str(e)}')
|
||||
return None
|
||||
|
||||
def fetch_latest_announcement(self):
|
||||
url = 'http://localhost:3232/api.php?action=latest_announcement'
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
# 确保返回的是字典类型
|
||||
if isinstance(data, dict):
|
||||
return data
|
||||
else:
|
||||
messagebox.showerror("错误", "获取公告失败: 无效的数据格式")
|
||||
return None
|
||||
except requests.exceptions.RequestException as e:
|
||||
messagebox.showerror("错误", f"获取公告失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def show_latest_announcement(self):
|
||||
def thread_func():
|
||||
announcement = self.fetch_latest_announcement()
|
||||
if announcement:
|
||||
self.announcement_title.config(text=announcement.get('title', '无标题'))
|
||||
self.announcement_content.config(state=tk.NORMAL)
|
||||
self.announcement_content.delete(1.0, tk.END)
|
||||
self.announcement_content.insert(tk.END, announcement.get('content', '无内容'))
|
||||
self.announcement_content.config(state=tk.DISABLED)
|
||||
self.announcement_time.config(text=f"发布时间: {announcement.get('created_at', '未知')}")
|
||||
self.announcement_window.deiconify()
|
||||
|
||||
threading.Thread(target=thread_func).start()
|
||||
|
||||
def download_version(self, version_id):
|
||||
url = f'http://localhost:3232/api.php?action=download&version_id={version_id}'
|
||||
try:
|
||||
response = requests.get(f'http://localhost:3232/api/app/{app_id}')
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.exceptions.RequestException as e:
|
||||
messagebox.showerror("错误", f"获取应用详情失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def on_search(self):
|
||||
# 清空现有应用卡片
|
||||
for widget in self.scrollable_frame.winfo_children():
|
||||
widget.destroy()
|
||||
# 加载搜索结果
|
||||
search_term = self.search_entry.get().strip()
|
||||
self.load_apps(search_term)
|
||||
|
||||
def load_apps(self, search_term=None):
|
||||
apps = self.fetch_apps(search_term)
|
||||
if not apps:
|
||||
ttk.Label(self.scrollable_frame, text="没有找到应用程序").pack(pady=20)
|
||||
return
|
||||
|
||||
# 创建应用卡片网格
|
||||
for i, app in enumerate(apps):
|
||||
frame = ttk.LabelFrame(self.scrollable_frame, text=app.get("name"))
|
||||
frame.grid(row=i//2, column=i%2, padx=10, pady=10, sticky="nsew")
|
||||
|
||||
ttk.Label(frame, text=f"描述: {app.get('description', '无')}").pack(anchor="w", padx=5, pady=2)
|
||||
ttk.Label(frame, text=f"评分: {app.get('avg_rating', '暂无')}/5").pack(anchor="w", padx=5, pady=2)
|
||||
ttk.Label(frame, text=f"适用平台: {','.join(app.get('platforms', []))}").pack(anchor="w", padx=5, pady=2)
|
||||
ttk.Button(frame, text="查看详情", command=lambda a=app: self.show_details(a)).pack(pady=5)
|
||||
|
||||
# 配置网格权重使卡片自适应
|
||||
self.scrollable_frame.grid_columnconfigure(0, weight=1)
|
||||
self.scrollable_frame.grid_columnconfigure(1, weight=1)
|
||||
|
||||
def show_details(self, app):
|
||||
# 获取完整应用详情
|
||||
app_details = self.fetch_app_details(app['id'])
|
||||
if not app_details:
|
||||
return
|
||||
|
||||
detail_window = tk.Toplevel(self.root)
|
||||
detail_window.title(app_details.get("name"))
|
||||
detail_window.geometry("600x400")
|
||||
|
||||
# 创建滚动区域
|
||||
canvas = tk.Canvas(detail_window)
|
||||
scrollbar = ttk.Scrollbar(detail_window, orient="vertical", command=canvas.yview)
|
||||
scrollable_frame = ttk.Frame(canvas)
|
||||
|
||||
scrollable_frame.bind(
|
||||
"<Configure>",
|
||||
lambda e: canvas.configure(
|
||||
scrollregion=canvas.bbox("all")
|
||||
)
|
||||
)
|
||||
|
||||
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||||
canvas.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
canvas.pack(side="left", fill="both", expand=True)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
|
||||
# 应用基本信息
|
||||
ttk.Label(scrollable_frame, text=f"名称: {app_details.get('name')}", font=('Arial', 12, 'bold')).pack(anchor="w", padx=10, pady=5)
|
||||
ttk.Label(scrollable_frame, text=f"描述: {app_details.get('description', '无')}").pack(anchor="w", padx=10, pady=5)
|
||||
ttk.Label(scrollable_frame, text=f"评分: {app_details.get('avg_rating', '暂无')}/5").pack(anchor="w", padx=10, pady=5)
|
||||
ttk.Label(scrollable_frame, text=f"适用年龄: {app_details.get('age_rating', '未知')}").pack(anchor="w", padx=10, pady=5)
|
||||
ttk.Label(scrollable_frame, text=f"适用平台: {','.join(app_details.get('platforms', []))}").pack(anchor="w", padx=10, pady=5)
|
||||
|
||||
# 版本信息
|
||||
versions = app_details.get('versions', [])
|
||||
if versions:
|
||||
ttk.Label(scrollable_frame, text="\n=== 版本列表 ===", font=('Arial', 10, 'bold')).pack(anchor="w", padx=10, pady=10)
|
||||
for version in versions:
|
||||
version_frame = ttk.Frame(scrollable_frame)
|
||||
version_frame.pack(anchor="w", padx=15, pady=5, fill=tk.X)
|
||||
|
||||
ttk.Label(version_frame, text=f"版本 {version.get('version_name', '未知')}", font=('Arial', 9, 'bold')).pack(anchor="w")
|
||||
ttk.Label(version_frame, text=f"发布日期: {version.get('created_at', '未知')}").pack(anchor="w")
|
||||
ttk.Label(version_frame, text=f"文件大小: {version.get('file_size', '未知')}").pack(anchor="w")
|
||||
ttk.Button(version_frame, text="下载", command=lambda v=version: self.download_version(v)).pack(anchor="e", pady=5)
|
||||
|
||||
ttk.Button(scrollable_frame, text="关闭", command=detail_window.destroy).pack(pady=20)
|
||||
|
||||
def download_version(self, version):
|
||||
def download_thread():
|
||||
try:
|
||||
# 询问保存路径
|
||||
save_path = filedialog.asksaveasfilename(
|
||||
defaultextension=".apk",
|
||||
filetypes=[("APK files", "*.apk"), ("All files", "*")],
|
||||
initialfile=version.get('file_name', f"app_{version['id']}.apk")
|
||||
)
|
||||
if not save_path:
|
||||
return
|
||||
|
||||
# 下载文件
|
||||
response = requests.get(f'http://localhost:3232/api/download/{version["id"]}', stream=True)
|
||||
response.raise_for_status()
|
||||
|
||||
total_size = int(response.headers.get('content-length', 0))
|
||||
block_size = 1024 # 1 KB
|
||||
progress = 0
|
||||
|
||||
with open(save_path, 'wb') as file:
|
||||
for data in response.iter_content(block_size):
|
||||
progress += len(data)
|
||||
file.write(data)
|
||||
# 更新进度条
|
||||
progress_var.set((progress / total_size) * 100)
|
||||
root.update_idletasks()
|
||||
|
||||
messagebox.showinfo("成功", f"文件已下载至:\n{save_path}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
messagebox.showerror("错误", f"下载失败: {str(e)}")
|
||||
finally:
|
||||
progress_window.destroy()
|
||||
|
||||
# 创建进度窗口
|
||||
progress_window = tk.Toplevel(self.root)
|
||||
progress_window.title(f"下载版本 {version.get('version_name', '未知')}")
|
||||
progress_window.geometry("300x100")
|
||||
progress_window.resizable(False, False)
|
||||
|
||||
ttk.Label(progress_window, text="下载中...").pack(pady=10)
|
||||
progress_var = tk.DoubleVar()
|
||||
progress_bar = ttk.Progressbar(progress_window, variable=progress_var, maximum=100)
|
||||
progress_bar.pack(fill=tk.X, padx=20, pady=10)
|
||||
|
||||
# 启动下载线程
|
||||
threading.Thread(target=download_thread).start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = tk.Tk()
|
||||
app = AppStoreGUI(root)
|
||||
root.mainloop()
|
||||
2
windows/requirements.txt
Normal file
2
windows/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
requests==2.31.0
|
||||
pyinstaller==6.8.0
|
||||
Reference in New Issue
Block a user