修复了在屏幕边缘不能滚动的bug
添加双端逻辑 将导航栏标签居中 修改页面选择栏 新增编辑器表格样式 新增用户管理 更换新logo
This commit is contained in:
parent
d8a524eca2
commit
2c367ad0ba
@ -2,9 +2,9 @@
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>CYBER-1</title>
|
||||
<title>CYBER</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
20
public/images/logo.svg
Normal file
20
public/images/logo.svg
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="318px" height="233px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g><path style="opacity:0.913" fill="#000000" d="M 44.5,-0.5 C 47.1667,-0.5 49.8333,-0.5 52.5,-0.5C 58.4261,2.69925 60.5928,7.69925 59,14.5C 55.1566,21.1145 49.6566,22.9479 42.5,20C 36.8842,14.9145 36.0509,9.08115 40,2.5C 41.6516,1.60064 43.1516,0.60064 44.5,-0.5 Z"/></g>
|
||||
<g><path style="opacity:0.913" fill="#000000" d="M 264.5,-0.5 C 267.167,-0.5 269.833,-0.5 272.5,-0.5C 278.426,2.69925 280.593,7.69925 279,14.5C 275.157,21.1145 269.657,22.9479 262.5,20C 256.884,14.9145 256.051,9.08115 260,2.5C 261.652,1.60064 263.152,0.60064 264.5,-0.5 Z"/></g>
|
||||
<g><path style="opacity:0.928" fill="#000000" d="M 65.5,2.5 C 93.8333,2.5 122.167,2.5 150.5,2.5C 150.5,7.5 150.5,12.5 150.5,17.5C 122.167,17.5 93.8333,17.5 65.5,17.5C 65.5,12.5 65.5,7.5 65.5,2.5 Z"/></g>
|
||||
<g><path style="opacity:0.928" fill="#000000" d="M 166.5,2.5 C 194.833,2.5 223.167,2.5 251.5,2.5C 251.5,7.5 251.5,12.5 251.5,17.5C 223.167,17.5 194.833,17.5 166.5,17.5C 166.5,12.5 166.5,7.5 166.5,2.5 Z"/></g>
|
||||
<g><path style="opacity:0.992" fill="#000000" d="M 41.5,26.5 C 46.1667,26.5 50.8333,26.5 55.5,26.5C 55.5,86.1667 55.5,145.833 55.5,205.5C 50.8333,205.5 46.1667,205.5 41.5,205.5C 41.5,145.833 41.5,86.1667 41.5,26.5 Z"/></g>
|
||||
<g><path style="opacity:0.992" fill="#000000" d="M 261.5,26.5 C 266.167,26.5 270.833,26.5 275.5,26.5C 275.5,86.1667 275.5,145.833 275.5,205.5C 270.833,205.5 266.167,205.5 261.5,205.5C 261.5,145.833 261.5,86.1667 261.5,26.5 Z"/></g>
|
||||
<g><path style="opacity:0.935" fill="#000000" d="M 91.5,35.5 C 105.24,35.0542 118.907,35.5542 132.5,37C 140.171,41.1738 143.505,47.6738 142.5,56.5C 138.167,56.5 133.833,56.5 129.5,56.5C 129.919,53.6755 128.919,51.5088 126.5,50C 115.833,49.3333 105.167,49.3333 94.5,50C 91.8554,51.2865 90.8554,53.4532 91.5,56.5C 86.8333,56.5 82.1667,56.5 77.5,56.5C 76.9437,45.93 81.6103,38.93 91.5,35.5 Z"/></g>
|
||||
<g><path style="opacity:0.938" fill="#000000" d="M 173.5,38.5 C 178.167,38.5 182.833,38.5 187.5,38.5C 187.334,47.1731 187.5,55.8397 188,64.5C 188.75,65.1258 189.584,65.6258 190.5,66C 201.167,66.6667 211.833,66.6667 222.5,66C 223.333,65.1667 224.167,64.3333 225,63.5C 225.5,55.1733 225.666,46.84 225.5,38.5C 229.833,38.5 234.167,38.5 238.5,38.5C 238.666,48.5056 238.5,58.5056 238,68.5C 235.886,72.948 232.719,76.448 228.5,79C 214.889,80.593 201.223,80.9264 187.5,80C 180.763,78.2639 176.263,74.0972 174,67.5C 173.5,57.8391 173.334,48.1724 173.5,38.5 Z"/></g>
|
||||
<g><path style="opacity:0.935" fill="#000000" d="M 77.5,63.5 C 82.1667,63.5 86.8333,63.5 91.5,63.5C 91.3336,71.5069 91.5003,79.5069 92,87.5C 92.5,88 93,88.5 93.5,89C 104.833,89.6667 116.167,89.6667 127.5,89C 128.861,87.4157 129.528,85.5824 129.5,83.5C 133.833,83.5 138.167,83.5 142.5,83.5C 143.624,92.7555 139.958,99.2555 131.5,103C 117.5,103.667 103.5,103.667 89.5,103C 83.2792,101.077 79.4458,96.9099 78,90.5C 77.5002,81.5062 77.3336,72.5062 77.5,63.5 Z"/></g>
|
||||
<g><path style="opacity:0.897" fill="#000000" d="M 199.5,84.5 C 204.167,84.5 208.833,84.5 213.5,84.5C 213.5,91.8333 213.5,99.1667 213.5,106.5C 208.833,106.5 204.167,106.5 199.5,106.5C 199.5,99.1667 199.5,91.8333 199.5,84.5 Z"/></g>
|
||||
<g><path style="opacity:0.95" fill="#000000" d="M 82.5,126.5 C 96.8372,126.333 111.171,126.5 125.5,127C 134.959,130.381 139.625,137.214 139.5,147.5C 139.289,149.686 138.956,151.853 138.5,154C 141.675,157.519 144.508,161.352 147,165.5C 147.667,171.5 147.667,177.5 147,183.5C 144.833,188.333 141.333,191.833 136.5,194C 118.503,194.5 100.503,194.667 82.5,194.5C 82.5,181.167 82.5,167.833 82.5,154.5C 95.5043,154.667 108.504,154.5 121.5,154C 125.212,151.487 126.378,147.987 125,143.5C 124.167,142.667 123.333,141.833 122.5,141C 109.171,140.5 95.8375,140.333 82.5,140.5C 82.5,135.833 82.5,131.167 82.5,126.5 Z M 96.5,168.5 C 108.505,168.333 120.505,168.5 132.5,169C 135.167,172.667 135.167,176.333 132.5,180C 120.505,180.5 108.505,180.667 96.5,180.5C 96.5,176.5 96.5,172.5 96.5,168.5 Z"/></g>
|
||||
<g><path style="opacity:0.95" fill="#000000" d="M 175.5,127.5 C 192.837,127.333 210.17,127.5 227.5,128C 233.87,129.436 238.037,133.269 240,139.5C 240.667,145.5 240.667,151.5 240,157.5C 236.667,165.167 230.833,169.333 222.5,170C 229.5,178.333 236.5,186.667 243.5,195C 237.167,195.667 230.833,195.667 224.5,195C 217.833,186.667 211.167,178.333 204.5,170C 199.511,169.501 194.511,169.334 189.5,169.5C 189.5,178.167 189.5,186.833 189.5,195.5C 184.833,195.5 180.167,195.5 175.5,195.5C 175.5,182.167 175.5,168.833 175.5,155.5C 191.503,155.667 207.503,155.5 223.5,155C 226.683,153.982 228.016,151.815 227.5,148.5C 227.881,145.675 226.881,143.509 224.5,142C 208.17,141.5 191.837,141.333 175.5,141.5C 175.5,136.833 175.5,132.167 175.5,127.5 Z"/></g>
|
||||
<g><path style="opacity:0.912" fill="#000000" d="M 52.5,232.5 C 49.8333,232.5 47.1667,232.5 44.5,232.5C 38.5739,229.301 36.4072,224.301 38,217.5C 41.8434,210.885 47.3434,209.052 54.5,212C 60.1158,217.086 60.9491,222.919 57,229.5C 55.3484,230.399 53.8484,231.399 52.5,232.5 Z"/></g>
|
||||
<g><path style="opacity:0.912" fill="#000000" d="M 272.5,232.5 C 269.833,232.5 267.167,232.5 264.5,232.5C 258.574,229.301 256.407,224.301 258,217.5C 261.843,210.885 267.343,209.052 274.5,212C 280.116,217.086 280.949,222.919 277,229.5C 275.348,230.399 273.848,231.399 272.5,232.5 Z"/></g>
|
||||
<g><path style="opacity:0.928" fill="#000000" d="M 65.5,214.5 C 93.8333,214.5 122.167,214.5 150.5,214.5C 150.5,219.5 150.5,224.5 150.5,229.5C 122.167,229.5 93.8333,229.5 65.5,229.5C 65.5,224.5 65.5,219.5 65.5,214.5 Z"/></g>
|
||||
<g><path style="opacity:0.928" fill="#000000" d="M 166.5,214.5 C 194.833,214.5 223.167,214.5 251.5,214.5C 251.5,219.5 251.5,224.5 251.5,229.5C 223.167,229.5 194.833,229.5 166.5,229.5C 166.5,224.5 166.5,219.5 166.5,214.5 Z"/></g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.9 KiB |
31
public/logo.svg
Normal file
31
public/logo.svg
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="296px" height="295px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g><path style="opacity:0.998" fill="#f2f2f2" d="M 31.5,14.5 C 108.834,14.3333 186.167,14.5 263.5,15C 271.667,17.8333 277.167,23.3333 280,31.5C 280.667,108.833 280.667,186.167 280,263.5C 277.167,271.667 271.667,277.167 263.5,280C 186.167,280.667 108.833,280.667 31.5,280C 23.3333,277.167 17.8333,271.667 15,263.5C 14.3333,186.167 14.3333,108.833 15,31.5C 18.0191,23.3156 23.5191,17.649 31.5,14.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#080808" d="M 33.5,30.5 C 45.146,29.9709 49.646,35.3043 47,46.5C 40.5926,53.1063 34.2592,53.1063 28,46.5C 26.0165,39.6603 27.8498,34.3269 33.5,30.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#080808" d="M 253.5,30.5 C 264.983,29.8221 269.483,35.1554 267,46.5C 263.212,51.1532 258.378,52.6532 252.5,51C 244.402,43.7564 244.735,36.9231 253.5,30.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#afafaf" d="M 54.5,34.5 C 82.6618,33.502 110.995,33.1687 139.5,33.5C 139.821,38.6946 139.487,43.6946 138.5,48.5C 138.5,43.8333 138.5,39.1667 138.5,34.5C 110.5,34.5 82.5,34.5 54.5,34.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#afafaf" d="M 240.5,34.5 C 212.5,34.5 184.5,34.5 156.5,34.5C 156.5,39.1667 156.5,43.8333 156.5,48.5C 155.513,43.6946 155.179,38.6946 155.5,33.5C 184.005,33.1687 212.338,33.502 240.5,34.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#060606" d="M 54.5,34.5 C 82.5,34.5 110.5,34.5 138.5,34.5C 138.5,39.1667 138.5,43.8333 138.5,48.5C 110.5,48.5 82.5,48.5 54.5,48.5C 54.5,43.8333 54.5,39.1667 54.5,34.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#060606" d="M 240.5,34.5 C 240.5,39.1667 240.5,43.8333 240.5,48.5C 212.5,48.5 184.5,48.5 156.5,48.5C 156.5,43.8333 156.5,39.1667 156.5,34.5C 184.5,34.5 212.5,34.5 240.5,34.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#000000" d="M 30.5,58.5 C 35.1667,58.5 39.8333,58.5 44.5,58.5C 44.5,117.5 44.5,176.5 44.5,235.5C 39.8333,235.5 35.1667,235.5 30.5,235.5C 30.5,176.5 30.5,117.5 30.5,58.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#000000" d="M 250.5,58.5 C 255.167,58.5 259.833,58.5 264.5,58.5C 264.5,117.5 264.5,176.5 264.5,235.5C 259.833,235.5 255.167,235.5 250.5,235.5C 250.5,176.5 250.5,117.5 250.5,58.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#020202" d="M 79.5,86.5 C 75.5,86.5 71.5,86.5 67.5,86.5C 67.5,84.5 67.5,82.5 67.5,80.5C 69.1363,75.0248 72.4696,70.8581 77.5,68C 91.5,67.3333 105.5,67.3333 119.5,68C 127.688,71.3709 131.688,77.5375 131.5,86.5C 127.167,86.5 122.833,86.5 118.5,86.5C 118.822,84.2776 118.155,82.4443 116.5,81C 104.833,80.3333 93.1667,80.3333 81.5,81C 80.1387,82.5843 79.472,84.4176 79.5,86.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#020202" d="M 162.5,70.5 C 166.833,70.5 171.167,70.5 175.5,70.5C 175.174,78.6841 175.508,86.6841 176.5,94.5C 176.414,95.4959 176.748,96.3292 177.5,97C 189.167,97.6667 200.833,97.6667 212.5,97C 213.823,96.1841 214.489,95.0174 214.5,93.5C 215.492,86.0182 215.825,78.3516 215.5,70.5C 219.5,70.5 223.5,70.5 227.5,70.5C 227.666,79.1731 227.5,87.8397 227,96.5C 225.909,103.848 221.743,108.348 214.5,110C 201.167,110.667 187.833,110.667 174.5,110C 168.912,107.414 165.079,103.247 163,97.5C 162.5,88.5062 162.334,79.5062 162.5,70.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#a7a7a7" d="M 162.5,70.5 C 166.97,69.5139 171.637,69.1805 176.5,69.5C 176.5,77.8333 176.5,86.1667 176.5,94.5C 175.508,86.6841 175.174,78.6841 175.5,70.5C 171.167,70.5 166.833,70.5 162.5,70.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#4b4b4b" d="M 227.5,70.5 C 223.5,70.5 219.5,70.5 215.5,70.5C 215.825,78.3516 215.492,86.0182 214.5,93.5C 214.5,85.5 214.5,77.5 214.5,69.5C 219.032,69.1818 223.366,69.5151 227.5,70.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#717171" d="M 67.5,80.5 C 67.5,82.5 67.5,84.5 67.5,86.5C 71.5,86.5 75.5,86.5 79.5,86.5C 75.3656,87.4849 71.0323,87.8182 66.5,87.5C 66.1984,84.9407 66.5318,82.6074 67.5,80.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#050505" d="M 66.5,94.5 C 70.8333,94.5 75.1667,94.5 79.5,94.5C 79.3336,102.507 79.5003,110.507 80,118.5C 80.8333,119.333 81.6667,120.167 82.5,121C 93.8333,121.667 105.167,121.667 116.5,121C 118.229,119.211 118.896,117.044 118.5,114.5C 122.833,114.5 127.167,114.5 131.5,114.5C 132.227,122.879 128.894,129.046 121.5,133C 107.569,134.614 93.5691,134.947 79.5,134C 72.9299,131.762 68.7633,127.262 67,120.5C 66.5002,111.84 66.3336,103.173 66.5,94.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#060606" d="M 188.5,116.5 C 192.833,116.5 197.167,116.5 201.5,116.5C 201.5,123.5 201.5,130.5 201.5,137.5C 197.167,137.5 192.833,137.5 188.5,137.5C 188.5,130.5 188.5,123.5 188.5,116.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#030303" d="M 71.5,158.5 C 86.1705,158.333 100.837,158.5 115.5,159C 121.307,160.807 125.141,164.64 127,170.5C 127.759,175.707 127.592,180.874 126.5,186C 136.945,194.434 139.445,204.934 134,217.5C 131.313,221.194 127.813,223.694 123.5,225C 106.17,225.5 88.8365,225.667 71.5,225.5C 71.5,212.167 71.5,198.833 71.5,185.5C 85.1707,185.667 98.8374,185.5 112.5,185C 114.045,183.178 114.712,181.011 114.5,178.5C 114.712,175.989 114.045,173.822 112.5,172C 98.8374,171.5 85.1707,171.333 71.5,171.5C 71.5,167.167 71.5,162.833 71.5,158.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#030303" d="M 193.5,200.5 C 188.36,199.512 183.026,199.179 177.5,199.5C 177.5,208.5 177.5,217.5 177.5,226.5C 173.167,226.5 168.833,226.5 164.5,226.5C 164.5,213.167 164.5,199.833 164.5,186.5C 181.17,186.667 197.837,186.5 214.5,186C 216.045,184.178 216.712,182.011 216.5,179.5C 216.712,176.989 216.045,174.822 214.5,173C 197.837,172.5 181.17,172.333 164.5,172.5C 164.5,168.167 164.5,163.833 164.5,159.5C 182.17,159.333 199.836,159.5 217.5,160C 223.307,161.807 227.141,165.64 229,171.5C 230.947,180.633 229.113,188.799 223.5,196C 219.406,198.198 215.073,199.698 210.5,200.5C 217.381,209.092 224.381,217.592 231.5,226C 225.833,226.667 220.167,226.667 214.5,226C 207.288,217.626 200.288,209.126 193.5,200.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#f3f3f3" d="M 120.5,199.5 C 121.496,199.414 122.329,199.748 123,200.5C 123.667,203.5 123.667,206.5 123,209.5C 122.583,210.756 121.75,211.423 120.5,211.5C 108.833,211.5 97.1667,211.5 85.5,211.5C 85.5,207.5 85.5,203.5 85.5,199.5C 97.1667,199.5 108.833,199.5 120.5,199.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#b4b4b4" d="M 193.5,200.5 C 188.5,200.5 183.5,200.5 178.5,200.5C 178.826,209.349 178.493,218.016 177.5,226.5C 177.5,217.5 177.5,208.5 177.5,199.5C 183.026,199.179 188.36,199.512 193.5,200.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#b4b4b4" d="M 120.5,199.5 C 108.833,199.5 97.1667,199.5 85.5,199.5C 85.5,203.5 85.5,207.5 85.5,211.5C 97.1667,211.5 108.833,211.5 120.5,211.5C 108.678,212.495 96.6783,212.828 84.5,212.5C 84.5,207.833 84.5,203.167 84.5,198.5C 96.6783,198.172 108.678,198.505 120.5,199.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#070707" d="M 32.5,242.5 C 42.84,241.023 48.0066,245.356 48,255.5C 43.2648,264.345 36.9315,265.679 29,259.5C 26.1189,252.866 27.2856,247.199 32.5,242.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#070707" d="M 252.5,242.5 C 266.944,242.09 270.944,248.257 264.5,261C 256.564,265.635 250.73,263.802 247,255.5C 246.333,253.5 246.333,251.5 247,249.5C 248.666,247.004 250.5,244.671 252.5,242.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#434343" d="M 54.5,246.5 C 82.6618,245.502 110.995,245.169 139.5,245.5C 139.819,250.363 139.486,255.03 138.5,259.5C 138.5,255.167 138.5,250.833 138.5,246.5C 110.5,246.5 82.5,246.5 54.5,246.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#434343" d="M 240.5,246.5 C 212.5,246.5 184.5,246.5 156.5,246.5C 156.5,250.833 156.5,255.167 156.5,259.5C 155.514,255.03 155.181,250.363 155.5,245.5C 184.005,245.169 212.338,245.502 240.5,246.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#000000" d="M 54.5,246.5 C 82.5,246.5 110.5,246.5 138.5,246.5C 138.5,250.833 138.5,255.167 138.5,259.5C 110.5,259.5 82.5,259.5 54.5,259.5C 54.5,255.167 54.5,250.833 54.5,246.5 Z"/></g>
|
||||
<g><path style="opacity:1" fill="#000000" d="M 240.5,246.5 C 240.5,250.833 240.5,255.167 240.5,259.5C 212.5,259.5 184.5,259.5 156.5,259.5C 156.5,255.167 156.5,250.833 156.5,246.5C 184.5,246.5 212.5,246.5 240.5,246.5 Z"/></g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.2 KiB |
20
src/App.vue
20
src/App.vue
@ -1,12 +1,10 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<NavBar/>
|
||||
<div style="margin-top: 60px">
|
||||
<div class="main-container">
|
||||
<router-view />
|
||||
</div>
|
||||
|
||||
<LoadingSpinner v-if="store.state.loading.isLoading"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -49,3 +47,19 @@ watch(
|
||||
}
|
||||
)
|
||||
</script>
|
||||
<style>
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-content: center;
|
||||
}
|
||||
.main-container {
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
margin-top: 60px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
}
|
||||
</style>
|
@ -2,6 +2,7 @@
|
||||
<nav :class="[themeClass, 'navbar']">
|
||||
<div class="nav-left">
|
||||
<button class="logo" @click="store.commit('toggleTheme')">
|
||||
<!-- <img alt="LOGO" src="/images/logo.svg">-->
|
||||
CYBER
|
||||
</button>
|
||||
</div>
|
||||
@ -32,7 +33,7 @@
|
||||
</router-link>
|
||||
<router-link v-else to="/account/setting"><div class="user-info">
|
||||
<img :src="store.getters.profileImage" alt="User Avatar" class="avatar" />
|
||||
<span class="username">{{ store.state.userInfo.username }}</span>
|
||||
<span v-if="windowWidth > 550" class="username">{{ store.state.userInfo.username }}</span>
|
||||
</div></router-link>
|
||||
</div>
|
||||
</nav>
|
||||
@ -67,9 +68,11 @@ const toggleMobileMenu = () => {
|
||||
}
|
||||
|
||||
// 判定
|
||||
const isMobile = ref(window.innerWidth < 910)
|
||||
const windowWidth = ref(window.innerWidth);
|
||||
const isMobile = ref(window.innerWidth < 1100)
|
||||
const handleResize = () => {
|
||||
isMobile.value = window.innerWidth < 910
|
||||
windowWidth.value = window.innerWidth;
|
||||
isMobile.value = window.innerWidth < 1100
|
||||
if (!isMobile.value) {
|
||||
showMobileMenu.value = false // 桌面端时关闭移动菜单
|
||||
}
|
||||
@ -126,6 +129,7 @@ const toggleTheme = () => {
|
||||
|
||||
.theme-dark .hamburger-icon,
|
||||
.theme-dark .hamburger-icon::before,
|
||||
.theme-dark .hamburger-icon::before,
|
||||
.theme-dark .hamburger-icon::after {
|
||||
background: #0ff;
|
||||
}
|
||||
@ -194,9 +198,12 @@ const toggleTheme = () => {
|
||||
}
|
||||
|
||||
.nav-center {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.nav-items {
|
||||
|
169
src/components/PagingController.vue
Normal file
169
src/components/PagingController.vue
Normal file
@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="paging-container">
|
||||
<!-- 上一页按钮 -->
|
||||
<button
|
||||
class="page-btn"
|
||||
:disabled="currentPage <= 1 || loading"
|
||||
@click="goPageFunc && goPageFunc(currentPage - 1)"
|
||||
>
|
||||
上一页
|
||||
</button>
|
||||
|
||||
<!-- 页码列表 -->
|
||||
<template v-for="(item, index) in pageNumbers" :key="index">
|
||||
<button
|
||||
v-if="typeof item === 'number'"
|
||||
class="page-btn"
|
||||
:class="{ active: item === currentPage }"
|
||||
@click="goPageFunc && goPageFunc(item)"
|
||||
:disabled="loading"
|
||||
>
|
||||
{{ item }}
|
||||
</button>
|
||||
<span v-else class="page-ellipsis">{{ item }}</span>
|
||||
</template>
|
||||
<button
|
||||
class="page-btn"
|
||||
@click="goPageFunc(amount)"
|
||||
v-if="!pageNumbers.includes(amount)"
|
||||
:disabled="loading"
|
||||
:class="{ active: amount === currentPage }"
|
||||
>
|
||||
{{ amount }}
|
||||
</button>
|
||||
|
||||
<!-- 下一页按钮 -->
|
||||
<button
|
||||
class="page-btn"
|
||||
:disabled="currentPage >= amount || loading"
|
||||
@click="goPageFunc && goPageFunc(currentPage + 1)"
|
||||
>
|
||||
下一页
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
// 定义 props
|
||||
const props = defineProps({
|
||||
loading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
amount: {
|
||||
type: Number,
|
||||
required: true, // 总页数
|
||||
},
|
||||
currentPage: {
|
||||
type: Number,
|
||||
required: true, // 当前页
|
||||
},
|
||||
goPageFunc: {
|
||||
type: Function,
|
||||
required: false, // 点击页码时的回调函数
|
||||
},
|
||||
});
|
||||
|
||||
// 计算显示的页码数组
|
||||
const pageNumbers = computed(() => {
|
||||
const total = props.amount;
|
||||
const current = props.currentPage;
|
||||
const maxDisplay = 5; // 最多显示 10 个页码
|
||||
|
||||
if (total <= 1) return [1]; // 只有一页时只显示 1
|
||||
if (total <= maxDisplay) {
|
||||
// 总页数少于等于 maxDisplay 时,显示所有页码
|
||||
return Array.from({ length: total }, (_, i) => i + 1);
|
||||
}
|
||||
|
||||
const pages = [];
|
||||
const halfDisplay = Math.floor((maxDisplay - 1) / 2); // 留一个位置给最后一页
|
||||
|
||||
let start, end;
|
||||
|
||||
// 如果当前页靠近开头,显示前 maxDisplay-1 页 + 最后一页
|
||||
if (current <= halfDisplay + 1) {
|
||||
start = 1;
|
||||
end = maxDisplay - 1;
|
||||
}
|
||||
// 如果当前页靠近结尾,显示最后 maxDisplay 页
|
||||
else if (current >= total - halfDisplay) {
|
||||
start = total - maxDisplay + 1;
|
||||
end = total;
|
||||
}
|
||||
// 否则,显示当前页附近的页码 + 最后一页
|
||||
else {
|
||||
start = current - halfDisplay;
|
||||
end = current + halfDisplay - 1; // -1 因为要留位置给最后一页
|
||||
}
|
||||
|
||||
// 调整 start 和 end,确保在合法范围内
|
||||
start = Math.max(1, start);
|
||||
end = Math.min(total - 1, end); // end 最多到 total-1,因为 total 会单独添加
|
||||
|
||||
// 如果 start > 1,添加第 1 页和可能的省略号
|
||||
if (start > 1) {
|
||||
pages.push(1);
|
||||
if (start > 2) pages.push('...');
|
||||
}
|
||||
|
||||
// 添加页码
|
||||
for (let i = start; i <= end; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
|
||||
// 如果 end < total - 1,添加省略号
|
||||
if (end < total - 1) {
|
||||
pages.push('...');
|
||||
}
|
||||
|
||||
return pages;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.paging-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.page-btn {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: black;
|
||||
color: cyan;
|
||||
height: 25px;
|
||||
padding: 0 5px;
|
||||
border-radius: 4px;
|
||||
border: cyan solid 1px;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.page-btn:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.theme-light .page-btn {
|
||||
background-color: white;
|
||||
color: black;
|
||||
border: #b2b2b2 solid 1px;
|
||||
}
|
||||
|
||||
.page-btn.active {
|
||||
background: cyan;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.theme-light .page-btn.active {
|
||||
background: #2a2a2a;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
@ -3,19 +3,20 @@ import {onMounted, onUnmounted, ref} from 'vue';
|
||||
import MarkdownViewer from '../components/mdRenderer.vue';
|
||||
import api from "../utils/axios.js";
|
||||
import swal from "../utils/sweetalert.js";
|
||||
import PagingController from "../components/PagingController.vue";
|
||||
|
||||
|
||||
const messages = ref([]);
|
||||
|
||||
const pageLoading = ref(false);
|
||||
const amount = ref(null);
|
||||
const amount = ref(1);
|
||||
const currentPage = ref(1);
|
||||
|
||||
async function refreshLog(page, size) {
|
||||
page = page || 1;
|
||||
size = size || 5;
|
||||
const res = await api.get(`/getweblog?PAGE=${page}&PAGESIZE=${size}`);
|
||||
if (! res.data) {
|
||||
if (!res.data) {
|
||||
swal.tip('error', '加载失败, 刷新重试')
|
||||
return;
|
||||
}
|
||||
@ -47,18 +48,9 @@ onMounted(async () => {
|
||||
<div class="log-container">
|
||||
<div v-if="!amount">正在加载...</div>
|
||||
<div class="log-bar" v-for="log in messages" :class="{loading: pageLoading}">
|
||||
<MarkdownViewer :markdownContent="`### ${log.date} - [${log.version}] - 由${log.poster}提交\n` + log.content" />
|
||||
</div>
|
||||
<div class="paging-container">
|
||||
<div class="page-btn" :class="{active: currentPage === 1}" @click="goPage(1)" v-if="amount">1</div>
|
||||
<span v-if="currentPage > 5">...</span>
|
||||
<div class="page-btn" v-for="index in ((a, b) => Array.from({ length: b - a + 1 }, (_, i) => a + i))
|
||||
(currentPage>4?(currentPage - 3):2, currentPage<amount-4?(currentPage + 4):amount-1)"
|
||||
@click="goPage(index)" :class="{active: currentPage === index}">{{ index }}
|
||||
</div>
|
||||
<span v-if="currentPage < amount - 5">...</span>
|
||||
<div class="page-btn" @click="goPage(amount)" :class="{active: currentPage === amount}" v-if="amount > 1">{{ amount }}</div>
|
||||
<MarkdownViewer :markdownContent="`### ${log.date} - [${log.version}] - 由${log.poster}提交\n` + log.content"/>
|
||||
</div>
|
||||
<paging-controller :current-page="currentPage" :amount="amount" :go-page-func="goPage" :loading="pageLoading"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -68,29 +60,34 @@ onMounted(async () => {
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: calc(100vh - 60px);
|
||||
overflow-y: auto;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.theme-light .container {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.log-container {
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
background: #212121;
|
||||
width: calc(100% - 40px);
|
||||
width: 80%;
|
||||
height: auto;
|
||||
padding: 20px;
|
||||
gap: 15px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.theme-light .log-container {
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
.log-bar {
|
||||
background: black;
|
||||
width: calc(100% - 20px);
|
||||
@ -98,9 +95,11 @@ onMounted(async () => {
|
||||
border-radius: 15px;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.theme-light .log-bar {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.paging-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -172,6 +172,7 @@ const filterBlogs = () => {
|
||||
flex-wrap: wrap; /* 允许标签换行 */
|
||||
gap: 10px;
|
||||
justify-content: center; /* 使标签居中 */
|
||||
padding: 0 200px;
|
||||
}
|
||||
|
||||
.tags span {
|
||||
@ -199,7 +200,8 @@ const filterBlogs = () => {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
width: 100%;
|
||||
width: calc(100% - 400px);
|
||||
padding: 0 200px;
|
||||
}
|
||||
|
||||
.theme-light .tags span.active {
|
||||
|
@ -172,8 +172,8 @@ const handleRegister = async () => {
|
||||
整体页面布局
|
||||
========================================== */
|
||||
.auth-page {
|
||||
width: 100%;
|
||||
min-height: calc(100vh - 60px);
|
||||
margin-top: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -37,9 +37,11 @@ onMounted(() => {
|
||||
<router-link to="/account/draft">草稿箱</router-link>
|
||||
</li>
|
||||
<li v-if="store.getters.isAdmin">
|
||||
<router-link to="/account/upload-log">管理员: 上传日志</router-link>
|
||||
<router-link to="/account/upload-log">管理: 上传日志</router-link>
|
||||
</li>
|
||||
<li v-if="store.getters.isSuperAdmin">
|
||||
<router-link to="/account/user-management">超管: 管理用户</router-link>
|
||||
</li>
|
||||
|
||||
<li v-if="isMobile">
|
||||
<router-link to="/account">用户信息</router-link>
|
||||
</li>
|
||||
@ -65,6 +67,7 @@ onMounted(() => {
|
||||
.account-container {
|
||||
display: flex;
|
||||
flex-wrap: inherit;
|
||||
width: 100%;
|
||||
height: calc(100vh - 60px);
|
||||
overflow-x: hidden;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
@ -103,14 +106,15 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-grow: 1;
|
||||
padding: 20px 0;
|
||||
overflow-y: scroll;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
/* 右侧用户信息部分 */
|
||||
.user-info {
|
||||
flex-grow: 0;
|
||||
width: 280px;
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
@ -136,7 +140,6 @@ onMounted(() => {
|
||||
|
||||
.user-info {
|
||||
background-color: #2e2e2e;
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
margin-left: 0;
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
|
||||
@ -210,9 +213,6 @@ onMounted(() => {
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
width: 100%;
|
||||
|
224
src/pages/accountPages/Account_admin_userManage.vue
Normal file
224
src/pages/accountPages/Account_admin_userManage.vue
Normal file
@ -0,0 +1,224 @@
|
||||
<script setup>
|
||||
|
||||
import PagingController from "../../components/PagingController.vue";
|
||||
import {onMounted, ref} from "vue";
|
||||
import api from "../../utils/axios.js";
|
||||
import store from "../../store/index.js";
|
||||
import swal from "../../utils/sweetalert.js";
|
||||
import {getDomain} from "../../utils/getDomain.js";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
const currentPage = ref(1);
|
||||
const amount = ref(1);
|
||||
const userList = ref([]);
|
||||
const pageLoading = ref(false);
|
||||
|
||||
const refreshList = async (page, pageSize) => {
|
||||
pageSize = pageSize || 10;
|
||||
pageLoading.value = true;
|
||||
try {
|
||||
const response = await api.get(`/getuserlist?PAGE=${page}&PAGESIZE=${pageSize}`);
|
||||
if (! response.data) {
|
||||
return 1;
|
||||
}
|
||||
amount.value = Math.ceil(response.amount / pageSize);
|
||||
userList.value = response.data.map((user) => {return {...user, ...{showEmail: false}}});
|
||||
pageLoading.value = false;
|
||||
return 0;
|
||||
} catch {
|
||||
pageLoading.value = false;
|
||||
swal.tip('error', '加载失败...')
|
||||
}
|
||||
};
|
||||
const goPage = async (page) => {
|
||||
const messageTemp = userList.value
|
||||
currentPage.value = page;
|
||||
if (await refreshList(page) !== 0) {
|
||||
userList.value = messageTemp;
|
||||
}
|
||||
};
|
||||
|
||||
const role2Text = (id) => {
|
||||
switch (id) {
|
||||
case 0:
|
||||
return '普通';
|
||||
case 1:
|
||||
return '已认证';
|
||||
case 2:
|
||||
return '管理员';
|
||||
case 3:
|
||||
return '超管';
|
||||
}
|
||||
};
|
||||
|
||||
const changeRole = (uid) => {
|
||||
Swal.fire({
|
||||
title: '修改账号权限为',
|
||||
html: `
|
||||
<div>
|
||||
<input type="radio" id="option1" name="option" value="0">
|
||||
<label for="option1"></label>普通<br>
|
||||
<input type="radio" id="option2" name="option" value="1">
|
||||
<label for="option1"></label>认证<br>
|
||||
<input type="radio" id="option3" name="option" value="2">
|
||||
<label for="option2"></label>管理<br>
|
||||
</div>
|
||||
`,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '提交',
|
||||
cancelButtonText: '取消',
|
||||
preConfirm: () => {
|
||||
// 获取选中的单选框值
|
||||
const selectedOption = document.querySelector('input[name="option"]:checked');
|
||||
if (!selectedOption) {
|
||||
Swal.showValidationMessage('选一个');
|
||||
return false;
|
||||
}
|
||||
return selectedOption.value; // 返回选中的值
|
||||
}
|
||||
}).then(async (result) => {
|
||||
if (result.isConfirmed) {
|
||||
let response;
|
||||
try {
|
||||
response = await api.put(`/users/${uid}/role`, {
|
||||
role: Number(result.value)
|
||||
})
|
||||
if (response.code === 0) {
|
||||
swal.tip('success', '更改成功');
|
||||
refreshList(currentPage.value);
|
||||
return;
|
||||
}
|
||||
swal.tip('error', '更改失败');
|
||||
} catch (e) {
|
||||
swal.tip('error', '网络连接错误');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
refreshList(1, 10);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div v-if="userList.length === 0">正在加载</div>
|
||||
<div class="user-bar" v-for="user in userList" :class="{loading: pageLoading}">
|
||||
<div class="role-display">{{ role2Text(user.role_id) }}</div>
|
||||
<div class="left">
|
||||
<img :src=" `https://${getDomain()}/data/user/profile/` + user.profile" alt="空">
|
||||
<div class="content">
|
||||
<span class="username">{{ user.username }}</span>
|
||||
<span class="birth">{{ user.birth }}</span>
|
||||
<span class="email" @click="user.showEmail = false" v-if="user.showEmail">{{ user.email }}</span>
|
||||
<span class="email" v-else @click="user.showEmail = true">查看邮箱</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="operating">
|
||||
<div class="change-role" @click="changeRole(user.uid)" v-if="user.role_id !== 3">修改权限</div>
|
||||
</div>
|
||||
</div>
|
||||
<paging-controller v-if="userList.length !== 0" :current-page="currentPage" :amount="amount" :go-page-func="goPage"/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
width: calc(100% - 40px);
|
||||
height: calc(100% - 40px);
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
padding: 20px;
|
||||
gap: 20px;
|
||||
}
|
||||
.container.loading {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.user-bar {
|
||||
position: relative;
|
||||
border-radius: 15px;
|
||||
background: #262626;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
padding: 20px;
|
||||
width: calc(100% - 40px);
|
||||
}
|
||||
.theme-light .user-bar {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
.left img {
|
||||
width: 65px;
|
||||
height: 65px;
|
||||
border-radius: 50%;
|
||||
|
||||
}
|
||||
.left .content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.content .username {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.content .birth, .email {
|
||||
font-size: 14px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.email {
|
||||
cursor: pointer;
|
||||
}
|
||||
.role-display {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
font-size: 70px;
|
||||
text-wrap: none;
|
||||
opacity: 0.08;
|
||||
z-index: 0;
|
||||
}
|
||||
.operating {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.change-role {
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.change-role:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.approve {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
background: #2a2a2a;
|
||||
border: cyan solid 1px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.approve:hover {
|
||||
background: cyan;
|
||||
color: black;
|
||||
}
|
||||
.theme-light .approve {
|
||||
background: white;
|
||||
border: black solid 1px;
|
||||
}
|
||||
|
||||
</style>
|
@ -42,6 +42,7 @@ function createNewDraft() {
|
||||
<style scoped>
|
||||
.container {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -35,6 +35,7 @@ const showProjects = ref(false);
|
||||
<style scoped>
|
||||
.container {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -241,7 +241,7 @@ watch(autoSaveInterval, () => {
|
||||
<style scoped>
|
||||
/* ========== 公共容器区 ========== */
|
||||
.container {
|
||||
//height: calc(100vh - 60px);
|
||||
height: auto;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
|
@ -1,13 +1,83 @@
|
||||
<script setup>
|
||||
import {ref} from 'vue';
|
||||
import {ref, onMounted, onBeforeUnmount} from 'vue'
|
||||
import store from "../../store/index.js";
|
||||
|
||||
const avatarRef = ref(null)
|
||||
const redCoverRef = ref(null);
|
||||
let animationFrame = null
|
||||
let rotateSpeed = 0
|
||||
let rotateValue = 0
|
||||
let redValue = 0;
|
||||
let maxSpeed = 16 // 最大旋转速度(度/帧)
|
||||
let shakeIntensity = 0 // 抖动强度
|
||||
let isHovering = false
|
||||
|
||||
const animate = () => {
|
||||
if (!isHovering || !avatarRef.value || !redCoverRef.value) return
|
||||
|
||||
// 加速逻辑
|
||||
rotateSpeed += 0.01;
|
||||
rotateValue += rotateSpeed;
|
||||
|
||||
if (rotateSpeed > maxSpeed) {
|
||||
// 达到最大速度后开始增加抖动强度
|
||||
shakeIntensity = Math.min(shakeIntensity + 0.2, 500)
|
||||
}
|
||||
|
||||
redValue = Math.min(redValue + 0.0001, 0.8);
|
||||
|
||||
|
||||
// 生成抖动偏移
|
||||
const shakeX = (Math.random() - 0.5) * shakeIntensity
|
||||
const shakeY = (Math.random() - 0.5) * shakeIntensity
|
||||
|
||||
// 应用变换
|
||||
avatarRef.value.style.transform = `
|
||||
translate(${shakeX}px, ${shakeY}px)
|
||||
rotate(${rotateValue}deg)
|
||||
`
|
||||
redCoverRef.value.style.opacity = redValue;
|
||||
|
||||
animationFrame = requestAnimationFrame(animate)
|
||||
}
|
||||
|
||||
const resetState = () => {
|
||||
rotateSpeed = 0
|
||||
redValue = 0;
|
||||
shakeIntensity = 0
|
||||
if (avatarRef.value) {
|
||||
avatarRef.value.style.transform = 'none'
|
||||
redCoverRef.value.style.opacity = '0'
|
||||
}
|
||||
}
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
isHovering = true
|
||||
animationFrame = requestAnimationFrame(animate)
|
||||
}
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
isHovering = false;
|
||||
rotateSpeed = 0
|
||||
rotateValue = 0
|
||||
cancelAnimationFrame(animationFrame)
|
||||
resetState()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="user-info-card">
|
||||
<div style="position: relative;">
|
||||
<img :src="store.getters.profileImage" alt="User Avatar" class="avatar"/>
|
||||
<div class="avatar" ref="avatarRef"
|
||||
@mouseenter="handleMouseEnter"
|
||||
@mouseleave="handleMouseLeave">
|
||||
<div class="red-overlay" ref="redCoverRef"/>
|
||||
<img
|
||||
|
||||
:src="store.getters.profileImage"
|
||||
alt="User Avatar"
|
||||
|
||||
|
||||
/>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<h3>{{ store.state.userInfo.username }}</h3>
|
||||
@ -19,9 +89,11 @@ import store from "../../store/index.js";
|
||||
<style scoped>
|
||||
.user-info-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 20px;
|
||||
padding: 30px 0;
|
||||
@ -40,20 +112,39 @@ import store from "../../store/index.js";
|
||||
.user-info-card p {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
.red-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 0, 0, 0);
|
||||
mix-blend-mode: multiply;
|
||||
transition: background 0.25s ease-in-out;
|
||||
}
|
||||
.avatar {
|
||||
position: relative;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 20px;
|
||||
transition: transform 2s ease;
|
||||
overflow: hidden;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.avatar:hover {
|
||||
transform: rotate(3600deg);
|
||||
transition: transform 10s ease;
|
||||
.red-overlay {
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgb(255, 0, 0);
|
||||
opacity: 0;
|
||||
mix-blend-mode: darken;
|
||||
}
|
||||
.avatar img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: transform 0.1s linear;
|
||||
will-change: transform; /* 优化动画性能 */
|
||||
|
||||
}
|
||||
</style>
|
@ -6,6 +6,7 @@ import api from "../../../utils/axios.js";
|
||||
import store from "../../../store/index.js";
|
||||
import swal from "../../../utils/sweetalert.js";
|
||||
import AuthService from "../../../../services/auth.js";
|
||||
import PagingController from "../../../components/PagingController.vue";
|
||||
|
||||
const messages = ref(store.state.demosLocal.board?.messages || []);
|
||||
const amount = ref(store.state.demosLocal.board?.amount || 0);
|
||||
@ -147,16 +148,7 @@ onBeforeUnmount(() => {
|
||||
<Message v-for="msg in messages" :message="msg" @refresh-board="refreshBoard"/>
|
||||
<div v-if="messages?.length === 0">正在加载...</div>
|
||||
</div>
|
||||
<div class="paging-container">
|
||||
<div class="page-btn" :class="{active: currentPage === 1}" @click="goPage(1)" v-if="amount">1</div>
|
||||
<span v-if="currentPage > 5">...</span>
|
||||
<div class="page-btn" v-for="index in ((a, b) => Array.from({ length: b - a + 1 }, (_, i) => a + i))
|
||||
(currentPage>4?(currentPage - 3):2, currentPage<amount-4?(currentPage + 4):amount-1)"
|
||||
@click="goPage(index)" :class="{active: currentPage === index}">{{ index }}
|
||||
</div>
|
||||
<span v-if="currentPage < amount - 5">...</span>
|
||||
<div class="page-btn" @click="goPage(amount)" :class="{active: currentPage === amount}" v-if="amount > 1">{{ amount }}</div>
|
||||
</div>
|
||||
<paging-controller :current-page="currentPage" :amount="amount" :go-page-func="goPage" :loading="pageLoading"/>
|
||||
</div>
|
||||
<div class="sender-container">
|
||||
<div class="sender-tips">
|
||||
@ -194,10 +186,10 @@ onBeforeUnmount(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
width: 90%;
|
||||
width: calc(80%);
|
||||
height: auto;
|
||||
padding: 20px;
|
||||
margin-bottom: 150px;
|
||||
margin: 0 auto 150px;
|
||||
border-radius: 15px;
|
||||
background: rgba(128, 128, 128, 0.06);
|
||||
border: cyan solid 1px;
|
||||
|
@ -11,13 +11,14 @@ import Account_setting from "../pages/accountPages/Account_setting.vue";
|
||||
import Account_draft from "../pages/accountPages/Account_draft.vue";
|
||||
import Account_userInfo from "../pages/accountPages/Account_userInfo.vue";
|
||||
import Account_admin_uploadLog from "../pages/accountPages/Account_admin_uploadLog.vue";
|
||||
import Account_admin_userManage from "../pages/accountPages/Account_admin_userManage.vue";
|
||||
import Projects from "../pages/Projects_home.vue";
|
||||
import Demos_home from "../pages/Demos_home.vue";
|
||||
import Board_page from "../pages/demoPages/messageBoard/Board_page.vue";
|
||||
import Tools_home from "../pages/Tools_home.vue";
|
||||
import GpaCalculator_page from "../pages/toolPages/gpaCalculator/gpaCalculator_page.vue";
|
||||
import About from "../pages/About.vue";
|
||||
import Test from "../pages/Editor.vue";
|
||||
import Editor from "../pages/Editor.vue";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@ -65,12 +66,13 @@ const routes = [
|
||||
{path: 'draft', component: Account_draft},
|
||||
{path: 'setting', component: Account_setting},
|
||||
{path: '', component: Account_userInfo},
|
||||
{path: 'upload-log', component: Account_admin_uploadLog}
|
||||
{path: 'upload-log', component: Account_admin_uploadLog},
|
||||
{path: 'user-management', component: Account_admin_userManage}
|
||||
]
|
||||
}, {
|
||||
path: '/test_page',
|
||||
name: 'Test',
|
||||
component: Test
|
||||
path: '/editor',
|
||||
name: 'Editor',
|
||||
component: Editor
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -78,6 +78,7 @@ const store = createStore({
|
||||
}
|
||||
},
|
||||
isAdmin: state => state.userInfo.role_id >= 2,
|
||||
isSuperAdmin: state => state.userInfo.role_id >= 3,
|
||||
},
|
||||
plugins: [
|
||||
createPersistedStatePlugin({
|
||||
|
@ -30,8 +30,7 @@ a:hover {
|
||||
|
||||
/* 通用容器 */
|
||||
.container {
|
||||
width: 90%;
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user