| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
- <title>中科兴皖 · 移动端 UI Demo(Vant)</title>
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@4.9.21/lib/index.css" />
- <style>
- :root {
- --brand-green: #0a9f6d;
- --brand-green-dark: #087f57;
- --brand-green-soft: #e9f9f2;
- --barley-yellow: #d6a343;
- --barley-yellow-soft: #fff7e8;
- --bg-page: #f5f8f7;
- --surface: #ffffff;
- --text-primary: #1f2a26;
- --text-muted: #6b7b74;
- --border: #dcebe4;
- --danger: #d14343;
- --warning: #cf8a19;
- --success: #189b65;
- --radius-card: 14px;
- --radius-lg: 20px;
- --shadow-soft: 0 4px 16px rgba(8, 127, 87, 0.08);
- --van-primary-color: #0a9f6d;
- --van-success-color: #189b65;
- --van-warning-color: #cf8a19;
- --van-danger-color: #d14343;
- --van-border-color: #dcebe4;
- --van-background: #f5f8f7;
- --van-background-2: #ffffff;
- --van-text-color: #1f2a26;
- --van-text-color-2: #6b7b74;
- --van-button-primary-background: #0a9f6d;
- --van-button-primary-border-color: #0a9f6d;
- --van-button-radius: 10px;
- --van-cell-group-inset-padding: 12px;
- --van-cell-group-inset-radius: 14px;
- --van-dialog-confirm-button-text-color: #0a9f6d;
- --van-nav-bar-icon-color: #ffffff;
- --van-nav-bar-text-color: #ffffff;
- --van-nav-bar-title-text-color: #ffffff;
- --van-nav-bar-background: transparent;
- --van-tabbar-item-active-color: #0a9f6d;
- --van-tag-radius: 6px;
- }
- * { box-sizing: border-box; }
- html, body {
- margin: 0;
- padding: 0;
- min-height: 100%;
- font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", sans-serif;
- color: var(--text-primary);
- background:
- radial-gradient(900px 420px at 10% -10%, rgba(10, 159, 109, 0.14), transparent 55%),
- radial-gradient(700px 380px at 90% 0%, rgba(214, 163, 67, 0.12), transparent 50%),
- linear-gradient(180deg, #eef6f2 0%, #f7faf9 40%, #f3f6f5 100%);
- }
- body {
- display: flex;
- justify-content: center;
- padding: 24px 16px 40px;
- }
- #app {
- width: 100%;
- max-width: 1120px;
- display: grid;
- grid-template-columns: 280px 1fr;
- gap: 24px;
- align-items: start;
- }
- .doc-panel {
- position: sticky;
- top: 24px;
- background: rgba(255, 255, 255, 0.92);
- border: 1px solid var(--border);
- border-radius: 18px;
- padding: 20px 18px;
- box-shadow: var(--shadow-soft);
- backdrop-filter: blur(8px);
- }
- .doc-panel h1 {
- margin: 0 0 6px;
- font-size: 20px;
- line-height: 1.3;
- color: var(--brand-green-dark);
- }
- .doc-panel .sub {
- margin: 0 0 16px;
- font-size: 12px;
- color: var(--text-muted);
- line-height: 1.5;
- }
- .doc-panel .section-title {
- margin: 16px 0 8px;
- font-size: 12px;
- font-weight: 600;
- color: var(--text-muted);
- letter-spacing: 0.04em;
- text-transform: uppercase;
- }
- .screen-nav {
- display: flex;
- flex-direction: column;
- gap: 8px;
- }
- .screen-nav button {
- appearance: none;
- border: 1px solid var(--border);
- background: #fff;
- border-radius: 12px;
- padding: 12px 14px;
- text-align: left;
- cursor: pointer;
- font-size: 14px;
- color: var(--text-primary);
- transition: border-color .2s, background .2s, color .2s;
- }
- .screen-nav button:hover {
- border-color: #a7e0c8;
- background: var(--brand-green-soft);
- }
- .screen-nav button.active {
- border-color: var(--brand-green);
- background: var(--brand-green-soft);
- color: var(--brand-green-dark);
- font-weight: 600;
- }
- .token-row {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- }
- .token-chip {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- font-size: 11px;
- color: var(--text-muted);
- background: #fff;
- border: 1px solid var(--border);
- border-radius: 999px;
- padding: 4px 8px 4px 4px;
- }
- .token-chip i {
- width: 14px;
- height: 14px;
- border-radius: 50%;
- display: block;
- }
- .notes {
- margin: 12px 0 0;
- padding: 0;
- list-style: none;
- font-size: 12px;
- color: var(--text-muted);
- line-height: 1.55;
- }
- .notes li {
- position: relative;
- padding-left: 12px;
- margin-bottom: 6px;
- }
- .notes li::before {
- content: "";
- position: absolute;
- left: 0;
- top: 7px;
- width: 5px;
- height: 5px;
- border-radius: 50%;
- background: var(--brand-green);
- }
- .phone-stage {
- display: flex;
- justify-content: center;
- }
- .phone {
- width: 390px;
- height: 844px;
- background: #111;
- border-radius: 42px;
- padding: 12px;
- box-shadow:
- 0 24px 60px rgba(15, 40, 30, 0.18),
- inset 0 0 0 1px rgba(255, 255, 255, 0.08);
- position: relative;
- }
- .phone::before {
- content: "";
- position: absolute;
- top: 18px;
- left: 50%;
- transform: translateX(-50%);
- width: 118px;
- height: 28px;
- background: #0a0a0a;
- border-radius: 20px;
- z-index: 5;
- }
- .phone-screen {
- width: 100%;
- height: 100%;
- border-radius: 32px;
- overflow: hidden;
- background: var(--bg-page);
- position: relative;
- display: flex;
- flex-direction: column;
- }
- .screen-scroll {
- flex: 1;
- overflow-y: auto;
- -webkit-overflow-scrolling: touch;
- padding-bottom: 16px;
- }
- /* ========== 首页 ========== */
- .home-header {
- background: linear-gradient(145deg, var(--brand-green-dark), var(--brand-green));
- color: #fff;
- padding: 54px 16px 28px;
- border-radius: 0 0 var(--radius-lg) var(--radius-lg);
- position: relative;
- overflow: hidden;
- }
- .home-header::after {
- content: "";
- position: absolute;
- right: -40px;
- top: -30px;
- width: 160px;
- height: 160px;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.08);
- }
- .home-header .brand {
- font-size: 12px;
- opacity: 0.88;
- letter-spacing: 0.04em;
- margin-bottom: 8px;
- }
- .home-header h2 {
- margin: 0;
- font-size: 22px;
- font-weight: 700;
- letter-spacing: 0.02em;
- }
- .home-header .meta {
- margin-top: 10px;
- display: flex;
- align-items: center;
- gap: 8px;
- font-size: 12px;
- opacity: 0.92;
- }
- .home-header .meta .dot {
- width: 4px;
- height: 4px;
- border-radius: 50%;
- background: rgba(255,255,255,.7);
- }
- .home-body {
- padding: 14px 12px 0;
- }
- .panel {
- background: var(--surface);
- border: 1px solid var(--border);
- border-radius: var(--radius-card);
- box-shadow: var(--shadow-soft);
- padding: 14px 12px;
- margin-bottom: 12px;
- }
- .panel-hd {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 12px;
- padding: 0 4px;
- }
- .panel-hd h3 {
- margin: 0;
- font-size: 15px;
- font-weight: 650;
- }
- .panel-hd .hint {
- font-size: 11px;
- color: var(--text-muted);
- }
- .menu-grid {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 10px 6px;
- }
- .menu-card {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: flex-start;
- gap: 8px;
- min-height: 84px;
- padding: 10px 4px 6px;
- border: none;
- background: transparent;
- cursor: pointer;
- border-radius: 12px;
- transition: background .18s, transform .18s;
- -webkit-tap-highlight-color: transparent;
- }
- .menu-card:active {
- background: var(--brand-green-soft);
- transform: scale(0.97);
- }
- .menu-icon {
- width: 48px;
- height: 48px;
- border-radius: 14px;
- display: flex;
- align-items: center;
- justify-content: center;
- background: var(--brand-green-soft);
- color: var(--brand-green-dark);
- font-size: 24px;
- box-shadow: inset 0 0 0 1px rgba(10, 159, 109, 0.08);
- }
- .menu-icon.tone-yellow {
- background: var(--barley-yellow-soft);
- color: #a67b20;
- }
- .menu-icon.tone-slate {
- background: #eef2f1;
- color: #3d524a;
- }
- .menu-title {
- font-size: 12px;
- line-height: 1.25;
- color: var(--text-primary);
- text-align: center;
- max-width: 72px;
- word-break: break-all;
- }
- .perm-banner {
- display: flex;
- gap: 8px;
- align-items: flex-start;
- padding: 10px 12px;
- border-radius: 12px;
- background: var(--barley-yellow-soft);
- border: 1px solid #f0e0b8;
- font-size: 12px;
- color: #8a6520;
- margin-bottom: 12px;
- line-height: 1.45;
- }
- /* ========== 待办摘要 ========== */
- .todo-panel {
- padding: 14px 12px 12px;
- background:
- linear-gradient(165deg, rgba(233, 249, 242, 0.95) 0%, #fff 42%),
- #fff;
- }
- .todo-stats {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 8px;
- margin-bottom: 12px;
- }
- .todo-stat {
- position: relative;
- border: none;
- border-radius: 12px;
- padding: 12px 10px 10px;
- text-align: left;
- cursor: pointer;
- background: #fff;
- box-shadow: 0 2px 10px rgba(8, 127, 87, 0.06);
- border: 1px solid rgba(220, 235, 228, 0.9);
- transition: transform .18s, box-shadow .18s;
- -webkit-tap-highlight-color: transparent;
- overflow: hidden;
- }
- .todo-stat:active {
- transform: scale(0.97);
- }
- .todo-stat::before {
- content: "";
- position: absolute;
- left: 0;
- top: 0;
- bottom: 0;
- width: 3px;
- border-radius: 3px 0 0 3px;
- }
- .todo-stat.tone-warn::before { background: var(--warning); }
- .todo-stat.tone-pay::before { background: var(--barley-yellow); }
- .todo-stat.tone-danger::before { background: var(--danger); }
- .todo-stat.tone-warn { background: linear-gradient(180deg, #fffaf0 0%, #fff 70%); }
- .todo-stat.tone-pay { background: linear-gradient(180deg, #fff8ec 0%, #fff 70%); }
- .todo-stat.tone-danger { background: linear-gradient(180deg, #fff5f5 0%, #fff 70%); }
- .todo-stat .num {
- display: block;
- font-size: 22px;
- font-weight: 700;
- line-height: 1.1;
- letter-spacing: -0.02em;
- color: var(--text-primary);
- font-variant-numeric: tabular-nums;
- }
- .todo-stat.tone-warn .num { color: #b77412; }
- .todo-stat.tone-pay .num { color: #9a7424; }
- .todo-stat.tone-danger .num { color: var(--danger); }
- .todo-stat .label {
- display: block;
- margin-top: 4px;
- font-size: 11px;
- color: var(--text-muted);
- }
- .todo-stat .stat-icon {
- position: absolute;
- right: 8px;
- top: 8px;
- width: 26px;
- height: 26px;
- border-radius: 8px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 14px;
- opacity: 0.9;
- }
- .todo-stat.tone-warn .stat-icon {
- background: rgba(207, 138, 25, 0.12);
- color: var(--warning);
- }
- .todo-stat.tone-pay .stat-icon {
- background: rgba(214, 163, 67, 0.16);
- color: #a67b20;
- }
- .todo-stat.tone-danger .stat-icon {
- background: rgba(209, 67, 67, 0.12);
- color: var(--danger);
- }
- .todo-list {
- display: flex;
- flex-direction: column;
- gap: 8px;
- }
- .todo-item {
- display: flex;
- align-items: center;
- gap: 10px;
- width: 100%;
- padding: 10px 10px;
- border: 1px solid var(--border);
- border-radius: 12px;
- background: #fff;
- cursor: pointer;
- text-align: left;
- transition: background .15s, border-color .15s;
- }
- .todo-item:active {
- background: var(--brand-green-soft);
- border-color: #b7e5d0;
- }
- .todo-item .ti-icon {
- width: 36px;
- height: 36px;
- border-radius: 10px;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- font-size: 18px;
- }
- .todo-item .ti-icon.i-approve {
- background: #fff3e0;
- color: var(--warning);
- }
- .todo-item .ti-icon.i-pay {
- background: var(--barley-yellow-soft);
- color: #a67b20;
- }
- .todo-item .ti-icon.i-alert {
- background: #fdecec;
- color: var(--danger);
- }
- .todo-item .ti-body {
- flex: 1;
- min-width: 0;
- }
- .todo-item .ti-title {
- font-size: 13px;
- font-weight: 600;
- color: var(--text-primary);
- line-height: 1.3;
- }
- .todo-item .ti-desc {
- margin-top: 2px;
- font-size: 11px;
- color: var(--text-muted);
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .todo-item .ti-right {
- display: flex;
- flex-direction: column;
- align-items: flex-end;
- gap: 4px;
- flex-shrink: 0;
- }
- .todo-item .ti-time {
- font-size: 10px;
- color: #9aada4;
- }
- /* ========== 列表 ========== */
- .page-nav {
- background: linear-gradient(145deg, var(--brand-green-dark), var(--brand-green));
- padding: 44px 8px 10px;
- }
- .page-nav .van-nav-bar {
- background: transparent;
- }
- .page-nav .van-hairline--bottom::after {
- display: none;
- }
- .list-page {
- flex: 1;
- display: flex;
- flex-direction: column;
- min-height: 0;
- background:
- radial-gradient(420px 220px at 100% 0%, rgba(214, 163, 67, 0.16), transparent 55%),
- radial-gradient(380px 240px at 0% 30%, rgba(10, 159, 109, 0.10), transparent 50%),
- linear-gradient(180deg, #e8f5ef 0%, #f3f7f5 38%, #f7f9f8 100%);
- }
- .list-page .screen-scroll {
- background: transparent;
- }
- .list-hero {
- background:
- radial-gradient(220px 140px at 92% 18%, rgba(255, 255, 255, 0.18), transparent 60%),
- linear-gradient(155deg, #066647 0%, #0a9f6d 48%, #12b37a 100%);
- padding: 44px 14px 22px;
- border-radius: 0 0 28px 28px;
- position: relative;
- overflow: hidden;
- }
- .list-hero::after {
- content: "";
- position: absolute;
- width: 180px;
- height: 180px;
- right: -50px;
- bottom: -70px;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.08);
- pointer-events: none;
- }
- .list-hero .van-nav-bar {
- background: transparent;
- margin: 0 -6px;
- }
- .list-hero .van-hairline--bottom::after {
- display: none;
- }
- .list-hero-copy {
- position: relative;
- z-index: 1;
- margin: 4px 2px 14px;
- color: #fff;
- }
- .list-hero-copy .eyebrow {
- font-size: 11px;
- letter-spacing: 0.08em;
- opacity: 0.82;
- margin-bottom: 4px;
- }
- .list-hero-copy h2 {
- margin: 0;
- font-size: 22px;
- font-weight: 700;
- letter-spacing: 0.01em;
- }
- .list-hero-copy .subline {
- margin-top: 6px;
- font-size: 12px;
- opacity: 0.88;
- display: flex;
- align-items: center;
- gap: 8px;
- }
- .list-hero-copy .subline em {
- font-style: normal;
- font-weight: 700;
- background: rgba(255, 255, 255, 0.18);
- border-radius: 999px;
- padding: 1px 8px;
- }
- .list-search {
- position: relative;
- z-index: 1;
- }
- .list-search .van-search {
- padding: 0;
- }
- .list-search .van-search__content {
- background: rgba(255, 255, 255, 0.96);
- border-radius: 14px;
- box-shadow: 0 8px 24px rgba(4, 70, 48, 0.18);
- }
- .list-filter-dock {
- margin: -14px 12px 0;
- position: relative;
- z-index: 3;
- background: rgba(255, 255, 255, 0.92);
- border: 1px solid rgba(220, 235, 228, 0.95);
- border-radius: 18px;
- padding: 10px;
- box-shadow: 0 10px 28px rgba(15, 55, 40, 0.08);
- backdrop-filter: blur(10px);
- }
- .seg-tabs {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 4px;
- padding: 4px;
- border-radius: 14px;
- background: #eef6f2;
- }
- .seg-tab {
- border: none;
- background: transparent;
- border-radius: 11px;
- padding: 8px 4px;
- cursor: pointer;
- color: #6b7b74;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 2px;
- transition: all .2s;
- -webkit-tap-highlight-color: transparent;
- }
- .seg-tab .lab {
- font-size: 12px;
- line-height: 1.2;
- }
- .seg-tab .count {
- font-size: 11px;
- font-weight: 700;
- font-variant-numeric: tabular-nums;
- color: #9aada4;
- }
- .seg-tab.active {
- background: #fff;
- color: var(--brand-green-dark);
- box-shadow: 0 4px 12px rgba(8, 127, 87, 0.14);
- font-weight: 650;
- }
- .seg-tab.active .count {
- color: var(--brand-green);
- }
- .list-wrap {
- padding: 16px 12px 0;
- }
- .biz-card {
- position: relative;
- border: none;
- border-radius: 20px;
- padding: 0;
- margin-bottom: 14px;
- overflow: hidden;
- box-shadow: 0 10px 28px rgba(20, 55, 40, 0.08);
- transition: transform .16s, box-shadow .16s;
- isolation: isolate;
- }
- .biz-card:active {
- transform: translateY(1px) scale(0.995);
- box-shadow: 0 6px 16px rgba(20, 55, 40, 0.07);
- }
- .biz-card::before {
- content: "";
- position: absolute;
- inset: 0;
- z-index: 0;
- pointer-events: none;
- }
- .biz-card::after {
- content: "";
- position: absolute;
- width: 140px;
- height: 140px;
- right: -36px;
- top: -48px;
- border-radius: 50%;
- z-index: 0;
- pointer-events: none;
- opacity: 0.55;
- }
- .biz-card.status-warning {
- background: linear-gradient(145deg, #fff9ef 0%, #ffffff 46%, #fffaf3 100%);
- }
- .biz-card.status-warning::before {
- background: linear-gradient(90deg, rgba(214, 163, 67, 0.22), transparent 28%);
- }
- .biz-card.status-warning::after {
- background: radial-gradient(circle, rgba(214, 163, 67, 0.28), transparent 68%);
- }
- .biz-card.status-primary {
- background: linear-gradient(145deg, #eaf8f1 0%, #ffffff 48%, #f3fbf7 100%);
- }
- .biz-card.status-primary::before {
- background: linear-gradient(90deg, rgba(10, 159, 109, 0.18), transparent 28%);
- }
- .biz-card.status-primary::after {
- background: radial-gradient(circle, rgba(10, 159, 109, 0.22), transparent 68%);
- }
- .biz-card.status-danger {
- background: linear-gradient(145deg, #fff1f1 0%, #ffffff 48%, #fff7f7 100%);
- }
- .biz-card.status-danger::before {
- background: linear-gradient(90deg, rgba(209, 67, 67, 0.16), transparent 28%);
- }
- .biz-card.status-danger::after {
- background: radial-gradient(circle, rgba(209, 67, 67, 0.18), transparent 68%);
- }
- .biz-card.status-success {
- background: linear-gradient(145deg, #eef8f3 0%, #ffffff 50%, #f6fbf8 100%);
- }
- .biz-card.status-success::before {
- background: linear-gradient(90deg, rgba(24, 155, 101, 0.16), transparent 28%);
- }
- .biz-card.status-success::after {
- background: radial-gradient(circle, rgba(24, 155, 101, 0.18), transparent 68%);
- }
- .biz-card .card-inner {
- position: relative;
- z-index: 1;
- padding: 14px 14px 12px;
- }
- .biz-card .row-top {
- display: flex;
- align-items: flex-start;
- gap: 10px;
- margin-bottom: 12px;
- }
- .biz-card .type-icon {
- width: 44px;
- height: 44px;
- border-radius: 14px;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- font-size: 22px;
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.65);
- }
- .biz-card.status-warning .type-icon {
- background: linear-gradient(145deg, #ffe8b8, #f6d089);
- color: #7a5610;
- }
- .biz-card.status-primary .type-icon {
- background: linear-gradient(145deg, #8fe0bf, #0a9f6d);
- color: #fff;
- }
- .biz-card.status-danger .type-icon {
- background: linear-gradient(145deg, #ffb4b4, #e85a5a);
- color: #fff;
- }
- .biz-card.status-success .type-icon {
- background: linear-gradient(145deg, #b8efd4, #189b65);
- color: #fff;
- }
- .biz-card .title-wrap {
- flex: 1;
- min-width: 0;
- padding-top: 1px;
- }
- .biz-card .type-chip {
- display: inline-flex;
- align-items: center;
- gap: 4px;
- font-size: 10px;
- font-weight: 650;
- letter-spacing: 0.04em;
- padding: 2px 7px;
- border-radius: 999px;
- margin-bottom: 5px;
- }
- .biz-card.status-warning .type-chip {
- background: rgba(214, 163, 67, 0.16);
- color: #8f6718;
- }
- .biz-card.status-primary .type-chip {
- background: rgba(10, 159, 109, 0.12);
- color: #087f57;
- }
- .biz-card.status-danger .type-chip {
- background: rgba(209, 67, 67, 0.12);
- color: #b13535;
- }
- .biz-card.status-success .type-chip {
- background: rgba(24, 155, 101, 0.12);
- color: #147a50;
- }
- .biz-card .title {
- font-size: 15px;
- font-weight: 700;
- line-height: 1.35;
- color: #173128;
- letter-spacing: -0.01em;
- }
- .biz-card .status-pill {
- flex-shrink: 0;
- margin-top: 2px;
- font-size: 11px;
- font-weight: 700;
- padding: 4px 9px;
- border-radius: 999px;
- letter-spacing: 0.02em;
- }
- .biz-card.status-warning .status-pill {
- background: #fff;
- color: #a67b20;
- box-shadow: 0 2px 8px rgba(214, 163, 67, 0.22);
- }
- .biz-card.status-primary .status-pill {
- background: #0a9f6d;
- color: #fff;
- box-shadow: 0 4px 12px rgba(10, 159, 109, 0.28);
- }
- .biz-card.status-danger .status-pill {
- background: #d14343;
- color: #fff;
- box-shadow: 0 4px 12px rgba(209, 67, 67, 0.25);
- }
- .biz-card.status-success .status-pill {
- background: rgba(24, 155, 101, 0.12);
- color: #147a50;
- }
- .biz-card .body-row {
- display: flex;
- gap: 12px;
- align-items: stretch;
- }
- .biz-card .desc-box {
- flex: 1;
- margin: 0;
- padding: 10px 11px;
- border-radius: 12px;
- background: rgba(255, 255, 255, 0.72);
- border: 1px solid rgba(255, 255, 255, 0.9);
- font-size: 12px;
- color: #3f564c;
- line-height: 1.5;
- backdrop-filter: blur(2px);
- }
- .biz-card .side-metric {
- width: 86px;
- flex-shrink: 0;
- border-radius: 12px;
- padding: 8px 8px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: flex-end;
- text-align: right;
- background: rgba(255, 255, 255, 0.65);
- border: 1px solid rgba(255, 255, 255, 0.95);
- }
- .biz-card .side-metric .amt-label {
- font-size: 10px;
- color: #8aa198;
- }
- .biz-card .side-metric .amt {
- margin-top: 2px;
- font-size: 15px;
- font-weight: 800;
- color: #173128;
- letter-spacing: -0.02em;
- font-variant-numeric: tabular-nums;
- line-height: 1.15;
- }
- .biz-card.status-warning .side-metric .amt { color: #9a7424; }
- .biz-card.status-primary .side-metric .amt { color: #087f57; }
- .biz-card.status-danger .side-metric .amt { color: #b13535; }
- .biz-card .side-metric .time {
- margin-top: 6px;
- font-size: 10px;
- color: #8aa198;
- }
- .biz-card .meta-row {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- margin-top: 12px;
- }
- .biz-card .meta-row .mi {
- display: inline-flex;
- align-items: center;
- gap: 4px;
- font-size: 11px;
- color: #5d746a;
- background: rgba(255, 255, 255, 0.7);
- border-radius: 999px;
- padding: 3px 8px;
- }
- .biz-card .meta-row .van-icon {
- font-size: 12px;
- color: #7f968c;
- }
- .biz-card .code-pill {
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
- font-size: 10px;
- letter-spacing: 0.02em;
- }
- .biz-card .footer {
- margin-top: 12px;
- padding-top: 10px;
- border-top: 1px dashed rgba(140, 170, 155, 0.28);
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 8px;
- }
- .biz-card .footer .left-hint {
- font-size: 11px;
- color: #7f968c;
- display: inline-flex;
- align-items: center;
- gap: 4px;
- }
- .biz-card .footer .left-hint i {
- width: 6px;
- height: 6px;
- border-radius: 50%;
- display: inline-block;
- background: currentColor;
- opacity: 0.75;
- }
- .biz-card.status-warning .footer .left-hint { color: #a67b20; }
- .biz-card.status-primary .footer .left-hint { color: #0a9f6d; }
- .biz-card.status-danger .footer .left-hint { color: #d14343; }
- .biz-card.status-success .footer .left-hint { color: #189b65; }
- .biz-card .footer .ops {
- display: flex;
- gap: 8px;
- }
- .biz-card .footer .van-button {
- min-width: 64px;
- border-radius: 999px;
- height: 30px;
- }
- .biz-card .footer .van-button--primary {
- background: linear-gradient(135deg, #12b37a, #087f57);
- border: none;
- box-shadow: 0 4px 12px rgba(8, 127, 87, 0.28);
- }
- .list-end {
- padding: 8px 0 28px;
- text-align: center;
- font-size: 11px;
- color: #8aa198;
- letter-spacing: 0.08em;
- }
- .list-end span {
- display: inline-flex;
- align-items: center;
- gap: 10px;
- }
- .list-end span::before,
- .list-end span::after {
- content: "";
- width: 28px;
- height: 1px;
- background: linear-gradient(90deg, transparent, #c5d8ce);
- }
- .list-end span::after {
- background: linear-gradient(90deg, #c5d8ce, transparent);
- }
- .list-empty-pad {
- padding: 40px 0;
- }
- /* ========== 详情 ========== */
- .detail-page {
- flex: 1;
- display: flex;
- flex-direction: column;
- min-height: 0;
- background:
- radial-gradient(380px 200px at 100% 8%, rgba(214, 163, 67, 0.14), transparent 55%),
- linear-gradient(180deg, #e8f5ef 0%, #f4f7f6 42%, #f7f9f8 100%);
- }
- .detail-page .screen-scroll {
- background: transparent;
- padding-bottom: 88px;
- }
- .detail-hero {
- position: relative;
- overflow: hidden;
- padding: 44px 14px 28px;
- border-radius: 0 0 28px 28px;
- color: #fff;
- }
- .detail-hero.status-warning {
- background:
- radial-gradient(220px 140px at 90% 10%, rgba(255, 255, 255, 0.2), transparent 60%),
- linear-gradient(155deg, #a67b20 0%, #d6a343 45%, #e8c06a 100%);
- }
- .detail-hero.status-primary {
- background:
- radial-gradient(220px 140px at 90% 10%, rgba(255, 255, 255, 0.18), transparent 60%),
- linear-gradient(155deg, #066647 0%, #0a9f6d 48%, #12b37a 100%);
- }
- .detail-hero.status-danger {
- background:
- radial-gradient(220px 140px at 90% 10%, rgba(255, 255, 255, 0.16), transparent 60%),
- linear-gradient(155deg, #9c2f2f 0%, #d14343 50%, #e36a6a 100%);
- }
- .detail-hero.status-success {
- background:
- radial-gradient(220px 140px at 90% 10%, rgba(255, 255, 255, 0.16), transparent 60%),
- linear-gradient(155deg, #0f6b48 0%, #189b65 52%, #3bb883 100%);
- }
- .detail-hero::after {
- content: "";
- position: absolute;
- width: 180px;
- height: 180px;
- right: -48px;
- bottom: -70px;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.1);
- pointer-events: none;
- }
- .detail-hero .van-nav-bar {
- background: transparent;
- margin: 0 -6px;
- }
- .detail-hero .van-hairline--bottom::after {
- display: none;
- }
- .detail-hero-body {
- position: relative;
- z-index: 1;
- margin-top: 6px;
- }
- .detail-hero .type-line {
- display: flex;
- align-items: center;
- gap: 8px;
- margin-bottom: 10px;
- }
- .detail-hero .type-chip {
- font-size: 11px;
- font-weight: 650;
- letter-spacing: 0.04em;
- padding: 3px 9px;
- border-radius: 999px;
- background: rgba(255, 255, 255, 0.2);
- color: #fff;
- }
- .detail-hero .status-chip {
- font-size: 11px;
- font-weight: 700;
- padding: 3px 9px;
- border-radius: 999px;
- background: #fff;
- color: #173128;
- }
- .detail-hero.status-warning .status-chip { color: #8f6718; }
- .detail-hero.status-primary .status-chip { color: #087f57; }
- .detail-hero.status-danger .status-chip { color: #b13535; }
- .detail-hero.status-success .status-chip { color: #147a50; }
- .detail-hero h2 {
- margin: 0;
- font-size: 20px;
- font-weight: 750;
- line-height: 1.35;
- letter-spacing: -0.01em;
- }
- .detail-hero .code {
- margin-top: 8px;
- font-size: 12px;
- opacity: 0.92;
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
- letter-spacing: 0.02em;
- }
- .detail-metric-dock {
- margin: -18px 12px 0;
- position: relative;
- z-index: 3;
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 8px;
- padding: 12px;
- border-radius: 18px;
- background: rgba(255, 255, 255, 0.94);
- border: 1px solid rgba(220, 235, 228, 0.95);
- box-shadow: 0 12px 30px rgba(15, 55, 40, 0.1);
- backdrop-filter: blur(10px);
- }
- .detail-metric {
- text-align: center;
- padding: 4px 2px;
- }
- .detail-metric .lab {
- display: block;
- font-size: 10px;
- color: #8aa198;
- margin-bottom: 4px;
- }
- .detail-metric .val {
- display: block;
- font-size: 14px;
- font-weight: 750;
- color: #173128;
- line-height: 1.25;
- word-break: break-all;
- }
- .detail-metric .val.money {
- color: #9a7424;
- font-variant-numeric: tabular-nums;
- }
- .detail-body {
- padding: 16px 12px 0;
- }
- .detail-section {
- background: #fff;
- border-radius: 18px;
- border: 1px solid rgba(220, 235, 228, 0.9);
- box-shadow: 0 8px 22px rgba(20, 55, 40, 0.05);
- padding: 14px 14px 12px;
- margin-bottom: 12px;
- overflow: hidden;
- }
- .detail-section .sec-hd {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 12px;
- }
- .detail-section .sec-hd h3 {
- margin: 0;
- font-size: 14px;
- font-weight: 700;
- color: #173128;
- display: inline-flex;
- align-items: center;
- gap: 8px;
- }
- .detail-section .sec-hd h3::before {
- content: "";
- width: 4px;
- height: 14px;
- border-radius: 999px;
- background: linear-gradient(180deg, #12b37a, #087f57);
- }
- .detail-section .sec-hd .more {
- border: none;
- background: transparent;
- color: var(--brand-green);
- font-size: 12px;
- cursor: pointer;
- padding: 0;
- }
- .kv-list {
- display: flex;
- flex-direction: column;
- gap: 10px;
- }
- .kv-row {
- display: flex;
- align-items: flex-start;
- gap: 12px;
- font-size: 13px;
- line-height: 1.45;
- }
- .kv-row .k {
- width: 72px;
- flex-shrink: 0;
- color: #8aa198;
- }
- .kv-row .v {
- flex: 1;
- color: #173128;
- font-weight: 550;
- word-break: break-all;
- }
- .detail-note {
- margin: 0;
- padding: 10px 12px;
- border-radius: 12px;
- background: linear-gradient(135deg, #fff8ec, #fffdf8);
- border: 1px solid #f0e0b8;
- color: #8a6520;
- font-size: 12px;
- line-height: 1.5;
- }
- /* 详情 · 流转记录 */
- .flow-list {
- display: flex;
- flex-direction: column;
- gap: 0;
- position: relative;
- }
- .flow-item {
- position: relative;
- display: flex;
- gap: 12px;
- padding-bottom: 14px;
- }
- .flow-item:last-child {
- padding-bottom: 0;
- }
- .flow-item:not(:last-child) .flow-rail::after {
- content: "";
- position: absolute;
- left: 50%;
- top: 36px;
- bottom: -14px;
- width: 2px;
- transform: translateX(-50%);
- background: linear-gradient(180deg, rgba(10, 159, 109, 0.45), rgba(220, 235, 228, 0.9));
- border-radius: 2px;
- }
- .flow-rail {
- position: relative;
- width: 36px;
- flex-shrink: 0;
- display: flex;
- justify-content: center;
- }
- .flow-dot {
- width: 36px;
- height: 36px;
- border-radius: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- color: #fff;
- box-shadow: 0 6px 14px rgba(8, 127, 87, 0.2);
- position: relative;
- z-index: 1;
- }
- .flow-item.done .flow-dot {
- background: linear-gradient(145deg, #2ecf8f, #0a9f6d);
- }
- .flow-item.warn .flow-dot {
- background: linear-gradient(145deg, #ecc06a, #d6a343);
- box-shadow: 0 6px 14px rgba(214, 163, 67, 0.28);
- }
- .flow-item.danger .flow-dot {
- background: linear-gradient(145deg, #ef7a7a, #d14343);
- box-shadow: 0 6px 14px rgba(209, 67, 67, 0.25);
- }
- .flow-card {
- flex: 1;
- min-width: 0;
- border-radius: 14px;
- padding: 11px 12px;
- background: linear-gradient(160deg, #f7fbf9 0%, #ffffff 55%);
- border: 1px solid #e4eee9;
- box-shadow: 0 4px 12px rgba(20, 55, 40, 0.04);
- }
- .flow-item.warn .flow-card {
- background: linear-gradient(160deg, #fff9ef 0%, #ffffff 60%);
- border-color: #f0e0b8;
- }
- .flow-item.danger .flow-card {
- background: linear-gradient(160deg, #fff4f4 0%, #ffffff 60%);
- border-color: #f3d0d0;
- }
- .flow-card .flow-top {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- gap: 8px;
- margin-bottom: 4px;
- }
- .flow-card .flow-title {
- font-size: 13px;
- font-weight: 700;
- color: #173128;
- line-height: 1.35;
- }
- .flow-card .flow-badge {
- flex-shrink: 0;
- font-size: 10px;
- font-weight: 700;
- padding: 2px 7px;
- border-radius: 999px;
- letter-spacing: 0.02em;
- }
- .flow-item.done .flow-badge {
- background: rgba(10, 159, 109, 0.12);
- color: #087f57;
- }
- .flow-item.warn .flow-badge {
- background: rgba(214, 163, 67, 0.16);
- color: #8f6718;
- }
- .flow-item.danger .flow-badge {
- background: rgba(209, 67, 67, 0.12);
- color: #b13535;
- }
- .flow-card .flow-desc {
- font-size: 12px;
- color: #5d746a;
- line-height: 1.5;
- }
- .flow-card .flow-meta {
- margin-top: 8px;
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- align-items: center;
- }
- .flow-card .flow-meta .chip {
- display: inline-flex;
- align-items: center;
- gap: 4px;
- font-size: 11px;
- color: #6b7b74;
- background: rgba(255, 255, 255, 0.9);
- border: 1px solid #e8f0ec;
- border-radius: 999px;
- padding: 2px 8px;
- }
- .flow-card .flow-meta .van-icon {
- font-size: 12px;
- color: #8aa198;
- }
- .pay-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 10px;
- padding: 10px 12px;
- border-radius: 12px;
- background: #f7faf9;
- margin-bottom: 8px;
- }
- .pay-row:last-child {
- margin-bottom: 0;
- }
- .pay-row .left .name {
- font-size: 13px;
- font-weight: 650;
- color: #173128;
- }
- .pay-row .left .sub {
- margin-top: 2px;
- font-size: 11px;
- color: #8aa198;
- }
- .pay-row .right {
- text-align: right;
- }
- .pay-row .right .amt {
- font-size: 14px;
- font-weight: 750;
- color: #9a7424;
- font-variant-numeric: tabular-nums;
- }
- /* 详情 · 附件 */
- .d-attach-list {
- display: flex;
- flex-direction: column;
- gap: 10px;
- }
- .d-attach-upload {
- width: 100%;
- border: 1.5px dashed #9ad9be;
- border-radius: 14px;
- background:
- linear-gradient(135deg, rgba(233, 249, 242, 0.95), rgba(255, 255, 255, 0.95)),
- #e9f9f2;
- padding: 14px 12px;
- display: flex;
- align-items: center;
- gap: 12px;
- cursor: pointer;
- text-align: left;
- transition: transform .15s, border-color .15s;
- }
- .d-attach-upload:active {
- transform: scale(0.99);
- border-color: #0a9f6d;
- }
- .d-attach-upload .up-icon {
- width: 44px;
- height: 44px;
- border-radius: 12px;
- background: linear-gradient(145deg, #2ecf8f, #0a9f6d);
- color: #fff;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22px;
- box-shadow: 0 6px 14px rgba(10, 159, 109, 0.28);
- flex-shrink: 0;
- }
- .d-attach-upload .up-text {
- flex: 1;
- min-width: 0;
- }
- .d-attach-upload .up-title {
- font-size: 13px;
- font-weight: 700;
- color: #087f57;
- }
- .d-attach-upload .up-sub {
- margin-top: 2px;
- font-size: 11px;
- color: #6b7b74;
- line-height: 1.4;
- }
- .d-attach-card {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px;
- border-radius: 14px;
- background: #fff;
- border: 1px solid #e4eee9;
- box-shadow: 0 4px 14px rgba(20, 55, 40, 0.05);
- position: relative;
- overflow: hidden;
- }
- .d-attach-card::before {
- content: "";
- position: absolute;
- left: 0;
- top: 0;
- bottom: 0;
- width: 3px;
- background: var(--file-accent, #0a9f6d);
- }
- .d-attach-card.ft-pdf { --file-accent: #d14343; }
- .d-attach-card.ft-img { --file-accent: #0a9f6d; }
- .d-attach-card.ft-xls { --file-accent: #189b65; }
- .d-attach-card.ft-doc { --file-accent: #3b82f6; }
- .d-attach-card.ft-other { --file-accent: #d6a343; }
- .d-attach-card .file-icon {
- width: 46px;
- height: 46px;
- border-radius: 13px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 1px;
- flex-shrink: 0;
- color: #fff;
- font-weight: 800;
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
- }
- .d-attach-card.ft-pdf .file-icon {
- background: linear-gradient(145deg, #f07171, #d14343);
- }
- .d-attach-card.ft-img .file-icon {
- background: linear-gradient(145deg, #3dca96, #0a9f6d);
- }
- .d-attach-card.ft-xls .file-icon {
- background: linear-gradient(145deg, #4fbf8a, #189b65);
- }
- .d-attach-card.ft-doc .file-icon {
- background: linear-gradient(145deg, #6ea8ff, #3b82f6);
- }
- .d-attach-card.ft-other .file-icon {
- background: linear-gradient(145deg, #e4bc6a, #d6a343);
- }
- .d-attach-card .file-icon .ext {
- font-size: 10px;
- letter-spacing: 0.04em;
- line-height: 1;
- text-transform: uppercase;
- }
- .d-attach-card .file-icon .van-icon {
- font-size: 16px;
- opacity: 0.95;
- }
- .d-attach-card .file-body {
- flex: 1;
- min-width: 0;
- }
- .d-attach-card .file-name {
- font-size: 13px;
- font-weight: 650;
- color: #173128;
- line-height: 1.35;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .d-attach-card .file-meta {
- margin-top: 4px;
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- font-size: 11px;
- color: #8aa198;
- }
- .d-attach-card .file-ops {
- display: flex;
- flex-direction: column;
- gap: 6px;
- flex-shrink: 0;
- }
- .d-attach-card .file-ops button {
- border: none;
- cursor: pointer;
- width: 32px;
- height: 32px;
- border-radius: 10px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- transition: transform .12s;
- }
- .d-attach-card .file-ops button:active {
- transform: scale(0.94);
- }
- .d-attach-card .file-ops .btn-preview {
- background: var(--brand-green-soft);
- color: var(--brand-green-dark);
- }
- .d-attach-card .file-ops .btn-download {
- background: #f0f5f3;
- color: #4d635a;
- }
- .d-attach-card .file-ops .icon-download {
- width: 14px;
- height: 14px;
- display: block;
- }
- .detail-footer {
- position: absolute;
- left: 0;
- right: 0;
- bottom: 0;
- display: flex;
- gap: 10px;
- padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
- background: rgba(255, 255, 255, 0.96);
- border-top: 1px solid var(--border);
- backdrop-filter: blur(10px);
- z-index: 4;
- }
- .detail-footer .van-button {
- flex: 1;
- height: 44px;
- border-radius: 999px;
- }
- .detail-footer .van-button--primary {
- background: linear-gradient(135deg, #12b37a, #087f57);
- border: none;
- box-shadow: 0 6px 16px rgba(8, 127, 87, 0.28);
- }
- /* ========== 表单 ========== */
- .form-page {
- padding-bottom: 88px;
- }
- .form-section-title {
- margin: 14px 16px 8px;
- font-size: 13px;
- font-weight: 600;
- color: var(--text-muted);
- }
- .form-tip {
- margin: 8px 16px 0;
- font-size: 11px;
- color: var(--text-muted);
- line-height: 1.45;
- }
- .form-footer {
- position: absolute;
- left: 0;
- right: 0;
- bottom: 0;
- display: flex;
- gap: 10px;
- padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
- background: rgba(255, 255, 255, 0.96);
- border-top: 1px solid var(--border);
- backdrop-filter: blur(8px);
- z-index: 3;
- }
- .form-footer .van-button {
- flex: 1;
- height: 44px;
- }
- /* ========== 组件展示 ========== */
- .comps-page {
- flex: 1;
- display: flex;
- flex-direction: column;
- min-height: 0;
- background:
- radial-gradient(400px 220px at 100% 0%, rgba(214, 163, 67, 0.14), transparent 55%),
- radial-gradient(360px 200px at 0% 20%, rgba(10, 159, 109, 0.1), transparent 50%),
- linear-gradient(180deg, #e8f5ef 0%, #f4f7f6 40%, #f7f9f8 100%);
- }
- .comps-page .screen-scroll {
- background: transparent;
- }
- .comps-hero {
- background:
- radial-gradient(220px 140px at 90% 15%, rgba(255, 255, 255, 0.18), transparent 60%),
- linear-gradient(155deg, #066647 0%, #0a9f6d 48%, #12b37a 100%);
- padding: 44px 14px 22px;
- border-radius: 0 0 28px 28px;
- color: #fff;
- position: relative;
- overflow: hidden;
- }
- .comps-hero::after {
- content: "";
- position: absolute;
- width: 160px;
- height: 160px;
- right: -40px;
- bottom: -60px;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.08);
- pointer-events: none;
- }
- .comps-hero .van-nav-bar {
- background: transparent;
- margin: 0 -6px;
- }
- .comps-hero .van-hairline--bottom::after {
- display: none;
- }
- .comps-hero-copy {
- position: relative;
- z-index: 1;
- margin: 4px 2px 0;
- }
- .comps-hero-copy .eyebrow {
- font-size: 11px;
- letter-spacing: 0.08em;
- opacity: 0.82;
- margin-bottom: 4px;
- }
- .comps-hero-copy h2 {
- margin: 0;
- font-size: 22px;
- font-weight: 750;
- }
- .comps-hero-copy .subline {
- margin-top: 6px;
- font-size: 12px;
- opacity: 0.9;
- line-height: 1.45;
- }
- .comp-section {
- padding: 14px 12px 28px;
- }
- .comp-block {
- position: relative;
- background: #fff;
- border: 1px solid rgba(220, 235, 228, 0.95);
- border-radius: 18px;
- padding: 0;
- margin-bottom: 12px;
- box-shadow: 0 8px 22px rgba(20, 55, 40, 0.05);
- overflow: hidden;
- }
- .comp-block .comp-hd {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 12px 14px;
- background: linear-gradient(135deg, #f3faf6 0%, #ffffff 70%);
- border-bottom: 1px solid #eef4f1;
- }
- .comp-block .comp-hd .hd-icon {
- width: 32px;
- height: 32px;
- border-radius: 10px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- color: #fff;
- background: linear-gradient(145deg, #2ecf8f, #0a9f6d);
- box-shadow: 0 4px 10px rgba(10, 159, 109, 0.22);
- flex-shrink: 0;
- }
- .comp-block .comp-hd .hd-icon.tone-amber {
- background: linear-gradient(145deg, #ecc06a, #d6a343);
- box-shadow: 0 4px 10px rgba(214, 163, 67, 0.25);
- }
- .comp-block .comp-hd .hd-icon.tone-rose {
- background: linear-gradient(145deg, #f07171, #d14343);
- box-shadow: 0 4px 10px rgba(209, 67, 67, 0.22);
- }
- .comp-block .comp-hd .hd-icon.tone-slate {
- background: linear-gradient(145deg, #8aa198, #5d746a);
- box-shadow: 0 4px 10px rgba(93, 116, 106, 0.2);
- }
- .comp-block .comp-hd h4 {
- margin: 0;
- font-size: 14px;
- font-weight: 750;
- color: #173128;
- line-height: 1.2;
- }
- .comp-block .comp-hd .hd-sub {
- margin-top: 2px;
- font-size: 11px;
- color: #8aa198;
- }
- .comp-block .comp-bd {
- padding: 14px;
- }
- .comp-row {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- align-items: center;
- margin-bottom: 10px;
- }
- .comp-row:last-child { margin-bottom: 0; }
- .label-muted {
- font-size: 11px;
- color: #8aa198;
- width: 100%;
- margin-bottom: 2px;
- }
- .comps-page .van-button--primary {
- background: linear-gradient(135deg, #12b37a, #087f57);
- border: none;
- box-shadow: 0 4px 12px rgba(8, 127, 87, 0.22);
- }
- .comps-page .van-button--primary.van-button--plain {
- background: #fff;
- box-shadow: none;
- color: #0a9f6d;
- border-color: #0a9f6d;
- }
- .comps-page .van-button--round {
- border-radius: 999px;
- }
- .tag-grid {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- }
- .demo-tag {
- display: inline-flex;
- align-items: center;
- gap: 5px;
- padding: 5px 10px;
- border-radius: 999px;
- font-size: 12px;
- font-weight: 650;
- }
- .demo-tag i {
- width: 6px;
- height: 6px;
- border-radius: 50%;
- background: currentColor;
- }
- .demo-tag.t-warn {
- background: #fff3e0;
- color: #a67b20;
- }
- .demo-tag.t-primary {
- background: #0a9f6d;
- color: #fff;
- }
- .demo-tag.t-success {
- background: rgba(24, 155, 101, 0.12);
- color: #147a50;
- }
- .demo-tag.t-danger {
- background: #fdecec;
- color: #d14343;
- }
- .demo-tag.t-muted {
- background: #eef2f1;
- color: #6b7b74;
- }
- .comp-cell-list {
- display: flex;
- flex-direction: column;
- gap: 8px;
- }
- .comp-cell-item {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px 12px;
- border-radius: 14px;
- background: linear-gradient(160deg, #f7fbf9 0%, #ffffff 55%);
- border: 1px solid #e4eee9;
- box-shadow: 0 4px 12px rgba(20, 55, 40, 0.04);
- cursor: pointer;
- text-align: left;
- width: 100%;
- transition: transform .15s, border-color .15s;
- }
- .comp-cell-item:active {
- transform: scale(0.99);
- border-color: #b7e5d0;
- }
- .comp-cell-item .ci-icon {
- width: 40px;
- height: 40px;
- border-radius: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 18px;
- color: #fff;
- flex-shrink: 0;
- background: linear-gradient(145deg, #2ecf8f, #0a9f6d);
- box-shadow: 0 4px 10px rgba(10, 159, 109, 0.2);
- }
- .comp-cell-item .ci-icon.tone-amber {
- background: linear-gradient(145deg, #ecc06a, #d6a343);
- box-shadow: 0 4px 10px rgba(214, 163, 67, 0.22);
- }
- .comp-cell-item .ci-icon.tone-slate {
- background: linear-gradient(145deg, #8aa198, #5d746a);
- box-shadow: 0 4px 10px rgba(93, 116, 106, 0.18);
- }
- .comp-cell-item .ci-body {
- flex: 1;
- min-width: 0;
- }
- .comp-cell-item .ci-title {
- font-size: 14px;
- font-weight: 700;
- color: #173128;
- line-height: 1.3;
- }
- .comp-cell-item .ci-label {
- margin-top: 2px;
- font-size: 11px;
- color: #8aa198;
- line-height: 1.4;
- }
- .comp-cell-item .ci-right {
- display: flex;
- align-items: center;
- gap: 6px;
- flex-shrink: 0;
- color: #8aa198;
- font-size: 13px;
- font-weight: 550;
- }
- .comp-cell-item .ci-right .val {
- color: #4d635a;
- }
- .comp-feedback-grid {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 8px;
- }
- .comp-feedback-grid .fb-btn {
- border: 1px solid #e4eee9;
- background: linear-gradient(160deg, #f7fbf9, #fff);
- border-radius: 14px;
- padding: 14px 8px;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 8px;
- cursor: pointer;
- color: #173128;
- font-size: 12px;
- font-weight: 650;
- transition: transform .15s;
- }
- .comp-feedback-grid .fb-btn:active {
- transform: scale(0.97);
- }
- .comp-feedback-grid .fb-btn .fb-icon {
- width: 40px;
- height: 40px;
- border-radius: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 20px;
- color: #fff;
- }
- .comp-feedback-grid .fb-btn.fb-toast .fb-icon {
- background: linear-gradient(145deg, #2ecf8f, #0a9f6d);
- }
- .comp-feedback-grid .fb-btn.fb-dialog .fb-icon {
- background: linear-gradient(145deg, #ecc06a, #d6a343);
- }
- .comp-feedback-grid .fb-btn.fb-sheet .fb-icon {
- background: linear-gradient(145deg, #8aa198, #5d746a);
- }
- .comp-steps-wrap {
- padding: 8px 4px 0;
- border-radius: 12px;
- background: #f7faf9;
- }
- .comp-seg {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 4px;
- padding: 4px;
- border-radius: 14px;
- background: #eef6f2;
- margin-bottom: 12px;
- }
- .comp-seg button {
- border: none;
- background: transparent;
- border-radius: 11px;
- padding: 8px 4px;
- font-size: 12px;
- color: #6b7b74;
- cursor: pointer;
- }
- .comp-seg button.active {
- background: #fff;
- color: #087f57;
- font-weight: 700;
- box-shadow: 0 4px 12px rgba(8, 127, 87, 0.12);
- }
- .comp-notice-stack {
- display: flex;
- flex-direction: column;
- gap: 12px;
- }
- .comp-notice-bar {
- border-radius: 14px;
- overflow: hidden;
- border: 1px solid #c7e6d6;
- box-shadow: 0 4px 12px rgba(10, 159, 109, 0.08);
- min-height: 44px;
- }
- .comp-empty-box {
- border-radius: 16px;
- background: linear-gradient(165deg, #eef9f3 0%, #ffffff 55%);
- border: 1.5px dashed #b7e5d0;
- padding: 28px 16px 24px;
- min-height: 220px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.8);
- }
- .comp-empty-box .van-empty {
- padding: 0;
- }
- .comp-empty-box .van-empty__description {
- margin-top: 12px;
- color: #5d746a;
- font-size: 13px;
- }
- .comp-empty-box .empty-tip {
- margin-top: 4px;
- font-size: 11px;
- color: #8aa198;
- text-align: center;
- line-height: 1.45;
- }
- .comp-loading-box {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 12px;
- padding: 28px 16px;
- min-height: 120px;
- border-radius: 16px;
- background: linear-gradient(165deg, #f3faf6 0%, #ffffff 60%);
- border: 1px solid #e4eee9;
- color: #087f57;
- font-size: 13px;
- font-weight: 650;
- box-shadow: 0 4px 12px rgba(20, 55, 40, 0.04);
- }
- .comp-loading-box .load-sub {
- font-size: 11px;
- font-weight: 500;
- color: #8aa198;
- }
- .comps-page .comp-attach-list {
- display: flex;
- flex-direction: column;
- gap: 10px;
- width: 100%;
- }
- .comps-page .comp-upload-tile {
- width: 100%;
- border: 1.5px dashed #9ad9be;
- border-radius: 16px;
- background:
- radial-gradient(120px 80px at 12% 30%, rgba(46, 207, 143, 0.16), transparent 70%),
- linear-gradient(145deg, #e9f9f2 0%, #ffffff 55%);
- padding: 16px 14px;
- display: flex;
- align-items: center;
- gap: 12px;
- cursor: pointer;
- text-align: left;
- transition: transform .15s, border-color .15s, box-shadow .15s;
- box-shadow: 0 6px 16px rgba(10, 159, 109, 0.08);
- }
- .comps-page .comp-upload-tile:active {
- transform: scale(0.99);
- border-color: #0a9f6d;
- box-shadow: 0 4px 12px rgba(10, 159, 109, 0.12);
- }
- .comps-page .comp-upload-tile .up-icon {
- width: 48px;
- height: 48px;
- border-radius: 14px;
- background: linear-gradient(145deg, #2ecf8f, #0a9f6d);
- color: #fff;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 24px;
- box-shadow: 0 6px 14px rgba(10, 159, 109, 0.28);
- flex-shrink: 0;
- }
- .comps-page .comp-upload-tile .up-text {
- flex: 1;
- min-width: 0;
- }
- .comps-page .comp-upload-tile .up-title {
- display: block;
- font-size: 14px;
- font-weight: 750;
- color: #087f57;
- }
- .comps-page .comp-upload-tile .up-sub {
- display: block;
- margin-top: 3px;
- font-size: 11px;
- color: #6b7b74;
- line-height: 1.45;
- }
- .comps-page .comp-file-card {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px;
- border-radius: 14px;
- background: #fff;
- border: 1px solid #e4eee9;
- box-shadow: 0 4px 14px rgba(20, 55, 40, 0.05);
- position: relative;
- overflow: hidden;
- }
- .comps-page .comp-file-card::before {
- content: "";
- position: absolute;
- left: 0;
- top: 0;
- bottom: 0;
- width: 3px;
- background: #3b82f6;
- }
- .comps-page .comp-file-card .file-icon {
- width: 46px;
- height: 46px;
- border-radius: 13px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 1px;
- flex-shrink: 0;
- color: #fff;
- font-weight: 800;
- background: linear-gradient(145deg, #6ea8ff, #3b82f6);
- }
- .comps-page .comp-file-card .file-icon .ext {
- font-size: 10px;
- letter-spacing: 0.04em;
- line-height: 1;
- text-transform: uppercase;
- }
- .comps-page .comp-file-card .file-icon .van-icon {
- font-size: 16px;
- }
- .comps-page .comp-file-card .file-body {
- flex: 1;
- min-width: 0;
- }
- .comps-page .comp-file-card .file-name {
- font-size: 13px;
- font-weight: 650;
- color: #173128;
- line-height: 1.35;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .comps-page .comp-file-card .file-meta {
- margin-top: 4px;
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- font-size: 11px;
- color: #8aa198;
- }
- .comps-page .comp-file-card .file-ops {
- display: flex;
- flex-direction: column;
- gap: 6px;
- flex-shrink: 0;
- }
- .comps-page .comp-file-card .file-ops button {
- border: none;
- cursor: pointer;
- width: 32px;
- height: 32px;
- border-radius: 10px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- }
- .comps-page .comp-file-card .file-ops .btn-preview {
- background: var(--brand-green-soft);
- color: var(--brand-green-dark);
- }
- .comps-page .comp-file-card .file-ops .btn-del {
- background: #fdecec;
- color: #d14343;
- }
- /* 表单页等仍可能用到的旧附件样式 */
- .upload-card {
- width: 88px;
- height: 88px;
- border: 1.5px dashed #9ad9be;
- border-radius: 14px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 6px;
- color: #087f57;
- background: linear-gradient(145deg, #e9f9f2, #fff);
- font-size: 11px;
- font-weight: 650;
- }
- .attach-card {
- width: 140px;
- min-height: 88px;
- border: 1px solid #e4eee9;
- border-radius: 14px;
- padding: 10px 12px;
- background: #fff;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- font-size: 11px;
- }
- .attach-card .name {
- color: #173128;
- font-weight: 650;
- word-break: break-all;
- line-height: 1.35;
- }
- .attach-card .ops {
- display: flex;
- gap: 10px;
- color: #0a9f6d;
- font-weight: 650;
- }
- .demo-badge {
- display: inline-flex;
- align-items: center;
- gap: 4px;
- padding: 2px 8px;
- border-radius: 999px;
- font-size: 11px;
- background: var(--brand-green-soft);
- color: var(--brand-green-dark);
- }
- @media (max-width: 900px) {
- #app {
- grid-template-columns: 1fr;
- }
- .doc-panel {
- position: static;
- }
- .phone {
- width: min(390px, 100%);
- height: min(844px, calc(100vh - 120px));
- min-height: 640px;
- }
- }
- @media (prefers-reduced-motion: reduce) {
- * { transition: none !important; }
- }
- </style>
- </head>
- <body>
- <div id="app">
- <aside class="doc-panel">
- <h1>移动端 UI Demo</h1>
- <p class="sub">
- Vant 4 + 三墙 Token(#0a9f6d)。首页为权限菜单卡片;列表 / 新增编辑 / 组件风格可在下方切换预览。
- </p>
- <div class="section-title">页面预览</div>
- <div class="screen-nav">
- <button
- v-for="s in screens"
- :key="s.key"
- :class="{ active: screen === s.key }"
- type="button"
- @click="goScreen(s.key)"
- >{{ s.label }}</button>
- </div>
- <div class="section-title">设计 Token</div>
- <div class="token-row">
- <span class="token-chip"><i style="background:#0a9f6d"></i>#0a9f6d</span>
- <span class="token-chip"><i style="background:#087f57"></i>#087f57</span>
- <span class="token-chip"><i style="background:#e9f9f2"></i>#e9f9f2</span>
- <span class="token-chip"><i style="background:#d6a343"></i>#d6a343</span>
- <span class="token-chip"><i style="background:#f5f8f7"></i>#f5f8f7</span>
- </div>
- <div class="section-title">权限说明</div>
- <ul class="notes">
- <li>菜单来自「菜单管理 → 移动端菜单」树</li>
- <li>仅展示当前登录人角色/职位拥有的菜单权限</li>
- <li>卡片:上 Icon、下标题;建议 4 列宫格</li>
- <li>本 Demo 用右侧开关模拟「无某菜单权限」</li>
- </ul>
- <div class="section-title" v-if="screen === 'home'">权限模拟</div>
- <div v-if="screen === 'home'" style="display:flex;flex-direction:column;gap:8px;">
- <van-checkbox
- v-for="m in allMenus"
- :key="m.id"
- v-model="grantedMap[m.id]"
- shape="square"
- icon-size="16px"
- >{{ m.title }}</van-checkbox>
- </div>
- </aside>
- <div class="phone-stage">
- <div class="phone">
- <div class="phone-screen">
- <!-- ===== 首页 ===== -->
- <template v-if="screen === 'home'">
- <div class="screen-scroll">
- <header class="home-header">
- <div class="brand">中科兴皖 · 移动工作台</div>
- <h2>你好,{{ userName }}</h2>
- <div class="meta">
- <span>{{ roleName }}</span>
- <span class="dot"></span>
- <span>{{ deptName }}</span>
- </div>
- </header>
- <div class="home-body">
- <div class="perm-banner">
- 菜单卡片按角色权限动态渲染;无权限的功能入口不会出现在宫格中。
- </div>
- <section class="panel">
- <div class="panel-hd">
- <h3>功能菜单</h3>
- <span class="hint">共 {{ visibleMenus.length }} 项</span>
- </div>
- <div v-if="visibleMenus.length" class="menu-grid">
- <button
- v-for="m in visibleMenus"
- :key="m.id"
- type="button"
- class="menu-card"
- @click="onMenuTap(m)"
- >
- <div class="menu-icon" :class="m.tone">
- <van-icon :name="m.icon" />
- </div>
- <span class="menu-title">{{ m.title }}</span>
- </button>
- </div>
- <div v-else class="list-empty-pad">
- <van-empty description="暂无可用菜单权限" image-size="80" />
- </div>
- </section>
- <section class="panel todo-panel">
- <div class="panel-hd">
- <h3>待办摘要</h3>
- <button type="button" class="demo-badge" style="border:none;cursor:pointer;" @click="screen = 'list'">
- 查看全部
- <van-icon name="arrow" style="font-size:11px;" />
- </button>
- </div>
- <div class="todo-stats">
- <button type="button" class="todo-stat tone-warn" @click="goListTab(2)">
- <span class="stat-icon"><van-icon name="passed" /></span>
- <span class="num">{{ todoStats.approve }}</span>
- <span class="label">待审批</span>
- </button>
- <button type="button" class="todo-stat tone-pay" @click="goListTab(2)">
- <span class="stat-icon"><van-icon name="balance-pay" /></span>
- <span class="num">{{ todoStats.pay }}</span>
- <span class="label">待确认缴费</span>
- </button>
- <button type="button" class="todo-stat tone-danger" @click="goListTab(0)">
- <span class="stat-icon"><van-icon name="warning-o" /></span>
- <span class="num">{{ todoStats.alert }}</span>
- <span class="label">异常提醒</span>
- </button>
- </div>
- <div class="todo-list">
- <button
- v-for="t in todoItems"
- :key="t.id"
- type="button"
- class="todo-item"
- @click="screen = 'list'"
- >
- <div class="ti-icon" :class="t.iconClass">
- <van-icon :name="t.icon" />
- </div>
- <div class="ti-body">
- <div class="ti-title">{{ t.title }}</div>
- <div class="ti-desc">{{ t.desc }}</div>
- </div>
- <div class="ti-right">
- <van-tag :type="t.tagType" plain size="medium">{{ t.tag }}</van-tag>
- <span class="ti-time">{{ t.time }}</span>
- </div>
- </button>
- </div>
- </section>
- </div>
- </div>
- </template>
- <!-- ===== 列表 ===== -->
- <template v-else-if="screen === 'list'">
- <div class="list-page">
- <div class="screen-scroll">
- <header class="list-hero">
- <van-nav-bar left-arrow @click-left="screen = 'home'">
- <template #title>
- <span style="font-weight:600;">业务列表</span>
- </template>
- <template #right>
- <van-icon name="plus" size="20" @click="openForm('create')" />
- </template>
- </van-nav-bar>
- <div class="list-hero-copy">
- <div class="eyebrow">WORKSPACE</div>
- <h2>今日待办一览</h2>
- <div class="subline">
- <span>当前展示</span>
- <em>{{ filteredList.length }} 条</em>
- <span v-if="keyword">· 已筛选</span>
- </div>
- </div>
- <div class="list-search">
- <van-search
- v-model="keyword"
- shape="round"
- background="transparent"
- placeholder="搜索单号 / 名称 / 负责人"
- />
- </div>
- </header>
- <div class="list-filter-dock">
- <div class="seg-tabs">
- <button
- v-for="(tab, idx) in listTabs"
- :key="tab.key"
- type="button"
- class="seg-tab"
- :class="{ active: listTab === idx }"
- @click="listTab = idx"
- >
- <span class="lab">{{ tab.label }}</span>
- <span class="count">{{ tabCount(idx) }}</span>
- </button>
- </div>
- </div>
- <div class="list-wrap">
- <article
- v-for="item in filteredList"
- :key="item.id"
- class="biz-card"
- :class="'status-' + item.tagType"
- >
- <div class="card-inner">
- <div class="row-top">
- <div class="type-icon">
- <van-icon :name="item.icon" />
- </div>
- <div class="title-wrap">
- <div class="type-chip">{{ item.typeLabel }}</div>
- <div class="title">{{ item.title }}</div>
- </div>
- <span class="status-pill">{{ item.status }}</span>
- </div>
- <div class="body-row">
- <p class="desc-box">{{ item.desc }}</p>
- <div class="side-metric">
- <template v-if="item.amount">
- <span class="amt-label">金额</span>
- <span class="amt">¥{{ item.amount }}</span>
- </template>
- <template v-else>
- <span class="amt-label">进度</span>
- <span class="amt" style="font-size:13px;">{{ item.progress || '—' }}</span>
- </template>
- <span class="time">{{ item.time }}</span>
- </div>
- </div>
- <div class="meta-row">
- <span class="mi code-pill">{{ item.code }}</span>
- <span class="mi"><van-icon name="manager-o" />{{ item.owner }}</span>
- </div>
- <div class="footer">
- <span class="left-hint"><i></i>{{ item.hint || '点击操作处理' }}</span>
- <div class="ops">
- <van-button size="small" plain hairline type="default" @click="openDetail(item)">查看</van-button>
- <van-button size="small" type="primary" @click="openForm('edit', item)">处理</van-button>
- </div>
- </div>
- </div>
- </article>
- <div v-if="!filteredList.length" class="list-empty-pad">
- <van-empty description="暂无数据" />
- </div>
- <div v-else class="list-end"><span>已经到底了</span></div>
- </div>
- </div>
- </div>
- </template>
- <!-- ===== 详情 ===== -->
- <template v-else-if="screen === 'detail'">
- <div class="detail-page" v-if="detail">
- <div class="screen-scroll">
- <header class="detail-hero" :class="'status-' + detail.tagType">
- <van-nav-bar left-arrow @click-left="screen = 'list'">
- <template #title>
- <span style="font-weight:600;">业务详情</span>
- </template>
- <template #right>
- <van-icon name="ellipsis" size="20" @click="showAction = true" />
- </template>
- </van-nav-bar>
- <div class="detail-hero-body">
- <div class="type-line">
- <span class="type-chip">{{ detail.typeLabel }}</span>
- <span class="status-chip">{{ detail.status }}</span>
- </div>
- <h2>{{ detail.title }}</h2>
- <div class="code">{{ detail.code }}</div>
- </div>
- </header>
- <div class="detail-metric-dock">
- <div class="detail-metric">
- <span class="lab">{{ detail.amount ? '金额' : '进度' }}</span>
- <span class="val" :class="{ money: !!detail.amount }">
- {{ detail.amount ? ('¥' + detail.amount) : (detail.progress || '—') }}
- </span>
- </div>
- <div class="detail-metric">
- <span class="lab">负责人</span>
- <span class="val">{{ detail.owner }}</span>
- </div>
- <div class="detail-metric">
- <span class="lab">更新时间</span>
- <span class="val">{{ detail.time }}</span>
- </div>
- </div>
- <div class="detail-body">
- <section class="detail-section">
- <div class="sec-hd"><h3>基础信息</h3></div>
- <div class="kv-list">
- <div class="kv-row"><span class="k">业务类型</span><span class="v">{{ detail.typeLabel }}</span></div>
- <div class="kv-row"><span class="k">所属部门</span><span class="v">{{ detail.dept || deptName }}</span></div>
- <div class="kv-row"><span class="k">客户/对象</span><span class="v">{{ detail.customer || '—' }}</span></div>
- <div class="kv-row"><span class="k">创建时间</span><span class="v">{{ detail.createdAt || '—' }}</span></div>
- </div>
- <p class="detail-note" style="margin-top:12px;">{{ detail.desc }}</p>
- </section>
- <section class="detail-section">
- <div class="sec-hd"><h3>业务进度</h3></div>
- <van-steps :active="detail.stepActive" active-color="#0a9f6d" inactive-color="#dcebe4">
- <van-step v-for="s in detail.steps" :key="s">{{ s }}</van-step>
- </van-steps>
- </section>
- <section class="detail-section" v-if="detail.payments && detail.payments.length">
- <div class="sec-hd">
- <h3>缴费明细</h3>
- <button type="button" class="more" @click="toast('查看全部缴费')">全部</button>
- </div>
- <div
- v-for="p in detail.payments"
- :key="p.id"
- class="pay-row"
- >
- <div class="left">
- <div class="name">{{ p.name }}</div>
- <div class="sub">{{ p.sub }}</div>
- </div>
- <div class="right">
- <div class="amt">¥{{ p.amount }}</div>
- <van-tag :type="p.tagType" plain size="medium" style="margin-top:4px;">{{ p.status }}</van-tag>
- </div>
- </div>
- </section>
- <section class="detail-section">
- <div class="sec-hd">
- <h3>附件</h3>
- <span style="font-size:11px;color:#8aa198;">{{ detail.attachments.length }} 个文件</span>
- </div>
- <div class="d-attach-list">
- <button type="button" class="d-attach-upload" @click="toast('上传附件')">
- <span class="up-icon"><van-icon name="plus" /></span>
- <span class="up-text">
- <span class="up-title">添加附件</span>
- <span class="up-sub">支持 PDF / 图片 / Office,可多选上传</span>
- </span>
- <van-icon name="arrow" color="#0a9f6d" />
- </button>
- <div
- v-for="a in detail.attachments"
- :key="a.name"
- class="d-attach-card"
- :class="fileTypeClass(a.name)"
- >
- <div class="file-icon">
- <van-icon :name="fileTypeIcon(a.name)" />
- <span class="ext">{{ fileExt(a.name) }}</span>
- </div>
- <div class="file-body">
- <div class="file-name">{{ a.name }}</div>
- <div class="file-meta">
- <span>{{ a.size || '—' }}</span>
- <span>{{ a.time || '—' }}</span>
- </div>
- </div>
- <div class="file-ops">
- <button type="button" class="btn-preview" title="预览" @click="toast('预览 ' + a.name)">
- <van-icon name="eye-o" />
- </button>
- <button type="button" class="btn-download" title="下载" @click="toast('下载 ' + a.name)">
- <svg class="icon-download" viewBox="0 0 24 24" aria-hidden="true">
- <path fill="currentColor" d="M12 3a1 1 0 0 1 1 1v8.59l2.3-2.3a1 1 0 1 1 1.4 1.42l-4 4a1 1 0 0 1-1.4 0l-4-4a1 1 0 1 1 1.4-1.42L11 12.59V4a1 1 0 0 1 1-1Z"/>
- <path fill="currentColor" d="M5 15a1 1 0 0 1 1 1v2h12v-2a1 1 0 1 1 2 0v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2a1 1 0 0 1 1-1Z"/>
- </svg>
- </button>
- </div>
- </div>
- </div>
- </section>
- <section class="detail-section">
- <div class="sec-hd">
- <h3>流转记录</h3>
- <span style="font-size:11px;color:#8aa198;">{{ detail.logs.length }} 步</span>
- </div>
- <div class="flow-list">
- <div
- v-for="(log, idx) in detail.logs"
- :key="idx"
- class="flow-item"
- :class="log.tone"
- >
- <div class="flow-rail">
- <div class="flow-dot">
- <van-icon :name="log.icon || 'checked'" />
- </div>
- </div>
- <div class="flow-card">
- <div class="flow-top">
- <div class="flow-title">{{ log.title }}</div>
- <span class="flow-badge">{{ log.badge || (log.tone === 'warn' ? '进行中' : '已完成') }}</span>
- </div>
- <div class="flow-desc">{{ log.desc }}</div>
- <div class="flow-meta">
- <span class="chip" v-if="log.actor">
- <van-icon name="manager-o" />{{ log.actor }}
- </span>
- <span class="chip">
- <van-icon name="clock-o" />{{ log.time }}
- </span>
- </div>
- </div>
- </div>
- </div>
- </section>
- </div>
- </div>
- <div class="detail-footer">
- <van-button plain hairline type="default" @click="screen = 'list'">返回</van-button>
- <van-button type="primary" @click="openForm('edit', detail)">编辑处理</van-button>
- </div>
- </div>
- </template>
- <!-- ===== 新增 / 编辑 ===== -->
- <template v-else-if="screen === 'form'">
- <div class="page-nav">
- <van-nav-bar
- :title="formMode === 'create' ? '新增业务' : '编辑业务'"
- left-arrow
- @click-left="screen = 'list'"
- />
- </div>
- <div class="screen-scroll form-page">
- <div class="form-section-title">基础信息</div>
- <van-form @submit="onSubmit">
- <van-cell-group inset>
- <van-field
- v-model="form.name"
- name="name"
- label="名称"
- placeholder="请输入名称"
- :rules="[{ required: true, message: '请填写名称' }]"
- />
- <van-field
- v-model="form.code"
- name="code"
- label="业务单号"
- placeholder="系统自动生成或手工录入"
- />
- <van-field
- v-model="form.typeLabel"
- is-link
- readonly
- name="type"
- label="类型"
- placeholder="请选择类型"
- @click="showTypePicker = true"
- />
- <van-field name="urgent" label="加急">
- <template #input>
- <van-switch v-model="form.urgent" size="20px" active-color="#0a9f6d" />
- </template>
- </van-field>
- </van-cell-group>
- <div class="form-section-title">业务详情</div>
- <van-cell-group inset>
- <van-field
- v-model="form.owner"
- name="owner"
- label="负责人"
- placeholder="请输入负责人"
- />
- <van-field
- v-model="form.date"
- is-link
- readonly
- name="date"
- label="业务日期"
- placeholder="选择日期"
- @click="showDate = true"
- />
- <van-field
- v-model="form.amount"
- type="number"
- name="amount"
- label="金额"
- placeholder="0.00"
- >
- <template #right-icon>元</template>
- </van-field>
- <van-field
- v-model="form.remark"
- rows="3"
- autosize
- type="textarea"
- maxlength="200"
- name="remark"
- label="备注"
- placeholder="选填,最多 200 字"
- show-word-limit
- />
- </van-cell-group>
- <div class="form-section-title">附件</div>
- <van-cell-group inset>
- <van-cell title="协议附件" label="支持多附件,逗号分隔存储">
- <template #value>
- <div style="display:flex;gap:8px;justify-content:flex-end;">
- <div class="upload-card">
- <van-icon name="plus" size="20" />
- <span>上传</span>
- </div>
- </div>
- </template>
- </van-cell>
- <van-cell>
- <div style="display:flex;gap:8px;flex-wrap:wrap;">
- <div class="attach-card">
- <div class="name">协议扫描件.pdf</div>
- <div class="ops">
- <span>预览</span>
- <span>删除</span>
- </div>
- </div>
- </div>
- </van-cell>
- </van-cell-group>
- <p class="form-tip">表单:label 在左、控件在右(Vant Field 默认);必填项校验失败时 Toast / 字段红字提示。底栏固定「取消 / 保存」。</p>
- <div class="form-footer">
- <van-button plain hairline type="default" @click="screen = 'list'">取消</van-button>
- <van-button round block type="primary" native-type="submit">保存</van-button>
- </div>
- </van-form>
- </div>
- <van-popup v-model:show="showTypePicker" position="bottom" round>
- <van-picker
- :columns="typeColumns"
- @confirm="onTypeConfirm"
- @cancel="showTypePicker = false"
- />
- </van-popup>
- <van-calendar
- v-model:show="showDate"
- @confirm="onDateConfirm"
- color="#0a9f6d"
- />
- </template>
- <!-- ===== 组件风格 ===== -->
- <template v-else>
- <div class="comps-page">
- <div class="screen-scroll">
- <header class="comps-hero">
- <van-nav-bar left-arrow @click-left="screen = 'home'">
- <template #title>
- <span style="font-weight:600;">组件风格</span>
- </template>
- </van-nav-bar>
- <div class="comps-hero-copy">
- <div class="eyebrow">DESIGN SYSTEM</div>
- <h2>组件合集</h2>
- <div class="subline">按钮 · 状态 · 反馈 · 步骤 · 附件等基础样式规范</div>
- </div>
- </header>
- <div class="comp-section">
- <section class="comp-block">
- <div class="comp-hd">
- <div class="hd-icon"><van-icon name="font-o" /></div>
- <div>
- <h4>按钮 Button</h4>
- <div class="hd-sub">每组仅一个实心主色行动</div>
- </div>
- </div>
- <div class="comp-bd">
- <div class="comp-row">
- <span class="label-muted">常用尺寸</span>
- <van-button type="primary" size="small" round>主按钮</van-button>
- <van-button type="primary" size="small" plain hairline round>描边主色</van-button>
- <van-button type="default" size="small" plain hairline round>次要</van-button>
- <van-button type="danger" size="small" plain hairline round>危险</van-button>
- </div>
- <div class="comp-row">
- <van-button type="primary" block round style="height:44px;">通栏主按钮(44px)</van-button>
- </div>
- </div>
- </section>
- <section class="comp-block status-demo">
- <div class="comp-hd">
- <div class="hd-icon tone-amber"><van-icon name="label-o" /></div>
- <div>
- <h4>状态 Tag</h4>
- <div class="hd-sub">语义色:待处理 / 进行中 / 完成 / 异常</div>
- </div>
- </div>
- <div class="comp-bd">
- <div class="tag-grid">
- <span class="demo-tag t-warn"><i></i>待处理</span>
- <span class="demo-tag t-primary"><i></i>进行中</span>
- <span class="demo-tag t-success"><i></i>已完成</span>
- <span class="demo-tag t-danger"><i></i>异常</span>
- <span class="demo-tag t-muted"><i></i>已作废</span>
- </div>
- <div class="comp-row" style="margin-top:12px;">
- <span class="label-muted">Vant Tag 兼容</span>
- <van-tag type="warning" plain round>待处理</van-tag>
- <van-tag type="primary" round>进行中</van-tag>
- <van-tag type="success" plain round>已完成</van-tag>
- <van-tag type="danger" plain round>异常</van-tag>
- </div>
- </div>
- </section>
- <section class="comp-block">
- <div class="comp-hd">
- <div class="hd-icon tone-slate"><van-icon name="orders-o" /></div>
- <div>
- <h4>单元格 Cell</h4>
- <div class="hd-sub">分组列表与跳转行</div>
- </div>
- </div>
- <div class="comp-bd">
- <div class="comp-cell-list">
- <button type="button" class="comp-cell-item" @click="toast('查看内容')">
- <span class="ci-icon"><van-icon name="orders-o" /></span>
- <span class="ci-body">
- <span class="ci-title">标题</span>
- <span class="ci-label">基础信息展示</span>
- </span>
- <span class="ci-right"><span class="val">内容</span></span>
- </button>
- <button type="button" class="comp-cell-item" @click="toast('跳转详情')">
- <span class="ci-icon tone-amber"><van-icon name="guide-o" /></span>
- <span class="ci-body">
- <span class="ci-title">可跳转</span>
- <span class="ci-label">点击进入下一页</span>
- </span>
- <span class="ci-right">
- <span class="val">详情</span>
- <van-icon name="arrow" />
- </span>
- </button>
- <button type="button" class="comp-cell-item" @click="toast('状态:启用')">
- <span class="ci-icon tone-slate"><van-icon name="manager-o" /></span>
- <span class="ci-body">
- <span class="ci-title">带标签</span>
- <span class="ci-label">辅助说明文字</span>
- </span>
- <span class="ci-right">
- <span class="demo-tag t-success" style="padding:3px 8px;font-size:11px;"><i></i>启用</span>
- </span>
- </button>
- </div>
- </div>
- </section>
- <section class="comp-block">
- <div class="comp-hd">
- <div class="hd-icon"><van-icon name="volume-o" /></div>
- <div>
- <h4>通知 / 空态 / 加载</h4>
- <div class="hd-sub">提示条与占位反馈</div>
- </div>
- </div>
- <div class="comp-bd">
- <div class="comp-notice-stack">
- <div class="comp-notice-bar">
- <van-notice-bar
- left-icon="volume-o"
- text="移动端菜单在「菜单管理 → 移动端菜单」下配置,按角色授权后展示。"
- background="#e9f9f2"
- color="#087f57"
- />
- </div>
- <div class="comp-empty-box">
- <van-empty description="暂无相关数据" image-size="110" />
- <div class="empty-tip">可下拉刷新,或点击上方按钮新增一条记录</div>
- </div>
- <div class="comp-loading-box">
- <van-loading type="spinner" color="#0a9f6d" size="28px" />
- <div>内容加载中…</div>
- <div class="load-sub">正在拉取最新业务数据</div>
- </div>
- </div>
- </div>
- </section>
- <section class="comp-block">
- <div class="comp-hd">
- <div class="hd-icon tone-amber"><van-icon name="comment-o" /></div>
- <div>
- <h4>反馈 Feedback</h4>
- <div class="hd-sub">Toast / Dialog / ActionSheet</div>
- </div>
- </div>
- <div class="comp-bd">
- <div class="comp-feedback-grid">
- <button type="button" class="fb-btn fb-toast" @click="toast('操作成功')">
- <span class="fb-icon"><van-icon name="comment-o" /></span>
- Toast
- </button>
- <button type="button" class="fb-btn fb-dialog" @click="showDialog">
- <span class="fb-icon"><van-icon name="info-o" /></span>
- Dialog
- </button>
- <button type="button" class="fb-btn fb-sheet" @click="showAction = true">
- <span class="fb-icon"><van-icon name="wap-nav" /></span>
- Sheet
- </button>
- </div>
- </div>
- </section>
- <section class="comp-block">
- <div class="comp-hd">
- <div class="hd-icon"><van-icon name="cluster-o" /></div>
- <div>
- <h4>分段 / 步骤</h4>
- <div class="hd-sub">筛选分段与流程进度</div>
- </div>
- </div>
- <div class="comp-bd">
- <div class="comp-seg">
- <button type="button" :class="{ active: compTab === 0 }" @click="compTab = 0">资料</button>
- <button type="button" :class="{ active: compTab === 1 }" @click="compTab = 1">进度</button>
- <button type="button" :class="{ active: compTab === 2 }" @click="compTab = 2">附件</button>
- </div>
- <div class="comp-steps-wrap">
- <van-steps :active="compTab === 2 ? 2 : 1" active-color="#0a9f6d" inactive-color="#dcebe4">
- <van-step>提交</van-step>
- <van-step>审批中</van-step>
- <van-step>完成</van-step>
- </van-steps>
- </div>
- </div>
- </section>
- <section class="comp-block">
- <div class="comp-hd">
- <div class="hd-icon tone-rose"><van-icon name="description" /></div>
- <div>
- <h4>附件卡片</h4>
- <div class="hd-sub">上传入口与文件摘要卡</div>
- </div>
- </div>
- <div class="comp-bd">
- <div class="comp-attach-list">
- <button type="button" class="comp-upload-tile" @click="toast('上传附件')">
- <span class="up-icon"><van-icon name="plus" /></span>
- <span class="up-text">
- <span class="up-title">添加附件</span>
- <span class="up-sub">支持 PDF / 图片 / Office,点按即可上传</span>
- </span>
- <van-icon name="arrow" color="#0a9f6d" />
- </button>
- <div class="comp-file-card">
- <div class="file-icon">
- <van-icon name="records" />
- <span class="ext">DOCX</span>
- </div>
- <div class="file-body">
- <div class="file-name">质检报告.docx</div>
- <div class="file-meta">
- <span>1.2 MB</span>
- <span>08-10 10:20</span>
- </div>
- </div>
- <div class="file-ops">
- <button type="button" class="btn-preview" @click="toast('预览')">
- <van-icon name="eye-o" />
- </button>
- <button type="button" class="btn-del" @click="toast('删除')">
- <van-icon name="delete-o" />
- </button>
- </div>
- </div>
- </div>
- </div>
- </section>
- </div>
- </div>
- </div>
- </template>
- <van-action-sheet
- v-model:show="showAction"
- :actions="actions"
- cancel-text="取消"
- close-on-click-action
- @select="onActionSelect"
- />
- </div>
- </div>
- </div>
- </div>
- <script src="https://cdn.jsdelivr.net/npm/vue@3.5.13/dist/vue.global.prod.js"></script>
- <script src="https://cdn.jsdelivr.net/npm/vant@4.9.21/lib/vant.min.js"></script>
- <script>
- const { createApp, computed, reactive, ref } = Vue
- createApp({
- setup() {
- const screen = ref('home')
- const screens = [
- { key: 'home', label: '1. 首页 · 权限菜单卡片' },
- { key: 'list', label: '2. 列表界面' },
- { key: 'detail', label: '3. 详情界面' },
- { key: 'form', label: '4. 新增 / 编辑表单' },
- { key: 'comps', label: '5. 组件风格合集' }
- ]
- const userName = '张三'
- const roleName = '业务专员'
- const deptName = '样本运营部'
- const allMenus = [
- { id: 'm1', title: '待办审批', icon: 'todo-list-o', tone: '', path: 'list' },
- { id: 'm2', title: '采集协议', icon: 'orders-o', tone: '', path: 'list' },
- { id: 'm3', title: '缴费确认', icon: 'balance-pay', tone: 'tone-yellow', path: 'list' },
- { id: 'm4', title: '样本接收', icon: 'logistics', tone: '', path: 'list' },
- { id: 'm5', title: '质检工单', icon: 'certificate', tone: 'tone-slate', path: 'list' },
- { id: 'm6', title: '生产任务', icon: 'cluster-o', tone: '', path: 'list' },
- { id: 'm7', title: '库位出库', icon: 'send-gift-o', tone: 'tone-yellow', path: 'list' },
- { id: 'm8', title: '我的', icon: 'user-o', tone: 'tone-slate', path: 'comps' }
- ]
- const grantedMap = reactive({
- m1: true, m2: true, m3: true, m4: true,
- m5: true, m6: false, m7: true, m8: true
- })
- const visibleMenus = computed(() =>
- allMenus.filter((m) => grantedMap[m.id])
- )
- const todoStats = { approve: 3, pay: 1, alert: 1 }
- const todoItems = [
- {
- id: 't1',
- title: '采集协议审批',
- desc: '王女士 · 分期首期待复核',
- tag: '待审批',
- tagType: 'warning',
- time: '09:20',
- icon: 'todo-list-o',
- iconClass: 'i-approve'
- },
- {
- id: 't2',
- title: '缴费确认',
- desc: 'XY20260810001 · ¥12,800',
- tag: '待确认',
- tagType: 'warning',
- time: '昨天',
- icon: 'balance-pay',
- iconClass: 'i-pay'
- },
- {
- id: 't3',
- title: '质检异常',
- desc: 'QC-8821 · 结论待录入',
- tag: '异常',
- tagType: 'danger',
- time: '08-08',
- icon: 'warning-o',
- iconClass: 'i-alert'
- }
- ]
- const keyword = ref('')
- const listTab = ref(0)
- const listTabs = [
- { key: 'all', label: '全部' },
- { key: 'doing', label: '进行中' },
- { key: 'pending', label: '待处理' },
- { key: 'done', label: '已完成' }
- ]
- const listData = [
- {
- id: 1, title: '脐带血采集协议-王女士', code: 'XY20260810001',
- typeLabel: '采集协议', icon: 'orders-o',
- owner: '李四', time: '08-10 09:20', amount: '12,800', progress: '',
- desc: '分期缴费 · 待财务确认首期款项',
- status: '待处理', tagType: 'warning', hint: '需尽快确认',
- customer: '王女士', dept: '样本运营部', createdAt: '2026-08-08 14:20',
- stepActive: 1,
- steps: ['建档', '缴费确认', '样本采集', '入库'],
- payments: [
- { id: 'p1', name: '第 1 期(首期)', sub: '应缴 08-10 · 微信转账', amount: '6,400', status: '待确认', tagType: 'warning' },
- { id: 'p2', name: '第 2 期', sub: '应缴 09-10 · 未到期', amount: '6,400', status: '待缴费', tagType: 'primary' }
- ],
- attachments: [
- { name: '协议扫描件.pdf', size: '2.4 MB', time: '08-08 14:18' },
- { name: '身份证明.jpg', size: '860 KB', time: '08-08 14:19' }
- ],
- logs: [
- { title: '提交审批', desc: '提交采集协议,等待财务确认首期', time: '08-08 14:22', tone: 'done', actor: '李四', icon: 'edit', badge: '已完成' },
- { title: '流转至财务', desc: '系统自动分配至财务确认岗', time: '08-08 14:23', tone: 'done', actor: '系统', icon: 'cluster-o', badge: '已完成' },
- { title: '待确认缴费', desc: '首期 ¥6,400 等待财务核对附件', time: '08-10 09:20', tone: 'warn', actor: '财务岗', icon: 'balance-pay', badge: '进行中' }
- ]
- },
- {
- id: 2, title: '样本接收单-批次 A12', code: 'YS20260809018',
- typeLabel: '样本接收', icon: 'logistics',
- owner: '王五', time: '08-09 16:40', amount: '', progress: '在途',
- desc: '冷链在途 · 预计今日 18:00 前到达库房',
- status: '进行中', tagType: 'primary', hint: '跟踪物流',
- customer: '批次 A12', dept: '仓储物流部', createdAt: '2026-08-09 09:10',
- stepActive: 2,
- steps: ['发运', '在途', '到库签收', '入库'],
- payments: [],
- attachments: [{ name: '冷链温度记录.pdf', size: '1.1 MB', time: '08-09 11:28' }],
- logs: [
- { title: '创建接收单', desc: '创建样本接收单并绑定批次 A12', time: '08-09 09:10', tone: 'done', actor: '王五', icon: 'orders-o', badge: '已完成' },
- { title: '冷链发出', desc: '承运方已取件,全程温控开启', time: '08-09 11:30', tone: 'done', actor: '承运方', icon: 'logistics', badge: '已完成' },
- { title: '运输中', desc: '当前位置:城东中转站', time: '08-09 16:40', tone: 'warn', actor: '物流跟踪', icon: 'location-o', badge: '进行中' }
- ]
- },
- {
- id: 3, title: '质检工单-QC-8821', code: 'ZJ20260808007',
- typeLabel: '质检工单', icon: 'certificate',
- owner: '赵六', time: '08-08 11:05', amount: '', progress: '阻塞',
- desc: '检测结论待录入,阻塞下游入库流程',
- status: '异常', tagType: 'danger', hint: '优先处理',
- customer: '样本 S-8821', dept: '质检中心', createdAt: '2026-08-07 16:40',
- stepActive: 1,
- steps: ['取样', '检测', '结论录入', '关闭'],
- payments: [],
- attachments: [{ name: '初检原始数据.xlsx', size: '428 KB', time: '08-08 10:48' }],
- logs: [
- { title: '工单创建', desc: '系统根据接收单自动生成质检工单', time: '08-07 16:40', tone: 'done', actor: '系统', icon: 'add-o', badge: '已完成' },
- { title: '检测完成', desc: '完成实验操作,待录入结论', time: '08-08 10:50', tone: 'done', actor: '赵六', icon: 'certificate', badge: '已完成' },
- { title: '异常挂起', desc: '结论超时未录入,已阻塞入库', time: '08-08 11:05', tone: 'danger', actor: '质控规则', icon: 'warning-o', badge: '异常' }
- ]
- },
- {
- id: 4, title: '出库申请-库位 B-03', code: 'CK20260807002',
- typeLabel: '库位出库', icon: 'send-gift-o',
- owner: '张三', time: '08-07 14:22', amount: '', progress: '完结',
- desc: '复核已完成,可归档关闭',
- status: '已完成', tagType: 'success', hint: '已完结',
- customer: '库位 B-03', dept: '仓储物流部', createdAt: '2026-08-06 10:00',
- stepActive: 3,
- steps: ['申请', '复核', '出库', '归档'],
- payments: [],
- attachments: [{ name: '出库单签收.pdf', size: '980 KB', time: '08-07 14:18' }],
- logs: [
- { title: '提交申请', desc: '发起出库申请,指定库位 B-03', time: '08-06 10:00', tone: 'done', actor: '张三', icon: 'edit', badge: '已完成' },
- { title: '复核通过', desc: '库管复核通过,允许出库', time: '08-07 11:10', tone: 'done', actor: '库管', icon: 'passed', badge: '已完成' },
- { title: '出库完成', desc: '实物已交接,单据归档', time: '08-07 14:22', tone: 'done', actor: '张三', icon: 'checked', badge: '已完成' }
- ]
- }
- ]
- const detail = ref(null)
- function openDetail(item) {
- detail.value = item || listData[0]
- screen.value = 'detail'
- }
- function goScreen(key) {
- if (key === 'detail' && !detail.value) detail.value = listData[0]
- screen.value = key
- }
- function fileExt(name) {
- const i = (name || '').lastIndexOf('.')
- return i >= 0 ? name.slice(i + 1).slice(0, 4) : 'FILE'
- }
- function fileTypeClass(name) {
- const ext = fileExt(name).toLowerCase()
- if (ext === 'pdf') return 'ft-pdf'
- if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext)) return 'ft-img'
- if (['xls', 'xlsx', 'csv'].includes(ext)) return 'ft-xls'
- if (['doc', 'docx'].includes(ext)) return 'ft-doc'
- return 'ft-other'
- }
- function fileTypeIcon(name) {
- const cls = fileTypeClass(name)
- if (cls === 'ft-pdf') return 'description'
- if (cls === 'ft-img') return 'photo-o'
- if (cls === 'ft-xls') return 'bar-chart-o'
- if (cls === 'ft-doc') return 'records'
- return 'bag-o'
- }
- function rowsByTab(idx) {
- if (idx === 1) return listData.filter((r) => r.status === '进行中')
- if (idx === 2) return listData.filter((r) => r.status === '待处理')
- if (idx === 3) return listData.filter((r) => r.status === '已完成')
- return listData
- }
- function tabCount(idx) {
- return rowsByTab(idx).length
- }
- const filteredList = computed(() => {
- const kw = keyword.value.trim()
- let rows = rowsByTab(listTab.value)
- if (!kw) return rows
- return rows.filter((r) =>
- r.title.includes(kw) || r.code.includes(kw) || r.owner.includes(kw)
- )
- })
- function goListTab(idx) {
- listTab.value = idx
- screen.value = 'list'
- }
- const formMode = ref('create')
- const form = reactive({
- name: '',
- code: '',
- typeLabel: '',
- urgent: false,
- owner: '',
- date: '',
- amount: '',
- remark: ''
- })
- const showTypePicker = ref(false)
- const showDate = ref(false)
- const typeColumns = [
- { text: '采集协议', value: 'protocol' },
- { text: '样本接收', value: 'sample' },
- { text: '质检工单', value: 'qc' },
- { text: '出库申请', value: 'outbound' }
- ]
- function resetForm() {
- form.name = ''
- form.code = ''
- form.typeLabel = ''
- form.urgent = false
- form.owner = userName
- form.date = ''
- form.amount = ''
- form.remark = ''
- }
- function openForm(mode, item) {
- formMode.value = mode
- if (mode === 'edit' && item) {
- form.name = item.title
- form.code = item.code
- form.typeLabel = '采集协议'
- form.urgent = item.tagType === 'danger'
- form.owner = item.owner
- form.date = '2026-08-10'
- form.amount = '12800'
- form.remark = item.desc
- } else {
- resetForm()
- }
- screen.value = 'form'
- }
- function onTypeConfirm({ selectedOptions }) {
- form.typeLabel = selectedOptions[0]?.text || ''
- showTypePicker.value = false
- }
- function onDateConfirm(val) {
- const d = val
- const y = d.getFullYear()
- const m = String(d.getMonth() + 1).padStart(2, '0')
- const day = String(d.getDate()).padStart(2, '0')
- form.date = `${y}-${m}-${day}`
- showDate.value = false
- }
- function onSubmit() {
- vant.showToast({ message: formMode.value === 'create' ? '已保存(新增)' : '已保存(编辑)', type: 'success' })
- screen.value = 'list'
- }
- function onMenuTap(m) {
- if (m.path === 'list') screen.value = 'list'
- else if (m.path === 'comps') screen.value = 'comps'
- else vant.showToast(m.title)
- }
- function toast(msg) {
- vant.showToast(msg)
- }
- function showDialog() {
- vant.showConfirmDialog({
- title: '确认提交',
- message: '提交后将进入审批流程,是否继续?',
- confirmButtonText: '确认',
- confirmButtonColor: '#0a9f6d'
- }).then(() => {
- vant.showToast('已确认')
- }).catch(() => {})
- }
- const showAction = ref(false)
- const actions = [
- { name: '查看', color: '#1f2a26' },
- { name: '编辑', color: '#0a9f6d' },
- { name: '作废', color: '#d14343' }
- ]
- const compTab = ref(0)
- function onActionSelect(action) {
- vant.showToast(action.name)
- }
- return {
- screen, screens, goScreen, userName, roleName, deptName,
- allMenus, grantedMap, visibleMenus,
- todoStats, todoItems, goListTab,
- keyword, listTab, listTabs, tabCount, filteredList,
- detail, openDetail, fileExt, fileTypeClass, fileTypeIcon,
- formMode, form, showTypePicker, showDate, typeColumns,
- openForm, onTypeConfirm, onDateConfirm, onSubmit, onMenuTap,
- toast, showDialog, showAction, actions, onActionSelect, compTab
- }
- }
- }).use(vant).mount('#app')
- </script>
- </body>
- </html>
|