Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# uwhLife / 芜忧皖江

> 本项目使用 vibe coding 方式编写,并在人工审查、测试和发布流程中持续整理。
> 本项目使用 vibe coding 方式编写。

本项目灵感来自 ![自在东湖](https://chilleast.soilzhu.su/)

uwhLife / 芜忧皖江 是一个面向芜湖学院校园生活场景的 Flutter 移动端应用。项目当前聚焦常用校园入口和移动端体验:统一门户、校园应用列表、付款码、开门、热水/洗浴、智慧课堂、消息与个人页等。

当前仓库名建议使用 `uwhLife`,Flutter/Dart 包名已改为 `uwhlife`,以符合 Dart 包命名约束。

<div align="center" style="display: flex; justify-content: center; align-items: center; gap: 16px; flex-wrap: wrap;">
<a href="https://stikstore.app/altdirect/?url=https://raw.giteeusercontent.com/coldin04/uwhlife_source/raw/master/source.json" target="_blank">
Expand Down
Binary file added web/assets/app-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/img/applist.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/img/applist_dark.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/img/homepage.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/img/homepage_dark.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/img/message.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/img/message_dark.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/img/qrcard.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/img/qrcard_dark.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
125 changes: 125 additions & 0 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#22c55e" />
<meta name="description" content="芜忧皖江" />
<title>芜忧皖江</title>
<link rel="icon" href="assets/app-icon.png" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" />
<link rel="stylesheet" href="styles/base.css" />
<link rel="stylesheet" href="styles/hero.css" />
<link rel="stylesheet" href="styles/features.css" />
<script src="script.js?v=theme-screenshots-2" defer></script>
</head>
<body>
<header class="site-header">
<nav class="nav-shell" aria-label="主导航">
<a class="brand" href="#top" aria-label="芜忧皖江首页">
<img src="assets/app-icon.png" alt="" width="36" height="36" />
<span>芜忧皖江</span>
</a>
<div class="nav-links" aria-label="项目链接">
<a class="nav-link" href="https://github.com/Coldin04/uwhLife" target="_blank" rel="noreferrer">
<i class="bi bi-github" aria-hidden="true"></i>
<span>GitHub</span>
</a>
<a class="nav-link" href="https://gitee.com/coldin04/uwhlife_source/releases" target="_blank" rel="noreferrer">
<span>Gitee</span>
<i class="bi bi-box-arrow-up-right" aria-hidden="true"></i>
</a>
</div>
</nav>
</header>

<main id="top">
<section class="hero" aria-labelledby="hero-title">
<div class="hero-layout">
<div class="hero-copy">
<h1 id="hero-title"><span>芜忧皖江</span></h1>
<p class="hero-tagline">更易用的芜湖学院客户端</p>
<div class="download-actions" aria-label="下载方式">
<a class="download-button download-button--android" href="https://gitee.com/coldin04/uwhlife_source/releases/download/latest/UWHLife_android_arm64-v8a.apk" target="_blank" rel="noreferrer">
<i class="bi bi-android2" aria-hidden="true"></i>
<span>Android APK</span>
</a>
<a class="download-button download-button--ios" href="https://stikstore.app/altdirect/?url=https://raw.giteeusercontent.com/coldin04/uwhlife_source/raw/master/source.json" target="_blank" rel="noreferrer">
<i class="bi bi-apple" aria-hidden="true"></i>
<span>iOS AltStore</span>
</a>
</div>
<p class="download-meta">
<a href="https://gitee.com/coldin04/uwhlife_source/releases" target="_blank" rel="noreferrer">更多版本</a>
<span aria-hidden="true"> · </span>
<a href="https://gitee.com/coldin04/uwhlife_source/releases/download/latest/UWHLife_unsigned.ipa" target="_blank" rel="noreferrer">IPA 下载</a>
</p>
</div>

<div class="hero-device" aria-label="应用截图展示位置">
<div class="phone-shell">
<div class="phone-screen">
<img class="phone-screenshot" src="img/homepage_dark.jpeg" data-dark-src="img/homepage_dark.jpeg" data-light-src="img/homepage.jpeg" width="1080" height="2340" alt="芜忧皖江应用截图" />
</div>
</div>
</div>
</div>
</section>

<section class="features" aria-labelledby="features-title">
<div class="features-heading">
<p>功能</p>
<h2 id="features-title">校园生活,掌上就有。</h2>
</div>
<div class="feature-bento">
<article class="feature-card feature-card--life">
<div class="feature-copy">
<h3>生活</h3>
<!-- FUTURE: Add a concise description for 生活. -->
</div>
<div class="feature-shot feature-shot--left">
<div class="feature-phone">
<div class="feature-phone-screen">
<img class="feature-screenshot" src="img/applist_dark.jpeg" data-dark-src="img/applist_dark.jpeg" data-light-src="img/applist.jpeg" width="1080" height="2340" alt="便捷生活功能截图" />
</div>
</div>
</div>
</article>
<article class="feature-card feature-card--notice">
<div class="feature-copy">
<h3>通知</h3>
<!-- FUTURE: Add a concise description for 通知. -->
</div>
<div class="feature-shot feature-shot--right">
<div class="feature-phone">
<div class="feature-phone-screen">
<img class="feature-screenshot" src="img/message_dark.jpeg" data-dark-src="img/message_dark.jpeg" data-light-src="img/message.jpeg" width="1080" height="2340" alt="校园通知功能截图" />
</div>
</div>
</div>
</article>
<article class="feature-card feature-card--payment">
<div class="feature-copy">
<h3>支付</h3>
<!-- FUTURE: Add a concise description for 支付. -->
</div>
<div class="feature-shot feature-shot--bottom">
<div class="feature-phone">
<div class="feature-phone-screen">
<img class="feature-screenshot" src="img/qrcard_dark.jpeg" data-dark-src="img/qrcard_dark.jpeg" data-light-src="img/qrcard.jpeg" width="1080" height="2340" alt="支付功能截图" />
</div>
</div>
</div>
</article>
</div>
</section>
</main>

<footer class="site-footer">
<p class="footer-note">本项目灵感来自 <a href="https://chilleast.soilzhu.su/" target="_blank" rel="noreferrer">自在东湖</a> ,仅供学习使用。</p>
<span>© 2026 uwhLife contributors · MIT License</span>
<span aria-hidden="true"> · </span>
<a href="https://cold04.com" target="_blank" rel="noreferrer">Coldin04</a>
</footer>
</body>
</html>
24 changes: 24 additions & 0 deletions web/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const screenshotImages = document.querySelectorAll('[data-dark-src][data-light-src]');
const colorScheme = window.matchMedia('(prefers-color-scheme: dark)');

function applyScreenshotTheme(theme) {
screenshotImages.forEach((image) => {
image.dataset.displayedTheme = theme;
image.src = image.dataset[`${theme}Src`];
});
}

screenshotImages.forEach((image) => {
image.addEventListener('error', () => {
if (image.dataset.displayedTheme === 'light') {
image.dataset.displayedTheme = 'dark';
image.src = image.dataset.darkSrc;
}
});
});

applyScreenshotTheme(colorScheme.matches ? 'dark' : 'light');

colorScheme.addEventListener('change', (event) => {
applyScreenshotTheme(event.matches ? 'dark' : 'light');
});
45 changes: 45 additions & 0 deletions web/styles/base.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
:root {
color-scheme: light dark;
--brand: #22c55e;
--mint: #a2efc1;
--teal: #43c99a;
--ink: #111827;
--muted: #6b7280;
--surface: #ffffff;
--page: #f9fafb;
--line: #e5e7eb;
--phone: #111827;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { min-width: 320px; margin: 0; color: var(--ink); background: var(--page); font-family: Inter, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif; }
button, a { font: inherit; }
button:focus-visible, a:focus-visible { outline: 3px solid color-mix(in srgb, var(--brand), white 25%); outline-offset: 3px; }
h1, h2, h3, p { margin-top: 0; }

.site-header { position: sticky; top: 0; z-index: 10; padding: 13px 24px; background: color-mix(in srgb, var(--page), transparent 20%); border-bottom: 1px solid color-mix(in srgb, var(--line), transparent 42%); backdrop-filter: blur(16px); }
.nav-shell { width: min(1120px, 100%); height: 46px; margin: auto; display: flex; justify-content: space-between; align-items: center; }
.brand { display: flex; align-items: center; gap: 11px; color: var(--ink); font-size: 17px; font-weight: 700; text-decoration: none; }
.brand img { border-radius: 9px; box-shadow: 0 4px 12px rgb(34 197 94 / 22%); }
.nav-links { display: flex; align-items: center; gap: 6px; }
.nav-link { min-height: 40px; padding: 0 12px; display: inline-flex; align-items: center; justify-content: center; gap: 7px; color: var(--muted); border-radius: 8px; font-size: 14px; font-weight: 600; text-decoration: none; }
.nav-link:hover { color: var(--ink); background: color-mix(in srgb, var(--brand), transparent 92%); }
.nav-link i { font-size: 15px; }

.site-footer { padding: 34px 24px; color: var(--muted); border-top: 1px solid var(--line); font-size: 14px; text-align: center; }
.site-footer a { color: inherit; text-decoration: none; }
.site-footer a:hover { color: var(--ink); }
.footer-note { margin: 8px 0 0; font-size: 12px; }

@media (max-width: 760px) {
.site-header { padding-inline: 16px; }
.nav-links { gap: 0; }
.nav-link { padding-inline: 8px; font-size: 13px; }
}

@media (prefers-color-scheme: dark) {
:root { --ink: #f3f4f6; --muted: #9ca3af; --surface: #273244; --page: #1f2937; --line: #374151; --phone: #111827; }
}

@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } }
41 changes: 41 additions & 0 deletions web/styles/features.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.features { width: min(100% - 48px, 1120px); min-height: calc(100svh - 73px); height: calc(100svh - 73px); margin: 0 auto; padding: 58px 0 54px; display: flex; flex-direction: column; border-top: 1px solid var(--line); }
.features-heading p { margin-bottom: 12px; color: var(--brand); font-size: 14px; font-weight: 700; }
.features-heading h2 { max-width: 600px; margin-bottom: 34px; font-size: 42px; font-weight: 700; line-height: 1.2; letter-spacing: 0; }
.feature-bento { min-height: 0; flex: 1; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); grid-auto-rows: minmax(0, 1fr); align-items: stretch; gap: 18px; }
.feature-card { position: relative; min-height: 0; display: flex; flex-direction: column; justify-content: space-between; overflow: hidden; color: var(--ink); background: var(--surface); border: 1px solid var(--line); border-radius: 8px; }
.feature-copy { padding: 52px 52px 0; }
.feature-copy h3 { margin: 0; font-size: 42px; font-weight: 700; line-height: 1.16; letter-spacing: 0; }
.feature-shot { width: min(70%, 250px); min-height: 250px; max-height: 333px; margin: 24px auto 0; flex: 1 1 auto; overflow: hidden; background: transparent; }
.feature-phone { width: 100%; height: auto; padding: 6px; background: var(--phone); border: 1px solid color-mix(in srgb, var(--phone), white 18%); border-radius: 28px; box-shadow: 0 18px 34px rgb(17 24 39 / 20%); }
.feature-phone-screen { width: 100%; aspect-ratio: 9 / 19.5; height: auto; overflow: hidden; background: var(--page); border-radius: 21px; }
.feature-phone-screen img:not([hidden]) { display: block; width: 100%; height: 100%; object-fit: contain; object-position: center top; }

@media (max-width: 760px) {
.features { width: min(100% - 32px, 1120px); min-height: auto; height: auto; padding: 82px 0; display: block; }
.features-heading h2 { margin-bottom: 40px; font-size: 34px; }
.feature-bento { display: grid; grid-template-columns: 1fr; gap: 14px; }
.feature-card, .feature-card--notice { grid-row: auto; min-height: 440px; }
.feature-copy { padding: 34px 34px 0; }
.feature-copy h3 { font-size: 33px; }
.feature-shot { width: min(74%, 250px); margin-top: 20px; }
}

@media (max-width: 989px) and (min-width: 761px) {
.features { min-height: auto; height: auto; display: block; }
.feature-bento { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, 302px); grid-auto-rows: auto; align-items: stretch; }
.feature-card--notice { grid-area: 1 / 1; }
.feature-card--life { grid-area: 1 / 2 / 3 / 3; }
.feature-card--payment { grid-area: 2 / 1; }
.feature-card--life, .feature-card--notice, .feature-card--payment { justify-content: space-between; }
.feature-card--notice, .feature-card--payment { justify-content: flex-start; }
.feature-card--notice .feature-copy, .feature-card--payment .feature-copy { padding: 28px 32px 0; }
.feature-card--notice .feature-copy h3, .feature-card--payment .feature-copy h3 { font-size: 32px; }
.feature-card--notice .feature-shot, .feature-card--payment .feature-shot { width: min(56%, 211px); min-height: 211px; max-height: 211px; margin: 24px auto 0; flex: 0 0 211px; }
.feature-card--life .feature-copy { padding: 36px 40px 0; }
.feature-card--life .feature-copy h3 { font-size: 36px; }
.feature-card--life .feature-shot { width: min(66%, 235px); min-height: 493px; max-height: none; margin: 0 auto; flex: 0 0 493px; }
}

@media (min-width: 990px) {
.feature-card--life, .feature-card--notice, .feature-card--payment { height: 500px; min-height: 500px; }
}
51 changes: 51 additions & 0 deletions web/styles/hero.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.hero { min-height: calc(100svh - 73px); display: grid; place-items: center; padding: 52px clamp(40px, 5vw, 80px); }
.hero-layout { width: min(100%, 1030px); margin: 0 auto; display: grid; grid-template-columns: minmax(0, 1fr) 360px; align-items: center; gap: clamp(56px, 11vw, 150px); }
.hero-copy { padding-block: 42px; }
h1 { margin-bottom: 0; font-size: 78px; font-weight: 700; line-height: 1.08; letter-spacing: 0; }
h1 span { display: block; background: linear-gradient(105deg, var(--brand), var(--mint) 53%, var(--teal)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero-tagline { margin: 24px 0 28px; color: #6b7280; font-size: 18px; line-height: 1.5; }

.download-actions { width: fit-content; max-width: 100%; display: flex; flex-wrap: wrap; gap: 20px; }
.download-button { min-height: 52px; padding: 0 18px; display: inline-flex; align-items: center; justify-content: center; gap: 9px; color: var(--ink); border: 1px solid var(--line); border-radius: 999px; background: var(--surface); font-size: 15px; text-decoration: none; transition: background-color 160ms ease, border-color 160ms ease, transform 160ms ease; }
.download-button:hover { color: var(--ink); border-color: color-mix(in srgb, var(--brand), var(--line) 60%); transform: translateY(-1px); }
.download-button > i { font-size: 20px; }
.download-button span { font-size: 15px; font-weight: 600; }
.download-button--android { color: white; border-color: transparent; background: linear-gradient(108deg, var(--brand), var(--teal)); box-shadow: 0 15px 30px rgb(34 197 94 / 18%); }
.download-button--android:hover { color: white; border-color: transparent; }
.download-meta { margin: 12px 0 0; color: var(--muted); font-size: 13px; }
.download-meta a { color: inherit; text-decoration: none; }
.download-meta a:hover { color: var(--ink); }

.hero-device { display: grid; place-items: center; }
.phone-shell { position: relative; width: clamp(230px, calc(46.15svh - 92px), 300px); padding: 10px; background: var(--phone); border: 1px solid color-mix(in srgb, var(--phone), white 17%); border-radius: 42px; box-shadow: 0 28px 60px rgb(17 24 39 / 28%); }
.phone-screen { width: 100%; aspect-ratio: 9 / 19.5; height: auto; overflow: hidden; border-radius: 34px; background: var(--surface); }
.phone-screenshot:not([hidden]) { display: block; width: 100%; height: 100%; object-fit: contain; object-position: center top; }

@media (max-width: 880px) {
.hero { padding: 72px 24px 68px; }
.hero-layout { grid-template-columns: 1fr; gap: 46px; justify-items: center; text-align: center; }
.hero-copy { padding: 0; display: flex; flex-direction: column; align-items: center; }
.hero-device { order: 2; }
.phone-shell { width: min(260px, 74vw); }
}

@media (max-width: 760px) {
h1 { font-size: 62px; }
}

@media (max-width: 989px) and (min-width: 761px) {
h1 { font-size: 64px; }
}

@media (max-height: 700px) and (min-width: 881px) {
.hero { padding-block: 20px; }
h1 { font-size: 56px; }
.hero-copy { padding-block: 0; }
.download-actions { gap: 14px; }
.download-button { min-height: 46px; }
}

@media (prefers-color-scheme: dark) {
.phone-screen { background: #1f2937; }
.hero-tagline { color: #aeb6c4; }
}
Loading
Loading