feat(web-admin): add mp webview sheet and list chrome styles.

Provide shared Bottom Sheet and filter/speed-dial/fab tokens for the transport plan mobile redesign.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-09-15 09:27:11 +08:00
1 parent 3795fa2b5a
commit 8c69a01e16
2 files changed
+284

No files matched your search

@@ -0,0 +1,132 @@
<template>
<Teleport to="body">
<div v-show="modelValue" class="mp-sheet-mask" @click="close" />
<div
v-show="modelValue"
class="mp-sheet"
:class="{ 'mp-sheet--half': half }"
role="dialog"
aria-modal="true"
>
<div class="mp-sheet-handle" />
<div class="mp-sheet-head">
<h3>{{ title }}</h3>
<button class="mp-sheet-close" type="button" @click="close">关闭</button>
</div>
<div v-if="searchable" class="mp-sheet-search">
<input
v-model="keyword"
class="mp-sheet-search-input"
type="search"
:placeholder="searchPlaceholder"
autocomplete="off"
/>
</div>
<div class="mp-sheet-body">
<slot :keyword="keyword" />
</div>
<div v-if="$slots.footer" class="mp-sheet-foot">
<slot name="footer" />
</div>
</div>
</Teleport>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
const props = withDefaults(
defineProps<{
modelValue: boolean
title?: string
searchable?: boolean
searchPlaceholder?: string
half?: boolean
}>(),
{
title: '',
searchable: false,
searchPlaceholder: '搜索',
half: false
}
)
const emit = defineEmits<{ 'update:modelValue': [boolean] }>()
const keyword = ref('')
watch(
() => props.modelValue,
(open) => {
if (open) keyword.value = ''
}
)
function close() {
emit('update:modelValue', false)
}
</script>
<style scoped lang="scss">
.mp-sheet-mask {
position: fixed;
inset: 0;
z-index: 2000;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: none;
}
.mp-sheet {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 2001;
max-height: 78vh;
display: flex;
flex-direction: column;
background: #fff;
border-radius: 16px 16px 0 0;
padding-bottom: env(safe-area-inset-bottom, 0px);
}
.mp-sheet--half { max-height: 50vh; }
.mp-sheet-handle {
width: 36px;
height: 4px;
margin: 8px auto 0;
border-radius: 999px;
background: #dcebe4;
}
.mp-sheet-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
}
.mp-sheet-head h3 { margin: 0; font-size: 16px; }
.mp-sheet-close {
border: none;
background: none;
color: #0a9f6d;
font-size: 14px;
cursor: pointer;
}
.mp-sheet-search { padding: 0 16px 8px; }
.mp-sheet-search-input {
width: 100%;
height: 36px;
padding: 0 12px;
border: 1px solid #dcebe4;
border-radius: 8px;
font-size: 14px;
}
.mp-sheet-body {
flex: 1;
overflow: auto;
padding: 0 8px 12px;
-webkit-overflow-scrolling: touch;
}
.mp-sheet-foot {
display: flex;
gap: 8px;
padding: 10px 16px;
border-top: 1px solid #dcebe4;
}
</style>
+152
View File
@@ -161,3 +161,155 @@
border-radius: 12px;
-webkit-overflow-scrolling: touch;
}
/* === redesign 2026-09-15 === */
.mp-wv-filter-wrap {
position: relative;
z-index: 20;
}
.mp-wv-filter-bar {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
background: #fff;
border-bottom: 1px solid #dcebe4;
position: sticky;
top: 0;
z-index: 20;
}
.mp-wv-filter-trig {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
height: 40px;
font-size: 13px;
color: #1f2a26;
background: #fff;
border: none;
border-right: 1px solid #dcebe4;
cursor: pointer;
}
.mp-wv-filter-trig:last-child { border-right: none; }
.mp-wv-filter-trig.open { color: #0a9f6d; font-weight: 600; }
.mp-wv-filter-panel {
display: none;
position: absolute;
left: 0;
right: 0;
background: #fff;
border-bottom: 1px solid #dcebe4;
max-height: 280px;
overflow: auto;
z-index: 21;
}
.mp-wv-filter-panel.open { display: block; }
.mp-wv-filter-opt {
display: flex;
justify-content: space-between;
width: 100%;
padding: 12px 16px;
font-size: 14px;
text-align: left;
background: #fff;
border: none;
border-bottom: 1px solid #eef4f1;
cursor: pointer;
}
.mp-wv-filter-opt.active { color: #0a9f6d; font-weight: 600; }
.mp-wv-filter-mask {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.35);
z-index: 19;
}
.mp-wv-filter-mask.open { display: block; }
.mp-wv-speed-dial {
position: fixed;
right: 16px;
bottom: calc(16px + env(safe-area-inset-bottom, 0px));
z-index: 30;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
}
.mp-wv-speed-main {
width: 52px;
height: 52px;
border-radius: 50%;
border: none;
background: #0a9f6d;
color: #fff;
font-size: 28px;
line-height: 1;
box-shadow: 0 8px 20px rgba(10, 159, 109, 0.35);
cursor: pointer;
}
.mp-wv-speed-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-radius: 999px;
border: 1px solid #dcebe4;
background: #fff;
font-size: 13px;
cursor: pointer;
}
.mp-wv-speed-item.primary {
background: #0a9f6d;
border-color: #0a9f6d;
color: #fff;
}
.mp-wv-speed-menu { display: none; flex-direction: column; gap: 8px; align-items: flex-end; }
.mp-wv-speed-dial.open .mp-wv-speed-menu { display: flex; }
.mp-wv-speed-mask {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.25);
z-index: 29;
}
.mp-wv-speed-mask.open { display: block; }
.mp-wv-fab-bar {
position: sticky;
bottom: 0;
display: flex;
gap: 8px;
padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
background: #fff;
border-top: 1px solid #dcebe4;
z-index: 15;
}
.mp-wv-fab-bar .mp-wv-btn { flex: 1; }
.mp-wv-fab-bar .mp-wv-btn-more { flex: 0 0 44px; }
.mp-wv-section {
margin: 0 0 12px;
padding: 12px;
background: #fff;
border: 1px solid #dcebe4;
border-radius: 14px;
}
.mp-wv-section h3 {
margin: 0 0 10px;
font-size: 14px;
font-weight: 700;
color: #087f57;
}
.mp-wv-kv {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 6px 0;
font-size: 13px;
border-bottom: 1px solid #eef4f1;
}
.mp-wv-kv span:first-child { color: #6b7b74; flex-shrink: 0; }
.mp-wv-kv span:last-child { text-align: right; word-break: break-all; }
.mp-wv-pad-speed { padding-bottom: 88px; }