feat: Web 版上线 footpr.linyux.cn —— 登录页 + 按用户隔离的命令 API + 静态托管
This commit is contained in:
@@ -0,0 +1 @@
|
||||
import{r as g,e as v,j as t,S as j,s as S}from"./index-BunBoxtn.js";import{C as c,a as d,b as x}from"./card-CgBa5fs5.js";import{u as N,i as u,a as m}from"./useChart-CwGNJ-9_.js";import{u as k,i as w,a as C,b as L,c as R,d as $,e as z,f as T}from"./echarts-BIWefrIc.js";k([w,C,L,R,$,z,T]);function q(){const[l,n]=g.useState(""),i=N(),e=v({queryKey:["stats",l||void 0],queryFn:()=>S(l?Number(l):void 0)}).data,p=u("2"),r=u("3"),h=m(()=>({grid:{left:34,right:12,top:14,bottom:24},tooltip:{trigger:"axis"},xAxis:{type:"category",data:(e==null?void 0:e.monthly.map(a=>a.ym.slice(2)))??[],axisLine:{lineStyle:{color:r}},axisTick:{show:!1},axisLabel:{color:r,fontSize:10}},yAxis:{type:"value",splitLine:{lineStyle:{color:"rgba(128,138,155,0.15)"}},axisLabel:{color:r,fontSize:10}},series:[{type:"bar",data:(e==null?void 0:e.monthly.map(a=>a.count))??[],barMaxWidth:18,itemStyle:{color:"#3056d3",borderRadius:[3,3,0,0]}}]}),[e,i]),b=m(()=>{const a=((e==null?void 0:e.by_type)??[]).filter(s=>s.count>0);return{tooltip:{trigger:"item"},legend:{orient:"vertical",right:4,top:"middle",textStyle:{color:p,fontSize:11},itemWidth:10,itemHeight:10},series:[{type:"pie",radius:["48%","72%"],center:["34%","50%"],label:{show:!1},data:a.map(s=>({name:s.label,value:s.count,itemStyle:{color:s.color||"#64748b"}}))}]}},[e,i]),y=m(()=>{const a=((e==null?void 0:e.by_level)??[]).filter(s=>s.count>0).slice().reverse();return{grid:{left:64,right:24,top:8,bottom:22},tooltip:{trigger:"axis"},xAxis:{type:"value",splitLine:{lineStyle:{color:"rgba(128,138,155,0.15)"}},axisLabel:{color:r,fontSize:10}},yAxis:{type:"category",data:a.map(s=>s.label),axisLine:{show:!1},axisTick:{show:!1},axisLabel:{color:p,fontSize:11}},series:[{type:"bar",data:a.map(s=>({value:s.count,itemStyle:{color:s.color||"#3056d3",borderRadius:[0,3,3,0]}})),barMaxWidth:14}]}},[e,i]),f=((e==null?void 0:e.metrics)??[]).slice(0,3);return t.jsxs("div",{className:"mx-auto max-w-[1080px] px-6 py-5",children:[t.jsxs("div",{className:"mb-4 flex flex-wrap items-center justify-between gap-3",children:[t.jsxs("div",{className:"flex flex-wrap gap-3",children:[t.jsx(o,{label:"总记录",value:(e==null?void 0:e.total)??0}),t.jsx(o,{label:l?l+" 年新增":"本年新增",value:(e==null?void 0:e.total_this_year)??0}),t.jsx(o,{label:"附件总数",value:(e==null?void 0:e.attachment_total)??0}),f.map(a=>t.jsx(o,{label:a.label,value:a.value,unit:a.unit??""},a.key))]}),t.jsx(j,{className:"w-[120px]",value:l,onChange:a=>n(a.target.value),options:[{value:"",label:"全部年份"},...((e==null?void 0:e.years)??[]).map(a=>({value:String(a),label:a+" 年"}))]})]}),!(e!=null&&e.metrics.length)&&t.jsx("div",{className:"mb-4 rounded-md border border-dashed border-line px-4 py-2 text-[12px] text-ink-3",children:"提示:在「统计」配置中注册指标(如志愿时长、读书本数),带单位的数值字段即可在此量化展示。"}),t.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-4",children:[t.jsxs(c,{children:[t.jsx(d,{title:"近 12 个月活跃度"}),t.jsx(x,{children:t.jsx("div",{ref:h,className:"h-[220px]"})})]}),t.jsxs(c,{children:[t.jsx(d,{title:"类型分布"}),t.jsx(x,{children:t.jsx("div",{ref:b,className:"h-[220px]"})})]}),t.jsxs(c,{className:"col-span-2",children:[t.jsx(d,{title:"级别分布"}),t.jsx(x,{children:t.jsx("div",{ref:y,className:"h-[200px]"})})]})]})]})}function o({label:l,value:n,unit:i}){return t.jsxs("div",{className:"min-w-[120px] rounded-lg border border-line bg-surface px-4 py-3 shadow-card",children:[t.jsx("div",{className:"text-[12px] text-ink-3",children:l}),t.jsxs("div",{className:"num mt-0.5 text-[22px] font-semibold leading-tight text-ink-1",children:[n,i&&t.jsx("span",{className:"ml-1 text-[12px] font-normal text-ink-3",children:i})]})]})}export{q as DashboardPage};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{j as s,a}from"./index-BunBoxtn.js";function i({className:r,...e}){return s.jsx("div",{className:a("rounded-lg border border-line bg-surface shadow-card",r),...e})}function t({title:r,extra:e,className:n}){return s.jsxs("div",{className:a("flex min-h-11 flex-wrap items-center justify-between gap-2 border-b border-line px-4 py-1.5",n),children:[s.jsx("h3",{className:"text-[13px] font-semibold text-ink-1",children:r}),e&&s.jsx("div",{className:"flex flex-wrap items-center gap-2",children:e})]})}function c({className:r,...e}){return s.jsx("div",{className:a("p-4",r),...e})}export{i as C,t as a,c as b};
|
||||
@@ -0,0 +1 @@
|
||||
async function o(r,e={},n){return window.__TAURI_INTERNALS__.invoke(r,e,n)}export{o as invoke};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{r}from"./index-BunBoxtn.js";import{g as f}from"./echarts-BIWefrIc.js";function p(){const[n,o]=r.useState(0);return r.useEffect(()=>{const s=()=>o(t=>t+1);return window.addEventListener("fp-theme-change",s),()=>window.removeEventListener("fp-theme-change",s)},[]),n}function d(n){return getComputedStyle(document.documentElement).getPropertyValue("--text-"+n).trim()||"#454e5c"}function h(n,o){const s=r.useRef(null),[t,i]=r.useState(null),u=r.useRef(n);return u.current=n,r.useEffect(()=>{if(!t)return;const e=f(t);s.current=e,e.setOption(u.current(),{notMerge:!0});const c=new ResizeObserver(()=>e.resize());return c.observe(t),()=>{c.disconnect(),e.dispose(),s.current=null}},[t]),r.useEffect(()=>{var e;(e=s.current)==null||e.setOption(u.current(),{notMerge:!0})},[t,...o]),e=>i(e)}export{h as a,d as i,p as u};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#3056d3" />
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<link rel="icon" href="/icons/pwa-192.png" />
|
||||
<link rel="apple-touch-icon" href="/icons/pwa-192.png" />
|
||||
<title>足迹 Footprints</title>
|
||||
<script type="module" crossorigin src="/assets/index-BunBoxtn.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BMVMeOX_.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "足迹 Footprints",
|
||||
"short_name": "足迹",
|
||||
"description": "个人成长足迹档案:记录、检索、量化、导出",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait-primary",
|
||||
"background_color": "#101216",
|
||||
"theme_color": "#3056d3",
|
||||
"lang": "zh-CN",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/pwa-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/pwa-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// PWA Service Worker v2
|
||||
// - 页面导航:网络优先(构建更新后强制拿到新 index.html),离线才回落缓存
|
||||
// - 静态资源:缓存优先
|
||||
// - /api:直连不缓存
|
||||
const CACHE = 'fp-static-v2';
|
||||
const CORE = ['/', '/manifest.webmanifest', '/icons/pwa-192.png', '/icons/pwa-512.png'];
|
||||
|
||||
self.addEventListener('install', (e) => {
|
||||
self.skipWaiting();
|
||||
e.waitUntil(caches.open(CACHE).then((c) => c.addAll(CORE)));
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (e) => {
|
||||
e.waitUntil(
|
||||
caches.keys()
|
||||
.then((ks) => Promise.all(ks.filter((k) => k !== CACHE).map((k) => caches.delete(k))))
|
||||
.then(() => self.clients.claim())
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (e) => {
|
||||
const url = new URL(e.request.url);
|
||||
if (e.request.method !== 'GET' || url.pathname.startsWith('/api')) return;
|
||||
|
||||
if (e.request.mode === 'navigate') {
|
||||
e.respondWith(
|
||||
fetch(e.request).then((res) => {
|
||||
const copy = res.clone();
|
||||
caches.open(CACHE).then((c) => c.put('/index.html', copy));
|
||||
return res;
|
||||
}).catch(() => caches.match('/index.html').then((h) => h || caches.match('/')))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
e.respondWith(
|
||||
caches.match(e.request).then((hit) => hit || fetch(e.request).then((res) => {
|
||||
if (res.ok && url.origin === location.origin) {
|
||||
const copy = res.clone();
|
||||
caches.open(CACHE).then((c) => c.put(e.request, copy));
|
||||
}
|
||||
return res;
|
||||
}))
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user