Compare commits

..

59 Commits

Author SHA1 Message Date
jxxghp
f335b4e436 v1.2.3 2023-09-20 06:56:25 +08:00
jxxghp
ab293edf4c 更新 AccountSettingRule.vue 2023-09-19 23:44:17 +08:00
jxxghp
88917070bf Merge pull request #43 from thofx/fix_bug
try fix bug
2023-09-19 22:46:46 +08:00
thofx
5bba5cb2bc try fix bug 2023-09-19 22:44:18 +08:00
jxxghp
098916bfa5 Merge pull request #42 from thofx/fix_bug 2023-09-19 22:16:37 +08:00
thofx
bb79aaed8b fix bug 2023-09-19 22:11:58 +08:00
jxxghp
bc5c5a2835 fix ui 2023-09-19 21:43:39 +08:00
jxxghp
4c11199de2 feat 规则页面拆分 2023-09-19 21:41:07 +08:00
jxxghp
2e987701a8 fix router 2023-09-19 18:02:44 +08:00
jxxghp
4f625291a5 roolback 2023-09-19 17:24:05 +08:00
jxxghp
048f2abd87 fix 2023-09-19 17:20:51 +08:00
jxxghp
31dea532c5 更新 App.vue 2023-09-19 14:57:38 +08:00
jxxghp
3d54e5d965 更新 VerticalNav.vue 2023-09-19 14:56:52 +08:00
jxxghp
aee2a5a161 Merge pull request #41 from cikezhu/main 2023-09-19 14:55:38 +08:00
叮叮当
198ea0104d 优化首次载入流程 2023-09-19 14:35:43 +08:00
叮叮当
1abdf6d15c 优化首次载入方式 2023-09-19 14:30:54 +08:00
叮叮当
e5b836462f 优化首次载入流程 2023-09-19 13:57:37 +08:00
jxxghp
552b20b5d9 fix ui 2023-09-19 13:41:05 +08:00
jxxghp
7d500aedb5 fix menu layout 2023-09-19 13:04:35 +08:00
jxxghp
751e823b8c test remove transition 2023-09-19 12:51:44 +08:00
jxxghp
59d47b2b15 Merge pull request #40 from cikezhu/main
去除多余的代码/修改记录位置的方式/修复IOS14登录页显示bug
2023-09-19 12:07:54 +08:00
叮叮当
b1635b0715 修复IOS14登录页显示bug 2023-09-19 10:42:46 +08:00
叮叮当
4d778e9ca9 修改记录位置的方式 2023-09-19 10:36:59 +08:00
叮叮当
af433286d0 Merge branch 'main' of https://github.com/jxxghp/MoviePilot-Frontend 2023-09-19 10:35:02 +08:00
叮叮当
2a41e8a726 修改记录位置的方式 2023-09-19 10:34:49 +08:00
叮叮当
6b41f3bb64 去除多余的代码 2023-09-19 10:33:52 +08:00
jxxghp
78c178b1f6 fix ui 2023-09-19 08:17:18 +08:00
jxxghp
20a6dd1aeb fix ui 2023-09-18 17:51:04 +08:00
jxxghp
3773dfb4a1 fix ui 2023-09-18 17:39:05 +08:00
jxxghp
e156b662a3 fix ui 2023-09-18 17:36:12 +08:00
jxxghp
38193a870b fix 2023-09-18 17:05:40 +08:00
jxxghp
e3a636772a fix 2023-09-17 20:02:26 +08:00
jxxghp
46b043fdc7 fix ui 2023-09-17 20:02:04 +08:00
jxxghp
a774ae87c2 Merge pull request #39 from WithdewHua/dev 2023-09-17 18:58:52 +08:00
WithdewHua
a332a7b402 feat: 订阅搜索支持默认包含与排除规则 2023-09-17 18:32:41 +08:00
jxxghp
2ee4d874da Merge pull request #38 from cikezhu/main 2023-09-16 16:54:07 +08:00
叮叮当
4f051e5251 增加滚动位置记录, 返回时恢复 2023-09-16 16:51:11 +08:00
jxxghp
e4a0b29162 更新 package.json 2023-09-16 16:38:02 +08:00
jxxghp
09234296f4 Merge pull request #37 from cikezhu/main 2023-09-16 16:37:24 +08:00
叮叮当
679228c8a7 修复keep-alive 2023-09-16 16:28:40 +08:00
jxxghp
a752e19878 roolback 2023-09-15 11:23:35 +08:00
jxxghp
0880c0e3b3 v1.2.1 2023-09-15 11:14:25 +08:00
jxxghp
948e65d383 v1.2.0-1 2023-09-14 17:22:23 +08:00
jxxghp
7cce57496d v1.2.0-1 2023-09-14 16:12:09 +08:00
jxxghp
e54e851f61 fix ios菜单点击两次 2023-09-14 16:11:44 +08:00
jxxghp
17020cf62d fix text 2023-09-14 10:39:35 +08:00
jxxghp
0c7be28eaa fix tooltip 2023-09-14 10:11:11 +08:00
jxxghp
0d5a183f2e fix ui 2023-09-14 09:49:03 +08:00
jxxghp
c222594bea fix size 2023-09-13 19:25:46 +08:00
jxxghp
3df8bdfbf2 fix ui 2023-09-12 09:24:45 +08:00
jxxghp
5722547d93 fix 日历 2023-09-12 08:53:14 +08:00
jxxghp
dea5ebd95d feat RSS地址维护 2023-09-11 18:02:00 +08:00
jxxghp
048e41c1ca feat 移动自定义订阅 2023-09-11 17:48:09 +08:00
jxxghp
5078036c51 fix ui 2023-09-11 16:34:34 +08:00
jxxghp
e7a128bf0d fix image size 2023-09-11 12:35:29 +08:00
jxxghp
0e46936231 fix ui 2023-09-11 12:32:20 +08:00
jxxghp
d91d3ef0ef feat TMDBID搜索 2023-09-11 11:59:07 +08:00
jxxghp
1f0dd907f9 1.1.8 2023-09-10 17:34:26 +08:00
jxxghp
3c555cbfca fix site api 2023-09-09 17:40:01 +08:00
44 changed files with 1015 additions and 1546 deletions

View File

@@ -30,121 +30,120 @@
</head>
<body>
<div id="app">
<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"
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/"
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)">
<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
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" />
</clipPath>
<g clip-path="url(#_clip5)">
<g transform="matrix(0.124502,0.074907,0.206623,-0.0414384,1997.62,-7.40235)">
<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);" />
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);" />
</g>
<g transform="matrix(1,0,0,1,-2157.67,-208.423)">
<g transform="matrix(-0.126036,0.0767377,0.569859,0.112933,2435.01,-3.09225)">
<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"
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(1,0,0,1,-2157.67,-208.423)">
<g transform="matrix(-0.126036,0.0767377,0.569859,0.112933,2435.01,-3.09225)">
<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
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" />
</clipPath>
<g clip-path="url(#_clip5)">
<g transform="matrix(0.124502,0.074907,0.206623,-0.0414384,1997.62,-7.40235)">
<path
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);" />
</g>
<g transform="matrix(-0.126036,0.0767377,0.569859,0.112933,2435.01,-3.09225)">
<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"
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>
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>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-70.0711,-0.927611,1.54482,-42.0752,2233.59,-20.1891)">
<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" />
</linearGradient>
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
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="1" style="stop-color:rgb(116,50,223);stop-opacity:1" />
</linearGradient>
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
gradientTransform="matrix(41.6089,41.5866,-41.5866,41.6089,2282.31,262.837)">
<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="_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>
</g>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-70.0711,-0.927611,1.54482,-42.0752,2233.59,-20.1891)">
<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" />
</linearGradient>
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
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="1" style="stop-color:rgb(116,50,223);stop-opacity:1" />
</linearGradient>
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"
gradientTransform="matrix(41.6089,41.5866,-41.5866,41.6089,2282.31,262.837)">
<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="_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 class="loading">
<div class="effect-1 effects"></div>
<div class="effect-2 effects"></div>
<div class="effect-3 effects"></div>
</div>
</div>
<div id="app">
</div>
<script type="module" src="/src/main.ts"></script>
<script>
@@ -159,4 +158,4 @@
</script>
</body>
</html>
</html>

View File

@@ -1,6 +1,6 @@
{
"name": "moviepilot",
"version": "1.1.7-2",
"version": "1.2.3",
"private": true,
"scripts": {
"dev": "vite --host",

View File

@@ -11,10 +11,11 @@ html {
#loading-bg {
position: absolute;
z-index: 999;
display: block;
background: var(--initial-loader-bg, #fff);
block-size: 100%;
inline-size: 100%;
block-size: 100vh;
inline-size: 100vw;
}
.loading-logo {
@@ -82,4 +83,4 @@ html {
opacity: 1;
transform: rotate(1turn);
}
}
}

View File

@@ -17,7 +17,6 @@
// This mixin is inspired from vuetify for adding hover styles via before pseudo element
@mixin before-pseudo() {
position: relative;
&::before {
position: absolute;
background: currentcolor;

View File

@@ -8,7 +8,6 @@
// This mixin is inspired from vuetify for adding hover styles via before pseudo element
@mixin before-pseudo() {
position: relative;
&::before {
position: absolute;
border-radius: inherit;

View File

@@ -33,3 +33,11 @@ $ps-track-size: 0.5rem;
.ps__thumb-y {
background-color: rgb(var(--v-theme-perfect-scrollbar-thumb)) !important;
}
// fix bug
@media(hover: none) {
.ps > .ps__rail-x,
.ps > .ps__rail-y {
opacity: 0.6;
}
}

View File

@@ -85,7 +85,7 @@ export default defineComponent({
<style scoped>
* {
backface-visibility: hidden;
perspective: 1000px;
perspective: 62.5rem;
transform: translateZ(0);
will-change: block-size;
}

View File

@@ -15,13 +15,7 @@ const props = withDefaults(defineProps<Props>(), {
})
const { mdAndDown } = useDisplay()
const refNav = ref()
/*
Close overlay side when route is changed
Close overlay vertical nav when link is clicked
*/
const route = useRoute()
watch(
@@ -31,9 +25,11 @@ watch(
},
)
// 是否滚动
const isVerticalNavScrolled = ref(false)
const updateIsVerticalNavScrolled = (val: boolean) => (isVerticalNavScrolled.value = val)
// 滚动响应
function handleNavScroll(evt: Event) {
isVerticalNavScrolled.value = (evt.target as HTMLElement).scrollTop > 0
}
@@ -83,9 +79,12 @@ function handleNavScroll(evt: Event) {
</template>
<style lang="scss">
@use "@configured-variables" as variables;
@use "@layouts/styles/mixins";
@use '@configured-variables' as variables;
@use '@layouts/styles/mixins';
.visible {
visibility: visible !important;
}
// 👉 Vertical Nav
.layout-vertical-nav {
position: fixed;
@@ -98,6 +97,11 @@ function handleNavScroll(evt: Event) {
inset-inline-start: 0;
transition: transform 0.25s ease-in-out, inline-size 0.25s ease-in-out, box-shadow 0.25s ease-in-out;
will-change: transform, inline-size;
visibility: hidden;
&:not(.overlay-nav) {
visibility: visible;
}
.nav-header {
display: flex;

View File

@@ -7,9 +7,6 @@ import store from './store'
const { global: globalTheme } = useTheme()
globalTheme.name.value = localStorage.getItem('theme') || 'light'
// 路由
const route = useRoute()
// 提示框
const $toast = useToast()
@@ -41,6 +38,6 @@ onBeforeMount(async () => {
<template>
<VApp>
<RouterView :key="route.fullPath" />
<RouterView />
</VApp>
</template>

View File

@@ -840,55 +840,6 @@ export interface Setting {
DOWNLOAD_PATH: string
}
// 自定义订阅
export interface Rss {
id?: number
// 名称
name?: string
// RSS地址
url?: string
// 类型
type?: string
// 标题
title?: string
// 年份
year?: string
// TMDBID
tmdbid?: number
// 季号
season?: number
// 海报
poster?: string
// 背景图
backdrop?: string
// 评分
vote?: number
// 简介
description?: string
// 总集数
total_episode?: number
// 包含
include?: string
// 排除
exclude?: string
// 洗版
best_version?: number
// 是否使用代理服务器
proxy?: number
// 是否使用过滤规则
filter?: boolean
// 保存路径
save_path?: string
// 已处理数量
processed?: number
// 附加信息
note?: string
// 最后更新时间
last_update?: string
// 状态 0-停用1-启用
state?: number
}
// 文件浏览接口
export interface EndPoints {
list: any

View File

@@ -53,7 +53,7 @@ async function installPlugin() {
:style="{ background: `${props.plugin?.plugin_color}` }"
>
<VAvatar
size="128"
size="8rem"
:class="{ shadow: isImageLoaded }"
>
<VImg

View File

@@ -212,7 +212,7 @@ const dropdownItems = ref([
</IconBtn>
</div>
<VAvatar
size="128"
size="8rem"
:class="{ shadow: isImageLoaded }"
>
<VImg
@@ -236,7 +236,7 @@ const dropdownItems = ref([
<!-- 插件配置页面 -->
<VDialog
v-model="pluginConfigDialog"
max-width="800"
max-width="50rem"
scrollable
persistent
>
@@ -265,7 +265,7 @@ const dropdownItems = ref([
<!-- 插件详情页面 -->
<VDialog
v-model="pluginInfoDialog"
max-width="1000"
max-width="62.5rem"
scrollable
persistent
>

View File

@@ -1,594 +0,0 @@
<script lang="ts" setup>
import { useToast } from 'vue-toast-notification'
import { calculateTimeDifference } from '@/@core/utils'
import { formatFileSize, formatSeason } from '@/@core/utils/formatters'
import api from '@/api'
import type { Rss, Site, TorrentInfo } from '@/api/types'
// 输入参数
const props = defineProps({
media: Object as PropType<Rss>,
})
// 定义触发的自定义事件
const emit = defineEmits(['remove', 'save'])
// 提示框
const $toast = useToast()
// 图片是否加载完成
const imageLoaded = ref(false)
// 订阅弹窗
const rssInfoDialog = ref(false)
// RSS预览窗口
const rssPreviewDialog = ref(false)
// 加载状态
const previewLoading = ref(false)
// 总条数
const previewTotalItems = ref(0)
// 每页条数
const previewItemsPerPage = ref(25)
// 预览表头
const previewHeaders = [
{ title: '标题', key: 'title', sortable: true },
{ title: '时间', key: 'pubdate', sortable: true },
{ title: '大小', key: 'size', sortable: true },
{ title: '', key: 'actions', sortable: false },
]
// 预览数据
const previewDataList = ref<TorrentInfo[]>([])
// 站点名称
const siteName = ref('')
// 订阅编辑表单
const rssForm = reactive<any>(props.media ?? {})
// 类型转换
rssForm.best_version = rssForm.best_version === 1
rssForm.proxy = rssForm.proxy === 1
rssForm.filter = rssForm.filter === 1
// 上一次更新时间
const lastUpdateText = ref(
`${
props.media?.last_update
? `${calculateTimeDifference(props.media?.last_update || '')}`
: ''
}`,
)
// 图片加载完成响应
function imageLoadHandler() {
imageLoaded.value = true
}
// 根据 type 返回不同的图标
function getIcon() {
if (props.media?.type === '电影')
return 'mdi-movie'
else if (props.media?.type === '电视剧')
return 'mdi-television-classic'
else
return 'mdi-help-circle'
}
// 计算文本颜色
function getTextColor() {
return imageLoaded.value ? 'white' : ''
}
// 计算文本类
function getTextClass() {
return imageLoaded.value ? 'text-white' : ''
}
// 删除订阅
async function removerRss() {
try {
const result: { [key: string]: any } = await api.delete(
`rss/${props.media?.id}`,
)
if (result.success) {
// 通知父组件刷新
emit('remove')
}
}
catch (e) {
console.log(e)
}
}
// 调用API修改订阅
async function updateRssInfo() {
rssInfoDialog.value = false
try {
const result: { [key: string]: any } = await api.put('rss', rssForm)
// 提示
if (result.success) {
$toast.success(`${props.media?.name} 更新成功!`)
// 通知父组件刷新
emit('remove')
}
else { $toast.error(`${props.media?.name} 更新失败:${result.message}`) }
}
catch (e) {
console.log(e)
}
}
// 查询站点名称
async function querySiteName() {
try {
const result: Site = await api.get(
`site/domain/${props.media?.url?.split('/')[2]}`,
)
if (result)
siteName.value = result.name
}
catch (e) {
// 截取URL中的主域名作为站点名称
siteName.value = props.media?.url?.split('/')[2] ?? '未知'
console.log(e)
}
}
// 预览按钮响应
async function handleRssPreview() {
rssPreviewDialog.value = true
previewLoading.value = true
await previewRss()
previewLoading.value = false
}
// 预览站点RSS
async function previewRss() {
try {
const result: TorrentInfo[] = await api.get(
`rss/preview/${props.media?.id}`,
)
previewDataList.value = result
}
catch (e) {
console.log(e)
}
}
// 编辑订阅响应
async function editRssDialog() {
rssInfoDialog.value = true
}
// 刷新按钮响应
async function refreshRss() {
try {
const result: { [key: string]: any } = await api.get(
`rss/refresh/${props.media?.id}`,
)
if (result.success)
$toast.success(`${props.media?.name} 已提交刷新任务!`)
}
catch (e) {
console.log(e)
}
}
// 生成1到50季的下拉框选项
const seasonItems = ref(
Array.from({ length: 50 }, (_, i) => i + 1).map(item => ({
title: `${item}`,
value: item,
})),
)
// 打开种子详情页面
function openTorrentDetail(page_url: string) {
window.open(page_url, '_blank')
}
// 下载种子文件
async function downloadTorrentFile(enclosure: string) {
window.open(enclosure, '_blank')
}
// 弹出菜单
const dropdownItems = ref([
{
title: '编辑',
value: 1,
props: {
prependIcon: 'mdi-file-edit-outline',
click: editRssDialog,
},
},
{
title: '预览',
value: 2,
props: {
prependIcon: 'mdi-eye-outline',
click: handleRssPreview,
},
},
{
title: '刷新',
value: 3,
props: {
prependIcon: 'mdi-refresh',
click: refreshRss,
},
},
{
title: '取消订阅',
value: 4,
props: {
prependIcon: 'mdi-trash-can-outline',
color: 'error',
click: removerRss,
},
},
])
onMounted(() => {
querySiteName()
})
</script>
<template>
<VCard
:key="props.media?.id"
:class="`${rssForm.best_version ? 'outline-dashed outline-1' : ''}`"
@click="editRssDialog"
>
<template #image>
<VImg
:src="props.media?.backdrop || props.media?.poster"
aspect-ratio="2/3"
cover
class="brightness-50"
@load="imageLoadHandler"
/>
</template>
<VCardItem>
<template #prepend>
<VIcon
size="1.9rem"
:color="getTextColor()"
:icon="getIcon()"
/>
</template>
<VCardTitle :class="getTextClass()">
{{ props.media?.name }}
{{ formatSeason(props.media?.season ? props.media?.season.toString() : "") }}
</VCardTitle>
<template #append>
<div class="me-n3">
<IconBtn>
<VIcon
icon="mdi-dots-vertical"
:color="getTextColor()"
/>
<VMenu
activator="parent"
close-on-content-click
>
<VList>
<VListItem
v-for="(item, i) in dropdownItems"
:key="i"
variant="plain"
:base-color="item.props.color"
@click="item.props.click"
>
<template #prepend>
<VIcon :icon="item.props.prependIcon" />
</template>
<VListItemTitle v-text="item.title" />
</VListItem>
</VList>
</VMenu>
</IconBtn>
</div>
</template>
</VCardItem>
<VCardText>
<p
class="clamp-text mb-0"
:class="getTextClass()"
>
{{ props.media?.description }}
</p>
</VCardText>
<VCardText class="d-flex justify-space-between align-center flex-wrap">
<div class="d-flex align-center">
<IconBtn
icon="mdi-star"
:color="getTextColor()"
class="me-1"
/>
<span
class="text-subtitle-2 me-4"
:class="getTextClass()"
>{{
props.media?.vote
}}</span>
<IconBtn
v-bind="props"
icon="mdi-progress-clock"
:color="getTextColor()"
class="me-1"
/>
<span
class="text-subtitle-2 me-4"
:class="getTextClass()"
>{{ props.media?.processed || 0 }}</span>
<IconBtn
v-if="siteName"
icon="mdi-web"
:color="getTextColor()"
class="me-1"
/>
<span
v-if="siteName"
class="text-subtitle-2 me-4"
:class="getTextClass()"
>
{{ siteName }}
</span>
</div>
</VCardText>
<VCardText
v-if="lastUpdateText"
class="absolute right-0 bottom-0 d-flex align-center p-2 text-gray-300"
>
<VIcon
icon="mdi-download"
class="me-1"
/> {{ lastUpdateText }}
</VCardText>
</VCard>
<!-- 订阅编辑弹窗 -->
<VDialog
v-model="rssInfoDialog"
max-width="800"
persistent
scrollable
>
<!-- Dialog Content -->
<VCard :title="`订阅 - ${props.media?.name}`">
<DialogCloseBtn @click="rssInfoDialog = false" />
<VCardText class="pt-2">
<VForm @submit.prevent="() => {}">
<VRow>
<VCol
cols="12"
>
<VTextField
v-model="rssForm.url"
label="RSS地址"
placeholder="https://example.com/rss"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VSelect
v-model="rssForm.type"
label="类型"
:items="[{ title: '电影', value: '电影' }, { title: '电视剧', value: '电视剧' }]"
readonly
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="rssForm.title"
label="标题"
readonly
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="rssForm.year"
label="年份"
readonly
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VSelect
v-show="rssForm.type === '电视剧'"
v-model="rssForm.season"
label="季"
:items="seasonItems"
readonly
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="rssForm.include"
label="包含"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="rssForm.exclude"
label="排除"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="rssForm.save_path"
label="保存路径"
placeholder="留空自动"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VSelect
v-model="rssForm.state"
label="状态"
:items="[{
title: '启用',
value: 1,
}, {
title: '停用',
value: 0,
}]"
/>
</VCol>
</VRow>
<VRow>
<VCol
cols="12"
md="4"
>
<VSwitch
v-model="rssForm.best_version"
label="洗版"
/>
</VCol>
<VCol
cols="12"
md="4"
>
<VSwitch
v-model="rssForm.proxy"
label="代理服务器"
/>
</VCol>
<VCol
cols="12"
md="4"
>
<VSwitch
v-model="rssForm.filter"
label="过滤规则"
/>
</VCol>
</VRow>
</VForm>
</VCardText>
<VCardActions>
<VBtn @click="rssInfoDialog = false">
取消
</VBtn>
<VSpacer />
<VBtn @click="updateRssInfo">
确定
</VBtn>
</VCardActions>
</VCard>
</VDialog>
<!-- RSS预览窗口 -->
<VDialog
v-model="rssPreviewDialog"
max-width="1280"
scrollable
>
<!-- Dialog Content -->
<VCard title="RSS预览">
<DialogCloseBtn @click="rssPreviewDialog = false" />
<VCardText class="pt-2">
<VDataTable
v-model:items-per-page="previewItemsPerPage"
:headers="previewHeaders"
:items="previewDataList"
:items-length="previewTotalItems"
:loading="previewLoading"
density="compact"
item-value="title"
return-object
fixed-header
items-per-page-text="每页条数"
page-text="{0}-{1} {2} "
>
<template #item.title="{ item }">
<div class="text-high-emphasis">
{{ item.raw.title }}
</div>
</template>
<template #item.size="{ item }">
<div class="text-nowrap whitespace-nowrap">
{{ formatFileSize(item.raw.size) }}
</div>
</template>
<template #item.pubdate="{ item }">
<div class="text-sm">
{{ item.raw.pubdate }}
</div>
</template>
<template #item.actions="{ item }">
<div class="me-n3">
<IconBtn>
<VIcon
icon="mdi-dots-vertical"
/>
<VMenu
activator="parent"
close-on-content-click
>
<VList>
<VListItem
variant="plain"
@click="openTorrentDetail(item.raw.page_url)"
>
<template #prepend>
<VIcon icon="mdi-information" />
</template>
<VListItemTitle>查看详情</VListItemTitle>
</VListItem>
<VListItem
variant="plain"
@click="downloadTorrentFile(item.raw.enclosure)"
>
<template #prepend>
<VIcon icon="mdi-download" />
</template>
<VListItemTitle>下载种子</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</IconBtn>
</div>
</template>
<template #no-data>
没有数据
</template>
</VDataTable>
</VCardText>
</VCard>
</VDialog>
</template>

View File

@@ -72,9 +72,6 @@ const resourceTotalItems = ref(0)
// 每页条数
const resourceItemsPerPage = ref(25)
// 当前页码
const resourceCurrentPage = ref(0)
// 用户名密码表单
const userPwForm = ref({
username: '',
@@ -244,13 +241,7 @@ function getVolumeFactorClass(downloadVolume: number, uploadVolume: number) {
async function getResourceList() {
resourceLoading.value = true
try {
resourceDataList.value = await api.get('search/title', {
params: {
keyword: resourceSearch.value,
page: resourceCurrentPage.value,
site: cardProps.site?.id,
},
})
resourceDataList.value = await api.get(`site/resource/${cardProps.site?.id}`)
resourceLoading.value = false
}
catch (error) {
@@ -437,7 +428,7 @@ onMounted(() => {
<!-- 站点编辑弹窗 -->
<VDialog
v-model="siteInfoDialog"
max-width="1000"
max-width="50rem"
persistent
scrollable
>
@@ -480,6 +471,12 @@ onMounted(() => {
</VCol>
</VRow>
<VRow>
<VCol cols="12">
<VTextField
v-model="siteForm.rss"
label="RSS地址"
/>
</VCol>
<VCol cols="12">
<VTextarea
v-model="siteForm.cookie"
@@ -562,7 +559,7 @@ onMounted(() => {
<!-- 站点资源弹窗 -->
<VDialog
v-model="resourceDialog"
max-width="1280"
max-width="80rem"
scrollable
>
<!-- Dialog Content -->

View File

@@ -325,7 +325,7 @@ const dropdownItems = ref([
<!-- 订阅编辑弹窗 -->
<VDialog
v-model="subscribeInfoDialog"
max-width="1000"
max-width="50rem"
persistent
scrollable
>

View File

@@ -0,0 +1,128 @@
<script lang="ts" setup>
import api from '@/api'
import type { MediaInfo } from '@/api/types'
interface TmdbItem {
title: string
overview: string
tmdbid: number
poster: string
}
// update:modelValue 事件
const emit = defineEmits(['update:modelValue', 'close'])
const items = ref<TmdbItem[]>([])
// 搜索词
const keyword = ref('')
// 加载中
const loading = ref(false)
// 选中条目
function selectMedia(item: TmdbItem) {
console.log(item)
emit('update:modelValue', item.tmdbid)
emit('close')
}
// TMDB图片转换为w500大小
function getW500Image(url = '') {
if (!url)
return ''
return url.replace('original', 'w500')
}
// 搜索词条
async function searchMedias() {
if (!keyword)
return
// 调用API搜索词条
try {
loading.value = true
const result: MediaInfo[] = await api.get('media/search', {
params: {
title: keyword.value,
page: 1,
count: 20,
},
})
// 清空
items.value = []
// 赋值
for (const item of result) {
items.value.push({
tmdbid: item.tmdb_id || 0,
poster: getW500Image(item.poster_path),
title: `${item.title}${item.year}`,
overview: `<span class="text-primary">${item.type}</span> ${item.overview}`,
})
}
loading.value = false
}
catch (e) {
console.error(e)
}
}
</script>
<template>
<VCard
class="mx-auto"
width="100%"
>
<VToolbar flat dense>
<VTextField
v-model="keyword"
density="compact"
label="输入名称搜索"
single-line
hide-details
flat
class="mx-3"
append-inner-icon="mdi-magnify"
:loading="loading"
@click:append-inner="searchMedias"
@keydown.enter="searchMedias"
/>
</VToolbar>
<VList
v-if="items.length > 0"
lines="three"
>
<template v-for="(item, i) in items" :key="i">
<VListItem
density="compact"
@click="selectMedia(item)"
>
<template #prepend>
<VImg
height="75"
width="50"
:src="item.poster"
aspect-ratio="2/3"
class="object-cover rounded shadow ring-gray-500 me-3"
cover
>
<template #placeholder>
<div class="w-full h-full">
<VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
</div>
</template>
</VImg>
</template>
<VListItemTitle>
{{ item.title }}
</VListItemTitle>
<VListItemSubtitle v-html="item.overview" />
</VListItem>
<VDivider v-if="i < items.length - 1" class="mt-1" inset />
</template>
</VList>
</VCard>
</template>

View File

@@ -10,6 +10,7 @@ import type { Context, EndPoints, FileItem } from '@/api/types'
import store from '@/store'
import api from '@/api'
import MediaInfoCard from '@/components/cards/MediaInfoCard.vue'
import TmdbSelectorCard from '@/components/cards/TmdbSelectorCard.vue'
// 输入参数
const inProps = defineProps({
@@ -92,6 +93,9 @@ const nameTestResult = ref<Context>()
// 识别结果对话框
const nameTestDialog = ref(false)
// TMDB选择对话框
const tmdbSelectorDialog = ref(false)
// 生成1到50季的下拉框选项
const seasonItems = ref(
Array.from({ length: 51 }, (_, i) => i).map(item => ({
@@ -262,7 +266,7 @@ async function transfer() {
stopLoadingProgress()
// 显示结果
if (res.success) {
$toast.success(`${currentItem.value?.name} 整理成`)
$toast.success(`${currentItem.value?.name} 整理成!`)
// 重新加载
load()
}
@@ -609,7 +613,7 @@ onMounted(() => {
<!-- 文件整理弹窗 -->
<VDialog
v-model="transferPopper"
max-width="800"
max-width="50rem"
scrollable
>
<template #activator="{ props }">
@@ -667,6 +671,8 @@ onMounted(() => {
label="TMDBID"
placeholder="留空自动识别"
:rules="[numberValidator]"
append-inner-icon="mdi-magnify"
@click:append-inner="tmdbSelectorDialog = true"
/>
</VCol>
<VCol
@@ -722,10 +728,10 @@ onMounted(() => {
</VForm>
</VCardText>
<VCardActions>
<div class="flex-grow-1" />
<VBtn depressed @click="transferPopper = false">
取消
</VBtn>
<VSpacer />
<VBtn
@click="transfer"
>
@@ -738,7 +744,7 @@ onMounted(() => {
<vDialog
v-model="progressDialog"
:scrim="false"
width="400"
width="25rem"
>
<vCard
color="primary"
@@ -757,7 +763,7 @@ onMounted(() => {
<!-- 识别结果对话框 -->
<vDialog
v-model="nameTestDialog"
width="800"
width="50rem"
>
<vCard>
<DialogCloseBtn @click="nameTestDialog = false" />
@@ -766,6 +772,17 @@ onMounted(() => {
</VCardItem>
</vCard>
</vDialog>
<!-- TMDB ID搜索框 -->
<vDialog
v-model="tmdbSelectorDialog"
width="40rem"
scrollable
>
<TmdbSelectorCard
v-model="transferForm.tmdbid"
@close="tmdbSelectorDialog = false"
/>
</vDialog>
</template>
<style lang="scss" scoped>

View File

@@ -180,9 +180,3 @@ const sortIcon = computed(() => {
</VDialog>
</VToolbar>
</template>
<style lang="scss" scoped>
.v-toolbar{
background: rgb(var(--v-table-header-background));
}
</style>

View File

@@ -35,6 +35,7 @@ function init() {
name: 'root',
children: [],
size: 0,
modify_time: 0,
}]
}

View File

@@ -11,6 +11,8 @@ const props = defineProps({
const slideview_content = ref()
// 分页切换状态
const disabled = ref(0)
// 记录滚动值
const slideview_scrollLeft = ref(0)
// 所有卡片数量
let slide_card_length: number
// 卡片间距
@@ -58,6 +60,7 @@ function countMaxNumber() {
// 修改分页切换按钮状态
function countDisabled() {
slideview_scrollLeft.value = slideview_content.value.scrollLeft
card_current = slideview_content.value.scrollLeft === 0 ? 0 : Math.trunc((slideview_content.value.scrollLeft + card_width / 2) / card_width)
if (slide_card_length * card_width <= slideview_content.value.clientWidth)
disabled.value = 3
@@ -81,6 +84,12 @@ onUnmounted(() => {
// 卸载事件
window.removeEventListener('resize', countMaxNumber)
})
onActivated(() => {
if (slideview_scrollLeft.value !== 0) {
// console.log(`onActivated: to_scrollLeft, ${slideview_scrollLeft.value}`)
slideview_content.value.scrollLeft = slideview_scrollLeft.value
}
})
</script>
<template>

View File

@@ -106,14 +106,6 @@ const superUser = store.state.auth.superUser
to: '/subscribe-tv',
}"
/>
<VerticalNavLink
v-if="superUser"
:item="{
title: '自定义',
icon: 'mdi-rss',
to: '/subscribe-rss',
}"
/>
<VerticalNavLink
:item="{
title: '日历',

View File

@@ -132,9 +132,9 @@ const ruleTestDialog = ref(false)
<VIcon icon="mdi-filter-cog-outline" />
</VAvatar>
<h6 class="text-base font-weight-medium mt-2 mb-0">
规则
优先级
</h6>
<span class="text-sm">过滤规则测试</span>
<span class="text-sm">优先级规则测试</span>
</VListItem>
</VCol>
</VRow>
@@ -184,7 +184,7 @@ const ruleTestDialog = ref(false)
max-width="50rem"
scrollable
>
<VCard title="过滤规则测试">
<VCard title="优先级测试">
<DialogCloseBtn @click="ruleTestDialog = false" />
<VCardText>
<RuleTestView />

View File

@@ -172,7 +172,7 @@ const avatar = store.state.auth.avatar
<!-- 重启进度框 -->
<vDialog
v-model="progressDialog"
width="400"
width="25rem"
>
<vCard
color="primary"

View File

@@ -6,14 +6,14 @@ import DefaultLayoutWithVerticalNav from './components/DefaultLayoutWithVertical
<DefaultLayoutWithVerticalNav>
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" v-if="$route.meta.keepAlive" :key="$route.name" />
<component :is="Component" v-if="$route.meta.keepAlive" :key="$route.fullPath" />
</keep-alive>
<component :is="Component" v-if="!$route.meta.keepAlive" :key="$route.name" />
<component :is="Component" v-if="!$route.meta.keepAlive" :key="$route.fullPath" />
</router-view>
</DefaultLayoutWithVerticalNav>
</template>
<style lang="scss">
// As we are using `layouts` plugin we need its styles to be imported
@use '@layouts/styles/default-layout';
@use "@layouts/styles/default-layout";
</style>

View File

@@ -2,7 +2,6 @@ import { createApp } from 'vue'
import '@/@iconify/icons-bundle'
import ToastPlugin from 'vue-toast-notification'
import VuetifyUseDialog from 'vuetify-use-dialog'
import { configureNProgress, doneNProgress, startNProgress } from '@/api/nprogress'
import App from '@/App.vue'
import vuetify from '@/plugins/vuetify'
import { loadFonts } from '@/plugins/webfontloader'
@@ -12,53 +11,26 @@ import '@core/scss/template/index.scss'
import '@layouts/styles/index.scss'
import '@styles/styles.scss'
import 'vue-toast-notification/dist/theme-default.css'
import { removeEl } from '@/util'
loadFonts()
// Nprogress
configureNProgress()
// Create vue app
const app = createApp(App)
// Use plugins Mount vue app
app.use(vuetify)
app
.use(vuetify)
.use(router)
.use(store)
.use(ToastPlugin, {
position: 'bottom-right',
})
.use(VuetifyUseDialog).mount('#app')
.use(VuetifyUseDialog)
.mount('#app')
// 记录和恢复滚动位置
const scrollPositions: { [key: string]: number } = {} // 用于存储每个路由的滚动位置
// 路由导航守卫
router.beforeEach((to, from, next) => {
const isAuthenticated = store.state.auth.token !== null
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login')
}
else {
// 只有 meta 中 keepAlive 为 true 的情况下才记录滚动位置
if (from.meta.keepAlive)
scrollPositions[from.fullPath] = window.scrollY
startNProgress()
next()
}
})
router.afterEach((to) => {
// 只有 meta 中 keepAlive 为 true 的情况下才恢复滚动位置
if (to.meta.keepAlive) {
const savedPosition = scrollPositions[to.fullPath]
if (savedPosition !== undefined) {
setTimeout(() => {
window.scrollTo(0, savedPosition)
}, 0)
}
}
doneNProgress()
})
// 小屏幕下1s后移除loading
if (window.innerWidth < 1024)
setTimeout(() => removeEl('#loading-bg'), 1000)
else
removeEl('#loading-bg')

View File

@@ -130,7 +130,7 @@ onMounted(() => {
<VCardItem class="justify-center mb-7">
<template #prepend>
<div class="d-flex pe-0">
<VImg :src="logo" width="64" />
<VImg :src="logo" width="64" height="64" />
</div>
</template>

View File

@@ -6,6 +6,8 @@ import AccountSettingRule from '@/views/setting/AccountSettingRule.vue'
import AccountSettingSite from '@/views/setting/AccountSettingSite.vue'
import AccountSettingWords from '@/views/setting/AccountSettingWords.vue'
import AccountSettingAbout from '@/views/setting/AccountSettingAbout.vue'
import AccountSettingSearch from '@/views/setting/AccountSettingSearch.vue'
import AccountSettingSubscribe from '@/views/setting/AccountSettingSubscribe.vue'
const route = useRoute()
@@ -23,6 +25,16 @@ const tabs = [
icon: 'mdi-web',
tab: 'site',
},
{
title: '搜索',
icon: 'mdi-magnify',
tab: 'search',
},
{
title: '订阅',
icon: 'mdi-rss',
tab: 'subscribe',
},
{
title: '规则',
icon: 'mdi-filter-cog',
@@ -64,13 +76,27 @@ const tabs = [
</transition>
</VWindowItem>
<!-- System -->
<!-- 用户 -->
<VWindowItem value="site">
<transition name="fade-slide" appear>
<AccountSettingSite />
</transition>
</VWindowItem>
<!-- 搜索 -->
<VWindowItem value="search">
<transition name="fade-slide" appear>
<AccountSettingSearch />
</transition>
</VWindowItem>
<!-- 订阅 -->
<VWindowItem value="subscribe">
<transition name="fade-slide" appear>
<AccountSettingSubscribe />
</transition>
</VWindowItem>
<!-- Notification -->
<VWindowItem value="filter">
<transition name="fade-slide" appear>
@@ -78,19 +104,19 @@ const tabs = [
</transition>
</VWindowItem>
<!-- Notification -->
<!-- 通知 -->
<VWindowItem value="notification">
<transition name="fade-slide" appear>
<AccountSettingNotification />
</transition>
</VWindowItem>
<!-- Words -->
<!-- 词表 -->
<VWindowItem value="words">
<transition name="fade-slide" appear>
<AccountSettingWords />
</transition>
</VWindowItem>
<!-- About -->
<!-- 关于 -->
<VWindowItem value="about">
<transition name="fade-slide" appear>
<AccountSettingAbout />

View File

@@ -1,9 +0,0 @@
<script setup lang="ts">
import RssListView from '@/views/subscribe/RssListView.vue'
</script>
<template>
<div>
<RssListView />
</div>
</template>

View File

@@ -1,9 +1,17 @@
import { createRouter, createWebHistory } from 'vue-router'
import { configureNProgress, doneNProgress, startNProgress } from '@/api/nprogress'
import store from '@/store'
// Nprogress
configureNProgress()
// Router
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
scrollBehavior() {
// 始终滚动到顶部
scrollBehavior(to, from, savedPosition) {
// 如果页面有缓存那么恢复其位置, 否则始终滚动到顶部
if (to.meta.keepAlive && savedPosition)
return savedPosition
return { top: 0 }
},
routes: [
@@ -48,13 +56,6 @@ const router = createRouter({
requiresAuth: true,
},
},
{
path: 'subscribe-rss',
component: () => import('../pages/subscribe-rss.vue'),
meta: {
requiresAuth: true,
},
},
{
path: 'calendar',
component: () => import('../pages/calendar.vue'),
@@ -154,4 +155,21 @@ const router = createRouter({
],
})
// 路由导航守卫
router.beforeEach((to, from, next) => {
const isAuthenticated = store.state.auth.token !== null
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login')
}
else {
startNProgress()
next()
}
})
router.afterEach(() => {
doneNProgress()
})
export default router

View File

@@ -40,7 +40,6 @@
width: 100%;
}
/* router view transition fade-slide */
.fade-slide-leave-active,
.fade-slide-enter-active {
@@ -107,8 +106,10 @@
border-radius: 3px;
background: rgb(var(--v-theme-perfect-scrollbar-thumb));
-webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
&:hover{
background: #a1a1a1;
@media(hover){
&:hover{
background: #a1a1a1;
}
}
}
@@ -122,3 +123,7 @@
-webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)!important;
backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)!important;
}
.v-toolbar{
background: rgb(var(--v-table-header-background));
}

6
src/util/dom.ts Normal file
View File

@@ -0,0 +1,6 @@
export function removeEl(selector: string) {
if (selector) {
const el = document.querySelector(selector)
el?.parentNode?.removeChild(el)
}
}

1
src/util/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './dom'

View File

@@ -5,6 +5,7 @@ import { useConfirm } from 'vuetify-use-dialog'
import { numberValidator, requiredValidator } from '@/@validators'
import api from '@/api'
import type { TransferHistory } from '@/api/types'
import TmdbSelectorCard from '@/components/cards/TmdbSelectorCard.vue'
// 确认框
const createConfirm = useConfirm()
@@ -71,6 +72,9 @@ const progressText = ref('请稍候 ...')
// 进度值
const progressValue = ref(0)
// TMDB选择对话框
const tmdbSelectorDialog = ref(false)
// 获取订阅列表数据
async function fetchData({
page,
@@ -412,6 +416,8 @@ const dropdownItems = ref([
v-model="redoTmdbId"
label="TMDB编号"
:rules="[requiredValidator, numberValidator]"
append-inner-icon="mdi-magnify"
@click:append-inner="tmdbSelectorDialog = true"
/>
</VCol>
</VRow>
@@ -440,7 +446,7 @@ const dropdownItems = ref([
<vDialog
v-model="progressDialog"
:scrim="false"
width="400"
width="25rem"
>
<vCard
color="primary"
@@ -455,6 +461,17 @@ const dropdownItems = ref([
</vCardText>
</vCard>
</vDialog>
<!-- TMDB ID搜索框 -->
<vDialog
v-model="tmdbSelectorDialog"
width="600"
scrollable
>
<TmdbSelectorCard
v-model="redoTmdbId"
@close="tmdbSelectorDialog = false"
/>
</vDialog>
</template>
<style lang="scss">

View File

@@ -408,7 +408,7 @@ onMounted(() => {
<!-- 站点编辑弹窗 -->
<VDialog
v-model="addUserDialog"
max-width="800"
max-width="50rem"
persistent
>
<!-- Dialog Content -->

View File

@@ -1,15 +1,6 @@
<script lang="ts" setup>
import { useToast } from 'vue-toast-notification'
import api from '@/api'
import FilterRuleCard from '@/components/cards/FilterRuleCard.vue'
// 规则卡片类型
interface FilterCard {
// 优先级
pri: string
// 已选规则
rules: string[]
}
// 提示框
const $toast = useToast()
@@ -23,34 +14,11 @@ const TorrentPriorityItems = [
{ title: '做种数优先', value: 'seeder' },
]
// 规则卡片列表
const filterCards = ref<FilterCard[]>([])
// 洗版规则卡片列表
const filterCards2 = ref<FilterCard[]>([])
// 查询已设置过滤规则
async function queryCustomFilters(ruleType: string) {
try {
const result: { [key: string]: any } = await api.get(`system/setting/${ruleType}`)
if (result.success) {
// 保存的是个字符串,需要分割成数组
const groups = result.data?.value?.split('>') ?? []
// 生成规则卡片
const cards = ruleType === 'FilterRules' ? filterCards : filterCards2
cards.value = groups?.map((group: string, index: number) => {
return {
pri: (index + 1).toString(),
rules: group.split('&'),
}
})
}
}
catch (error) {
console.log(error)
}
}
// 包含与排除规则
const defaultIncludeExcludeFilter = ref({
include: '',
exclude: '',
})
// 查询种子优先规则
async function queryTorrentPriority() {
@@ -66,31 +34,14 @@ async function queryTorrentPriority() {
}
}
// 保存用户设置的规则
async function saveCustomFilters(ruleType: string) {
// 查询包含与排除规则
async function queryIncludeExcludeFilter() {
try {
// 有值才处理
let value = ''
if (filterCards.value.length !== 0) {
// 将卡片规则接装为字符串
const cards = ruleType === 'FilterRules' ? filterCards : filterCards2
value = cards.value
.filter(card => card.rules.length > 0)
.map(card => card.rules.join('&'))
.join('>')
}
// 保存
const result: { [key: string]: any } = await api.post(
`system/setting/${ruleType}`,
value,
const result: { [key: string]: any } = await api.get(
'system/setting/DefaultIncludeExcludeFilter',
)
const msg = ruleType === 'FilterRules' ? '过滤规则' : '洗版规则'
if (result.success)
$toast.success(`${msg}保存成功`)
else
$toast.error(`${msg}保存失败!`)
if (result.data?.value)
defaultIncludeExcludeFilter.value = result.data?.value
}
catch (error) {
console.log(error)
@@ -116,135 +67,47 @@ async function saveTorrentPriority() {
}
}
// 更新规则卡片的值
function updateFilterCardValue(pri: string, rules: string[]) {
const card = filterCards.value.find(card => card.pri === pri)
if (card)
card.rules = rules
}
// 更新洗版规则卡片的值
function updateFilterCardValue2(pri: string, rules: string[]) {
const card = filterCards2.value.find(card => card.pri === pri)
if (card)
card.rules = rules
}
// 移除卡片
function filterCardClose(ruleType: string, pri: string) {
// 将pri对应的卡片从列表中删除并更新剩余卡片的序号
const updatedCards = (ruleType === 'FilterRules' ? filterCards.value : filterCards2.value)
.filter(card => card.pri !== pri)
.map((card, index) => {
card.pri = (index + 1).toString()
return card
})
// 更新 filterCards.value
if (ruleType === 'FilterRules')
filterCards.value = updatedCards
else
filterCards2.value = updatedCards
}
// 增加卡片
function addFilterCard(ruleType: string) {
const cards = ruleType === 'FilterRules' ? filterCards : filterCards2
// 优先级
const pri = (cards.value.length + 1).toString()
// 新卡片
const newCard: FilterCard = { pri, rules: [] }
// 添加到列表
cards.value.push(newCard)
// 保存包含与排除规则
async function saveIncludeExcludeFilter() {
try {
const result: { [key: string]: any } = await api.post(
'system/setting/DefaultIncludeExcludeFilter',
defaultIncludeExcludeFilter.value,
)
if (result.success)
$toast.success('默认包含/排除规则保存成功')
else
$toast.error('默认包含/排除规则保存失败!')
}
catch (error) {
console.log(error)
}
}
onMounted(() => {
queryTorrentPriority()
queryCustomFilters('FilterRules')
queryCustomFilters('FilterRules2')
queryIncludeExcludeFilter()
})
</script>
<template>
<VRow>
<VCol cols="12">
<VCard title="过滤规则">
<VCardSubtitle> 设置在搜索和订阅时默认使用的过滤规则 </VCardSubtitle>
<VCardItem>
<div class="grid gap-3 grid-filterrule-card">
<FilterRuleCard
v-for="(card, index) in filterCards"
:key="index"
:pri="card.pri"
:rules="card.rules"
@changed="updateFilterCardValue"
@close="filterCardClose('FilterRules', card.pri)"
/>
</div>
</VCardItem>
<VCardItem>
<VBtn
type="submit"
class="me-2"
@click="saveCustomFilters('FilterRules')"
>
保存
</VBtn>
<VBtn
color="success"
variant="tonal"
@click="addFilterCard('FilterRules')"
>
<VIcon icon="mdi-plus" />
<span class="d-none d-sm-block">增加规则</span>
</VBtn>
</VCardItem>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="洗版规则">
<VCardSubtitle> 设置在订阅洗版时使用的过滤规则匹配优先级1时洗版完成 </VCardSubtitle>
<VCardItem>
<div class="grid gap-3 grid-filterrule-card">
<FilterRuleCard
v-for="(card, index) in filterCards2"
:key="index"
:pri="card.pri"
:rules="card.rules"
@changed="updateFilterCardValue2"
@close="filterCardClose('FilterRules2', card.pri)"
/>
</div>
</VCardItem>
<VCardItem>
<VBtn
type="submit"
class="me-2"
@click="saveCustomFilters('FilterRules2')"
>
保存
</VBtn>
<VBtn
color="success"
variant="tonal"
@click="addFilterCard('FilterRules2')"
>
<VIcon icon="mdi-plus" />
<span class="d-none d-sm-block">增加规则</span>
</VBtn>
</VCardItem>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="下载优先规则">
<VCardSubtitle> 按站点优先级或资源种子数量排序和择优下载 </VCardSubtitle>
<VCardText>
<VSelect
v-model="selectedTorrentPriority"
:items="TorrentPriorityItems"
label="优先规则"
outlined
/>
<VForm>
<VRow>
<VCol cols="12" md="6">
<VSelect
v-model="selectedTorrentPriority"
:items="TorrentPriorityItems"
label="优先规则"
outlined
/>
</VCol>
</VRow>
</vform>
</VCardText>
<VCardItem>
<VBtn
@@ -256,6 +119,39 @@ onMounted(() => {
</VCardItem>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="默认过滤规则">
<VCardSubtitle> 设置在搜索和订阅时默认使用的过滤规则 </VCardSubtitle>
<VCardText>
<VForm>
<VRow>
<VCol cols="12" md="6">
<VTextField
v-model="defaultIncludeExcludeFilter.include"
type="text"
label="包含(关键字、正则式)"
/>
</VCol>
<VCol cols="12" md="6">
<VTextField
v-model="defaultIncludeExcludeFilter.exclude"
type="text"
label="排除(关键字、正则式)"
/>
</VCol>
</VRow>
</VForm>
</VCardText>
<VCardItem>
<VBtn
type="submit"
@click="saveIncludeExcludeFilter"
>
保存
</VBtn>
</VCardItem>
</VCard>
</VCol>
</VRow>
</template>

View File

@@ -0,0 +1,226 @@
<script lang="ts" setup>
import { useToast } from 'vue-toast-notification'
import api from '@/api'
import FilterRuleCard from '@/components/cards/FilterRuleCard.vue'
import type { Site } from '@/api/types'
// 规则卡片类型
interface FilterCard {
// 优先级
pri: string
// 已选规则
rules: string[]
}
// 提示框
const $toast = useToast()
// 规则卡片列表
const filterCards = ref<FilterCard[]>([])
// 所有站点
const allSites = ref<Site[]>([])
// 选中订阅站点
const selectedSites = ref<number[]>([])
// 查询已设置优先级规则
async function queryCustomFilters() {
try {
const result: { [key: string]: any } = await api.get('system/setting/SearchFilterRules')
if (result.success) {
// 保存的是个字符串,需要分割成数组
const groups = result.data?.value?.split('>') ?? []
// 生成规则卡片
filterCards.value = groups?.map((group: string, index: number) => {
return {
pri: (index + 1).toString(),
rules: group.split('&'),
}
})
}
}
catch (error) {
console.log(error)
}
}
// 保存用户设置的规则
async function saveCustomFilters() {
try {
// 有值才处理
let value = ''
if (filterCards.value.length !== 0) {
// 将卡片规则接装为字符串
value = filterCards.value
.filter(card => card.rules.length > 0)
.map(card => card.rules.join('&'))
.join('>')
}
// 保存
const result: { [key: string]: any } = await api.post(
'system/setting/SearchFilterRules',
value,
)
if (result.success)
$toast.success('搜索优先级保存成功')
else
$toast.error('搜索优先级保存失败!')
}
catch (error) {
console.log(error)
}
}
// 更新规则卡片的值
function updateFilterCardValue(pri: string, rules: string[]) {
const card = filterCards.value.find(card => card.pri === pri)
if (card)
card.rules = rules
}
// 移除卡片
function filterCardClose(pri: string) {
// 将pri对应的卡片从列表中删除并更新剩余卡片的序号
const updatedCards = filterCards.value
.filter(card => card.pri !== pri)
.map((card, index) => {
card.pri = (index + 1).toString()
return card
})
// 更新 filterCards.value
filterCards.value = updatedCards
}
// 增加卡片
function addFilterCard() {
// 优先级
const pri = (filterCards.value.length + 1).toString()
// 新卡片
const newCard: FilterCard = { pri, rules: [] }
// 添加到列表
filterCards.value.push(newCard)
}
// 查询所有站点
async function querySites() {
try {
const data: Site[] = await api.get('site')
// 过滤站点,只有启用的站点才显示
allSites.value = data.filter(item => item.is_active)
querySelectedSites()
}
catch (error) {
console.log(error)
}
}
// 查询用户选中的站点
async function querySelectedSites() {
try {
const result: { [key: string]: any } = await api.get('system/setting/IndexerSites')
selectedSites.value = result.data?.value ?? []
}
catch (error) {
console.log(error)
}
}
// 保存用户选中的站点
async function saveSelectedSites() {
try {
// 用户名密码
const result: { [key: string]: any } = await api.post('system/setting/IndexerSites', selectedSites.value)
if (result.success)
$toast.success('搜索站点保存成功')
else
$toast.error('搜索站点保存失败!')
}
catch (error) {
console.log(error)
}
}
onMounted(() => {
queryCustomFilters()
querySites()
})
</script>
<template>
<VRow>
<VCol cols="12">
<VCard title="搜索站点">
<VCardSubtitle> 只有选中的站点才会在搜索中使用</VCardSubtitle>
<VCardItem>
<VChipGroup v-model="selectedSites" column multiple>
<VChip
v-for="site in allSites"
:key="site.id"
:color="selectedSites.includes(site.id) ? 'primary' : ''"
filter
variant="outlined"
:value="site.id"
>
{{ site.name }}
</VChip>
</VChipGroup>
</VCardItem>
<VCardItem>
<VBtn type="submit" @click="saveSelectedSites">
保存
</VBtn>
</VCardItem>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="搜索优先级">
<VCardSubtitle> 设置在搜索时默认使用的优先级排序未在优先级中的资源将不在搜索结果中显示 </VCardSubtitle>
<VCardItem>
<div class="grid gap-3 grid-filterrule-card">
<FilterRuleCard
v-for="(card, index) in filterCards"
:key="index"
:pri="card.pri"
:rules="card.rules"
@changed="updateFilterCardValue"
@close="filterCardClose(card.pri)"
/>
</div>
</VCardItem>
<VCardItem>
<VBtn
type="submit"
class="me-2"
@click="saveCustomFilters()"
>
保存
</VBtn>
<VBtn
color="success"
variant="tonal"
@click="addFilterCard()"
>
<VIcon icon="mdi-plus" />
</VBtn>
</VCardItem>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
.grid-filterrule-card {
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
padding-block-end: 1rem;
}
</style>

View File

@@ -1,19 +1,10 @@
<script lang="ts" setup>
import { useToast } from 'vue-toast-notification'
import api from '@/api'
import type { Site } from '@/api/types'
// 提示框
const $toast = useToast()
// 选中搜索站点
const selectedSites = ref<number[]>([])
// 选中订阅站点
const selectedRssSites = ref<number[]>([])
// 所有站点
const allSites = ref<Site[]>([])
// 站点重置
const isConfirmResetSites = ref(false)
@@ -23,76 +14,6 @@ const resetSitesText = ref('重置站点数据')
// 站点重置按钮可用状态
const resetSitesDisabled = ref(false)
// 查询所有站点
async function querySites() {
try {
const data: Site[] = await api.get('site')
// 过滤站点,只有启用的站点才显示
allSites.value = data.filter(item => item.is_active)
querySelectedSites()
querySelectedRssSites()
}
catch (error) {
console.log(error)
}
}
// 查询用户选中的站点
async function querySelectedSites() {
try {
const result: { [key: string]: any } = await api.get('system/setting/IndexerSites')
selectedSites.value = result.data?.value ?? []
}
catch (error) {
console.log(error)
}
}
// 保存用户选中的站点
async function saveSelectedSites() {
try {
// 用户名密码
const result: { [key: string]: any } = await api.post('system/setting/IndexerSites', selectedSites.value)
if (result.success)
$toast.success('搜索站点保存成功')
else
$toast.error('搜索站点保存失败!')
}
catch (error) {
console.log(error)
}
}
// 查询用户选中的订阅站点
async function querySelectedRssSites() {
try {
const result: { [key: string]: any } = await api.get('system/setting/RssSites')
selectedRssSites.value = result.data?.value ?? []
}
catch (error) {
console.log(error)
}
}
// 保存用户选中的订阅站点
async function saveSelectedRssSites() {
try {
const result: { [key: string]: any } = await api.post('system/setting/RssSites', selectedRssSites.value)
if (result.success)
$toast.success('订阅站点保存成功')
else
$toast.error('订阅站点保存失败!')
}
catch (error) {
console.log(error)
}
}
// 重置站点
async function resetSites() {
try {
@@ -100,13 +21,12 @@ async function resetSites() {
resetSitesText.value = '正在重置...'
const result: { [key: string]: any } = await api.get('site/reset')
if (result.success) {
if (result.success)
$toast.success('站点重置成功请等待CookieCloud同步完成')
querySites()
}
else {
else
$toast.error('站点重置失败!')
}
resetSitesDisabled.value = false
resetSitesText.value = '重置站点数据'
}
@@ -114,66 +34,10 @@ async function resetSites() {
console.log(error)
}
}
onMounted(() => {
querySites()
})
</script>
<template>
<VRow>
<VCol cols="12">
<VCard title="搜索站点">
<VCardSubtitle> 只有选中的站点才会在搜索中使用</VCardSubtitle>
<VCardItem>
<VChipGroup v-model="selectedSites" column multiple>
<VChip
v-for="site in allSites"
:key="site.id"
:color="selectedSites.includes(site.id) ? 'primary' : ''"
filter
variant="outlined"
:value="site.id"
>
{{ site.name }}
</VChip>
</VChipGroup>
</VCardItem>
<VCardItem>
<VBtn type="submit" @click="saveSelectedSites">
保存
</VBtn>
</VCardItem>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="订阅站点">
<VCardSubtitle> 只有选中的站点才会在订阅中使用</VCardSubtitle>
<VCardItem>
<VChipGroup v-model="selectedRssSites" column multiple>
<VChip
v-for="site in allSites"
:key="site.id"
:color="selectedRssSites.includes(site.id) ? 'primary' : ''"
filter
variant="outlined"
:value="site.id"
>
{{ site.name }}
</VChip>
</VChipGroup>
</VCardItem>
<VCardItem>
<VBtn type="submit" @click="saveSelectedRssSites">
保存
</VBtn>
</VCardItem>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="站点重置">
<VCardText>

View File

@@ -0,0 +1,277 @@
<script lang="ts" setup>
import { useToast } from 'vue-toast-notification'
import api from '@/api'
import FilterRuleCard from '@/components/cards/FilterRuleCard.vue'
import type { Site } from '@/api/types'
// 规则卡片类型
interface FilterCard {
// 优先级
pri: string
// 已选规则
rules: string[]
}
// 提示框
const $toast = useToast()
// 订阅规则卡片列表
const subscribeFilterCards = ref<FilterCard[]>([])
// 洗版规则卡片列表
const bestVersionFilterCards = ref<FilterCard[]>([])
// 所有站点
const allSites = ref<Site[]>([])
// 选中订阅站点
const selectedRssSites = ref<number[]>([])
// 查询用户选中的订阅站点
async function querySelectedRssSites() {
try {
const result: { [key: string]: any } = await api.get('system/setting/RssSites')
selectedRssSites.value = result.data?.value ?? []
}
catch (error) {
console.log(error)
}
}
// 保存用户选中的订阅站点
async function saveSelectedRssSites() {
try {
const result: { [key: string]: any } = await api.post('system/setting/RssSites', selectedRssSites.value)
if (result.success)
$toast.success('订阅站点保存成功')
else
$toast.error('订阅站点保存失败!')
}
catch (error) {
console.log(error)
}
}
// 查询所有站点
async function querySites() {
try {
const data: Site[] = await api.get('site')
// 过滤站点,只有启用的站点才显示
allSites.value = data.filter(item => item.is_active)
querySelectedRssSites()
}
catch (error) {
console.log(error)
}
}
// 查询已设置优先级规则
async function queryCustomFilters(ruleType: string) {
try {
const result: { [key: string]: any } = await api.get(`system/setting/${ruleType}`)
if (result.success) {
// 保存的是个字符串,需要分割成数组
const groups = result.data?.value?.split('>') ?? []
// 生成规则卡片
const cards = ruleType === 'SubscribeFilterRules' ? subscribeFilterCards : bestVersionFilterCards
cards.value = groups?.map((group: string, index: number) => {
return {
pri: (index + 1).toString(),
rules: group.split('&'),
}
})
}
}
catch (error) {
console.log(error)
}
}
// 保存用户设置的规则
async function saveCustomFilters(ruleType: string) {
try {
// 有值才处理
let value = ''
const cards = ruleType === 'SubscribeFilterRules' ? subscribeFilterCards : bestVersionFilterCards
if (cards.value.length !== 0) {
// 将卡片规则接装为字符串
value = cards.value
.filter(card => card.rules.length > 0)
.map(card => card.rules.join('&'))
.join('>')
}
// 保存
const result: { [key: string]: any } = await api.post(
`system/setting/${ruleType}`,
value,
)
const msg = ruleType === 'SubscribeFilterRules' ? '订阅优先级' : '洗版优先级'
if (result.success)
$toast.success(`${msg}保存成功`)
else
$toast.error(`${msg}保存失败!`)
}
catch (error) {
console.log(error)
}
}
// 更新规则卡片的值
function updateFilterCardValue(pri: string, rules: string[]) {
const card = subscribeFilterCards.value.find(card => card.pri === pri)
if (card)
card.rules = rules
}
// 更新洗版规则卡片的值
function updateFilterCardValue2(pri: string, rules: string[]) {
const card = bestVersionFilterCards.value.find(card => card.pri === pri)
if (card)
card.rules = rules
}
// 移除卡片
function filterCardClose(ruleType: string, pri: string) {
// 将pri对应的卡片从列表中删除并更新剩余卡片的序号
const updatedCards = (ruleType === 'SubscribeFilterRules' ? subscribeFilterCards.value : bestVersionFilterCards.value)
.filter(card => card.pri !== pri)
.map((card, index) => {
card.pri = (index + 1).toString()
return card
})
// 更新 subscribeFilterCards.value
if (ruleType === 'SubscribeFilterRules')
subscribeFilterCards.value = updatedCards
else
bestVersionFilterCards.value = updatedCards
}
// 增加卡片
function addFilterCard(ruleType: string) {
const cards = ruleType === 'SubscribeFilterRules' ? subscribeFilterCards : bestVersionFilterCards
// 优先级
const pri = (cards.value.length + 1).toString()
// 新卡片
const newCard: FilterCard = { pri, rules: [] }
// 添加到列表
cards.value.push(newCard)
}
onMounted(() => {
querySites()
queryCustomFilters('SubscribeFilterRules')
queryCustomFilters('BestVersionFilterRules')
})
</script>
<template>
<VRow>
<VCol cols="12">
<VCard title="订阅站点">
<VCardSubtitle> 只有选中的站点才会在订阅中使用</VCardSubtitle>
<VCardItem>
<VChipGroup v-model="selectedRssSites" column multiple>
<VChip
v-for="site in allSites"
:key="site.id"
:color="selectedRssSites.includes(site.id) ? 'primary' : ''"
filter
variant="outlined"
:value="site.id"
>
{{ site.name }}
</VChip>
</VChipGroup>
</VCardItem>
<VCardItem>
<VBtn type="submit" @click="saveSelectedRssSites">
保存
</VBtn>
</VCardItem>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="订阅优先级">
<VCardSubtitle> 设置在正常订阅时默认使用的优先级未在优先级中的资源将不会自动下载 </VCardSubtitle>
<VCardItem>
<div class="grid gap-3 grid-filterrule-card">
<FilterRuleCard
v-for="(card, index) in subscribeFilterCards"
:key="index"
:pri="card.pri"
:rules="card.rules"
@changed="updateFilterCardValue"
@close="filterCardClose('SubscribeFilterRules', card.pri)"
/>
</div>
</VCardItem>
<VCardItem>
<VBtn
type="submit"
class="me-2"
@click="saveCustomFilters('SubscribeFilterRules')"
>
保存
</VBtn>
<VBtn
color="success"
variant="tonal"
@click="addFilterCard('SubscribeFilterRules')"
>
<VIcon icon="mdi-plus" />
</VBtn>
</VCardItem>
</VCard>
</VCol>
<VCol cols="12">
<VCard title="洗版优先级">
<VCardSubtitle> 设置在订阅洗版时使用的优先级匹配优先级1时洗版完成 </VCardSubtitle>
<VCardItem>
<div class="grid gap-3 grid-filterrule-card">
<FilterRuleCard
v-for="(card, index) in bestVersionFilterCards"
:key="index"
:pri="card.pri"
:rules="card.rules"
@changed="updateFilterCardValue2"
@close="filterCardClose('BestVersionFilterRules', card.pri)"
/>
</div>
</VCardItem>
<VCardItem>
<VBtn
type="submit"
class="me-2"
@click="saveCustomFilters('BestVersionFilterRules')"
>
保存
</VBtn>
<VBtn
color="success"
variant="tonal"
@click="addFilterCard('BestVersionFilterRules')"
>
<VIcon icon="mdi-plus" />
</VBtn>
</VCardItem>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
.grid-filterrule-card {
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
padding-block-end: 1rem;
}
</style>

View File

@@ -129,10 +129,11 @@ onMounted(() => {
<VTextarea
v-model="customIdentifiers"
auto-grow
placeholder="支持正则表达式,特殊字符需要\转义,一行为一组,支持种配置格式:
placeholder="支持正则表达式,特殊字符需要\转义,一行为一组,支持以下几种配置格式:
屏蔽词
被替换词 => 替换词
前定位词 <> 后定位词 >> 偏移量EP"
前定位词 <> 后定位词 >> 偏移量EP
被替换词 => 替换词 && 前定位词 <> 后定位词 >> 集偏移量EP"
/>
</VCardItem>
<VCardItem>

View File

@@ -135,7 +135,7 @@ onBeforeMount(fetchData)
<!-- Dialog Content -->
<VDialog
v-model="siteAddDialog"
max-width="800"
max-width="50rem"
persistent
scrollable
>
@@ -149,6 +149,7 @@ onBeforeMount(fetchData)
/>
</template>
<VCard title="新增站点">
<DialogCloseBtn @click="siteAddDialog = false" />
<VCardText class="pt-2">
<VForm @submit.prevent="() => {}">
<VRow>
@@ -185,6 +186,12 @@ onBeforeMount(fetchData)
</VCol>
</VRow>
<VRow>
<VCol cols="12">
<VTextField
v-model="siteForm.rss"
label="RSS地址"
/>
</VCol>
<VCol cols="12">
<VTextarea
v-model="siteForm.cookie"

View File

@@ -79,17 +79,7 @@ async function getSubscribes() {
subscribes.map(async sub => eventsHander(sub)),
)
// 自定义订阅
const rsses: Rss[] = await api.get('rss')
const rssEvents = await Promise.all(
rsses.map(async rss => eventsHander(rss)),
)
// 合并事件
const events = [...subEvents, ...rssEvents]
calendarOptions.value.events = events.flat().filter(event => event.start) as EventSourceInput
calendarOptions.value.events = subEvents.flat().filter(event => event.start) as EventSourceInput
}
catch (error) {
console.error(error)

View File

@@ -1,336 +0,0 @@
<script lang="ts" setup>
import PullRefresh from 'pull-refresh-vue3'
import { useToast } from 'vue-toast-notification'
import api from '@/api'
import type { Rss } from '@/api/types'
import NoDataFound from '@/components/NoDataFound.vue'
import RssCard from '@/components/cards/RssCard.vue'
import { numberValidator, requiredValidator } from '@/@validators'
import { doneNProgress, startNProgress } from '@/api/nprogress'
// 提示框
const $toast = useToast()
// 是否刷新过
const isRefreshed = ref(false)
// 新增按钮文本
const addBtnText = ref('新增订阅')
// 新增按钮状态
const addBtnState = ref(false)
// 新增自定义订阅对话框
const rssAddDialog = ref(false)
// 新增订阅表单
const rssForm = reactive({
// RSS地址
url: '',
// 类型
type: '电影',
// 标题
title: '',
// 年份
year: '',
// 季号
season: 1,
// 包含
include: '',
// 排除
exclude: '',
// 洗版
best_version: false,
// 是否使用代理服务器
proxy: false,
// 是否使用过滤规则
filter: true,
// 保存路径
save_path: '',
// 状态 0-停用1-启用
state: 1,
})
// 数据列表
const dataList = ref<Rss[]>([])
// 获取订阅列表数据
async function fetchData() {
try {
dataList.value = await api.get('rss')
isRefreshed.value = true
}
catch (error) {
console.error(error)
}
}
// 调用API 新增自定义订阅
async function addRss() {
if (!rssForm.url || !rssForm.title)
return
startNProgress()
addBtnText.value = '新增中...'
addBtnState.value = true
if (rssForm.type === '电影')
rssForm.season = 0
try {
const result: { [key: string]: string } = await api.post('rss', rssForm)
if (result.success) {
$toast.success('新增自定义订阅成功')
// 刷新数据
fetchData()
}
else { $toast.error(`新增自定义订阅失败:${result.message}`) }
rssAddDialog.value = false
}
catch (error) {
console.error(error)
}
doneNProgress()
addBtnText.value = '新增订阅'
addBtnState.value = false
}
// 生成1到50季的下拉框选项
const seasonItems = ref(
Array.from({ length: 50 }, (_, i) => i + 1).map(item => ({
title: `${item}`,
value: item,
})),
)
// 加载时获取数据
onBeforeMount(fetchData)
// 刷新状态
const loading = ref(false)
// 下拉刷新
function onRefresh() {
loading.value = true
fetchData()
loading.value = false
}
</script>
<template>
<div
v-if="!isRefreshed"
class="mt-12 w-full text-center text-gray-500 text-sm flex flex-col items-center"
>
<VProgressCircular
v-if="!isRefreshed"
size="48"
indeterminate
color="primary"
/>
</div>
<PullRefresh
v-model="loading"
@refresh="onRefresh"
>
<div
v-if="dataList.length > 0"
class="grid gap-3 grid-rss-card p-1"
>
<RssCard
v-for="data in dataList"
:key="data.id"
:media="data"
@remove="fetchData"
@save="fetchData"
/>
</div>
<NoDataFound
v-if="dataList.length === 0 && isRefreshed"
error-code="404"
error-title="没有自定义订阅"
error-description="点击右下角按钮新增订阅"
/>
</PullRefresh>
<!-- 新增订阅 -->
<VDialog
v-model="rssAddDialog"
max-width="800"
persistent
scrollable
>
<!-- Dialog Activator -->
<template #activator="{ props }">
<VBtn
icon="mdi-plus"
v-bind="props"
size="x-large"
class="fixed right-5 bottom-5"
/>
</template>
<!-- Dialog Content -->
<VCard title="新增自定义订阅">
<DialogCloseBtn @click="rssAddDialog = false" />
<VCardText class="pt-2">
<VForm @submit.prevent="() => {}">
<VRow>
<VCol
cols="12"
>
<VTextField
v-model="rssForm.url"
label="RSS地址"
placeholder="https://example.com/rss"
:rules="[requiredValidator]"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VSelect
v-model="rssForm.type"
label="类型"
:items="[{ title: '电影', value: '电影' }, { title: '电视剧', value: '电视剧' }]"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="rssForm.title"
label="标题"
:rules="[requiredValidator]"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="rssForm.year"
label="年份"
:rules="[numberValidator]"
/>
</VCol>
<VCol
v-if="rssForm.type === '电视剧'"
cols="12"
md="6"
>
<VSelect
v-model="rssForm.season"
label="季"
:items="seasonItems"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="rssForm.include"
label="包含"
placeholder="支持正则表达式"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="rssForm.exclude"
label="排除"
placeholder="支持正则表达式"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="rssForm.save_path"
label="保存路径"
placeholder="留空自动"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VSelect
v-model="rssForm.state"
label="状态"
:items="[{
title: '启用',
value: 1,
}, {
title: '停用',
value: 0,
}]"
/>
</VCol>
</VRow>
<VRow>
<VCol
cols="12"
md="4"
>
<VSwitch
v-model="rssForm.best_version"
label="洗版"
/>
</VCol>
<VCol
cols="12"
md="4"
>
<VSwitch
v-model="rssForm.proxy"
label="代理服务器"
/>
</VCol>
<VCol
cols="12"
md="4"
>
<VSwitch
v-model="rssForm.filter"
label="过滤规则"
/>
</VCol>
</VRow>
</VForm>
</VCardText>
<VCardActions>
<VBtn
@click="rssAddDialog = false"
>
取消
</VBtn>
<VSpacer />
<VBtn
color="primary"
:disabled="addBtnState"
@click="addRss"
>
{{ addBtnText }}
</VBtn>
</VCardActions>
</VCard>
</VDialog>
</template>
<style lang="scss">
.grid-rss-card {
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
padding-block-end: 1rem;
}
</style>

View File

@@ -69,11 +69,14 @@ async function ruleTest() {
v-model="ruleTestForm.ruletype"
label="规则类型"
:items="[{
title: '默认规则',
title: '订阅优先级',
value: '1',
}, {
title: '洗版规则',
title: '洗版优先级',
value: '2',
}, {
title: '搜索优先级',
value: '3',
}]"
/>
</VCol>

View File

@@ -4,6 +4,9 @@ module.exports = {
theme: {
extend: {},
},
future: {
hoverOnlyWhenSupported: true,
},
corePlugins: {
aspectRatio: false,
},