bloodfog 发表于 2024-6-18 20:42

闲来无聊写个简易的html娱乐用的计分器

本帖最后由 bloodfog 于 2024-6-19 15:27 编辑

闲来无聊写个简易的html娱乐用的计分器
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>QP计分器</title>
    <style>
      body {
            font-family: "仿宋";
            background-color: #f0f0f0;
            margin: 0;
            padding: 20px;
            font-size: 12px;
      }
      .container {
            max-width: 98%;
            margin: 0 auto;
            background-color: #fff;
            padding: 5px;
            border-radius: 10px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            overflow-x: auto; /* 添加水平滚动条 */
      }
      .controls {
            text-align: center;
            margin-bottom: 5px;
            font-size:12px;
      }
      .controls input, .controls button {
            padding: 5px;
            margin: 5px;
            border: 1px solid #ddd;
            border-radius: 5px;
            font-size: 14px;
            height: 32px;
            line-height: 22px;
      }
      table {
            width: 100%;
            border-collapse: collapse;
            table-layout: fixed; /* 表格宽度固定 */
      }
      th, td {
            border: 1px solid #ddd;
            padding: 5px;
            text-align: center;
            white-space: nowrap;
      }
      th {
            background-color: #f4f4f4;
      }
      .delete-btn {
            color: red;
            cursor: pointer;
      }
      input {
            width: 100%;
            border: none;
            text-align: center;
            box-sizing: border-box;
      }
      input:focus {
            border: 1px solid #ccc; /* 输入状态时的边框颜色为淡灰色 */
            outline: none;
      }
      tbody tr:nth-child(odd) {
            background-color: #d8eafc; /* 奇数行背景淡蓝色 */
      }
      tbody tr:nth-child(even) {
            background-color: #d0f0c0; /* 偶数行背景淡绿色 */
      }
      tbody tr:nth-child(odd) input {
            background-color: #d8eafc; /* 奇数行背景淡蓝色 */
            border: 1px solid #d8eafc;
      }
      tbody tr:nth-child(even) input {
            background-color: #d0f0c0; /* 偶数行背景淡绿色 */
            border: 1px solid #d0f0c0;
      }
      .alert-box {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: #fff;
            padding: 20px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            z-index: 1000;
            border-radius: 10px;
            text-align: center;
      }
      .alert-box h2 {
            margin: 0 0 10px 0;
      }
      .alert-box p {
            margin: 0 0 20px 0;
      }
    </style>
</head>
<body>
    <div class="container">
      <div class="controls">
            <input type="text" id="username" placeholder="输入玩家昵称" style="height:20px; width:90px; line-height:20px; padding:5px; font-size:14px;" autocomplete="off">
            <button>新玩家</button>
            <button>添加一局</button>
            <button>重置</button>
      </div>
      <table id="scoreTable">
            <thead>
                <tr>
                  <th style="width:70px; height:25px; line-height:25px;">玩家</th>
                  <th style="width:60px;">合计</th>
                  <th style="width:60px;">操作</th>
                </tr>
            </thead>
            <tbody>
            </tbody>
      </table>
    </div>

    <script>
      let users = JSON.parse(localStorage.getItem('users')) || [];
      let rounds = parseInt(localStorage.getItem('rounds')) || 0;

      function saveData() {
            localStorage.setItem('users', JSON.stringify(users));
            localStorage.setItem('rounds', rounds.toString());
      }

      function loadData() {
            if (users.length > 0) {
                for (let i = 0; i < rounds; i++) {
                  addRoundHeader(i);
                }
                users.forEach(user => addUserToTable(user.username, user.scores));
            }
      }

      function addUser() {
            const username = document.getElementById('username').value;
            if (!username) {
                showAlert("请先输入玩家昵称");
                return;
            }
            if (username && !users.find(user => user.username === username)) {
                const user = { username: username, scores: Array(rounds).fill(0) };
                users.push(user);
                addUserToTable(username, user.scores);
                saveData();
                document.getElementById('username').value = '';
            }
      }

      function addUserToTable(username, scores = []) {
            const table = document.getElementById('scoreTable').getElementsByTagName('tbody');
            const row = table.insertRow();
            row.setAttribute('data-username', username);

            const cell1 = row.insertCell(0);
            cell1.textContent = username;

            const totalCell = row.insertCell(1);
            totalCell.textContent = scores.reduce((a, b) => a + b, 0);

            scores.forEach(score => {
                const scoreCell = row.insertCell(-1);
                scoreCell.innerHTML = `<input type="number" value="${score}">`;
            });

            const cell2 = row.insertCell(-1);
            cell2.innerHTML = '<span class="delete-btn">移除</span>';
      }

      function confirmDeleteUser(element) {
            showConfirm("确定要移除此玩家吗?", () => {
                const row = element.parentNode.parentNode;
                const username = row.getAttribute('data-username');
                users = users.filter(user => user.username !== username);
                row.remove();
                saveData();
            });
      }

      function addRound() {
            rounds++;
            users.forEach(user => user.scores.unshift(0));
            addRoundHeader(rounds - 1);
            saveData();
      }

      function addRoundHeader(roundIndex) {
            const table = document.getElementById('scoreTable');
            const th = document.createElement('th');
            th.textContent = '第 ' + (roundIndex + 1) + ' 局';
            th.style.width = '60px'; // 固定宽度为60px
            const headerRow = table.getElementsByTagName('thead').rows;
            headerRow.insertBefore(th, headerRow.cells);

            const tbody = table.getElementsByTagName('tbody');
            Array.from(tbody.rows).forEach((row) => {
                const cell = row.insertCell(2);
                cell.innerHTML = `<input type="number" value="0">`;
            });
      }

      function updateScore(element) {
            const row = element.parentNode.parentNode;
            const username = row.getAttribute('data-username');
            const user = users.find(user => user.username === username);
            const index = Array.from(row.cells).indexOf(element.parentNode) - 2;
            user.scores = Number(element.value);
            row.cells.textContent = user.scores.reduce((a, b) => a + b, 0);
            saveData();
      }

      function resetData() {
            showConfirm("确定要重置全部数据吗?", () => {
                users = [];
                rounds = 0;
                saveData();
                document.getElementById('scoreTable').getElementsByTagName('tbody').innerHTML = '';
                document.getElementById('scoreTable').getElementsByTagName('thead').innerHTML = '<tr><th style="width:70px;">玩家</th><th style="width:60px;">合计</th><th style="width:60px;">操作</th></tr>';
            });
      }

      function showAlert(message) {
            const alertDiv = document.createElement('div');
            alertDiv.className = 'alert-box';
            alertDiv.innerHTML = `
                <h2>温馨提示</h2>
                <p>${message}</p>
                <button>明白了</button>
            `;
            document.body.appendChild(alertDiv);
      }

      function showConfirm(message, onConfirm) {
            const confirmDiv = document.createElement('div');
            confirmDiv.className = 'alert-box';
            confirmDiv.innerHTML = `
                <h2>温馨提示</h2>
                <p>${message}</p>
                <button id="confirm-yes">确定</button>
                <button>取消</button>
            `;
            document.body.appendChild(confirmDiv);
            document.getElementById('confirm-yes').addEventListener('click', function () {
                onConfirm();
                confirmDiv.remove();
            });
      }

      document.addEventListener('DOMContentLoaded', loadData);
    </script>
</body>
</html>


python版本
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import json
import os
import sys
from PIL import Image, ImageTk

def resource_path(relative_path):
    """ 获取资源文件的绝对路径 """
    if hasattr(sys, '_MEIPASS'):
      return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

class ScoreKeeper:
    def __init__(self, root):
      self.root = root
      self.root.title("计分器")
      self.root.geometry("800x400")

      # 更换程序图标
      icon_path = resource_path('head.ico')
      print(f"Icon path: {icon_path}")# 打印图标路径
      try:
            self.root.iconbitmap(icon_path)# 设置窗口图标
      except Exception as e:
            print(f"Error setting icon: {e}")
      
      # 使用PIL加载图标并设置
      try:
            icon = Image.open(icon_path)
            icon = ImageTk.PhotoImage(icon)
            self.root.iconphoto(False, icon)
      except FileNotFoundError:
            print("Icon file not found. Please ensure 'head.ico' is in the correct directory.")

      self.users = []
      self.rounds = 0

      self.load_data()

      self.controls_frame = tk.Frame(root, padx=5, pady=5)
      self.controls_frame.pack(fill=tk.X)

      self.username_entry = tk.Entry(self.controls_frame, font=("仿宋", 14))
      self.username_entry.grid(row=0, column=0, padx=5, pady=5)
      self.username_entry.config(width=10)

      self.add_user_btn = tk.Button(self.controls_frame, text="新玩家", command=self.add_user, font=("仿宋", 12))
      self.add_user_btn.grid(row=0, column=1, padx=5, pady=5)

      self.add_round_btn = tk.Button(self.controls_frame, text="添加一局", command=self.add_round, font=("仿宋", 12))
      self.add_round_btn.grid(row=0, column=2, padx=5, pady=5)

      self.reset_btn = tk.Button(self.controls_frame, text="重置", command=self.reset_data, font=("仿宋", 12))
      self.reset_btn.grid(row=0, column=3, padx=5, pady=5)

      self.table_frame = tk.Frame(root, padx=5, pady=5)
      self.table_frame.pack(fill=tk.BOTH, expand=True)

      self.scrollbar_x = ttk.Scrollbar(self.table_frame, orient="horizontal")
      self.scrollbar_x.pack(side=tk.BOTTOM, fill=tk.X)
      self.scrollbar_y = ttk.Scrollbar(self.table_frame, orient="vertical")
      self.scrollbar_y.pack(side=tk.RIGHT, fill=tk.Y)

      self.table = ttk.Treeview(self.table_frame, columns=[], show="headings", selectmode="browse",
                                  xscrollcommand=self.scrollbar_x.set, yscrollcommand=self.scrollbar_y.set)
      self.scrollbar_x.config(command=self.table.xview)
      self.scrollbar_y.config(command=self.table.yview)

      self.table.pack(fill=tk.BOTH, expand=True)
      self.table.bind("<Button-1>", self.handle_click)

      self.render_table()

    def save_data(self):
      data = {"users": self.users, "rounds": self.rounds}
      with open("data.json", "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=4)

    def load_data(self):
      try:
            with open("data.json", "r", encoding="utf-8") as f:
                data = json.load(f)
                self.users = data.get("users", [])
                self.rounds = data.get("rounds", 0)
      except FileNotFoundError:
            self.users = []
            self.rounds = 0

    def add_user(self):
      username = self.username_entry.get()
      if not username:
            messagebox.showinfo("温馨提示", "请先输入玩家昵称")
            return
      if username and not any(user['username'] == username for user in self.users):
            user = {"username": username, "scores": * self.rounds}
            self.users.append(user)
            self.render_table()
            self.save_data()
            self.username_entry.delete(0, tk.END)

    def add_round(self):
      self.rounds += 1
      for user in self.users:
            user['scores'].append(0)
      self.render_table()
      self.save_data()

    def update_score(self, user_index, round_index, score):
      self.users['scores'] = score
      self.render_table()
      self.save_data()

    def delete_user(self, user_index):
      confirm = messagebox.askyesno("温馨提示", "确定要移除此玩家吗?")
      if confirm:
            del self.users
            self.render_table()
            self.save_data()

    def reset_data(self):
      confirm = messagebox.askyesno("温馨提示", "确定要重置全部数据吗?")
      if confirm:
            self.users = []
            self.rounds = 0
            self.save_data()
            self.render_table()

    def render_table(self):
      for i in self.table.get_children():
            self.table.delete(i)

      columns = ["username", "total"] + [::-1] + ["actions"]
      self.table["columns"] = columns
      self.table.heading("username", text="玩家")
      self.table.heading("total", text="合计")
      for i in range(self.rounds):
            self.table.heading(f"round_{i+1}", text=f"第{i+1}局")
      self.table.heading("actions", text="操作")
      
      self.table.column("username", width=80, anchor=tk.CENTER, minwidth=80)
      self.table.column("total", width=80, anchor=tk.CENTER, minwidth=80)
      for i in range(self.rounds):
            self.table.column(f"round_{i+1}", width=80, anchor=tk.CENTER, minwidth=80)
      self.table.column("actions", width=80, anchor=tk.CENTER, minwidth=80)

      for user_index, user in enumerate(self.users):
            total_score = sum(user['scores'])
            values = , total_score] + user['scores'][::-1] + ["移除"]
            tag = 'evenrow' if user_index % 2 == 0 else 'oddrow'
            self.table.insert("", "end", values=values, tags=(tag,))

      # 添加样式使内容居中对齐
      style = ttk.Style()
      style.configure("Treeview.Heading", anchor="center")
      style.configure("Treeview", rowheight=25)
      style.configure("Treeview", highlightthickness=1, bd=1, relief="solid")

      # 添加表格边框线
      style.map("Treeview", background=[("selected", "#0099FF")], foreground=[("selected", "white")])
      style.configure("Treeview", highlightthickness=1, bd=1, relief="solid")
      style.layout("Treeview", [('Treeview.treearea', {'sticky': 'nswe'})])

      # 添加列之间的边框线
      style.configure("Treeview", bordercolor="black", borderwidth=1, relief="solid")

      # 添加行之间的背景颜色交替
      self.table.tag_configure('oddrow', background='white')
      self.table.tag_configure('evenrow', background='lightgrey')

      # 确保滚动条在添加新列后仍然有效
      self.table.update_idletasks()

    def handle_click(self, event):
      region = self.table.identify_region(event.x, event.y)
      if region == "cell":
            row_id = self.table.identify_row(event.y)
            column = self.table.identify_column(event.x)
            col_num = int(column.replace("#", "")) - 1
            if col_num == len(self.users["scores"]) + 2:# 操作列
                user_index = self.table.index(row_id)
                self.delete_user(user_index)
            elif 2 <= col_num <= len(self.users["scores"]) + 1:# 分数列
                user_index = self.table.index(row_id)
                round_index = len(self.users["scores"]) - (col_num - 2) - 1# 修正索引计算
                new_score_str = simpledialog.askstring("更新分数", f"请输入 {self.users['username']} 的第 {round_index + 1} 局分数:")
                if new_score_str is not None:
                  try:
                        new_score = int(new_score_str)
                        self.update_score(user_index, round_index, new_score)
                  except ValueError:
                        messagebox.showerror("输入错误", "请输入有效的整数分数")

if __name__ == "__main__":
    root = tk.Tk()
    app = ScoreKeeper(root)
    root.mainloop()

随遇而安8 发表于 2024-6-18 21:33

复制到文本文档里,改后缀.html,点击button没反应呢?

n3iuarem3t 发表于 2024-6-19 11:34

希望更新一下,添加分类和自动计分,比如斗地主,只需要给地主修改分数,其他两家就可以自动计算分数,目前这个需要一个个填,还是比较麻烦的,另外整体可以让字体大一点

ZhjhJZ 发表于 2024-6-18 22:03

闲着也是闲着,编个程序吧:lol

bloodfog 发表于 2024-6-18 22:13

随遇而安8 发表于 2024-6-18 21:33
复制到文本文档里,改后缀.html,点击button没反应呢?

直接下载 附件吧,代码附件打包了

chplifeng 发表于 2024-6-19 01:25

支持一下

meder 发表于 2024-6-19 02:05

感谢分享

vzzwcom 发表于 2024-6-19 08:36


感谢分享

shishiaiyin 发表于 2024-6-19 08:55

谢谢分享

静静想我1970 发表于 2024-6-19 09:17

原来积分是这么水出来的,果然我还是太咸鱼

Yukeer666 发表于 2024-6-19 09:29

感谢分享,很有帮助
页: [1] 2
查看完整版本: 闲来无聊写个简易的html娱乐用的计分器