4 Commits

Author SHA1 Message Date
c6d814f343 登录页图片压缩 2026-03-05 13:29:26 +08:00
73842a4012 fusion 2026-02-12 19:43:44 +08:00
38f8fe9bd0 ui样式回滚修复 2026-01-23 19:53:38 +08:00
21eec1085a 开启tk 状态灯 2026-01-23 16:12:37 +08:00
10 changed files with 829 additions and 407 deletions

461
package-lock.json generated
View File

@@ -24,12 +24,15 @@
"@vue/cli-plugin-router": "~5.0.0", "@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0", "@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0", "@vue/cli-service": "~5.0.0",
"autoprefixer": "^10.4.24",
"less": "^4.2.2", "less": "^4.2.2",
"less-loader": "^12.2.0", "less-loader": "^12.2.0",
"postcss": "^8.5.6",
"postcss-preset-env": "^10.1.5", "postcss-preset-env": "^10.1.5",
"postcss-px-to-viewport": "^1.1.1", "postcss-px-to-viewport": "^1.1.1",
"postcss-px-viewport": "^0.0.4", "postcss-px-viewport": "^0.0.4",
"postcss-viewport-units": "^0.1.6" "postcss-viewport-units": "^0.1.6",
"tailwindcss": "^3.4.17"
} }
}, },
"node_modules/@achrinza/node-ipc": { "node_modules/@achrinza/node-ipc": {
@@ -47,6 +50,19 @@
"node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22" "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22"
} }
}, },
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
@@ -4582,6 +4598,13 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"dev": true,
"license": "MIT"
},
"node_modules/array-flatten": { "node_modules/array-flatten": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -4629,9 +4652,9 @@
} }
}, },
"node_modules/autoprefixer": { "node_modules/autoprefixer": {
"version": "10.4.21", "version": "10.4.24",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.24.tgz",
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -4649,10 +4672,9 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"browserslist": "^4.24.4", "browserslist": "^4.28.1",
"caniuse-lite": "^1.0.30001702", "caniuse-lite": "^1.0.30001766",
"fraction.js": "^4.3.7", "fraction.js": "^5.3.4",
"normalize-range": "^0.1.2",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0" "postcss-value-parser": "^4.2.0"
}, },
@@ -4792,6 +4814,16 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/baseline-browser-mapping": {
"version": "2.9.19",
"resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
"integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.js"
}
},
"node_modules/batch": { "node_modules/batch": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
@@ -4935,9 +4967,9 @@
} }
}, },
"node_modules/browserslist": { "node_modules/browserslist": {
"version": "4.24.4", "version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -4955,10 +4987,11 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"caniuse-lite": "^1.0.30001688", "baseline-browser-mapping": "^2.9.0",
"electron-to-chromium": "^1.5.73", "caniuse-lite": "^1.0.30001759",
"node-releases": "^2.0.19", "electron-to-chromium": "^1.5.263",
"update-browserslist-db": "^1.1.1" "node-releases": "^2.0.27",
"update-browserslist-db": "^1.2.0"
}, },
"bin": { "bin": {
"browserslist": "cli.js" "browserslist": "cli.js"
@@ -5089,6 +5122,16 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/caniuse-api": { "node_modules/caniuse-api": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@@ -5103,9 +5146,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001707", "version": "1.0.30001767",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz",
"integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", "integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -6385,6 +6428,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/dir-glob": { "node_modules/dir-glob": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -6398,6 +6448,13 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"dev": true,
"license": "MIT"
},
"node_modules/dns-packet": { "node_modules/dns-packet": {
"version": "5.6.1", "version": "5.6.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
@@ -6573,9 +6630,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.129", "version": "1.5.283",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.129.tgz", "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.283.tgz",
"integrity": "sha512-JlXUemX4s0+9f8mLqib/bHH8gOHf5elKS6KeWG3sk3xozb/JTq/RLXIv8OKUWiK4Ah00Wm88EFj5PYkFr4RUPA==", "integrity": "sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==",
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
@@ -7197,16 +7254,16 @@
} }
}, },
"node_modules/fraction.js": { "node_modules/fraction.js": {
"version": "4.3.7", "version": "5.3.4",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-5.3.4.tgz",
"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": "*" "node": "*"
}, },
"funding": { "funding": {
"type": "patreon", "type": "github",
"url": "https://github.com/sponsors/rawify" "url": "https://github.com/sponsors/rawify"
} }
}, },
@@ -8198,6 +8255,16 @@
"url": "https://github.com/chalk/supports-color?sponsor=1" "url": "https://github.com/chalk/supports-color?sponsor=1"
} }
}, },
"node_modules/jiti": {
"version": "1.21.7",
"resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz",
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"license": "MIT",
"bin": {
"jiti": "bin/jiti.js"
}
},
"node_modules/joi": { "node_modules/joi": {
"version": "17.13.3", "version": "17.13.3",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz",
@@ -9226,9 +9293,9 @@
} }
}, },
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.19", "version": "2.0.27",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -9265,16 +9332,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/normalize-range": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/normalize-url": { "node_modules/normalize-url": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
@@ -9330,6 +9387,16 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.13.4", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@@ -9780,6 +9847,16 @@
"@vue/devtools-kit": "^7.7.2" "@vue/devtools-kit": "^7.7.2"
} }
}, },
"node_modules/pirates": {
"version": "4.0.7",
"resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz",
"integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/pkg-dir": { "node_modules/pkg-dir": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -9808,9 +9885,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.3", "version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -9827,7 +9904,7 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.8", "nanoid": "^3.3.11",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
}, },
@@ -10387,6 +10464,50 @@
"postcss": "^8.4" "postcss": "^8.4"
} }
}, },
"node_modules/postcss-import": {
"version": "15.1.0",
"resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"dev": true,
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
"resolve": "^1.1.7"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"postcss": "^8.0.0"
}
},
"node_modules/postcss-js": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.1.0.tgz",
"integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"camelcase-css": "^2.0.1"
},
"engines": {
"node": "^12 || ^14 || >= 16"
},
"peerDependencies": {
"postcss": "^8.4.21"
}
},
"node_modules/postcss-lab-function": { "node_modules/postcss-lab-function": {
"version": "7.0.8", "version": "7.0.8",
"resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.8.tgz", "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.8.tgz",
@@ -10417,6 +10538,71 @@
"postcss": "^8.4" "postcss": "^8.4"
} }
}, },
"node_modules/postcss-load-config": {
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"lilconfig": "^3.0.0",
"yaml": "^2.3.4"
},
"engines": {
"node": ">= 14"
},
"peerDependencies": {
"postcss": ">=8.0.9",
"ts-node": ">=9.0.0"
},
"peerDependenciesMeta": {
"postcss": {
"optional": true
},
"ts-node": {
"optional": true
}
}
},
"node_modules/postcss-load-config/node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
},
"node_modules/postcss-load-config/node_modules/yaml": {
"version": "2.8.2",
"resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.2.tgz",
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
"dev": true,
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
},
"funding": {
"url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/postcss-loader": { "node_modules/postcss-loader": {
"version": "6.2.1", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz",
@@ -10674,6 +10860,32 @@
"postcss": "^8.1.0" "postcss": "^8.1.0"
} }
}, },
"node_modules/postcss-nested": {
"version": "6.2.0",
"resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.2.0.tgz",
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"postcss-selector-parser": "^6.1.1"
},
"engines": {
"node": ">=12.0"
},
"peerDependencies": {
"postcss": "^8.2.14"
}
},
"node_modules/postcss-nesting": { "node_modules/postcss-nesting": {
"version": "13.0.1", "version": "13.0.1",
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz", "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz",
@@ -11788,6 +12000,26 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
"dev": true,
"license": "MIT",
"dependencies": {
"pify": "^2.3.0"
}
},
"node_modules/read-cache/node_modules/pify": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/read-pkg": { "node_modules/read-pkg": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -12776,6 +13008,39 @@
"postcss": "^8.2.15" "postcss": "^8.2.15"
} }
}, },
"node_modules/sucrase": {
"version": "3.35.1",
"resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.1.tgz",
"integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0",
"lines-and-columns": "^1.1.6",
"mz": "^2.7.0",
"pirates": "^4.0.1",
"tinyglobby": "^0.2.11",
"ts-interface-checker": "^0.1.9"
},
"bin": {
"sucrase": "bin/sucrase",
"sucrase-node": "bin/sucrase-node"
},
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/sucrase/node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/superjson": { "node_modules/superjson": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz",
@@ -12852,6 +13117,57 @@
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/tailwindcss": {
"version": "3.4.17",
"resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.17.tgz",
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"dev": true,
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.6.0",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.3.2",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.21.6",
"lilconfig": "^3.1.3",
"micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.1.1",
"postcss": "^8.4.47",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.2",
"postcss-nested": "^6.2.0",
"postcss-selector-parser": "^6.1.2",
"resolve": "^1.22.8",
"sucrase": "^3.35.0"
},
"bin": {
"tailwind": "lib/cli.js",
"tailwindcss": "lib/cli.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/tailwindcss/node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
},
"node_modules/tapable": { "node_modules/tapable": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -13068,6 +13384,54 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -13108,6 +13472,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -13211,9 +13582,9 @@
} }
}, },
"node_modules/update-browserslist-db": { "node_modules/update-browserslist-db": {
"version": "1.1.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {

View File

@@ -23,12 +23,15 @@
"@vue/cli-plugin-router": "~5.0.0", "@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0", "@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0", "@vue/cli-service": "~5.0.0",
"autoprefixer": "^10.4.24",
"less": "^4.2.2", "less": "^4.2.2",
"less-loader": "^12.2.0", "less-loader": "^12.2.0",
"postcss": "^8.5.6",
"postcss-preset-env": "^10.1.5", "postcss-preset-env": "^10.1.5",
"postcss-px-to-viewport": "^1.1.1", "postcss-px-to-viewport": "^1.1.1",
"postcss-px-viewport": "^0.0.4", "postcss-px-viewport": "^0.0.4",
"postcss-viewport-units": "^0.1.6" "postcss-viewport-units": "^0.1.6",
"tailwindcss": "^3.4.17"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",

View File

@@ -1,13 +0,0 @@
module.exports = {
plugins: {
'postcss-px-to-viewport': {
viewportWidth: 1600, // 视窗的宽度,对应设计稿宽度
viewportHeight: 900, // 视窗的高度,对应设计稿高度
unitPrecision: 3, // 指定 px 转换为视窗单位值的小数位数
viewportUnit: 'vw', // 指定需要转换成的视窗单位vw 或者 vh
selectorBlackList: ['.ignore', '.hairlines'], // 指定不需要转换的类
minPixelValue: 1, // 小于或等于 1 px 不转换为视窗单位
mediaQuery: false // 允许在媒体查询中转换 px
}
}
};

BIN
src/assets/logoBg2.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -78,7 +78,7 @@
sure: '确定', sure: '确定',
invitationType: '邀请类型', invitationType: '邀请类型',
invitationType1: '普票', invitationType1: '普票',
invitationType2: '票', invitationType2: '进阶票',
liveSessions: '直播场次', liveSessions: '直播场次',
viewSessions: '查看场次', viewSessions: '查看场次',
liveRevenue: '直播收益', liveRevenue: '直播收益',

View File

@@ -129,6 +129,16 @@ export function usePythonBridge() {
}); });
}; };
// 查询TK登录状态
const getTkLoginStatus = () => {
return new Promise((resolve) => {
if (!bridge.value) return resolve(false);
callBridge('getTkLoginStatus', (result) => {
resolve(result);
});
});
};
// 在组件挂载时初始化桥接 // 在组件挂载时初始化桥接
onMounted(initBridge); onMounted(initBridge);
@@ -143,5 +153,6 @@ export function usePythonBridge() {
exportToExcel, exportToExcel,
stopScript, stopScript,
getVersion, getVersion,
getTkLoginStatus,
}; };
} }

View File

@@ -67,7 +67,7 @@
<div class="version">{{ $t('login.version') }}:{{ version }}</div> <div class="version">{{ $t('login.version') }}:{{ version }}</div>
</div> </div>
<div class="auth-right"> <div class="auth-right">
<img src="@/assets/logoBg2.png" class="illustration" alt=""> <img src="@/assets/logoBg2.webp" class="illustration" alt="">
</div> </div>
</div> </div>
</div> </div>

View File

@@ -4,47 +4,36 @@
<header class="px-8 py-6 border-b border-slate-100 dark:border-slate-200/60 bg-white"> <header class="px-8 py-6 border-b border-slate-100 dark:border-slate-200/60 bg-white">
<div class="flex flex-wrap items-center gap-4"> <div class="flex flex-wrap items-center gap-4">
<div class="relative flex-1 min-w-[200px]"> <div class="relative flex-1 min-w-[200px]">
<select <select v-model="searchForm.country"
v-model="searchForm.country" class="w-full bg-slate-50 border-none rounded-xl py-3 pl-4 pr-10 text-sm text-slate-600 appearance-none focus:ring-2 focus:ring-primary/20 shadow-soft-inner outline-none">
class="w-full bg-slate-50 border-none rounded-xl py-3 pl-4 pr-10 text-sm text-slate-600 appearance-none focus:ring-2 focus:ring-primary/20 shadow-soft-inner outline-none"
>
<option value="">{{ $t('hostList.selectAll') }}</option> <option value="">{{ $t('hostList.selectAll') }}</option>
<option v-for="item in options" :key="item.value" :value="item.value">{{ item.label }}</option> <option v-for="item in options" :key="item.value" :value="item.value">{{ item.label }}</option>
</select> </select>
<span class="material-icons-round absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">expand_more</span> <span
class="material-icons-round absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">expand_more</span>
</div> </div>
<div class="relative flex-1 min-w-[200px]"> <div class="relative flex-1 min-w-[200px]">
<span class="material-icons-round absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">calendar_today</span> <span
<input class="material-icons-round absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">calendar_today</span>
ref="dateInput" <input ref="dateInput" v-model="searchForm.createTime" type="date" @click="openDatePicker"
v-model="searchForm.createTime" class="w-full bg-slate-50 border-none rounded-xl py-3 pl-10 pr-4 text-sm text-slate-600 focus:ring-2 focus:ring-primary/20 shadow-soft-inner outline-none cursor-pointer" />
type="date"
@click="openDatePicker"
class="w-full bg-slate-50 border-none rounded-xl py-3 pl-10 pr-4 text-sm text-slate-600 focus:ring-2 focus:ring-primary/20 shadow-soft-inner outline-none cursor-pointer"
/>
</div> </div>
<div class="relative flex-[1.5] min-w-[240px]"> <div class="relative flex-[1.5] min-w-[240px]">
<input <input v-model="searchForm.hostsId"
v-model="searchForm.hostsId"
class="w-full bg-slate-50 border-none rounded-xl py-3 px-4 text-sm text-slate-600 focus:ring-2 focus:ring-primary/20 shadow-soft-inner outline-none" class="w-full bg-slate-50 border-none rounded-xl py-3 px-4 text-sm text-slate-600 focus:ring-2 focus:ring-primary/20 shadow-soft-inner outline-none"
:placeholder="$t('hostList.placeHostId')" :placeholder="$t('hostList.placeHostId')" />
/>
</div> </div>
<div class="flex items-center gap-2 ml-auto"> <div class="flex items-center gap-2 ml-auto">
<button <button @click="serch"
@click="serch" class="bg-primary hover:bg-blue-700 text-white px-8 py-3 rounded-xl font-medium text-sm transition-all shadow-lg shadow-primary/20 flex items-center gap-2">
class="bg-primary hover:bg-blue-700 text-white px-8 py-3 rounded-xl font-medium text-sm transition-all shadow-lg shadow-primary/20 flex items-center gap-2"
>
<span class="material-icons-round text-sm">search</span> <span class="material-icons-round text-sm">search</span>
{{ $t('hostList.query') }} {{ $t('hostList.query') }}
</button> </button>
<button <button @click="filterdialogVisible = true"
@click="filterdialogVisible = true" class="bg-slate-100 hover:bg-slate-200 text-slate-600 p-3 rounded-xl transition-all">
class="bg-slate-100 hover:bg-slate-200 text-slate-600 p-3 rounded-xl transition-all"
>
<span class="material-icons-round">tune</span> <span class="material-icons-round">tune</span>
</button> </button>
</div> </div>
@@ -64,7 +53,7 @@
<col style="width: 12%;"> <col style="width: 12%;">
<col style="width: 5%;"> <col style="width: 5%;">
<col style="width: 8%;"> <col style="width: 8%;">
<col style="width: 15%;"> <col style="width: 8%;">
<col style="width: 6%;"> <col style="width: 6%;">
<col style="width: 10%;"> <col style="width: 10%;">
<col style="width: 8%;"> <col style="width: 8%;">
@@ -79,7 +68,8 @@
<th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.hostId') }}</th> <th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.hostId') }}</th>
<th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.grade') }}</th> <th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.grade') }}</th>
<th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.invitationType') }}</th> <th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.invitationType') }}</th>
<th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.liveSessions') }}/{{ $t('hostList.liveRevenue') }}</th> <!-- <th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.liveSessions') }}/{{ $t('hostList.liveRevenue') }}</th> -->
<th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.liveSessions') }}</th>
<th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.country') }}</th> <th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.country') }}</th>
<th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.creationTime') }}</th> <th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.creationTime') }}</th>
<th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.anchorcoins') }}</th> <th class="py-3 px-2 font-semibold bg-white">{{ $t('hostList.anchorcoins') }}</th>
@@ -99,10 +89,8 @@
<!-- Host ID --> <!-- Host ID -->
<td class="py-4 px-2 border-b border-slate-50 group-last:border-none"> <td class="py-4 px-2 border-b border-slate-50 group-last:border-none">
<span <span @click="openHTML(row.hostId)"
@click="openHTML(row.hostId)" class="font-medium text-slate-900 border-b border-transparent group-hover:border-primary/30 transition-all cursor-pointer hover:text-primary">
class="font-medium text-slate-900 border-b border-transparent group-hover:border-primary/30 transition-all cursor-pointer hover:text-primary"
>
{{ row.hostId }} {{ row.hostId }}
</span> </span>
</td> </td>
@@ -114,10 +102,8 @@
<!-- Invitation Type --> <!-- Invitation Type -->
<td class="py-4 px-2 border-b border-slate-50 group-last:border-none"> <td class="py-4 px-2 border-b border-slate-50 group-last:border-none">
<span <span class="px-3 py-1 text-[10px] font-bold uppercase rounded-full"
class="px-3 py-1 text-[10px] font-bold uppercase rounded-full" :class="row.invitationType == 1 ? 'bg-green-50 text-green-600' : 'bg-amber-50 text-amber-600'">
:class="row.invitationType == 1 ? 'bg-green-50 text-green-600' : 'bg-amber-50 text-amber-600'"
>
{{ row.invitationType == 1 ? $t('hostList.invitationType1') : $t('hostList.invitationType2') }} {{ row.invitationType == 1 ? $t('hostList.invitationType1') : $t('hostList.invitationType2') }}
</span> </span>
</td> </td>
@@ -131,12 +117,12 @@
> >
{{ $t('hostList.viewSessions') }} {{ $t('hostList.viewSessions') }}
</button> </button>
<button <!-- <button
@click="getRevenueStats(row.hostId)" @click="getRevenueStats(row.hostId)"
class="px-3 py-1.5 bg-sky-50 text-sky-600 hover:bg-sky-600 hover:text-white rounded-lg text-xs font-medium transition-all" class="px-3 py-1.5 bg-sky-50 text-sky-600 hover:bg-sky-600 hover:text-white rounded-lg text-xs font-medium transition-all"
> >
{{ $t('hostList.viewRevenue') }} {{ $t('hostList.viewRevenue') }}
</button> </button> -->
</div> </div>
</td> </td>
@@ -191,51 +177,40 @@
</div> </div>
<!-- Footer / Pagination --> <!-- Footer / Pagination -->
<footer class="px-8 py-6 border-t border-slate-100 dark:border-slate-200/60 bg-white flex flex-wrap items-center justify-between gap-4"> <footer
class="px-8 py-6 border-t border-slate-100 dark:border-slate-200/60 bg-white flex flex-wrap items-center justify-between gap-4">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="relative"> <div class="relative">
<select <select v-model="pageSize" @change="handleSizeChange"
v-model="pageSize" class="bg-slate-50 border-none rounded-lg py-2 pl-4 pr-10 text-sm text-slate-600 appearance-none focus:ring-2 focus:ring-primary/20 shadow-soft-inner outline-none">
@change="handleSizeChange"
class="bg-slate-50 border-none rounded-lg py-2 pl-4 pr-10 text-sm text-slate-600 appearance-none focus:ring-2 focus:ring-primary/20 shadow-soft-inner outline-none"
>
<option :value="10">10/page</option> <option :value="10">10/page</option>
<option :value="20">20/page</option> <option :value="20">20/page</option>
<option :value="50">50/page</option> <option :value="50">50/page</option>
<option :value="100">100/page</option> <option :value="100">100/page</option>
</select> </select>
<span class="material-icons-round absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span> <span
class="material-icons-round absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
</div> </div>
<span class="text-xs text-slate-400 font-medium">总条数: <span class="text-primary">{{ total }}</span></span> <span class="text-xs text-slate-400 font-medium">总条数: <span class="text-primary">{{ total }}</span></span>
</div> </div>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<button <button @click="changePage(page - 1)" :disabled="page <= 1"
@click="changePage(page - 1)" class="p-2 text-slate-400 hover:bg-slate-100 rounded-lg transition-colors disabled:opacity-50">
:disabled="page <= 1"
class="p-2 text-slate-400 hover:bg-slate-100 rounded-lg transition-colors disabled:opacity-50"
>
<span class="material-icons-round text-lg">chevron_left</span> <span class="material-icons-round text-lg">chevron_left</span>
</button> </button>
<!-- Page Numbers --> <!-- Page Numbers -->
<template v-for="(p, index) in paginationPages" :key="index"> <template v-for="(p, index) in paginationPages" :key="index">
<span v-if="p === '...'" class="w-8 h-8 flex items-center justify-center text-slate-400 text-sm">...</span> <span v-if="p === '...'" class="w-8 h-8 flex items-center justify-center text-slate-400 text-sm">...</span>
<button <button v-else @click="changePage(p)" class="w-8 h-8 rounded-lg text-xs font-bold transition-all"
v-else :class="p === page ? 'bg-slate-900 text-white shadow-md' : 'text-slate-600 hover:bg-slate-100'">
@click="changePage(p)"
class="w-8 h-8 rounded-lg text-xs font-bold transition-all"
:class="p === page ? 'bg-slate-900 text-white shadow-md' : 'text-slate-600 hover:bg-slate-100'"
>
{{ p }} {{ p }}
</button> </button>
</template> </template>
<button <button @click="changePage(page + 1)" :disabled="page >= totalPages"
@click="changePage(page + 1)" class="p-2 text-slate-400 hover:bg-slate-100 rounded-lg transition-colors disabled:opacity-50">
:disabled="page >= totalPages"
class="p-2 text-slate-400 hover:bg-slate-100 rounded-lg transition-colors disabled:opacity-50"
>
<span class="material-icons-round text-lg">chevron_right</span> <span class="material-icons-round text-lg">chevron_right</span>
</button> </button>
</div> </div>
@@ -275,7 +250,9 @@
</el-select> </el-select>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<div><label class="text-sm text-slate-500 mb-1 block">{{ $t('hostList.ascending') }}/{{ $t('hostList.descending') }}</label></div> <div><label class="text-sm text-slate-500 mb-1 block">{{ $t('hostList.ascending') }}/{{
$t('hostList.descending')
}}</label></div>
<el-select v-model="sortData.sortForm" filterable :placeholder="$t('hostList.selectPlaceholder')" <el-select v-model="sortData.sortForm" filterable :placeholder="$t('hostList.selectPlaceholder')"
class="w-full"> class="w-full">
<el-option <el-option
@@ -298,7 +275,8 @@
</el-dialog> </el-dialog>
<!-- Dialogs --> <!-- Dialogs -->
<LiveRecordDialog v-model:modelValue="liveDetailDialogVisible" :rows="liveDetailRecords" @select="handleLiveSelect" /> <LiveRecordDialog v-model:modelValue="liveDetailDialogVisible" :rows="liveDetailRecords"
@select="handleLiveSelect" />
<el-dialog v-model="revenueDialogVisible" :title="$t('hostList.liveRevenue')" width="80vw" top="6vh" <el-dialog v-model="revenueDialogVisible" :title="$t('hostList.liveRevenue')" width="80vw" top="6vh"
:close-on-click-modal="false" destroy-on-close> :close-on-click-modal="false" destroy-on-close>
@@ -663,6 +641,7 @@ function formatTimestamp(value) {
padding: 4px; padding: 4px;
width: 190px; width: 190px;
} }
.history-sparkline-top, .history-sparkline-top,
.history-sparkline-bottom { .history-sparkline-bottom {
display: flex; display: flex;
@@ -671,6 +650,7 @@ function formatTimestamp(value) {
color: #64748b; color: #64748b;
margin-bottom: 2px; margin-bottom: 2px;
} }
.history-sparkline { .history-sparkline {
background-color: #fff; background-color: #fff;
border-radius: 2px; border-radius: 2px;

View File

@@ -1,73 +1,92 @@
<template> <template>
<div class="grid grid-cols-1 lg:grid-cols-4 gap-4 mb-4"> <div class="grid grid-cols-1 lg:grid-cols-12 gap-4 mb-4">
<!-- Stat Cards --> <!-- Stat Cards -->
<div class="bg-white dark:bg-slate-900 p-6 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800"> <!-- 总数量 (较小) -->
<div class="flex items-center justify-between mb-2">
<span class="text-sm font-medium text-slate-500">{{ $t('workbenches.totalnumber') }}</span>
<span class="material-icons-round text-primary/40">analytics</span>
</div>
<div class="text-3xl font-bold text-slate-900 dark:text-white">{{ hostData.totalCount }}</div>
</div>
<div class="bg-white dark:bg-slate-900 p-6 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800">
<div class="flex items-center justify-between mb-2">
<span class="text-sm font-medium text-slate-500">{{ $t('workbenches.createHost') }}</span>
<span class="material-icons-round text-secondary/40">person_add</span>
</div>
<div class="text-3xl font-bold text-slate-900 dark:text-white">{{ hostData.validAnchorsCount }}</div>
</div>
<div class="bg-white dark:bg-slate-900 p-6 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800">
<div class="flex items-center justify-between mb-2">
<span class="text-sm font-medium text-slate-500">{{ $t('workbenches.query') }} / {{ $t('workbenches.invite')
}}</span>
<span class="material-icons-round text-slate-400">compare_arrows</span>
</div>
<div class="text-3xl font-bold text-slate-900 dark:text-white">{{ hostData.checkedDataCount }} <span
class="text-slate-300 text-lg mx-2">/</span> {{ hostData.canInvitationCount }}</div>
</div>
<div <div
class="bg-white dark:bg-slate-900 p-6 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800 flex flex-col justify-center"> class="lg:col-span-2 bg-white dark:bg-slate-900 p-4 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800">
<div class="flex items-center justify-between mb-1">
<span class="text-xs font-medium text-slate-500">{{ $t('workbenches.totalnumber') }}</span>
<span class="material-icons-round text-primary/40 text-lg">analytics</span>
</div>
<div class="text-xl font-bold text-slate-900 dark:text-white">{{ hostData.totalCount }}</div>
</div>
<!-- 新建主播 (较小) -->
<div
class="lg:col-span-2 bg-white dark:bg-slate-900 p-4 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800">
<div class="flex items-center justify-between mb-1">
<span class="text-xs font-medium text-slate-500">{{ $t('workbenches.createHost') }}</span>
<span class="material-icons-round text-secondary/40 text-lg">person_add</span>
</div>
<div class="text-xl font-bold text-slate-900 dark:text-white">{{ hostData.validAnchorsCount }}</div>
</div>
<!-- 查询 (较小) -->
<div
class="lg:col-span-2 bg-white dark:bg-slate-900 p-4 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800">
<div class="flex items-center justify-between mb-1">
<span class="text-xs font-medium text-slate-500">{{ $t('workbenches.query') }}</span>
<span class="material-icons-round text-amber-400/60 text-lg">search</span>
</div>
<div class="text-xl font-bold text-slate-900 dark:text-white">{{ hostData.checkedDataCount }}</div>
</div>
<!-- 邀请 (较大突出显示) -->
<div
class="lg:col-span-3 bg-gradient-to-br from-primary to-blue-600 p-5 rounded-xl shadow-lg shadow-primary/20 text-white relative overflow-hidden">
<div class="absolute top-0 right-0 w-24 h-24 bg-white/10 rounded-full -translate-y-1/2 translate-x-1/2"></div>
<div class="flex items-center justify-between mb-2 relative z-10">
<span class="text-sm font-medium text-white/80">{{ $t('workbenches.invite') }}</span>
<span class="material-icons-round text-white/60">mail_outline</span>
</div>
<div class="text-3xl font-bold text-white relative z-10">{{ hostData.canInvitationCount }}</div>
<div class="text-xs text-white/60 mt-1">可邀请主播</div>
</div>
<!-- 运行时间 (较大) -->
<div
class="lg:col-span-3 bg-white dark:bg-slate-900 p-5 rounded-xl shadow-sm border border-slate-100 dark:border-slate-800 flex flex-col justify-center">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div>
<span class="text-xs font-semibold text-slate-400 uppercase tracking-wider block mb-1">{{ <span class="text-xs font-semibold text-slate-400 uppercase tracking-wider block mb-1">{{
$t('workbenches.runTime') }}</span> $t('workbenches.runTime') }}</span>
<div class="text-2xl font-mono font-bold text-primary">{{ formattedTime }}</div> <div class="text-2xl font-mono font-bold text-primary">{{ formattedTime }}</div>
</div> </div>
<div class="flex items-center gap-2">
<span class="w-4 h-4 rounded-full" :class="isTkLoggedIn ? 'bg-emerald-500' : 'bg-red-500'"></span>
<button @click="openTK" <button @click="openTK"
class="bg-primary hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-semibold transition-all shadow-lg shadow-primary/25"> class="bg-primary hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-semibold shadow-lg shadow-primary/25">
{{ $t('workbenches.openTK') }} {{ $t('workbenches.openTK') }}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- Guild Accounts --> <!-- Guild Accounts -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div v-for="(item, index) in 2" :key="index" class="bg-white border border-slate-100 p-5 rounded-xl shadow-sm"> <div v-for="(item, index) in 2" :key="index" class="bg-white border border-slate-100 p-5 rounded-xl shadow-sm">
<div class="flex justify-between items-center mb-6"> <div class="flex justify-between items-center mb-6">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="w-2 h-2 rounded-full" :class="tkData[index].code == 1 ? 'bg-emerald-500' : 'bg-red-500'"></span> <span class="w-4 h-4 rounded-full" :class="tkData[index].code == 1 ? 'bg-emerald-500' : 'bg-red-500'"></span>
<h3 class="font-bold text-slate-800 dark:text-white">{{ $t('workbenches.guildAccount') }} {{ index === 0 ? 'A' <h3 class="font-bold text-slate-800 dark:text-white">{{ $t('workbenches.guildAccount') }} {{ index === 0 ? 'A'
: 'B' }}</h3> : 'B' }}</h3>
</div> </div>
<span class="text-xs text-slate-500">{{ $t('workbenches.queriedNum') }}: {{ tkData[index].num }}</span> <span class=" text-slate-500" style="font-size: 17px;">{{ $t('workbenches.queriedNum') }}: {{
tkData[index].num }}</span>
</div> </div>
<div class="space-y-4"> <div class="space-y-4">
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
<div> <div>
<label class="text-xs font-semibold text-slate-500 mb-1 block">{{ $t('workbenches.guildAccount') }}</label> <label class="text-xs font-semibold text-slate-500 mb-1 block">{{ $t('workbenches.guildAccount') }}</label>
<input <el-input v-model="tkData[index].account" :placeholder="$t('workbenches.guildAccountPlace')"
class="w-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-primary/20 outline-none transition-all disabled:opacity-50" :disabled="!(tkData[index].code == 0 && !isLogin[index])" class="el-input-custom" />
type="text" v-model="tkData[index].account" :placeholder="$t('workbenches.guildAccountPlace')"
:disabled="!(tkData[index].code == 0 && !isLogin[index])" />
</div> </div>
<div> <div>
<label class="text-xs font-semibold text-slate-500 mb-1 block">{{ $t('workbenches.guildPass') }}</label> <label class="text-xs font-semibold text-slate-500 mb-1 block">{{ $t('workbenches.guildPass') }}</label>
<div class="relative"> <el-input v-model="tkData[index].password" type="password" show-password
<input :placeholder="$t('workbenches.guildPassPlace')" :disabled="!(tkData[index].code == 0 && !isLogin[index])"
class="w-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-primary/20 outline-none transition-all disabled:opacity-50" class="el-input-custom" />
type="password" v-model="tkData[index].password" :placeholder="$t('workbenches.guildPassPlace')"
:disabled="!(tkData[index].code == 0 && !isLogin[index])" />
</div>
</div> </div>
</div> </div>
<button @click="loginTK(index)" :disabled="!(tkData[index].code == 0 && !isLogin[index])" <button @click="loginTK(index)" :disabled="!(tkData[index].code == 0 && !isLogin[index])"
@@ -219,30 +238,24 @@
class="flex flex-col lg:flex-row items-center justify-between gap-6 pt-4 border-t border-slate-100 dark:border-slate-800"> class="flex flex-col lg:flex-row items-center justify-between gap-6 pt-4 border-t border-slate-100 dark:border-slate-800">
<div class="flex items-center gap-6"> <div class="flex items-center gap-6">
<div class="flex items-center gap-2 cursor-pointer group" @click="toggleFilter('filterGame')"> <div class="flex items-center gap-2 cursor-pointer group" @click="toggleFilter('filterGame')">
<span <span class="w-4 h-4 rounded border-2 flex items-center justify-center transition-all"
class="w-4 h-4 rounded border-2 flex items-center justify-center transition-all" :class="pyData.filterGame ? 'bg-primary border-primary' : 'bg-white border-slate-300'">
:class="pyData.filterGame ? 'bg-primary border-primary' : 'bg-white border-slate-300'"
>
<span v-if="pyData.filterGame" class="material-icons-round text-white text-xs">check</span> <span v-if="pyData.filterGame" class="material-icons-round text-white text-xs">check</span>
</span> </span>
<span <span
class="text-sm text-slate-600 dark:text-slate-400 group-hover:text-primary transition-colors">过滤游戏主播</span> class="text-sm text-slate-600 dark:text-slate-400 group-hover:text-primary transition-colors">过滤游戏主播</span>
</div> </div>
<div class="flex items-center gap-2 cursor-pointer group" @click="toggleFilter('filterSelling')"> <div class="flex items-center gap-2 cursor-pointer group" @click="toggleFilter('filterSelling')">
<span <span class="w-4 h-4 rounded border-2 flex items-center justify-center transition-all"
class="w-4 h-4 rounded border-2 flex items-center justify-center transition-all" :class="pyData.filterSelling ? 'bg-primary border-primary' : 'bg-white border-slate-300'">
:class="pyData.filterSelling ? 'bg-primary border-primary' : 'bg-white border-slate-300'"
>
<span v-if="pyData.filterSelling" class="material-icons-round text-white text-xs">check</span> <span v-if="pyData.filterSelling" class="material-icons-round text-white text-xs">check</span>
</span> </span>
<span <span
class="text-sm text-slate-600 dark:text-slate-400 group-hover:text-primary transition-colors">过滤带货主播</span> class="text-sm text-slate-600 dark:text-slate-400 group-hover:text-primary transition-colors">过滤带货主播</span>
</div> </div>
<div class="flex items-center gap-2 cursor-pointer group" @click="toggleFilter('rankingList')"> <div class="flex items-center gap-2 cursor-pointer group" @click="toggleFilter('rankingList')">
<span <span class="w-4 h-4 rounded border-2 flex items-center justify-center transition-all"
class="w-4 h-4 rounded border-2 flex items-center justify-center transition-all" :class="pyData.rankingList ? 'bg-primary border-primary' : 'bg-white border-slate-300'">
:class="pyData.rankingList ? 'bg-primary border-primary' : 'bg-white border-slate-300'"
>
<span v-if="pyData.rankingList" class="material-icons-round text-white text-xs">check</span> <span v-if="pyData.rankingList" class="material-icons-round text-white text-xs">check</span>
</span> </span>
<span <span
@@ -280,7 +293,7 @@ import { tkaccountuseinfo, getExpiredTime } from '@/api/account'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
const { locale } = useI18n() const { locale } = useI18n()
//导入python交互方法 //导入python交互方法
const { fetchDataConfig, fetchDataCount, loginBackStage, loginTikTok, backStageloginStatus, backStageloginStatusCopy } = usePythonBridge(); const { fetchDataConfig, fetchDataCount, loginBackStage, loginTikTok, backStageloginStatus, backStageloginStatusCopy, getTkLoginStatus } = usePythonBridge();
//ip国家 //ip国家
@@ -305,6 +318,10 @@ let hostData = ref({
//账号是否登陆中 //账号是否登陆中
let isLogin = ref([false, false]); let isLogin = ref([false, false]);
//TK登录状态
let isTkLoggedIn = ref(false);
//TK状态轮询定时器
let tkStatusTimer = ref(null);
//设置状态轮询定时器 //设置状态轮询定时器
let statusTimer = ref(null); let statusTimer = ref(null);
let statusTimerCopy = ref(null); let statusTimerCopy = ref(null);
@@ -610,6 +627,22 @@ const openTK = () => {
// console.log(isTk.value) // console.log(isTk.value)
loginTikTok(); loginTikTok();
// 开始轮询TK登录状态
if (tkStatusTimer.value) {
clearInterval(tkStatusTimer.value);
}
tkStatusTimer.value = setInterval(() => {
checkTkLoginStatus();
}, 3000);
}
// 检查TK登录状态
const checkTkLoginStatus = () => {
getTkLoginStatus().then((res) => {
isTkLoggedIn.value = res === true || res === 'true';
}).catch(() => {
isTkLoggedIn.value = false;
});
} }
function getloginStatus() { function getloginStatus() {
@@ -804,4 +837,41 @@ const checkVPN = async () => {
Most styles are replaced by Tailwind utility classes. Most styles are replaced by Tailwind utility classes.
We can keep specific overrides or custom animations here if needed. We can keep specific overrides or custom animations here if needed.
*/ */
/* Element Plus 输入框统一样式 */
.el-input-custom :deep(.el-input__wrapper) {
background-color: white;
border: 1px solid rgb(226, 232, 240);
border-radius: 0.5rem;
padding: 0.5rem 1rem;
box-shadow: none;
transition: all 0.15s ease;
}
.el-input-custom :deep(.el-input__wrapper:hover) {
border-color: rgb(203, 213, 225);
}
.el-input-custom :deep(.el-input__wrapper.is-focus) {
border-color: var(--el-color-primary);
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
.el-input-custom :deep(.el-input__inner) {
font-size: 0.875rem;
}
.el-input-custom :deep(.el-input__wrapper.is-disabled) {
opacity: 0.5;
}
/* 暗色模式支持 */
.dark .el-input-custom :deep(.el-input__wrapper) {
background-color: rgb(30, 41, 59);
border-color: rgb(51, 65, 85);
}
.dark .el-input-custom :deep(.el-input__wrapper:hover) {
border-color: rgb(71, 85, 105);
}
</style> </style>