mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-11 10:00:08 +08:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
121cb7e442 | ||
|
|
44163f0fb2 | ||
|
|
d43865fcad | ||
|
|
fed92f3853 | ||
|
|
823d2a816e | ||
|
|
046c21edf6 | ||
|
|
8236d80b42 | ||
|
|
90e7eb1c79 | ||
|
|
ef09868af1 | ||
|
|
028981e3ae | ||
|
|
e8a6274cf6 | ||
|
|
ffd0265526 | ||
|
|
13d7344bc0 | ||
|
|
2ad36f92c5 | ||
|
|
36b02f4423 | ||
|
|
01df990aa8 | ||
|
|
49b71fcf5d | ||
|
|
9e43d77ac4 | ||
|
|
3ab9af720b | ||
|
|
abae304f87 | ||
|
|
659d8bff66 | ||
|
|
1786e10101 | ||
|
|
bda7f929e7 | ||
|
|
c309f80a94 | ||
|
|
97c987c561 | ||
|
|
48949104e0 | ||
|
|
a38cc4fe34 | ||
|
|
495dfbcb28 | ||
|
|
6e4dbd912b | ||
|
|
82904d956d | ||
|
|
ec7118b376 | ||
|
|
058b32a263 | ||
|
|
e7b960838e | ||
|
|
14e776a287 | ||
|
|
73f11b920f | ||
|
|
5c93040a8e | ||
|
|
a517769e8a | ||
|
|
4bb59a9f05 | ||
|
|
b37879d2d4 | ||
|
|
05defc39d7 | ||
|
|
18bfad07d2 | ||
|
|
b83591255d | ||
|
|
804350bc81 | ||
|
|
46e1cae0bb | ||
|
|
81062d4580 | ||
|
|
55481db2ee | ||
|
|
ecdd12f5a9 | ||
|
|
ef92cdc183 | ||
|
|
08f4a6cf2c | ||
|
|
38889acb4e | ||
|
|
c0517cd29a | ||
|
|
084449ccf3 | ||
|
|
0e8203ae03 | ||
|
|
236440be52 |
352
index.html
352
index.html
@@ -1,214 +1,162 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" href="/favicon.ico" />
|
|
||||||
<meta name="viewport" content="initial-scale=1, viewport-fit=cover, width=device-width, user-scalable=no" />
|
|
||||||
<title>MoviePilot</title>
|
|
||||||
<meta name="Robots" content="noindex,nofollow,noarchive" />
|
|
||||||
<meta name="referrer" content="origin" />
|
|
||||||
<link rel="icon" type="image/png" href="/logo.png" />
|
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
||||||
<link rel="apple-touch-startup-image" href="/splash/apple-splash.jpg" />
|
|
||||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
|
||||||
<link rel="manifest" href="manifest.json" crossorigin="use-credentials" />
|
|
||||||
<meta name="mobile-web-app-capable" content="yes" />
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
||||||
<meta name="apple-mobile-web-app-title" content="MoviePilot" />
|
|
||||||
<meta name="description" content="MoviePilot" />
|
|
||||||
<meta name="format-detection" content="telephone=no" />
|
|
||||||
<meta name="referrer" content="never" />
|
|
||||||
<meta name="msapplication-TileColor" content="#7D34FD" />
|
|
||||||
<meta name="color-scheme" content="light dark" />
|
|
||||||
<meta name="theme-color" content="#28243D" media="(prefers-color-scheme: dark)" />
|
|
||||||
<meta name="theme-color" content="#F4F5FA" media="(prefers-color-scheme: light)" />
|
|
||||||
<meta name="HandheldFriendly" content="True" />
|
|
||||||
<meta name="MobileOptimized" content="320" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="/loader.css" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
<head>
|
||||||
<div id="loading-bg">
|
<meta http-equiv="pragma" content="no-cache">
|
||||||
<div class="loading-logo">
|
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate">
|
||||||
<!-- Logo -->
|
<meta http-equiv="expires" content="0">
|
||||||
<svg
|
<meta charset="UTF-8" />
|
||||||
width="100px"
|
<link rel="icon" href="/favicon.ico" />
|
||||||
height="100px"
|
<meta name="viewport" content="initial-scale=1, viewport-fit=cover, width=device-width, user-scalable=no" />
|
||||||
viewBox="0 0 192 192"
|
<title>MoviePilot</title>
|
||||||
version="1.1"
|
<meta name="Robots" content="noindex,nofollow,noarchive" />
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<meta name="referrer" content="origin" />
|
||||||
style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 2"
|
<link rel="icon" type="image/png" href="/logo.png" />
|
||||||
>
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
<g transform="matrix(1,0,0,1,-2606,-236)">
|
<link rel="apple-touch-startup-image" href="/splash/apple-splash.jpg" />
|
||||||
<g id="a2-c" transform="matrix(1,0,0,1,2606,236)">
|
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||||||
<rect x="0" y="0" width="192" height="192" style="fill: none" />
|
<link rel="manifest" href="manifest.json" crossorigin="use-credentials" />
|
||||||
<g transform="matrix(-0.800798,0.462341,-0.769972,-1.33363,1869.11,-896.718)">
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="MoviePilot" />
|
||||||
|
<meta name="description" content="MoviePilot" />
|
||||||
|
<meta name="format-detection" content="telephone=no" />
|
||||||
|
<meta name="referrer" content="never" />
|
||||||
|
<meta name="msapplication-TileColor" content="#7D34FD" />
|
||||||
|
<meta name="color-scheme" content="light dark" />
|
||||||
|
<meta name="theme-color" content="#28243D" media="(prefers-color-scheme: dark)" />
|
||||||
|
<meta name="theme-color" content="#F4F5FA" media="(prefers-color-scheme: light)" />
|
||||||
|
<meta name="HandheldFriendly" content="True" />
|
||||||
|
<meta name="MobileOptimized" content="320" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/loader.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="loading-bg">
|
||||||
|
<div class="loading-logo">
|
||||||
|
<!-- Logo -->
|
||||||
|
<svg width="100px" height="100px" viewBox="0 0 192 192" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 2">
|
||||||
|
<g transform="matrix(1,0,0,1,-2606,-236)">
|
||||||
|
<g id="a2-c" transform="matrix(1,0,0,1,2606,236)">
|
||||||
|
<rect x="0" y="0" width="192" height="192" style="fill: none" />
|
||||||
|
<g transform="matrix(-0.800798,0.462341,-0.769972,-1.33363,1869.11,-896.718)">
|
||||||
|
<path
|
||||||
|
d="M2241.27,-28.175C2238.86,-28.931 2236.64,-29.181 2234.48,-29.254L2159.78,-29.286L2165.01,-11.207C2167.16,-13.121 2169.64,-13.722 2172.26,-13.808L2222.12,-13.822C2223.52,-13.824 2225,-13.701 2226.78,-13.108L2241.27,-28.175Z"
|
||||||
|
style="fill: url(#_Linear1)" />
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
|
||||||
|
<path
|
||||||
|
d="M2205.67,331.428L2205.67,332.25L2205.67,352.835C2205.67,354.263 2204.91,355.583 2203.67,356.298C2202.43,357.012 2200.91,357.013 2199.67,356.3L2190.78,351.174C2189.73,350.595 2188.83,350.083 2188.03,349.59L2187.45,349.257C2186.66,348.725 2185.91,348.142 2185.21,347.461C2185.08,347.331 2184.95,347.198 2184.82,347.061C2184.26,346.457 2183.75,345.778 2183.3,344.995C2182.16,343.05 2181.69,341.024 2181.68,338.948L2181.67,268.923L2209.77,274.425C2207.5,275.639 2205.68,278.3 2205.67,281.429L2205.67,331.428Z"
|
||||||
|
style="fill: url(#_Linear2)" />
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
|
||||||
|
<path
|
||||||
|
d="M2295.93,363.064C2299.48,360.882 2301.46,357.55 2301.67,352.926L2301.67,277.879L2301.67,276.775L2301.67,252.515C2301.67,251.801 2302.05,251.14 2302.67,250.783C2303.29,250.426 2304.05,250.426 2304.67,250.784L2319.81,259.54C2321.59,260.617 2322.95,262.115 2324.04,263.875C2325.03,265.551 2325.56,267.37 2325.67,269.835L2325.67,339.91C2325.18,343.645 2323.51,346.705 2320.3,348.887L2295.93,363.064ZM2295.93,363.064C2295.73,363.184 2295.53,363.301 2295.32,363.414L2295.93,363.064Z"
|
||||||
|
style="fill: rgb(141, 81, 249)" />
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
|
||||||
|
<path
|
||||||
|
d="M2295.93,363.064C2299.48,360.882 2301.46,357.55 2301.67,352.926L2301.67,277.879L2301.67,276.775L2301.67,252.515C2301.67,251.801 2302.05,251.14 2302.67,250.783C2303.29,250.426 2304.05,250.426 2304.67,250.784L2319.81,259.54C2321.59,260.617 2322.95,262.115 2324.04,263.875C2325.03,265.551 2325.56,267.37 2325.67,269.835L2325.67,339.91C2325.18,343.645 2323.51,346.705 2320.3,348.887L2295.93,363.064ZM2299.79,360.238C2299.79,360.238 2320.03,348.464 2320.04,348.461C2323.1,346.372 2324.69,343.444 2325.17,339.877C2325.17,339.877 2325.17,269.846 2325.17,269.839C2325.06,267.482 2324.56,265.739 2323.61,264.133C2322.56,262.445 2321.26,261.005 2319.55,259.97L2304.42,251.217C2303.96,250.949 2303.39,250.948 2302.92,251.216C2302.46,251.484 2302.17,251.979 2302.17,252.515L2302.17,276.775L2302.17,277.879L2302.17,352.926C2302.17,352.933 2302.17,352.941 2302.17,352.948C2302.04,355.861 2301.23,358.279 2299.79,360.238Z"
|
||||||
|
style="fill: url(#_Linear3)" />
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
|
||||||
|
<path
|
||||||
|
d="M2253.67,223.256C2255.26,223.245 2257.02,223.56 2259.11,224.557L2275.67,234.102C2276.91,234.816 2277.67,236.138 2277.67,237.568L2277.67,259.508C2277.67,260.222 2277.29,260.882 2276.67,261.239C2276.05,261.597 2275.29,261.597 2274.67,261.24L2257.52,251.353C2256.38,250.731 2255.12,250.341 2253.67,250.347C2252.26,250.339 2250.99,250.721 2249.82,251.353L2187.87,287.04C2184.23,289.147 2181.96,292.478 2181.67,297.57L2181.68,269.865C2181.85,265.167 2183.93,261.653 2187.92,259.322L2248.23,224.557C2249.69,223.796 2251.5,223.29 2253.67,223.256Z"
|
||||||
|
style="fill: rgb(165, 118, 255)" />
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
|
||||||
|
<path
|
||||||
|
d="M2253.67,223.256C2255.26,223.245 2257.02,223.56 2259.11,224.557L2275.67,234.102C2276.91,234.816 2277.67,236.138 2277.67,237.568L2277.67,259.508C2277.67,260.222 2277.29,260.882 2276.67,261.239C2276.05,261.597 2275.29,261.597 2274.67,261.24L2257.52,251.353C2256.38,250.731 2255.12,250.341 2253.67,250.347C2252.26,250.339 2250.99,250.721 2249.82,251.353L2187.87,287.04C2184.23,289.147 2181.96,292.478 2181.67,297.57L2181.68,269.865C2181.85,265.167 2183.93,261.653 2187.92,259.322L2248.23,224.557C2249.69,223.796 2251.5,223.29 2253.67,223.256ZM2253.68,223.756C2251.6,223.789 2249.87,224.269 2248.47,224.996L2188.17,259.754C2184.35,261.992 2182.35,265.367 2182.18,269.874C2182.18,269.874 2182.17,292.759 2182.17,292.757C2183.25,290.047 2185.13,288.051 2187.62,286.607L2249.57,250.919C2249.58,250.917 2249.58,250.915 2249.59,250.913C2250.83,250.243 2252.17,249.839 2253.67,249.847C2255.21,249.841 2256.54,250.253 2257.76,250.914C2257.76,250.916 2257.76,250.917 2257.76,250.919L2274.92,260.807C2275.38,261.075 2275.95,261.074 2276.42,260.806C2276.88,260.538 2277.17,260.043 2277.17,259.508L2277.17,237.568C2277.17,236.317 2276.5,235.16 2275.42,234.535C2275.42,234.535 2258.88,225 2258.87,224.996C2256.87,224.049 2255.2,223.746 2253.68,223.756Z"
|
||||||
|
style="fill: url(#_Linear4)" />
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.800798,0.462341,0.769972,-1.33363,-1677.22,-896.858)">
|
||||||
|
<path
|
||||||
|
d="M2241.55,-28.184C2239.1,-28.989 2236.83,-29.204 2234.68,-29.295C2234.68,-29.295 2220.82,-29.3 2215.03,-29.303C2213.48,-29.303 2212.05,-28.808 2211.28,-28.004C2208.65,-25.275 2202.56,-18.936 2199.45,-15.709C2199.07,-15.306 2199.07,-14.809 2199.46,-14.406C2199.85,-14.004 2200.57,-13.758 2201.34,-13.761C2208.36,-13.788 2222.72,-13.845 2222.72,-13.845C2223.98,-13.851 2225.44,-13.657 2227.06,-13.117L2241.55,-28.184Z"
|
||||||
|
style="fill: rgb(141, 81, 249)" />
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(-4.32309,0,0,12.4454,9610.35,-1450.35)">
|
||||||
|
<path
|
||||||
|
d="M2205.31,121.966C2205.31,121.88 2205.18,121.8 2204.96,121.757C2204.74,121.714 2204.48,121.714 2204.27,121.757C2201.75,122.263 2195.36,123.547 2192.85,124.052C2192.63,124.095 2192.5,124.174 2192.5,124.261C2192.5,124.347 2192.63,124.426 2192.85,124.469C2195.36,124.974 2201.75,126.255 2204.27,126.759C2204.48,126.802 2204.74,126.802 2204.96,126.759C2205.18,126.716 2205.31,126.636 2205.31,126.55C2205.31,125.541 2205.31,122.976 2205.31,121.966Z"
|
||||||
|
style="fill: rgb(104, 0, 197)" />
|
||||||
|
<clipPath id="_clip5">
|
||||||
<path
|
<path
|
||||||
d="M2241.27,-28.175C2238.86,-28.931 2236.64,-29.181 2234.48,-29.254L2159.78,-29.286L2165.01,-11.207C2167.16,-13.121 2169.64,-13.722 2172.26,-13.808L2222.12,-13.822C2223.52,-13.824 2225,-13.701 2226.78,-13.108L2241.27,-28.175Z"
|
d="M2205.31,121.966C2205.31,121.88 2205.18,121.8 2204.96,121.757C2204.74,121.714 2204.48,121.714 2204.27,121.757C2201.75,122.263 2195.36,123.547 2192.85,124.052C2192.63,124.095 2192.5,124.174 2192.5,124.261C2192.5,124.347 2192.63,124.426 2192.85,124.469C2195.36,124.974 2201.75,126.255 2204.27,126.759C2204.48,126.802 2204.74,126.802 2204.96,126.759C2205.18,126.716 2205.31,126.636 2205.31,126.55C2205.31,125.541 2205.31,122.976 2205.31,121.966Z" />
|
||||||
style="fill: url(#_Linear1)"
|
</clipPath>
|
||||||
/>
|
<g clip-path="url(#_clip5)">
|
||||||
</g>
|
<g transform="matrix(0.124502,0.074907,0.206623,-0.0414384,1997.62,-7.40235)">
|
||||||
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
|
|
||||||
<path
|
|
||||||
d="M2205.67,331.428L2205.67,332.25L2205.67,352.835C2205.67,354.263 2204.91,355.583 2203.67,356.298C2202.43,357.012 2200.91,357.013 2199.67,356.3L2190.78,351.174C2189.73,350.595 2188.83,350.083 2188.03,349.59L2187.45,349.257C2186.66,348.725 2185.91,348.142 2185.21,347.461C2185.08,347.331 2184.95,347.198 2184.82,347.061C2184.26,346.457 2183.75,345.778 2183.3,344.995C2182.16,343.05 2181.69,341.024 2181.68,338.948L2181.67,268.923L2209.77,274.425C2207.5,275.639 2205.68,278.3 2205.67,281.429L2205.67,331.428Z"
|
|
||||||
style="fill: url(#_Linear2)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
|
|
||||||
<path
|
|
||||||
d="M2295.93,363.064C2299.48,360.882 2301.46,357.55 2301.67,352.926L2301.67,277.879L2301.67,276.775L2301.67,252.515C2301.67,251.801 2302.05,251.14 2302.67,250.783C2303.29,250.426 2304.05,250.426 2304.67,250.784L2319.81,259.54C2321.59,260.617 2322.95,262.115 2324.04,263.875C2325.03,265.551 2325.56,267.37 2325.67,269.835L2325.67,339.91C2325.18,343.645 2323.51,346.705 2320.3,348.887L2295.93,363.064ZM2295.93,363.064C2295.73,363.184 2295.53,363.301 2295.32,363.414L2295.93,363.064Z"
|
|
||||||
style="fill: rgb(141, 81, 249)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
|
|
||||||
<path
|
|
||||||
d="M2295.93,363.064C2299.48,360.882 2301.46,357.55 2301.67,352.926L2301.67,277.879L2301.67,276.775L2301.67,252.515C2301.67,251.801 2302.05,251.14 2302.67,250.783C2303.29,250.426 2304.05,250.426 2304.67,250.784L2319.81,259.54C2321.59,260.617 2322.95,262.115 2324.04,263.875C2325.03,265.551 2325.56,267.37 2325.67,269.835L2325.67,339.91C2325.18,343.645 2323.51,346.705 2320.3,348.887L2295.93,363.064ZM2299.79,360.238C2299.79,360.238 2320.03,348.464 2320.04,348.461C2323.1,346.372 2324.69,343.444 2325.17,339.877C2325.17,339.877 2325.17,269.846 2325.17,269.839C2325.06,267.482 2324.56,265.739 2323.61,264.133C2322.56,262.445 2321.26,261.005 2319.55,259.97L2304.42,251.217C2303.96,250.949 2303.39,250.948 2302.92,251.216C2302.46,251.484 2302.17,251.979 2302.17,252.515L2302.17,276.775L2302.17,277.879L2302.17,352.926C2302.17,352.933 2302.17,352.941 2302.17,352.948C2302.04,355.861 2301.23,358.279 2299.79,360.238Z"
|
|
||||||
style="fill: url(#_Linear3)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
|
|
||||||
<path
|
|
||||||
d="M2253.67,223.256C2255.26,223.245 2257.02,223.56 2259.11,224.557L2275.67,234.102C2276.91,234.816 2277.67,236.138 2277.67,237.568L2277.67,259.508C2277.67,260.222 2277.29,260.882 2276.67,261.239C2276.05,261.597 2275.29,261.597 2274.67,261.24L2257.52,251.353C2256.38,250.731 2255.12,250.341 2253.67,250.347C2252.26,250.339 2250.99,250.721 2249.82,251.353L2187.87,287.04C2184.23,289.147 2181.96,292.478 2181.67,297.57L2181.68,269.865C2181.85,265.167 2183.93,261.653 2187.92,259.322L2248.23,224.557C2249.69,223.796 2251.5,223.29 2253.67,223.256Z"
|
|
||||||
style="fill: rgb(165, 118, 255)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
|
|
||||||
<path
|
|
||||||
d="M2253.67,223.256C2255.26,223.245 2257.02,223.56 2259.11,224.557L2275.67,234.102C2276.91,234.816 2277.67,236.138 2277.67,237.568L2277.67,259.508C2277.67,260.222 2277.29,260.882 2276.67,261.239C2276.05,261.597 2275.29,261.597 2274.67,261.24L2257.52,251.353C2256.38,250.731 2255.12,250.341 2253.67,250.347C2252.26,250.339 2250.99,250.721 2249.82,251.353L2187.87,287.04C2184.23,289.147 2181.96,292.478 2181.67,297.57L2181.68,269.865C2181.85,265.167 2183.93,261.653 2187.92,259.322L2248.23,224.557C2249.69,223.796 2251.5,223.29 2253.67,223.256ZM2253.68,223.756C2251.6,223.789 2249.87,224.269 2248.47,224.996L2188.17,259.754C2184.35,261.992 2182.35,265.367 2182.18,269.874C2182.18,269.874 2182.17,292.759 2182.17,292.757C2183.25,290.047 2185.13,288.051 2187.62,286.607L2249.57,250.919C2249.58,250.917 2249.58,250.915 2249.59,250.913C2250.83,250.243 2252.17,249.839 2253.67,249.847C2255.21,249.841 2256.54,250.253 2257.76,250.914C2257.76,250.916 2257.76,250.917 2257.76,250.919L2274.92,260.807C2275.38,261.075 2275.95,261.074 2276.42,260.806C2276.88,260.538 2277.17,260.043 2277.17,259.508L2277.17,237.568C2277.17,236.317 2276.5,235.16 2275.42,234.535C2275.42,234.535 2258.88,225 2258.87,224.996C2256.87,224.049 2255.2,223.746 2253.68,223.756Z"
|
|
||||||
style="fill: url(#_Linear4)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.800798,0.462341,0.769972,-1.33363,-1677.22,-896.858)">
|
|
||||||
<path
|
|
||||||
d="M2241.55,-28.184C2239.1,-28.989 2236.83,-29.204 2234.68,-29.295C2234.68,-29.295 2220.82,-29.3 2215.03,-29.303C2213.48,-29.303 2212.05,-28.808 2211.28,-28.004C2208.65,-25.275 2202.56,-18.936 2199.45,-15.709C2199.07,-15.306 2199.07,-14.809 2199.46,-14.406C2199.85,-14.004 2200.57,-13.758 2201.34,-13.761C2208.36,-13.788 2222.72,-13.845 2222.72,-13.845C2223.98,-13.851 2225.44,-13.657 2227.06,-13.117L2241.55,-28.184Z"
|
|
||||||
style="fill: rgb(141, 81, 249)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(-4.32309,0,0,12.4454,9610.35,-1450.35)">
|
|
||||||
<path
|
|
||||||
d="M2205.31,121.966C2205.31,121.88 2205.18,121.8 2204.96,121.757C2204.74,121.714 2204.48,121.714 2204.27,121.757C2201.75,122.263 2195.36,123.547 2192.85,124.052C2192.63,124.095 2192.5,124.174 2192.5,124.261C2192.5,124.347 2192.63,124.426 2192.85,124.469C2195.36,124.974 2201.75,126.255 2204.27,126.759C2204.48,126.802 2204.74,126.802 2204.96,126.759C2205.18,126.716 2205.31,126.636 2205.31,126.55C2205.31,125.541 2205.31,122.976 2205.31,121.966Z"
|
|
||||||
style="fill: rgb(104, 0, 197)"
|
|
||||||
/>
|
|
||||||
<clipPath id="_clip5">
|
|
||||||
<path
|
<path
|
||||||
d="M2205.31,121.966C2205.31,121.88 2205.18,121.8 2204.96,121.757C2204.74,121.714 2204.48,121.714 2204.27,121.757C2201.75,122.263 2195.36,123.547 2192.85,124.052C2192.63,124.095 2192.5,124.174 2192.5,124.261C2192.5,124.347 2192.63,124.426 2192.85,124.469C2195.36,124.974 2201.75,126.255 2204.27,126.759C2204.48,126.802 2204.74,126.802 2204.96,126.759C2205.18,126.716 2205.31,126.636 2205.31,126.55C2205.31,125.541 2205.31,122.976 2205.31,121.966Z"
|
d="M1726.17,-64.249L1708.16,-72.303L1708.05,-23.514L1721.88,-32.386C1722.96,-33.241 1723.09,-33.944 1723.15,-34.636L1723.15,-54.373C1723.19,-56.238 1724.96,-57.594 1726.87,-56.686L1726.17,-64.249Z"
|
||||||
/>
|
style="fill: url(#_Linear6)" />
|
||||||
</clipPath>
|
</g>
|
||||||
<g clip-path="url(#_clip5)">
|
<g transform="matrix(-0.126036,0.0767377,0.569859,0.112933,2435.01,-3.09225)">
|
||||||
<g transform="matrix(0.124502,0.074907,0.206623,-0.0414384,1997.62,-7.40235)">
|
<path
|
||||||
<path
|
d="M1726.17,-45.661L1704.47,-40.254C1706.28,-40.527 1708.14,-40.212 1708.16,-39.416L1708.16,-18.976L1726.17,-18.976L1726.17,-45.661Z"
|
||||||
d="M1726.17,-64.249L1708.16,-72.303L1708.05,-23.514L1721.88,-32.386C1722.96,-33.241 1723.09,-33.944 1723.15,-34.636L1723.15,-54.373C1723.19,-56.238 1724.96,-57.594 1726.87,-56.686L1726.17,-64.249Z"
|
style="fill: rgb(141, 81, 249)" />
|
||||||
style="fill: url(#_Linear6)"
|
</g>
|
||||||
/>
|
<g transform="matrix(-0.126036,0.0767377,0.569859,0.112933,2435.01,-3.09225)">
|
||||||
</g>
|
<path
|
||||||
<g transform="matrix(-0.126036,0.0767377,0.569859,0.112933,2435.01,-3.09225)">
|
d="M1726.17,-45.661L1726.17,-18.976L1708.16,-18.976L1708.16,-39.416C1707.79,-40.732 1704.5,-40.298 1702.68,-40.025L1726.17,-45.661ZM1705.49,-40.491C1706.2,-40.507 1706.87,-40.464 1707.4,-40.327C1708.01,-40.173 1708.48,-39.899 1708.62,-39.436C1708.62,-39.429 1708.62,-39.423 1708.62,-39.416L1708.62,-19.152C1708.62,-19.152 1725.72,-19.152 1725.72,-19.152L1725.72,-45.345L1705.49,-40.491Z"
|
||||||
<path
|
style="fill: url(#_Radial7)" />
|
||||||
d="M1726.17,-45.661L1704.47,-40.254C1706.28,-40.527 1708.14,-40.212 1708.16,-39.416L1708.16,-18.976L1726.17,-18.976L1726.17,-45.661Z"
|
|
||||||
style="fill: rgb(141, 81, 249)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(-0.126036,0.0767377,0.569859,0.112933,2435.01,-3.09225)">
|
|
||||||
<path
|
|
||||||
d="M1726.17,-45.661L1726.17,-18.976L1708.16,-18.976L1708.16,-39.416C1707.79,-40.732 1704.5,-40.298 1702.68,-40.025L1726.17,-45.661ZM1705.49,-40.491C1706.2,-40.507 1706.87,-40.464 1707.4,-40.327C1708.01,-40.173 1708.48,-39.899 1708.62,-39.436C1708.62,-39.429 1708.62,-39.423 1708.62,-39.416L1708.62,-19.152C1708.62,-19.152 1725.72,-19.152 1725.72,-19.152L1725.72,-45.345L1705.49,-40.491Z"
|
|
||||||
style="fill: url(#_Radial7)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
</g>
|
||||||
<linearGradient
|
<defs>
|
||||||
id="_Linear1"
|
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
|
||||||
x1="0"
|
gradientTransform="matrix(-70.0711,-0.927611,1.54482,-42.0752,2233.59,-20.1891)">
|
||||||
y1="0"
|
<stop offset="0" style="stop-color: rgb(141, 81, 249); stop-opacity: 1" />
|
||||||
x2="1"
|
<stop offset="1" style="stop-color: rgb(116, 50, 223); stop-opacity: 1" />
|
||||||
y2="0"
|
</linearGradient>
|
||||||
gradientUnits="userSpaceOnUse"
|
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
|
||||||
gradientTransform="matrix(-70.0711,-0.927611,1.54482,-42.0752,2233.59,-20.1891)"
|
gradientTransform="matrix(4.78193e-15,-78.0949,78.0949,4.78193e-15,2195.72,354.021)">
|
||||||
>
|
<stop offset="0" style="stop-color: rgb(141, 81, 249); stop-opacity: 1" />
|
||||||
<stop offset="0" style="stop-color: rgb(141, 81, 249); stop-opacity: 1" />
|
<stop offset="1" style="stop-color: rgb(116, 50, 223); stop-opacity: 1" />
|
||||||
<stop offset="1" style="stop-color: rgb(116, 50, 223); stop-opacity: 1" />
|
</linearGradient>
|
||||||
</linearGradient>
|
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
|
||||||
<linearGradient
|
gradientTransform="matrix(41.6089,41.5866,-41.5866,41.6089,2282.31,262.837)">
|
||||||
id="_Linear2"
|
<stop offset="0" style="stop-color: rgb(211, 187, 255); stop-opacity: 1" />
|
||||||
x1="0"
|
<stop offset="1" style="stop-color: rgb(211, 187, 255); stop-opacity: 0" />
|
||||||
y1="0"
|
</linearGradient>
|
||||||
x2="1"
|
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
|
||||||
y2="0"
|
gradientTransform="matrix(9.25616,16.7005,-16.7005,9.25616,2215,243.712)">
|
||||||
gradientUnits="userSpaceOnUse"
|
<stop offset="0" style="stop-color: rgb(211, 187, 255); stop-opacity: 1" />
|
||||||
gradientTransform="matrix(4.78193e-15,-78.0949,78.0949,4.78193e-15,2195.72,354.021)"
|
<stop offset="1" style="stop-color: rgb(211, 187, 255); stop-opacity: 0" />
|
||||||
>
|
</linearGradient>
|
||||||
<stop offset="0" style="stop-color: rgb(141, 81, 249); stop-opacity: 1" />
|
<linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
|
||||||
<stop offset="1" style="stop-color: rgb(116, 50, 223); stop-opacity: 1" />
|
gradientTransform="matrix(-0.130164,-61.9937,59.4003,-0.135847,1711.63,-25.7957)">
|
||||||
</linearGradient>
|
<stop offset="0" style="stop-color: rgb(116, 50, 223); stop-opacity: 1" />
|
||||||
<linearGradient
|
<stop offset="0.51" style="stop-color: rgb(110, 38, 217); stop-opacity: 1" />
|
||||||
id="_Linear3"
|
<stop offset="1" style="stop-color: rgb(91, 0, 197); stop-opacity: 1" />
|
||||||
x1="0"
|
</linearGradient>
|
||||||
y1="0"
|
<radialGradient id="_Radial7" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
|
||||||
x2="1"
|
gradientTransform="matrix(13.8659,4.71436,-12.1609,5.37534,1708.16,-32.287)">
|
||||||
y2="0"
|
<stop offset="0" style="stop-color: rgb(211, 187, 255); stop-opacity: 1" />
|
||||||
gradientUnits="userSpaceOnUse"
|
<stop offset="1" style="stop-color: rgb(211, 187, 255); stop-opacity: 0" />
|
||||||
gradientTransform="matrix(41.6089,41.5866,-41.5866,41.6089,2282.31,262.837)"
|
</radialGradient>
|
||||||
>
|
</defs>
|
||||||
<stop offset="0" style="stop-color: rgb(211, 187, 255); stop-opacity: 1" />
|
</svg>
|
||||||
<stop offset="1" style="stop-color: rgb(211, 187, 255); stop-opacity: 0" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="_Linear4"
|
|
||||||
x1="0"
|
|
||||||
y1="0"
|
|
||||||
x2="1"
|
|
||||||
y2="0"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(9.25616,16.7005,-16.7005,9.25616,2215,243.712)"
|
|
||||||
>
|
|
||||||
<stop offset="0" style="stop-color: rgb(211, 187, 255); stop-opacity: 1" />
|
|
||||||
<stop offset="1" style="stop-color: rgb(211, 187, 255); stop-opacity: 0" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="_Linear6"
|
|
||||||
x1="0"
|
|
||||||
y1="0"
|
|
||||||
x2="1"
|
|
||||||
y2="0"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(-0.130164,-61.9937,59.4003,-0.135847,1711.63,-25.7957)"
|
|
||||||
>
|
|
||||||
<stop offset="0" style="stop-color: rgb(116, 50, 223); stop-opacity: 1" />
|
|
||||||
<stop offset="0.51" style="stop-color: rgb(110, 38, 217); stop-opacity: 1" />
|
|
||||||
<stop offset="1" style="stop-color: rgb(91, 0, 197); stop-opacity: 1" />
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient
|
|
||||||
id="_Radial7"
|
|
||||||
cx="0"
|
|
||||||
cy="0"
|
|
||||||
r="1"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(13.8659,4.71436,-12.1609,5.37534,1708.16,-32.287)"
|
|
||||||
>
|
|
||||||
<stop offset="0" style="stop-color: rgb(211, 187, 255); stop-opacity: 1" />
|
|
||||||
<stop offset="1" style="stop-color: rgb(211, 187, 255); stop-opacity: 0" />
|
|
||||||
</radialGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="loading">
|
|
||||||
<div class="effect-1 effects"></div>
|
|
||||||
<div class="effect-2 effects"></div>
|
|
||||||
<div class="effect-3 effects"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="app"></div>
|
<div class="loading">
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<div class="effect-1 effects"></div>
|
||||||
<script>
|
<div class="effect-2 effects"></div>
|
||||||
const loaderColor = localStorage.getItem('materio-initial-loader-bg') || '#FFFFFF'
|
<div class="effect-3 effects"></div>
|
||||||
const primaryColor = localStorage.getItem('materio-initial-loader-color') || '#9155FD'
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
<script>
|
||||||
|
const loaderColor = localStorage.getItem('materio-initial-loader-bg') || '#FFFFFF'
|
||||||
|
const primaryColor = localStorage.getItem('materio-initial-loader-color') || '#9155FD'
|
||||||
|
|
||||||
if (loaderColor) document.documentElement.style.setProperty('--initial-loader-bg', loaderColor)
|
if (loaderColor)
|
||||||
|
document.documentElement.style.setProperty('--initial-loader-bg', loaderColor)
|
||||||
|
|
||||||
if (primaryColor) document.documentElement.style.setProperty('--initial-loader-color', primaryColor)
|
if (primaryColor)
|
||||||
</script>
|
document.documentElement.style.setProperty('--initial-loader-color', primaryColor)
|
||||||
</body>
|
</script>
|
||||||
</html>
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "moviepilot",
|
"name": "moviepilot",
|
||||||
"version": "1.6.8",
|
"version": "1.7.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"bin": "dist/service.js",
|
"bin": "dist/service.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -121,11 +121,7 @@ function themeTransition() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<IconBtn @click="changeTheme">
|
<IconBtn @click="changeTheme">
|
||||||
<VTooltip text="切换主题">
|
<VIcon :icon="props.themes[currentThemeIndex].icon" />
|
||||||
<template #activator="{ props: _props }">
|
|
||||||
<VIcon v-bind="_props" :icon="props.themes[currentThemeIndex].icon" />
|
|
||||||
</template>
|
|
||||||
</VTooltip>
|
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -147,3 +147,23 @@ export function formatEp(nums: number[]): string {
|
|||||||
|
|
||||||
return formattedRanges.join('、')
|
return formattedRanges.join('、')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将yyyy-mm-dd hh:mm:ss转换为时间差,如:1小时前,1天前
|
||||||
|
export function formatDateDifference(dateString: string): string {
|
||||||
|
const date = new Date(dateString)
|
||||||
|
const currentDate = new Date()
|
||||||
|
const timeDifference = currentDate.getTime() - date.getTime()
|
||||||
|
const secondsDifference = Math.floor(timeDifference / 1000)
|
||||||
|
const minutesDifference = Math.floor(secondsDifference / 60)
|
||||||
|
const hoursDifference = Math.floor(minutesDifference / 60)
|
||||||
|
const daysDifference = Math.floor(hoursDifference / 24)
|
||||||
|
|
||||||
|
if (daysDifference > 0)
|
||||||
|
return `${daysDifference}天前`
|
||||||
|
else if (hoursDifference > 0)
|
||||||
|
return `${hoursDifference}小时前`
|
||||||
|
else if (minutesDifference > 0)
|
||||||
|
return `${minutesDifference}分钟前`
|
||||||
|
else
|
||||||
|
return '刚刚'
|
||||||
|
}
|
||||||
|
|||||||
11
src/App.vue
11
src/App.vue
@@ -1,8 +1,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useToast } from 'vue-toast-notification'
|
import { useToast } from 'vue-toast-notification'
|
||||||
import { useTheme } from 'vuetify'
|
import { useTheme } from 'vuetify'
|
||||||
|
|
||||||
import store from './store'
|
import store from './store'
|
||||||
|
|
||||||
|
// 提示框
|
||||||
|
const $toast = useToast()
|
||||||
|
|
||||||
|
// 设置主题
|
||||||
function setTheme() {
|
function setTheme() {
|
||||||
const { global: globalTheme } = useTheme()
|
const { global: globalTheme } = useTheme()
|
||||||
let theme = localStorage.getItem('theme') || 'light'
|
let theme = localStorage.getItem('theme') || 'light'
|
||||||
@@ -10,11 +15,6 @@ function setTheme() {
|
|||||||
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||||
globalTheme.name.value = theme
|
globalTheme.name.value = theme
|
||||||
}
|
}
|
||||||
// 第一时间应用主题
|
|
||||||
setTheme()
|
|
||||||
|
|
||||||
// 提示框
|
|
||||||
const $toast = useToast()
|
|
||||||
|
|
||||||
// SSE持续接收消息
|
// SSE持续接收消息
|
||||||
function startSSEMessager() {
|
function startSSEMessager() {
|
||||||
@@ -38,6 +38,7 @@ function startSSEMessager() {
|
|||||||
|
|
||||||
// 页面加载时,加载当前用户数据
|
// 页面加载时,加载当前用户数据
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
|
setTheme()
|
||||||
startSSEMessager()
|
startSSEMessager()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
115
src/api/types.ts
115
src/api/types.ts
@@ -80,6 +80,9 @@ export interface Subscribe {
|
|||||||
// 是否洗版,数字或者boolean
|
// 是否洗版,数字或者boolean
|
||||||
best_version: any
|
best_version: any
|
||||||
|
|
||||||
|
// 使用 imdbid 搜索
|
||||||
|
search_imdbid?: any
|
||||||
|
|
||||||
// 当前优先级
|
// 当前优先级
|
||||||
current_priority: number
|
current_priority: number
|
||||||
|
|
||||||
@@ -510,8 +513,11 @@ export interface DownloadingInfo {
|
|||||||
// 媒体信息
|
// 媒体信息
|
||||||
media: { [key: string]: any }
|
media: { [key: string]: any }
|
||||||
|
|
||||||
// 下载用户
|
// 下载用户ID
|
||||||
userid?: string
|
userid?: string
|
||||||
|
|
||||||
|
// 下载用户名称
|
||||||
|
username?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 缺失剧集信息
|
// 缺失剧集信息
|
||||||
@@ -658,6 +664,9 @@ export interface TorrentInfo {
|
|||||||
// 剩余免费时间
|
// 剩余免费时间
|
||||||
freedate_diff: string
|
freedate_diff: string
|
||||||
|
|
||||||
|
// 种子类型
|
||||||
|
category: string
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 识别元数据
|
// 识别元数据
|
||||||
@@ -793,18 +802,34 @@ export interface Context {
|
|||||||
|
|
||||||
// 用户信息
|
// 用户信息
|
||||||
export interface User {
|
export interface User {
|
||||||
|
// 用户ID
|
||||||
id: number
|
id: number
|
||||||
|
|
||||||
|
// 用户名称
|
||||||
name: string
|
name: string
|
||||||
|
|
||||||
|
// 用户密码
|
||||||
password: string
|
password: string
|
||||||
|
|
||||||
|
// 用户邮箱
|
||||||
email: string
|
email: string
|
||||||
|
|
||||||
|
// 是否激活
|
||||||
is_active: boolean
|
is_active: boolean
|
||||||
|
|
||||||
|
// 是否管理员
|
||||||
is_superuser: boolean
|
is_superuser: boolean
|
||||||
|
|
||||||
|
// 头像
|
||||||
avatar: string
|
avatar: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存储空间
|
// 存储空间
|
||||||
export interface Storage {
|
export interface Storage {
|
||||||
|
// 总空间
|
||||||
total_storage: number
|
total_storage: number
|
||||||
|
|
||||||
|
// 已使用空间
|
||||||
used_storage: number
|
used_storage: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -898,6 +923,7 @@ export interface NotificationSwitch {
|
|||||||
telegram: boolean
|
telegram: boolean
|
||||||
slack: boolean
|
slack: boolean
|
||||||
synologychat: boolean
|
synologychat: boolean
|
||||||
|
vocechat: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// 环境设置
|
// 环境设置
|
||||||
@@ -908,45 +934,132 @@ export interface Setting {
|
|||||||
|
|
||||||
// 文件浏览接口
|
// 文件浏览接口
|
||||||
export interface EndPoints {
|
export interface EndPoints {
|
||||||
|
// 文件列表
|
||||||
list: any
|
list: any
|
||||||
|
|
||||||
|
// 创建目录
|
||||||
mkdir: any
|
mkdir: any
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
delete: any
|
delete: any
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
download: any
|
download: any
|
||||||
|
|
||||||
|
// 图片预览
|
||||||
image: any
|
image: any
|
||||||
|
|
||||||
|
// 重命名
|
||||||
rename: any
|
rename: any
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文件浏览项目
|
// 文件浏览项目
|
||||||
export interface FileItem {
|
export interface FileItem {
|
||||||
|
// 类型
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
// 文件名
|
||||||
name: string
|
name: string
|
||||||
|
|
||||||
|
// 文件名不含扩展名
|
||||||
basename: string
|
basename: string
|
||||||
|
|
||||||
|
// 文件路径
|
||||||
path: string
|
path: string
|
||||||
|
|
||||||
|
// 文件扩展名
|
||||||
extension: string
|
extension: string
|
||||||
|
|
||||||
|
// 文件大小
|
||||||
size: number
|
size: number
|
||||||
|
|
||||||
|
// 文件子元素
|
||||||
children: FileItem[]
|
children: FileItem[]
|
||||||
|
|
||||||
|
// 文件创建时间
|
||||||
modify_time: number
|
modify_time: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// 媒体服务器播放条目
|
// 媒体服务器播放条目
|
||||||
export interface MediaServerPlayItem {
|
export interface MediaServerPlayItem {
|
||||||
|
// ID
|
||||||
id?: string | number
|
id?: string | number
|
||||||
|
|
||||||
|
// 标题
|
||||||
title: string
|
title: string
|
||||||
|
|
||||||
|
// 副标题
|
||||||
subtitle?: string
|
subtitle?: string
|
||||||
|
|
||||||
|
// 类型
|
||||||
type?: string
|
type?: string
|
||||||
|
|
||||||
|
// 海报
|
||||||
image?: string
|
image?: string
|
||||||
|
|
||||||
|
// 链接
|
||||||
link?: string
|
link?: string
|
||||||
|
|
||||||
|
// 播放百分比
|
||||||
percent?: number
|
percent?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// 媒体服务器媒体库
|
// 媒体服务器媒体库
|
||||||
export interface MediaServerLibrary {
|
export interface MediaServerLibrary {
|
||||||
|
// 服务器名称
|
||||||
server: string
|
server: string
|
||||||
|
|
||||||
|
// ID
|
||||||
id?: string | number
|
id?: string | number
|
||||||
|
|
||||||
|
// 名称
|
||||||
name: string
|
name: string
|
||||||
|
|
||||||
|
// 路径
|
||||||
path?: string
|
path?: string
|
||||||
|
|
||||||
|
// 类型
|
||||||
type?: string
|
type?: string
|
||||||
|
|
||||||
|
// 图片
|
||||||
image?: string
|
image?: string
|
||||||
|
|
||||||
|
// 图片列表
|
||||||
image_list?: string[]
|
image_list?: string[]
|
||||||
|
|
||||||
|
// 链接
|
||||||
link?: string
|
link?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 消息通知
|
||||||
|
export interface Message {
|
||||||
|
// 消息类型
|
||||||
|
mtype?: string
|
||||||
|
|
||||||
|
// 消息标题
|
||||||
|
title?: string
|
||||||
|
|
||||||
|
// 消息内容
|
||||||
|
text?: string
|
||||||
|
|
||||||
|
// 消息链接
|
||||||
|
link?: string
|
||||||
|
|
||||||
|
// 消息图片
|
||||||
|
image?: string
|
||||||
|
|
||||||
|
// 消息时间
|
||||||
|
date?: string
|
||||||
|
|
||||||
|
// 登记时间
|
||||||
|
reg_time?: string
|
||||||
|
|
||||||
|
// 用户ID
|
||||||
|
userid?: string
|
||||||
|
|
||||||
|
// 消息方向:0-接收,1-发送
|
||||||
|
action?: number
|
||||||
|
|
||||||
|
// JSON
|
||||||
|
note?: string
|
||||||
|
}
|
||||||
|
|||||||
BIN
src/assets/images/logos/fanart.webp
Normal file
BIN
src/assets/images/logos/fanart.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/images/logos/thetvdb.jpeg
Normal file
BIN
src/assets/images/logos/thetvdb.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
@@ -25,8 +25,8 @@ const isDownloading = ref(props.info?.state === 'downloading')
|
|||||||
|
|
||||||
// 监听props.info?.state的变化
|
// 监听props.info?.state的变化
|
||||||
watch(() => props.info?.state, (newValue) => {
|
watch(() => props.info?.state, (newValue) => {
|
||||||
isDownloading.value = newValue === 'downloading';
|
isDownloading.value = newValue === 'downloading'
|
||||||
});
|
})
|
||||||
|
|
||||||
// 图片是否加载完成
|
// 图片是否加载完成
|
||||||
const imageLoaded = ref(false)
|
const imageLoaded = ref(false)
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const selectFilterOptions = ref<{ [key: string]: string }[]>([
|
|||||||
{ title: '特效字幕', value: ' SPECSUB ' },
|
{ title: '特效字幕', value: ' SPECSUB ' },
|
||||||
{ title: '中文字幕', value: ' CNSUB ' },
|
{ title: '中文字幕', value: ' CNSUB ' },
|
||||||
{ title: '国语配音', value: ' CNVOI ' },
|
{ title: '国语配音', value: ' CNVOI ' },
|
||||||
|
{ title: '官种', value: ' GZ ' },
|
||||||
{ title: '排除: 国语配音', value: ' !CNVOI ' },
|
{ title: '排除: 国语配音', value: ' !CNVOI ' },
|
||||||
{ title: '粤语配音', value: ' HKVOI ' },
|
{ title: '粤语配音', value: ' HKVOI ' },
|
||||||
{ title: '排除: 粤语配音', value: ' !HKVOI ' },
|
{ title: '排除: 粤语配音', value: ' !HKVOI ' },
|
||||||
|
|||||||
112
src/components/cards/MessageCard.vue
Normal file
112
src/components/cards/MessageCard.vue
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { Message } from '@/api/types'
|
||||||
|
import { formatDateDifference } from '@core/utils/formatters'
|
||||||
|
|
||||||
|
// 输入参数
|
||||||
|
const props = defineProps({
|
||||||
|
message: Object as PropType<Message>,
|
||||||
|
width: String,
|
||||||
|
height: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 图片是否加载完成
|
||||||
|
const isImageLoaded = ref(false)
|
||||||
|
|
||||||
|
// 图片是否加载失败
|
||||||
|
const imageLoadError = ref(false)
|
||||||
|
|
||||||
|
// 图片加载完成
|
||||||
|
async function imageLoaded() {
|
||||||
|
isImageLoaded.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 链接打开新窗口
|
||||||
|
function openLink() {
|
||||||
|
if (props.message?.link)
|
||||||
|
window.open(props.message.link, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将note转换为json
|
||||||
|
function noteToJson() {
|
||||||
|
if (props.message?.note) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(props.message.note)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将\n转换为html属性的换行符
|
||||||
|
function replaceNewLine(value: string) {
|
||||||
|
if (!value)
|
||||||
|
return ''
|
||||||
|
return value.replace(/\n/g, '<br/>')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VCard
|
||||||
|
:width="props.width"
|
||||||
|
:height="props.height"
|
||||||
|
variant="tonal"
|
||||||
|
@click="openLink"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="props.message?.image"
|
||||||
|
class="relative text-center card-cover-blurred"
|
||||||
|
>
|
||||||
|
<VImg
|
||||||
|
:src="props.message?.image"
|
||||||
|
aspect-ratio="4/3"
|
||||||
|
cover
|
||||||
|
:class="{ shadow: isImageLoaded }"
|
||||||
|
@load="imageLoaded"
|
||||||
|
@error="imageLoadError = true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<VCardTitle v-if="props.message?.title" class="whitespace-break-spaces">
|
||||||
|
{{ props.message?.title }}
|
||||||
|
</VCardTitle>
|
||||||
|
<VAlert
|
||||||
|
v-if="props.message?.text && props.message?.action === 0"
|
||||||
|
variant="tonal"
|
||||||
|
type="success"
|
||||||
|
>
|
||||||
|
<template #prepend />
|
||||||
|
{{ props.message?.text }}
|
||||||
|
</VAlert>
|
||||||
|
<VCardText
|
||||||
|
v-if="props.message?.text && props.message?.action === 1"
|
||||||
|
v-html="replaceNewLine(props.message?.text)"
|
||||||
|
/>
|
||||||
|
<VCardText v-if="props.message?.note">
|
||||||
|
<VList>
|
||||||
|
<VListItem
|
||||||
|
v-for="(value, key) in noteToJson()"
|
||||||
|
:key="key"
|
||||||
|
two-line
|
||||||
|
>
|
||||||
|
<VListItemTitle v-if="value.title_year" class="font-bold">
|
||||||
|
{{ key + 1 }}. {{ value.title_year }}
|
||||||
|
</VListItemTitle>
|
||||||
|
<VListItemTitle v-if="value.enclosure" class="font-bold whitespace-break-spaces">
|
||||||
|
{{ key + 1 }}. {{ value.title }} {{ value.volume_factor }} ↑{{ value.seeders }}
|
||||||
|
</VListItemTitle>
|
||||||
|
<VListItemSubtitle v-if="value.type">
|
||||||
|
类型:{{ value.type }} 评分:{{ value.vote_average }}
|
||||||
|
</VListItemSubtitle>
|
||||||
|
<VListItemSubtitle v-if="value.enclosure" class="whitespace-break-spaces">
|
||||||
|
{{ value.description }}
|
||||||
|
</VListItemSubtitle>
|
||||||
|
</VListItem>
|
||||||
|
</VList>
|
||||||
|
</VCardText>
|
||||||
|
<div class="text-end">
|
||||||
|
<span v-if="props.message?.action === 0" class="text-sm italic me-2">{{ props.message?.userid }}</span>
|
||||||
|
<span class="text-sm italic me-2">{{ formatDateDifference(props.message?.reg_time || props.message?.date || '') }}</span>
|
||||||
|
</div>
|
||||||
|
</VCard>
|
||||||
|
</template>
|
||||||
@@ -49,7 +49,7 @@ async function installPlugin() {
|
|||||||
try {
|
try {
|
||||||
// 显示等待提示框
|
// 显示等待提示框
|
||||||
progressDialog.value = true
|
progressDialog.value = true
|
||||||
progressText.value = `正在安装 ${props.plugin?.plugin_name} ${props?.plugin?.plugin_version} 插件...`
|
progressText.value = `正在安装 ${props.plugin?.plugin_name} v${props?.plugin?.plugin_version} ...`
|
||||||
|
|
||||||
const result: { [key: string]: any } = await api.get(
|
const result: { [key: string]: any } = await api.get(
|
||||||
`plugin/install/${props.plugin?.id}`,
|
`plugin/install/${props.plugin?.id}`,
|
||||||
@@ -163,15 +163,6 @@ const dropdownItems = ref([
|
|||||||
</VMenu>
|
</VMenu>
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-if="props.plugin?.has_update"
|
|
||||||
class="me-n3 absolute top-0 left-1"
|
|
||||||
>
|
|
||||||
<VIcon
|
|
||||||
icon="mdi-new-box"
|
|
||||||
class="text-white"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<VAvatar
|
<VAvatar
|
||||||
size="8rem"
|
size="8rem"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -41,12 +41,18 @@ const pluginConfigDialog = ref(false)
|
|||||||
// 插件配置表单数据
|
// 插件配置表单数据
|
||||||
const pluginConfigForm = ref({})
|
const pluginConfigForm = ref({})
|
||||||
|
|
||||||
|
// 进度框
|
||||||
|
const progressDialog = ref(false)
|
||||||
|
|
||||||
// 插件表单配置项
|
// 插件表单配置项
|
||||||
let pluginFormItems = reactive([])
|
let pluginFormItems = reactive([])
|
||||||
|
|
||||||
// 插件数据页面
|
// 插件数据页面
|
||||||
const pluginInfoDialog = ref(false)
|
const pluginInfoDialog = ref(false)
|
||||||
|
|
||||||
|
// 进度框文本
|
||||||
|
const progressText = ref('正在更新插件...')
|
||||||
|
|
||||||
// 插件数据页面配置项
|
// 插件数据页面配置项
|
||||||
let pluginPageItems = reactive([])
|
let pluginPageItems = reactive([])
|
||||||
|
|
||||||
@@ -83,7 +89,12 @@ async function uninstallPlugin() {
|
|||||||
return
|
return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 显示等待提示框
|
||||||
|
progressDialog.value = true
|
||||||
|
progressText.value = `正在卸载 ${props.plugin?.plugin_name} ...`
|
||||||
const result: { [key: string]: any } = await api.delete(`plugin/${props.plugin?.id}`)
|
const result: { [key: string]: any } = await api.delete(`plugin/${props.plugin?.id}`)
|
||||||
|
// 隐藏等待提示框
|
||||||
|
progressDialog.value = false
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
$toast.success(`插件 ${props.plugin?.plugin_name} 已卸载`)
|
$toast.success(`插件 ${props.plugin?.plugin_name} 已卸载`)
|
||||||
|
|
||||||
@@ -221,6 +232,41 @@ async function resetPlugin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新插件
|
||||||
|
async function updatePlugin() {
|
||||||
|
try {
|
||||||
|
// 显示等待提示框
|
||||||
|
progressDialog.value = true
|
||||||
|
progressText.value = `正在更新 ${props.plugin?.plugin_name} ...`
|
||||||
|
|
||||||
|
const result: { [key: string]: any } = await api.get(
|
||||||
|
`plugin/install/${props.plugin?.id}`,
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
repo_url: props.plugin?.repo_url,
|
||||||
|
force: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 隐藏等待提示框
|
||||||
|
progressDialog.value = false
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
$toast.success(`插件 ${props.plugin?.plugin_name} 更新成功!`)
|
||||||
|
|
||||||
|
// 通知父组件刷新
|
||||||
|
emit('save')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$toast.error(`插件 ${props.plugin?.plugin_name} 更新失败:${result.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 访问作者主页
|
// 访问作者主页
|
||||||
function visitAuthorPage() {
|
function visitAuthorPage() {
|
||||||
window.open(props.plugin?.author_url, '_blank')
|
window.open(props.plugin?.author_url, '_blank')
|
||||||
@@ -254,8 +300,18 @@ const dropdownItems = ref([
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '重置',
|
title: '更新',
|
||||||
value: 3,
|
value: 3,
|
||||||
|
show: props.plugin?.has_update,
|
||||||
|
props: {
|
||||||
|
prependIcon: 'mdi-cancel',
|
||||||
|
color: 'success',
|
||||||
|
click: updatePlugin,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '重置',
|
||||||
|
value: 4,
|
||||||
show: true,
|
show: true,
|
||||||
props: {
|
props: {
|
||||||
prependIcon: 'mdi-cancel',
|
prependIcon: 'mdi-cancel',
|
||||||
@@ -265,7 +321,7 @@ const dropdownItems = ref([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '卸载',
|
title: '卸载',
|
||||||
value: 4,
|
value: 5,
|
||||||
show: true,
|
show: true,
|
||||||
props: {
|
props: {
|
||||||
prependIcon: 'mdi-trash-can-outline',
|
prependIcon: 'mdi-trash-can-outline',
|
||||||
@@ -275,7 +331,7 @@ const dropdownItems = ref([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '查看日志',
|
title: '查看日志',
|
||||||
value: 5,
|
value: 6,
|
||||||
show: true,
|
show: true,
|
||||||
props: {
|
props: {
|
||||||
prependIcon: 'mdi-file-document-outline',
|
prependIcon: 'mdi-file-document-outline',
|
||||||
@@ -286,7 +342,7 @@ const dropdownItems = ref([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '作者主页',
|
title: '作者主页',
|
||||||
value: 5,
|
value: 7,
|
||||||
show: true,
|
show: true,
|
||||||
props: {
|
props: {
|
||||||
prependIcon: 'mdi-home-circle-outline',
|
prependIcon: 'mdi-home-circle-outline',
|
||||||
@@ -294,6 +350,13 @@ const dropdownItems = ref([
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// 监听插件状态变化
|
||||||
|
watch(() => props.plugin?.has_update, (newHasUpdate, oldHasUpdate) => {
|
||||||
|
const updateItemIndex = dropdownItems.value.findIndex(item => item.value === 3)
|
||||||
|
if (updateItemIndex !== -1)
|
||||||
|
dropdownItems.value[updateItemIndex].show = newHasUpdate
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -313,6 +376,15 @@ const dropdownItems = ref([
|
|||||||
class="relative pa-4 text-center card-cover-blurred"
|
class="relative pa-4 text-center card-cover-blurred"
|
||||||
:style="{ background: `${backgroundColor}` }"
|
:style="{ background: `${backgroundColor}` }"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-if="props.plugin?.has_update"
|
||||||
|
class="me-n3 absolute top-0 left-1"
|
||||||
|
>
|
||||||
|
<VIcon
|
||||||
|
icon="mdi-new-box"
|
||||||
|
class="text-white"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="me-n3 absolute top-0 right-3">
|
<div class="me-n3 absolute top-0 right-3">
|
||||||
<IconBtn>
|
<IconBtn>
|
||||||
<VIcon icon="mdi-dots-vertical" class="text-white" />
|
<VIcon icon="mdi-dots-vertical" class="text-white" />
|
||||||
@@ -430,6 +502,25 @@ const dropdownItems = ref([
|
|||||||
</VCardActions>
|
</VCardActions>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
|
<!-- 更新插件进度框 -->
|
||||||
|
<VDialog
|
||||||
|
v-model="progressDialog"
|
||||||
|
:scrim="false"
|
||||||
|
width="25rem"
|
||||||
|
>
|
||||||
|
<VCard
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<VCardText class="text-center">
|
||||||
|
{{ progressText }}
|
||||||
|
<VProgressLinear
|
||||||
|
indeterminate
|
||||||
|
color="white"
|
||||||
|
class="mb-0 mt-1"
|
||||||
|
/>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</VDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ async function updateSiteInfo() {
|
|||||||
<template>
|
<template>
|
||||||
<VDialog
|
<VDialog
|
||||||
scrollable
|
scrollable
|
||||||
|
:close-on-back="false"
|
||||||
|
persistent
|
||||||
|
eager
|
||||||
max-width="60rem"
|
max-width="60rem"
|
||||||
>
|
>
|
||||||
<VCard
|
<VCard
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const subscribeForm = ref<Subscribe>({
|
|||||||
total_episode: 0,
|
total_episode: 0,
|
||||||
start_episode: 0,
|
start_episode: 0,
|
||||||
best_version: 0,
|
best_version: 0,
|
||||||
|
search_imdbid: 0,
|
||||||
sites: [],
|
sites: [],
|
||||||
type: '',
|
type: '',
|
||||||
name: '',
|
name: '',
|
||||||
@@ -99,6 +100,7 @@ async function getSubscribeInfo() {
|
|||||||
)
|
)
|
||||||
subscribeForm.value = result
|
subscribeForm.value = result
|
||||||
subscribeForm.value.best_version = subscribeForm.value.best_version === 1
|
subscribeForm.value.best_version = subscribeForm.value.best_version === 1
|
||||||
|
subscribeForm.value.search_imdbid = subscribeForm.value.search_imdbid === 1
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
@@ -343,6 +345,15 @@ watchEffect(() => {
|
|||||||
label="洗版"
|
label="洗版"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
<VCol
|
||||||
|
cols="12"
|
||||||
|
md="4"
|
||||||
|
>
|
||||||
|
<VSwitch
|
||||||
|
v-model="subscribeForm.search_imdbid"
|
||||||
|
label="使用 ImdbID 搜索"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const superUser = store.state.auth.superUser
|
|||||||
</IconBtn>
|
</IconBtn>
|
||||||
|
|
||||||
<!-- 👉 Shortcuts -->
|
<!-- 👉 Shortcuts -->
|
||||||
<ShortcutBar />
|
<ShortcutBar v-if="superUser" />
|
||||||
|
|
||||||
<!-- 👉 Theme -->
|
<!-- 👉 Theme -->
|
||||||
<NavbarThemeSwitcher class="me-2" />
|
<NavbarThemeSwitcher class="me-2" />
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ function openSearchDialog() {
|
|||||||
<VTextField
|
<VTextField
|
||||||
key="search_navbar"
|
key="search_navbar"
|
||||||
v-model="searchWord"
|
v-model="searchWord"
|
||||||
class="d-none d-lg-block text-disabled"
|
class="d-none d-lg-block text-disabled search-box"
|
||||||
density="compact"
|
density="compact"
|
||||||
variant="solo"
|
variant="solo"
|
||||||
label="搜索电影、电视剧"
|
label="搜索电影、电视剧"
|
||||||
@@ -98,3 +98,9 @@ function openSearchDialog() {
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.search-box div.v-input__control div[role="textbox"] {
|
||||||
|
border: 1px solid rgb(var(--v-theme-background));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import NameTestView from '@/views/system/NameTestView.vue'
|
|||||||
import NetTestView from '@/views/system/NetTestView.vue'
|
import NetTestView from '@/views/system/NetTestView.vue'
|
||||||
import LoggingView from '@/views/system/LoggingView.vue'
|
import LoggingView from '@/views/system/LoggingView.vue'
|
||||||
import RuleTestView from '@/views/system/RuleTestView.vue'
|
import RuleTestView from '@/views/system/RuleTestView.vue'
|
||||||
|
import ModuleTestView from '@/views/system/ModuleTestView.vue'
|
||||||
|
import MessageView from '@/views/system/MessageView.vue'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
import api from '@/api'
|
||||||
|
|
||||||
// App捷径
|
// App捷径
|
||||||
const appsMenu = ref(false)
|
const appsMenu = ref(false)
|
||||||
@@ -20,11 +23,56 @@ const loggingDialog = ref(false)
|
|||||||
// 过滤规则弹窗
|
// 过滤规则弹窗
|
||||||
const ruleTestDialog = ref(false)
|
const ruleTestDialog = ref(false)
|
||||||
|
|
||||||
|
// 系统健康检查弹窗
|
||||||
|
const systemTestDialog = ref(false)
|
||||||
|
|
||||||
|
// 消息中心弹窗
|
||||||
|
const messageDialog = ref(false)
|
||||||
|
|
||||||
|
// 输入消息
|
||||||
|
const user_message = ref('')
|
||||||
|
|
||||||
|
// 发送按钮是否可用
|
||||||
|
const sendButtonDisabled = ref(false)
|
||||||
|
|
||||||
|
// 聊天容器
|
||||||
|
const chatContainer = ref<HTMLDivElement>()
|
||||||
|
|
||||||
|
// 滚动到底部
|
||||||
|
function scrollMessageToEnd() {
|
||||||
|
nextTick(() => {
|
||||||
|
if (chatContainer.value) {
|
||||||
|
const scrollDiv = chatContainer.value.$el
|
||||||
|
scrollDiv.scrollTop = scrollDiv.scrollHeight
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 拼接全部日志url
|
// 拼接全部日志url
|
||||||
function allLoggingUrl() {
|
function allLoggingUrl() {
|
||||||
const token = store.state.auth.token
|
const token = store.state.auth.token
|
||||||
return `${import.meta.env.VITE_API_BASE_URL}system/logging?token=${token}&length=-1`
|
return `${import.meta.env.VITE_API_BASE_URL}system/logging?token=${token}&length=-1`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 发送消息
|
||||||
|
async function sendMessage() {
|
||||||
|
if (user_message.value) {
|
||||||
|
try {
|
||||||
|
sendButtonDisabled.value = true
|
||||||
|
await api.post(`message/web?text=${user_message.value}`)
|
||||||
|
user_message.value = ''
|
||||||
|
sendButtonDisabled.value = false
|
||||||
|
scrollMessageToEnd()
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
scrollMessageToEnd()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -44,11 +92,7 @@ function allLoggingUrl() {
|
|||||||
class="me-2"
|
class="me-2"
|
||||||
v-bind="props"
|
v-bind="props"
|
||||||
>
|
>
|
||||||
<VTooltip text="捷径">
|
<VIcon icon="mdi-checkbox-multiple-blank-outline" />
|
||||||
<template #activator="{ props: _props }">
|
|
||||||
<VIcon v-bind="_props" icon="mdi-checkbox-multiple-blank-outline" />
|
|
||||||
</template>
|
|
||||||
</VTooltip>
|
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
</template>
|
</template>
|
||||||
<!-- Menu Content -->
|
<!-- Menu Content -->
|
||||||
@@ -85,23 +129,23 @@ function allLoggingUrl() {
|
|||||||
</VCol>
|
</VCol>
|
||||||
<VCol
|
<VCol
|
||||||
cols="6"
|
cols="6"
|
||||||
class="text-center cursor-pointer pa-0 shortcut-icon"
|
class="text-center cursor-pointer pa-0 shortcut-icon border-e"
|
||||||
@click="() => {}"
|
@click="() => {}"
|
||||||
>
|
>
|
||||||
<VListItem
|
<VListItem
|
||||||
class="pa-4"
|
class="pa-4"
|
||||||
@click="netTestDialog = true"
|
@click="ruleTestDialog = true"
|
||||||
>
|
>
|
||||||
<VAvatar
|
<VAvatar
|
||||||
size="48"
|
size="48"
|
||||||
variant="tonal"
|
variant="tonal"
|
||||||
>
|
>
|
||||||
<VIcon icon="mdi-network-outline" />
|
<VIcon icon="mdi-filter-cog-outline" />
|
||||||
</VAvatar>
|
</VAvatar>
|
||||||
<h6 class="text-base font-weight-medium mt-2 mb-0">
|
<h6 class="text-base font-weight-medium mt-2 mb-0">
|
||||||
网络
|
优先级
|
||||||
</h6>
|
</h6>
|
||||||
<span class="text-sm">测试网速连通性</span>
|
<span class="text-sm">优先级规则测试</span>
|
||||||
</VListItem>
|
</VListItem>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -124,7 +168,51 @@ function allLoggingUrl() {
|
|||||||
<h6 class="text-base font-weight-medium mt-2 mb-0">
|
<h6 class="text-base font-weight-medium mt-2 mb-0">
|
||||||
日志
|
日志
|
||||||
</h6>
|
</h6>
|
||||||
<span class="text-sm">系统实时日志</span>
|
<span class="text-sm">实时日志</span>
|
||||||
|
</VListItem>
|
||||||
|
</VCol>
|
||||||
|
<VCol
|
||||||
|
cols="6"
|
||||||
|
class="text-center cursor-pointer pa-0 shortcut-icon"
|
||||||
|
@click="() => {}"
|
||||||
|
>
|
||||||
|
<VListItem
|
||||||
|
class="pa-4"
|
||||||
|
@click="netTestDialog = true"
|
||||||
|
>
|
||||||
|
<VAvatar
|
||||||
|
size="48"
|
||||||
|
variant="tonal"
|
||||||
|
>
|
||||||
|
<VIcon icon="mdi-network-outline" />
|
||||||
|
</VAvatar>
|
||||||
|
<h6 class="text-base font-weight-medium mt-2 mb-0">
|
||||||
|
网络
|
||||||
|
</h6>
|
||||||
|
<span class="text-sm">网速连通性测试</span>
|
||||||
|
</VListItem>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VRow class="ma-0 mt-n1 border-t">
|
||||||
|
<VCol
|
||||||
|
cols="6"
|
||||||
|
class="text-center cursor-pointer pa-0 shortcut-icon border-e"
|
||||||
|
@click="() => {}"
|
||||||
|
>
|
||||||
|
<VListItem
|
||||||
|
class="pa-4"
|
||||||
|
@click="systemTestDialog = true"
|
||||||
|
>
|
||||||
|
<VAvatar
|
||||||
|
size="48"
|
||||||
|
variant="tonal"
|
||||||
|
>
|
||||||
|
<VIcon icon="mdi-cog-outline" />
|
||||||
|
</VAvatar>
|
||||||
|
<h6 class="text-base font-weight-medium mt-2 mb-0">
|
||||||
|
系统
|
||||||
|
</h6>
|
||||||
|
<span class="text-sm">健康检查</span>
|
||||||
</VListItem>
|
</VListItem>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol
|
<VCol
|
||||||
@@ -134,18 +222,18 @@ function allLoggingUrl() {
|
|||||||
>
|
>
|
||||||
<VListItem
|
<VListItem
|
||||||
class="pa-4"
|
class="pa-4"
|
||||||
@click="ruleTestDialog = true"
|
@click="messageDialog = true"
|
||||||
>
|
>
|
||||||
<VAvatar
|
<VAvatar
|
||||||
size="48"
|
size="48"
|
||||||
variant="tonal"
|
variant="tonal"
|
||||||
>
|
>
|
||||||
<VIcon icon="mdi-filter-cog-outline" />
|
<VIcon icon="mdi-message-outline" />
|
||||||
</VAvatar>
|
</VAvatar>
|
||||||
<h6 class="text-base font-weight-medium mt-2 mb-0">
|
<h6 class="text-base font-weight-medium mt-2 mb-0">
|
||||||
优先级
|
消息
|
||||||
</h6>
|
</h6>
|
||||||
<span class="text-sm">优先级规则测试</span>
|
<span class="text-sm">消息中心</span>
|
||||||
</VListItem>
|
</VListItem>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -213,4 +301,54 @@ function allLoggingUrl() {
|
|||||||
</VCardText>
|
</VCardText>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
|
<!-- 系统健康检查弹窗 -->
|
||||||
|
<VDialog
|
||||||
|
v-model="systemTestDialog"
|
||||||
|
max-width="50rem"
|
||||||
|
scrollable
|
||||||
|
>
|
||||||
|
<VCard title="系统健康检查">
|
||||||
|
<DialogCloseBtn @click="systemTestDialog = false" />
|
||||||
|
<VCardText>
|
||||||
|
<ModuleTestView />
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</VDialog>
|
||||||
|
<!-- 消息中心弹窗 -->
|
||||||
|
<VDialog
|
||||||
|
v-model="messageDialog"
|
||||||
|
max-width="60rem"
|
||||||
|
scrollable
|
||||||
|
>
|
||||||
|
<VCard title="消息中心">
|
||||||
|
<DialogCloseBtn @click="messageDialog = false" />
|
||||||
|
<VCardText ref="chatContainer">
|
||||||
|
<MessageView @scroll="scrollMessageToEnd" />
|
||||||
|
</VCardText>
|
||||||
|
|
||||||
|
<VCardItem>
|
||||||
|
<VTextField
|
||||||
|
v-model="user_message"
|
||||||
|
placeholder="输入消息或命令"
|
||||||
|
outlined
|
||||||
|
hide-details
|
||||||
|
single-line
|
||||||
|
clearable
|
||||||
|
density="compact"
|
||||||
|
:disabled="sendButtonDisabled"
|
||||||
|
@keydown.enter="sendMessage"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<VBtn
|
||||||
|
color="primary"
|
||||||
|
:disabled="sendButtonDisabled"
|
||||||
|
@click="sendMessage"
|
||||||
|
>
|
||||||
|
发送
|
||||||
|
</VBtn>
|
||||||
|
</template>
|
||||||
|
</VTextField>
|
||||||
|
</VCardItem>
|
||||||
|
</VCard>
|
||||||
|
</VDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import DefaultLayoutWithVerticalNav from './components/DefaultLayoutWithVerticalNav.vue'
|
import DefaultLayoutWithVerticalNav from './components/DefaultLayoutWithVerticalNav.vue'
|
||||||
|
import api from '@/api'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (document.visibilityState === 'visible') {
|
||||||
|
api.get('user/current')
|
||||||
|
.catch(() => {
|
||||||
|
router.replace('/login')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DefaultLayoutWithVerticalNav>
|
<DefaultLayoutWithVerticalNav>
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component :is="Component" v-if="$route.meta.keepAlive" :key="$route.fullPath" />
|
<component :is="Component" v-if="route.meta.keepAlive" :key="route.fullPath" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
<component :is="Component" v-if="!$route.meta.keepAlive" :key="$route.fullPath" />
|
<component :is="Component" v-if="!route.meta.keepAlive" :key="route.fullPath" />
|
||||||
</router-view>
|
</router-view>
|
||||||
</DefaultLayoutWithVerticalNav>
|
</DefaultLayoutWithVerticalNav>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ function login() {
|
|||||||
store.dispatch('auth/updateUserName', username)
|
store.dispatch('auth/updateUserName', username)
|
||||||
store.dispatch('auth/updateAvatar', avatar)
|
store.dispatch('auth/updateAvatar', avatar)
|
||||||
|
|
||||||
// 跳转到首页
|
// 跳转到首页或回原始页面
|
||||||
router.push('/')
|
router.push(store.state.auth.originalPath ?? '/')
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
// 登录失败,显示错误提示
|
// 登录失败,显示错误提示
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export default {
|
|||||||
// set v-rating default color to primary
|
// set v-rating default color to primary
|
||||||
color: 'rgba(var(--v-theme-on-background),0.23)',
|
color: 'rgba(var(--v-theme-on-background),0.23)',
|
||||||
activeColor: 'warning',
|
activeColor: 'warning',
|
||||||
|
halfIncrements: true,
|
||||||
},
|
},
|
||||||
VProgressCircular: {
|
VProgressCircular: {
|
||||||
// set v-progress-circular default color to primary
|
// set v-progress-circular default color to primary
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ router.beforeEach((to, from, next) => {
|
|||||||
const isAuthenticated = store.state.auth.token !== null
|
const isAuthenticated = store.state.auth.token !== null
|
||||||
|
|
||||||
if (to.meta.requiresAuth && !isAuthenticated) {
|
if (to.meta.requiresAuth && !isAuthenticated) {
|
||||||
|
store.state.auth.originalPath = to.fullPath
|
||||||
next('/login')
|
next('/login')
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ interface AuthState {
|
|||||||
superUser: boolean
|
superUser: boolean
|
||||||
userName: string
|
userName: string
|
||||||
avatar: string
|
avatar: string
|
||||||
|
originalPath: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义根状态类型
|
// 定义根状态类型
|
||||||
@@ -23,6 +24,7 @@ const authModule: Module<AuthState, RootState> = {
|
|||||||
superUser: false,
|
superUser: false,
|
||||||
userName: '',
|
userName: '',
|
||||||
avatar: '',
|
avatar: '',
|
||||||
|
originalPath: null,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setToken(state, token: string) {
|
setToken(state, token: string) {
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ function initOptions(data: Context) {
|
|||||||
optionValue(resolutionFilterOptions.value, meta_info?.resource_pix)
|
optionValue(resolutionFilterOptions.value, meta_info?.resource_pix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 对季过滤选项进行排序
|
||||||
|
const sortSeasonFilterOptions = computed(() => {
|
||||||
|
return seasonFilterOptions.value.sort((a, b) => {
|
||||||
|
// 按字符串升序排序
|
||||||
|
return a.localeCompare(b, 'zh-Hans-CN', { sensitivity: 'accent' })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// 计算分组后的列表
|
// 计算分组后的列表
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 数据分组
|
// 数据分组
|
||||||
@@ -154,7 +162,7 @@ watchEffect(() => {
|
|||||||
<VCol v-if="seasonFilterOptions.length > 0" cols="6" md="">
|
<VCol v-if="seasonFilterOptions.length > 0" cols="6" md="">
|
||||||
<VSelect
|
<VSelect
|
||||||
v-model="filterForm.season"
|
v-model="filterForm.season"
|
||||||
:items="seasonFilterOptions"
|
:items="sortSeasonFilterOptions"
|
||||||
size="small"
|
size="small"
|
||||||
density="compact"
|
density="compact"
|
||||||
chips
|
chips
|
||||||
|
|||||||
@@ -55,17 +55,37 @@ async function fetchUninstalledPlugins() {
|
|||||||
state: 'market',
|
state: 'market',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
// 设置APP市场加载完成
|
||||||
isAppMarketLoaded.value = true
|
isAppMarketLoaded.value = true
|
||||||
|
// 设置更新状态
|
||||||
|
for (const uninstalled of uninstalledList.value) {
|
||||||
|
for (const data of dataList.value) {
|
||||||
|
if (uninstalled.id === data.id) {
|
||||||
|
data.has_update = true
|
||||||
|
data.repo_url = uninstalled.repo_url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载时获取数据
|
// 加载所有数据
|
||||||
onBeforeMount(() => {
|
function refreshData() {
|
||||||
fetchInstalledPlugins()
|
fetchInstalledPlugins()
|
||||||
fetchUninstalledPlugins()
|
fetchUninstalledPlugins()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取没有更新的插件
|
||||||
|
const getUnupdatedPlugins = computed(() => {
|
||||||
|
return uninstalledList.value.filter(item => !item.has_update)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载时获取数据
|
||||||
|
onBeforeMount(() => {
|
||||||
|
refreshData()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -89,8 +109,8 @@ onBeforeMount(() => {
|
|||||||
v-for="data in dataList"
|
v-for="data in dataList"
|
||||||
:key="data.id"
|
:key="data.id"
|
||||||
:plugin="data"
|
:plugin="data"
|
||||||
@remove="fetchInstalledPlugins"
|
@remove="refreshData"
|
||||||
@save="fetchInstalledPlugins"
|
@save="refreshData"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<NoDataFound
|
<NoDataFound
|
||||||
@@ -105,6 +125,7 @@ onBeforeMount(() => {
|
|||||||
fullscreen
|
fullscreen
|
||||||
scrollable
|
scrollable
|
||||||
:scrim="false"
|
:scrim="false"
|
||||||
|
:z-index="1010"
|
||||||
transition="dialog-bottom-transition"
|
transition="dialog-bottom-transition"
|
||||||
>
|
>
|
||||||
<!-- Dialog Activator -->
|
<!-- Dialog Activator -->
|
||||||
@@ -153,7 +174,7 @@ onBeforeMount(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="isAppMarketLoaded" class="grid gap-4 grid-plugin-card">
|
<div v-if="isAppMarketLoaded" class="grid gap-4 grid-plugin-card">
|
||||||
<PluginAppCard
|
<PluginAppCard
|
||||||
v-for="data in uninstalledList"
|
v-for="data in getUnupdatedPlugins"
|
||||||
:key="data.id"
|
:key="data.id"
|
||||||
:plugin="data"
|
:plugin="data"
|
||||||
@install="pluginInstalled"
|
@install="pluginInstalled"
|
||||||
|
|||||||
@@ -6,10 +6,6 @@ import NoDataFound from '@/components/NoDataFound.vue'
|
|||||||
import DownloadingCard from '@/components/cards/DownloadingCard.vue'
|
import DownloadingCard from '@/components/cards/DownloadingCard.vue'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
|
||||||
// 从Vuex Store中获取用户信息
|
|
||||||
const superUser = store.state.auth.superUser
|
|
||||||
const userName = store.state.auth.userName
|
|
||||||
|
|
||||||
// 定时器
|
// 定时器
|
||||||
let refreshTimer: NodeJS.Timer | null = null
|
let refreshTimer: NodeJS.Timer | null = null
|
||||||
|
|
||||||
@@ -42,10 +38,13 @@ function onRefresh() {
|
|||||||
|
|
||||||
// 过滤数据,管理员用户显示全部,非管理员只显示自己的订阅
|
// 过滤数据,管理员用户显示全部,非管理员只显示自己的订阅
|
||||||
const filteredDataList = computed(() => {
|
const filteredDataList = computed(() => {
|
||||||
|
// 从Vuex Store中获取用户信息
|
||||||
|
const superUser = store.state.auth.superUser
|
||||||
|
const userName = store.state.auth.userName
|
||||||
if (superUser)
|
if (superUser)
|
||||||
return dataList.value
|
return dataList.value
|
||||||
else
|
else
|
||||||
return dataList.value.filter(data => data.userid === userName)
|
return dataList.value.filter(data => data.userid === userName || data.username === userName)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 加载时获取数据
|
// 加载时获取数据
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ const notificationSettings = ref({
|
|||||||
SLACK_CHANNEL: '',
|
SLACK_CHANNEL: '',
|
||||||
SYNOLOGYCHAT_WEBHOOK: '',
|
SYNOLOGYCHAT_WEBHOOK: '',
|
||||||
SYNOLOGYCHAT_TOKEN: '',
|
SYNOLOGYCHAT_TOKEN: '',
|
||||||
|
VOCECHAT_HOST: '',
|
||||||
|
VOCECHAT_API_KEY: '',
|
||||||
|
VOCECHAT_CHANNEL_ID: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
// 消息渠道
|
// 消息渠道
|
||||||
@@ -49,6 +52,10 @@ const NotificationChannels = [
|
|||||||
title: 'SynologyChat',
|
title: 'SynologyChat',
|
||||||
value: 'synologychat',
|
value: 'synologychat',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'VoceChat',
|
||||||
|
value: 'vocechat',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
// 提示框
|
// 提示框
|
||||||
@@ -110,6 +117,9 @@ async function loadNotificationSettings() {
|
|||||||
SLACK_CHANNEL,
|
SLACK_CHANNEL,
|
||||||
SYNOLOGYCHAT_WEBHOOK,
|
SYNOLOGYCHAT_WEBHOOK,
|
||||||
SYNOLOGYCHAT_TOKEN,
|
SYNOLOGYCHAT_TOKEN,
|
||||||
|
VOCECHAT_HOST,
|
||||||
|
VOCECHAT_API_KEY,
|
||||||
|
VOCECHAT_CHANNEL_ID,
|
||||||
} = result2.data
|
} = result2.data
|
||||||
notificationSettings.value = {
|
notificationSettings.value = {
|
||||||
WECHAT_CORPID,
|
WECHAT_CORPID,
|
||||||
@@ -128,6 +138,9 @@ async function loadNotificationSettings() {
|
|||||||
SLACK_CHANNEL,
|
SLACK_CHANNEL,
|
||||||
SYNOLOGYCHAT_WEBHOOK,
|
SYNOLOGYCHAT_WEBHOOK,
|
||||||
SYNOLOGYCHAT_TOKEN,
|
SYNOLOGYCHAT_TOKEN,
|
||||||
|
VOCECHAT_HOST,
|
||||||
|
VOCECHAT_API_KEY,
|
||||||
|
VOCECHAT_CHANNEL_ID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,6 +230,9 @@ onMounted(() => {
|
|||||||
<VTab value="synologychat">
|
<VTab value="synologychat">
|
||||||
SynologyChat
|
SynologyChat
|
||||||
</VTab>
|
</VTab>
|
||||||
|
<VTab value="vocechat">
|
||||||
|
VoceChat
|
||||||
|
</VTab>
|
||||||
</VTabs>
|
</VTabs>
|
||||||
<VWindow
|
<VWindow
|
||||||
v-model="messagerTab"
|
v-model="messagerTab"
|
||||||
@@ -347,6 +363,31 @@ onMounted(() => {
|
|||||||
</VRow>
|
</VRow>
|
||||||
</VForm>
|
</VForm>
|
||||||
</VWindowItem>
|
</VWindowItem>
|
||||||
|
<VWindowItem value="vocechat">
|
||||||
|
<VForm>
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.VOCECHAT_HOST"
|
||||||
|
label="地址"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.VOCECHAT_API_KEY"
|
||||||
|
label="机器人密钥"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="notificationSettings.VOCECHAT_CHANNEL_ID"
|
||||||
|
label="频道ID"
|
||||||
|
placeholder="不包含#号"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VForm>
|
||||||
|
</VWindowItem>
|
||||||
</VWindow>
|
</VWindow>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -389,6 +430,9 @@ onMounted(() => {
|
|||||||
<th scope="col">
|
<th scope="col">
|
||||||
SynologyChat
|
SynologyChat
|
||||||
</th>
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
VoceChat
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -411,10 +455,13 @@ onMounted(() => {
|
|||||||
<td>
|
<td>
|
||||||
<VCheckbox v-model="message.synologychat" />
|
<VCheckbox v-model="message.synologychat" />
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<VCheckbox v-model="message.vocechat" />
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="messagemTypes.length === 0">
|
<tr v-if="messagemTypes.length === 0">
|
||||||
<td
|
<td
|
||||||
colspan="5"
|
colspan="6"
|
||||||
class="text-center"
|
class="text-center"
|
||||||
>
|
>
|
||||||
没有设置任何通知渠道
|
没有设置任何通知渠道
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const defaultFilterRules = ref({
|
|||||||
exclude: '',
|
exclude: '',
|
||||||
movie_size: '',
|
movie_size: '',
|
||||||
tv_size: '',
|
tv_size: '',
|
||||||
|
min_seeders: 0,
|
||||||
show_edit_dialog: false,
|
show_edit_dialog: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -589,7 +590,7 @@ onMounted(() => {
|
|||||||
label="排除(关键字、正则式)"
|
label="排除(关键字、正则式)"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="4">
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model="defaultFilterRules.movie_size"
|
v-model="defaultFilterRules.movie_size"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -597,7 +598,7 @@ onMounted(() => {
|
|||||||
placeholder="0-30"
|
placeholder="0-30"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="4">
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model="defaultFilterRules.tv_size"
|
v-model="defaultFilterRules.tv_size"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -605,6 +606,14 @@ onMounted(() => {
|
|||||||
placeholder="0-10"
|
placeholder="0-10"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VTextField
|
||||||
|
v-model="defaultFilterRules.min_seeders"
|
||||||
|
type="text"
|
||||||
|
label="最小做种数"
|
||||||
|
placeholder="0"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VSwitch
|
<VSwitch
|
||||||
v-model="defaultFilterRules.show_edit_dialog"
|
v-model="defaultFilterRules.show_edit_dialog"
|
||||||
|
|||||||
@@ -3,10 +3,14 @@
|
|||||||
import { useToast } from 'vue-toast-notification'
|
import { useToast } from 'vue-toast-notification'
|
||||||
import { VRow } from 'vuetify/lib/components/index.mjs'
|
import { VRow } from 'vuetify/lib/components/index.mjs'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
|
import { requiredValidator } from '@/@validators'
|
||||||
|
|
||||||
// 选中的媒体服务器
|
// 选中的媒体服务器
|
||||||
const selectedMediaServers = ref([])
|
const selectedMediaServers = ref([])
|
||||||
|
|
||||||
|
// 选中的下载器
|
||||||
|
const selectedDownloaders = ref([])
|
||||||
|
|
||||||
// 下载器选中标签页
|
// 下载器选中标签页
|
||||||
const downloaderTab = ref('qbittorrent')
|
const downloaderTab = ref('qbittorrent')
|
||||||
|
|
||||||
@@ -32,7 +36,6 @@ const mediaSettings = ref({
|
|||||||
|
|
||||||
// 下载器设置项
|
// 下载器设置项
|
||||||
const downloaderSettings = ref({
|
const downloaderSettings = ref({
|
||||||
DOWNLOADER: '',
|
|
||||||
DOWNLOADER_MONITOR: true,
|
DOWNLOADER_MONITOR: true,
|
||||||
TORRENT_TAG: '',
|
TORRENT_TAG: '',
|
||||||
QB_HOST: '',
|
QB_HOST: '',
|
||||||
@@ -183,10 +186,13 @@ async function saveMediaSetting() {
|
|||||||
// 调用API查询下载器设置
|
// 调用API查询下载器设置
|
||||||
async function loadDownladerSetting() {
|
async function loadDownladerSetting() {
|
||||||
try {
|
try {
|
||||||
const result: { [key: string]: any } = await api.get('system/env')
|
const result1: { [key: string]: any } = await api.get('system/setting/DOWNLOADER')
|
||||||
if (result.success) {
|
if (result1.success)
|
||||||
|
selectedDownloaders.value = result1.data?.value?.split(',')
|
||||||
|
|
||||||
|
const result2: { [key: string]: any } = await api.get('system/env')
|
||||||
|
if (result2.success) {
|
||||||
const {
|
const {
|
||||||
DOWNLOADER,
|
|
||||||
DOWNLOADER_MONITOR,
|
DOWNLOADER_MONITOR,
|
||||||
TORRENT_TAG,
|
TORRENT_TAG,
|
||||||
QB_HOST,
|
QB_HOST,
|
||||||
@@ -198,9 +204,8 @@ async function loadDownladerSetting() {
|
|||||||
TR_HOST,
|
TR_HOST,
|
||||||
TR_USER,
|
TR_USER,
|
||||||
TR_PASSWORD,
|
TR_PASSWORD,
|
||||||
} = result.data
|
} = result2.data
|
||||||
downloaderSettings.value = {
|
downloaderSettings.value = {
|
||||||
DOWNLOADER,
|
|
||||||
DOWNLOADER_MONITOR,
|
DOWNLOADER_MONITOR,
|
||||||
TORRENT_TAG,
|
TORRENT_TAG,
|
||||||
QB_HOST,
|
QB_HOST,
|
||||||
@@ -223,12 +228,16 @@ async function loadDownladerSetting() {
|
|||||||
// 调用API保存下载器设置
|
// 调用API保存下载器设置
|
||||||
async function saveDownloaderSetting() {
|
async function saveDownloaderSetting() {
|
||||||
try {
|
try {
|
||||||
const result: { [key: string]: any } = await api.post(
|
const result1: { [key: string]: any } = await api.post(
|
||||||
|
'system/setting/DOWNLOADER',
|
||||||
|
selectedDownloaders.value.join(','),
|
||||||
|
)
|
||||||
|
const result2: { [key: string]: any } = await api.post(
|
||||||
'system/env',
|
'system/env',
|
||||||
downloaderSettings.value,
|
downloaderSettings.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (result.success) {
|
if (result1.success && result2.success) {
|
||||||
$toast.success('保存下载器设置成功')
|
$toast.success('保存下载器设置成功')
|
||||||
reloadModule()
|
reloadModule()
|
||||||
}
|
}
|
||||||
@@ -331,13 +340,15 @@ onMounted(() => {
|
|||||||
<VRow>
|
<VRow>
|
||||||
<VCol cols="12">
|
<VCol cols="12">
|
||||||
<VCard title="下载器">
|
<VCard title="下载器">
|
||||||
<VCardSubtitle>只有选中的下载器才会被默认使用。</VCardSubtitle>
|
<VCardSubtitle>只有选中的第1个下载器才会被默认使用。</VCardSubtitle>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VForm>
|
<VForm>
|
||||||
<VRow>
|
<VRow>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VSelect
|
<VSelect
|
||||||
v-model="downloaderSettings.DOWNLOADER"
|
v-model="selectedDownloaders"
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
:items="Downloaders"
|
:items="Downloaders"
|
||||||
label="当前使用下载器"
|
label="当前使用下载器"
|
||||||
/>
|
/>
|
||||||
@@ -353,7 +364,7 @@ onMounted(() => {
|
|||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VSwitch
|
<VSwitch
|
||||||
v-model="downloaderSettings.DOWNLOADER_MONITOR"
|
v-model="downloaderSettings.DOWNLOADER_MONITOR"
|
||||||
label="监控下载器"
|
label="监控默认下载器"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
@@ -628,6 +639,7 @@ onMounted(() => {
|
|||||||
<VTextField
|
<VTextField
|
||||||
v-model="mediaSettings.DOWNLOAD_PATH"
|
v-model="mediaSettings.DOWNLOAD_PATH"
|
||||||
label="下载目录"
|
label="下载目录"
|
||||||
|
:rules="[requiredValidator]"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
@@ -682,6 +694,8 @@ onMounted(() => {
|
|||||||
<VTextField
|
<VTextField
|
||||||
v-model="mediaSettings.LIBRARY_PATH"
|
v-model="mediaSettings.LIBRARY_PATH"
|
||||||
label="媒体库目录"
|
label="媒体库目录"
|
||||||
|
placeholder="多个目录使用,分隔"
|
||||||
|
:rules="[requiredValidator]"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ onBeforeMount(fetchData)
|
|||||||
@click="siteAddDialog = true"
|
@click="siteAddDialog = true"
|
||||||
/>
|
/>
|
||||||
<SiteAddEditForm
|
<SiteAddEditForm
|
||||||
|
v-if="siteAddDialog"
|
||||||
v-model="siteAddDialog"
|
v-model="siteAddDialog"
|
||||||
oper="add"
|
oper="add"
|
||||||
@save="siteAddDialog = false; fetchData()"
|
@save="siteAddDialog = false; fetchData()"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const calendarOptions: Ref<CalendarOptions> = ref({
|
|||||||
],
|
],
|
||||||
initialView: 'dayGridMonth',
|
initialView: 'dayGridMonth',
|
||||||
weekends: true,
|
weekends: true,
|
||||||
|
firstDay: 1,
|
||||||
headerToolbar: {
|
headerToolbar: {
|
||||||
left: 'prev',
|
left: 'prev',
|
||||||
center: 'title',
|
center: 'title',
|
||||||
@@ -197,6 +198,11 @@ onMounted(() => {
|
|||||||
--fc-event-border-color: currentcolor;
|
--fc-event-border-color: currentcolor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 当天背景渐变
|
||||||
|
.fc-day-today {
|
||||||
|
background-image: linear-gradient(to bottom, #AF85FD ,rgba(var(--v-theme-on-surface), 0.04));
|
||||||
|
}
|
||||||
|
|
||||||
.v-application .fc a {
|
.v-application .fc a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,6 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 从Vuex Store中获取用户信息
|
|
||||||
const superUser = store.state.auth.superUser
|
|
||||||
const userName = store.state.auth.userName
|
|
||||||
|
|
||||||
// 是否刷新过
|
// 是否刷新过
|
||||||
const isRefreshed = ref(false)
|
const isRefreshed = ref(false)
|
||||||
|
|
||||||
@@ -47,10 +43,13 @@ function onRefresh() {
|
|||||||
|
|
||||||
// 过滤数据,管理员用户显示全部,非管理员只显示自己的订阅
|
// 过滤数据,管理员用户显示全部,非管理员只显示自己的订阅
|
||||||
const filteredDataList = computed(() => {
|
const filteredDataList = computed(() => {
|
||||||
|
// 从Vuex Store中获取用户信息
|
||||||
|
const superUser = store.state.auth.superUser
|
||||||
|
const userName = store.state.auth.userName
|
||||||
if (superUser)
|
if (superUser)
|
||||||
return dataList.value.filter(data => data.type === props.type)
|
return dataList.value.filter(data => data.type === props.type)
|
||||||
else
|
else
|
||||||
return dataList.value.filter(data => data.type === props.type && data.username === userName)
|
return dataList.value.filter(data => data.type === props.type && (data.username === userName))
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ function extractLogDetailsFromLogs(logs: string[]): { level: string; time: strin
|
|||||||
const matches = RegExp(logPattern).exec(log)
|
const matches = RegExp(logPattern).exec(log)
|
||||||
if (matches && matches.length === 5) {
|
if (matches && matches.length === 5) {
|
||||||
const [_, level, time, program, content] = matches
|
const [_, level, time, program, content] = matches
|
||||||
logDetails.push({ level, time, program, content })
|
logDetails.unshift({ level, time, program, content })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
137
src/views/system/MessageView.vue
Normal file
137
src/views/system/MessageView.vue
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import store from '@/store'
|
||||||
|
import type { Message } from '@/api/types'
|
||||||
|
import MessageCard from '@/components/cards/MessageCard.vue'
|
||||||
|
import api from '@/api'
|
||||||
|
|
||||||
|
// 定义事件
|
||||||
|
const emit = defineEmits(['scroll'])
|
||||||
|
|
||||||
|
// 消息列表
|
||||||
|
const messages = ref<Message[]>([])
|
||||||
|
// 当前页数据
|
||||||
|
const currData = ref<Message[]>([])
|
||||||
|
|
||||||
|
// 是否完成加载
|
||||||
|
const isLoaded = ref(false)
|
||||||
|
|
||||||
|
// 是否加载中
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 当前页码
|
||||||
|
const page = ref(1)
|
||||||
|
|
||||||
|
// SSE持续获取消息
|
||||||
|
function startSSEMessager() {
|
||||||
|
const token = store.state.auth.token
|
||||||
|
if (token) {
|
||||||
|
const eventSource = new EventSource(
|
||||||
|
`${import.meta.env.VITE_API_BASE_URL}system/message?token=${token}&role=user`,
|
||||||
|
)
|
||||||
|
|
||||||
|
eventSource.addEventListener('message', (event) => {
|
||||||
|
const message = event.data
|
||||||
|
if (message) {
|
||||||
|
const object = JSON.parse(message)
|
||||||
|
messages.value.push(object)
|
||||||
|
emit('scroll')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
eventSource.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用API加载存量消息
|
||||||
|
async function loadMessages({ done }: { done: any }) {
|
||||||
|
// 如果正在加载中,直接返回
|
||||||
|
if (loading.value) {
|
||||||
|
done('ok')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 设置加载中
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
currData.value = await api.get('message/web', {
|
||||||
|
params: {
|
||||||
|
page: page.value,
|
||||||
|
size: 20,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (currData.value.length > 0) {
|
||||||
|
// 合并数据
|
||||||
|
messages.value = [...currData.value, ...messages.value]
|
||||||
|
// 加载完成
|
||||||
|
done('ok')
|
||||||
|
if (page.value === 1) {
|
||||||
|
// 滚动到底部
|
||||||
|
emit('scroll')
|
||||||
|
}
|
||||||
|
// 页码+1
|
||||||
|
page.value++
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
done('ok')
|
||||||
|
}
|
||||||
|
loading.value = false
|
||||||
|
isLoaded.value = true
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 监听新消息
|
||||||
|
startSSEMessager()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VInfiniteScroll
|
||||||
|
mode="intersect"
|
||||||
|
side="start"
|
||||||
|
:items="messages"
|
||||||
|
class="overflow-hidden"
|
||||||
|
@load="loadMessages"
|
||||||
|
>
|
||||||
|
<template #loading>
|
||||||
|
<VProgressCircular
|
||||||
|
v-if="loading"
|
||||||
|
indeterminate
|
||||||
|
size="48"
|
||||||
|
class="mb-5"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<VRow
|
||||||
|
v-for="(msg, index) in messages"
|
||||||
|
:key="index"
|
||||||
|
:class="{
|
||||||
|
'justify-end': msg.action === 0,
|
||||||
|
'justify-start': msg.action === 1,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<VCol
|
||||||
|
cols="10"
|
||||||
|
lg="6"
|
||||||
|
xl="4"
|
||||||
|
style="position: relative;"
|
||||||
|
>
|
||||||
|
<MessageCard
|
||||||
|
:message="msg"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="messages.length === 0 && isLoaded && !loading"
|
||||||
|
class="w-full text-center flex flex-col items-center"
|
||||||
|
>
|
||||||
|
<span class="mb-3">当前没有消息</span>
|
||||||
|
</div>
|
||||||
|
</VInfiniteScroll>
|
||||||
|
</template>
|
||||||
75
src/views/system/ModuleTestView.vue
Normal file
75
src/views/system/ModuleTestView.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import api from '@/api'
|
||||||
|
|
||||||
|
// 定义所有的模块ID、名称列表
|
||||||
|
const modules = ref([
|
||||||
|
{ id: 'FileTransferModule', name: '媒体目录', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'IndexerModule', name: '站点索引', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'DoubanModule', name: '豆瓣', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'TheMovieDbModule', name: 'TheMovieDb', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'TheTvDbModule', name: 'TheTvDb', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'FanartModule', name: 'Fanart', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'EmbyModule', name: 'Emby', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'JellyfinModule', name: 'Jellyfin', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'PlexModule', name: 'Plex', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'WechatModule', name: '微信', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'TelegramModule', name: 'Telegram', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'SlackModule', name: 'Slack', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'SynologyChatModule', name: 'Synology Chat', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'VoceChatModule', name: 'VoceChat', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'QbittorrentModule', name: 'Qbittorrent', state: '', errmsg: '', loading: false },
|
||||||
|
{ id: 'TransmissionModule', name: 'Transmission', state: '', errmsg: '', loading: false },
|
||||||
|
])
|
||||||
|
|
||||||
|
// 调用API测试模块
|
||||||
|
async function moduleTest(index: number) {
|
||||||
|
try {
|
||||||
|
const target = modules.value[index]
|
||||||
|
const moduleid = target.id
|
||||||
|
target.loading = true
|
||||||
|
const result: { [key: string]: any } = await api.get(`system/moduletest/${moduleid}`)
|
||||||
|
target.loading = false
|
||||||
|
if (result.success) {
|
||||||
|
target.state = 'success'
|
||||||
|
target.name = `${target.name} - 正常`
|
||||||
|
}
|
||||||
|
else if (result.message?.includes('模块未加载')) {
|
||||||
|
target.state = ''
|
||||||
|
target.name = `${target.name} - 未启用`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
target.state = 'error'
|
||||||
|
target.name = `${target.name} - 错误!`
|
||||||
|
target.errmsg = result.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 加载
|
||||||
|
onMounted(async () => {
|
||||||
|
// 逐个检查所有模块
|
||||||
|
for (let i = 0; i < modules.value.length; i++)
|
||||||
|
await moduleTest(i)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VAlert
|
||||||
|
v-for="(module, index) in modules"
|
||||||
|
:key="index"
|
||||||
|
:type="module.state"
|
||||||
|
:title="module.name"
|
||||||
|
class="mb-2"
|
||||||
|
variant="tonal"
|
||||||
|
>
|
||||||
|
{{ module.errmsg }}
|
||||||
|
<template #append>
|
||||||
|
<VProgressCircular
|
||||||
|
v-if="module.loading"
|
||||||
|
indeterminate
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VAlert>
|
||||||
|
</template>
|
||||||
@@ -6,6 +6,8 @@ import slack from '@images/logos/slack.png'
|
|||||||
import telegram from '@images/logos/telegram.webp'
|
import telegram from '@images/logos/telegram.webp'
|
||||||
import tmdb from '@images/logos/tmdb.png'
|
import tmdb from '@images/logos/tmdb.png'
|
||||||
import wechat from '@images/logos/wechat.png'
|
import wechat from '@images/logos/wechat.png'
|
||||||
|
import fanart from '@images/logos/fanart.webp'
|
||||||
|
import tvdb from '@images/logos/thetvdb.jpeg'
|
||||||
|
|
||||||
interface Status {
|
interface Status {
|
||||||
OK: string
|
OK: string
|
||||||
@@ -57,6 +59,26 @@ const targets = ref<Address[]>([
|
|||||||
message: '未测试',
|
message: '未测试',
|
||||||
btndisable: false,
|
btndisable: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
image: tvdb,
|
||||||
|
name: 'api.thetvdb.com',
|
||||||
|
url: 'https://api.thetvdb.com/series/81189',
|
||||||
|
proxy: true,
|
||||||
|
status: 'Normal',
|
||||||
|
time: '',
|
||||||
|
message: '未测试',
|
||||||
|
btndisable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: fanart,
|
||||||
|
name: 'webservice.fanart.tv',
|
||||||
|
url: 'https://webservice.fanart.tv',
|
||||||
|
proxy: true,
|
||||||
|
status: 'Normal',
|
||||||
|
time: '',
|
||||||
|
message: '未测试',
|
||||||
|
btndisable: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
image: telegram,
|
image: telegram,
|
||||||
name: 'api.telegram.org',
|
name: 'api.telegram.org',
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ export default defineConfig({
|
|||||||
build: {
|
build: {
|
||||||
chunkSizeWarningLimit: 5000,
|
chunkSizeWarningLimit: 5000,
|
||||||
cssCodeSplit: false,
|
cssCodeSplit: false,
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
entryFileNames: '[name].js',
|
||||||
|
chunkFileNames: '[name].js',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
exclude: ['vuetify'],
|
exclude: ['vuetify'],
|
||||||
|
|||||||
Reference in New Issue
Block a user