Optimized the root .gitignore to exclude virtual environments, node modules, and temp folders to ensure clean and lightweight version tracking. Co-authored-by: Cursor <cursoragent@cursor.com>
2214 lines
48 KiB
JavaScript
2214 lines
48 KiB
JavaScript
// 【重要】必须使用 const Component 作为组件变量名
|
||
// 小程序端 - 登录 + 待办任务页 · 配色青绿 #16D1A1 / #00BFA5
|
||
|
||
const Component = function () {
|
||
var useState = React.useState;
|
||
var h = React.createElement;
|
||
|
||
var antd = window.antd || {};
|
||
var App = antd.App || React.Fragment;
|
||
var message = antd.message || {
|
||
info: function () {},
|
||
success: function () {},
|
||
error: function () {}
|
||
};
|
||
|
||
var COLOR_PRIMARY = '#16D1A1';
|
||
var COLOR_PRIMARY_DEEP = '#00BFA5';
|
||
var COLOR_TEXT = '#1D2129';
|
||
var COLOR_MUTED = '#86909C';
|
||
var COLOR_LINE = '#E5E6EB';
|
||
var COLOR_BG = '#FFFFFF';
|
||
var COLOR_INPUT_BG = '#F2F4F7';
|
||
|
||
var COLOR_PAGE_BG = '#F2F3F5';
|
||
|
||
var FONT_FAMILY =
|
||
'-apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", STHeiti, Tahoma, sans-serif';
|
||
|
||
var routeState = useState('login'); // login | todo
|
||
var route = routeState[0];
|
||
var setRoute = routeState[1];
|
||
|
||
var accountState = useState('');
|
||
var account = accountState[0];
|
||
var setAccount = accountState[1];
|
||
|
||
var passwordState = useState('');
|
||
var password = passwordState[0];
|
||
var setPassword = passwordState[1];
|
||
|
||
var agreeState = useState(false);
|
||
var agree = agreeState[0];
|
||
var setAgree = agreeState[1];
|
||
|
||
var submittingState = useState(false);
|
||
var submitting = submittingState[0];
|
||
var setSubmitting = submittingState[1];
|
||
|
||
var tabState = useState('todo'); // todo | biz | map | mine
|
||
var mainTab = tabState[0];
|
||
var setMainTab = tabState[1];
|
||
|
||
var bizNavState = useState('menu'); // menu | vehicleList | vehicleDetail
|
||
var bizNav = bizNavState[0];
|
||
var setBizNav = bizNavState[1];
|
||
|
||
var vehicleSearchState = useState('');
|
||
var vehicleSearch = vehicleSearchState[0];
|
||
var setVehicleSearch = vehicleSearchState[1];
|
||
|
||
var filterDrawerOpenState = useState(false);
|
||
var filterDrawerOpen = filterDrawerOpenState[0];
|
||
var setFilterDrawerOpen = filterDrawerOpenState[1];
|
||
|
||
var vehicleFilterAppliedState = useState({
|
||
status: '全部',
|
||
province: '全部',
|
||
city: '全部'
|
||
});
|
||
var vehicleFilterApplied = vehicleFilterAppliedState[0];
|
||
var setVehicleFilterApplied = vehicleFilterAppliedState[1];
|
||
|
||
var vehicleFilterDraftState = useState({
|
||
status: '全部',
|
||
province: '全部',
|
||
city: '全部'
|
||
});
|
||
var vehicleFilterDraft = vehicleFilterDraftState[0];
|
||
var setVehicleFilterDraft = vehicleFilterDraftState[1];
|
||
|
||
var selectedVehicleState = useState(null);
|
||
var selectedVehicle = selectedVehicleState[0];
|
||
var setSelectedVehicle = selectedVehicleState[1];
|
||
|
||
var vehicleDetailTabState = useState('basic'); // basic | spec | cert
|
||
var vehicleDetailTab = vehicleDetailTabState[0];
|
||
var setVehicleDetailTab = vehicleDetailTabState[1];
|
||
|
||
function gotoTodoPage() {
|
||
setRoute('todo');
|
||
setMainTab('todo');
|
||
}
|
||
|
||
function handleLogin() {
|
||
if (submitting) return;
|
||
if (!account || !password) {
|
||
message.info('请输入用户名和密码');
|
||
return;
|
||
}
|
||
if (!agree) {
|
||
message.info('请先阅读并同意相关协议');
|
||
return;
|
||
}
|
||
setSubmitting(true);
|
||
setTimeout(function () {
|
||
setSubmitting(false);
|
||
message.success('登录成功');
|
||
gotoTodoPage();
|
||
}, 450);
|
||
}
|
||
|
||
function handlePhoneAuthLogin() {
|
||
if (!agree) {
|
||
message.info('请先阅读并同意相关协议');
|
||
return;
|
||
}
|
||
gotoTodoPage();
|
||
}
|
||
|
||
function renderCapsuleCompact() {
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
width: 88,
|
||
height: 32,
|
||
borderRadius: 16,
|
||
border: '1px solid ' + COLOR_LINE,
|
||
background: 'rgba(255,255,255,0.96)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
padding: '0 10px',
|
||
boxSizing: 'border-box',
|
||
flexShrink: 0
|
||
}
|
||
},
|
||
h('div', { style: { width: 4, height: 4, borderRadius: 2, background: COLOR_MUTED, opacity: 0.75 } }),
|
||
h('div', { style: { width: 4, height: 4, borderRadius: 2, background: COLOR_MUTED, opacity: 0.75 } }),
|
||
h('div', { style: { width: 4, height: 4, borderRadius: 2, background: COLOR_MUTED, opacity: 0.75 } }),
|
||
h('div', {
|
||
style: { width: 10, height: 10, borderRadius: 5, border: '2px solid ' + COLOR_MUTED, opacity: 0.8 }
|
||
})
|
||
);
|
||
}
|
||
|
||
function renderCapsule() {
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
position: 'absolute',
|
||
top: 10,
|
||
right: 12,
|
||
width: 88,
|
||
height: 32,
|
||
borderRadius: 16,
|
||
border: '1px solid ' + COLOR_LINE,
|
||
background: 'rgba(255,255,255,0.9)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
padding: '0 10px',
|
||
boxSizing: 'border-box',
|
||
zIndex: 5
|
||
}
|
||
},
|
||
h('div', { style: { width: 4, height: 4, borderRadius: 2, background: COLOR_MUTED, opacity: 0.75 } }),
|
||
h('div', { style: { width: 4, height: 4, borderRadius: 2, background: COLOR_MUTED, opacity: 0.75 } }),
|
||
h('div', { style: { width: 4, height: 4, borderRadius: 2, background: COLOR_MUTED, opacity: 0.75 } }),
|
||
h('div', {
|
||
style: { width: 10, height: 10, borderRadius: 5, border: '2px solid ' + COLOR_MUTED, opacity: 0.8 }
|
||
})
|
||
);
|
||
}
|
||
|
||
function renderBrandLogo() {
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
justifyContent: 'center',
|
||
marginBottom: 32,
|
||
flexShrink: 0
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
width: 80,
|
||
height: 80,
|
||
borderRadius: 40,
|
||
background: COLOR_PRIMARY,
|
||
boxShadow: '0 14px 36px rgba(22,209,161,0.38)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center'
|
||
},
|
||
'aria-hidden': true
|
||
},
|
||
h(
|
||
'svg',
|
||
{
|
||
width: 44,
|
||
height: 44,
|
||
viewBox: '0 0 64 64',
|
||
fill: 'none',
|
||
xmlns: 'http://www.w3.org/2000/svg'
|
||
},
|
||
h('circle', { cx: 32, cy: 28, r: 16, stroke: '#FFFFFF', strokeWidth: 2.6 }),
|
||
h('circle', { cx: 32, cy: 28, r: 5, fill: '#FFFFFF' }),
|
||
h('path', {
|
||
d: 'M12 44 Q32 54 52 44',
|
||
stroke: '#FFFFFF',
|
||
strokeWidth: 2.6,
|
||
strokeLinecap: 'round'
|
||
})
|
||
)
|
||
)
|
||
);
|
||
}
|
||
|
||
function renderIconUser() {
|
||
var ink = COLOR_PRIMARY;
|
||
return h(
|
||
'svg',
|
||
{
|
||
width: 20,
|
||
height: 20,
|
||
viewBox: '0 0 24 24',
|
||
fill: 'none',
|
||
xmlns: 'http://www.w3.org/2000/svg',
|
||
'aria-hidden': true
|
||
},
|
||
h('circle', { cx: 12, cy: 8, r: 3.5, stroke: ink, strokeWidth: 1.7 }),
|
||
h('path', {
|
||
d: 'M6.5 20v-1c0-2.2 2.46-4 5.5-4s5.5 1.8 5.5 4v1',
|
||
stroke: ink,
|
||
strokeWidth: 1.7,
|
||
strokeLinecap: 'round',
|
||
strokeLinejoin: 'round'
|
||
})
|
||
);
|
||
}
|
||
|
||
function renderIconLock() {
|
||
var ink = COLOR_PRIMARY;
|
||
return h(
|
||
'svg',
|
||
{
|
||
width: 20,
|
||
height: 20,
|
||
viewBox: '0 0 24 24',
|
||
fill: 'none',
|
||
xmlns: 'http://www.w3.org/2000/svg',
|
||
'aria-hidden': true
|
||
},
|
||
h('path', {
|
||
d: 'M7 11V8a5 5 0 0 1 10 0v3',
|
||
stroke: ink,
|
||
strokeWidth: 1.7,
|
||
strokeLinecap: 'round'
|
||
}),
|
||
h('rect', {
|
||
x: 5,
|
||
y: 11,
|
||
width: 14,
|
||
height: 10,
|
||
rx: 2,
|
||
stroke: ink,
|
||
strokeWidth: 1.7
|
||
}),
|
||
h('circle', { cx: 12, cy: 15.5, r: 1.2, fill: ink })
|
||
);
|
||
}
|
||
|
||
function pillInputStyle(extra) {
|
||
var base = {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
background: COLOR_INPUT_BG,
|
||
borderRadius: 999,
|
||
paddingLeft: 16,
|
||
paddingRight: 16,
|
||
minHeight: 52,
|
||
boxSizing: 'border-box',
|
||
boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.04)'
|
||
};
|
||
if (!extra) return base;
|
||
for (var k in extra) {
|
||
if (Object.prototype.hasOwnProperty.call(extra, k)) base[k] = extra[k];
|
||
}
|
||
return base;
|
||
}
|
||
|
||
var MOCK_TODO_TASKS = [
|
||
{
|
||
id: 't1',
|
||
taskName: '交车任务',
|
||
project: '徐云霆测试项目8',
|
||
customer: '徐云霆55',
|
||
place: '羚牛氢能中心',
|
||
time: '2026-04-28 00:00:00'
|
||
},
|
||
{
|
||
id: 't2',
|
||
taskName: '交车任务',
|
||
project: '徐云霆测试项目9',
|
||
customer: '徐云霆55',
|
||
place: '羚牛氢能中心',
|
||
time: '2026-04-29 09:30:00'
|
||
}
|
||
];
|
||
|
||
var VEHICLE_FILTER_STATUS = ['全部', '租赁', '自营', '可运营', '待运营', '退出运营'];
|
||
var VEHICLE_FILTER_PROVINCES = ['全部', '江苏省', '浙江省', '广东省', '河北省', '福建省'];
|
||
|
||
/** 省份 → 下属城市(筛选「城市」选项随「省份」联动) */
|
||
var PROVINCE_CITY_MAP = {
|
||
江苏省: ['苏州市', '南京市', '无锡市'],
|
||
浙江省: ['杭州市', '宁波市', '温州市'],
|
||
广东省: ['深圳市', '广州市', '东莞市'],
|
||
河北省: ['石家庄市', '唐山市'],
|
||
福建省: ['福州市', '厦门市', '泉州市']
|
||
};
|
||
|
||
function getCityFilterOptionsByProvince(province) {
|
||
if (province === '全部' || !province) {
|
||
var seen = {};
|
||
var list = ['全部'];
|
||
Object.keys(PROVINCE_CITY_MAP).forEach(function (p) {
|
||
PROVINCE_CITY_MAP[p].forEach(function (c) {
|
||
if (!seen[c]) {
|
||
seen[c] = true;
|
||
list.push(c);
|
||
}
|
||
});
|
||
});
|
||
return list;
|
||
}
|
||
return ['全部'].concat(PROVINCE_CITY_MAP[province] ? PROVINCE_CITY_MAP[province].slice() : []);
|
||
}
|
||
|
||
function normalizeFilterDraft(draft) {
|
||
var cities = getCityFilterOptionsByProvince(draft.province);
|
||
var city = draft.city;
|
||
if (cities.indexOf(city) === -1) city = '全部';
|
||
return { status: draft.status, province: draft.province, city: city };
|
||
}
|
||
|
||
var MOCK_VEHICLES = (function () {
|
||
var statuses = ['租赁', '自营', '可运营', '待运营', '退出运营', '租赁', '自营', '可运营', '待运营', '退出运营'];
|
||
var provinces = ['江苏省', '浙江省', '广东省', '河北省', '福建省'];
|
||
var plates = [
|
||
'浙F08833F',
|
||
'苏A9H1234',
|
||
'粤B66D888',
|
||
'浙A12345F',
|
||
'冀A99999F',
|
||
'闽D01234F',
|
||
'苏E88888F',
|
||
'粤S77777F',
|
||
'浙C66666F',
|
||
'冀B55555F'
|
||
];
|
||
var vehicleTypes = [
|
||
'重型半挂牵引车',
|
||
'重型半挂牵引车',
|
||
'重型仓栅式货车',
|
||
'重型半挂牵引车',
|
||
'重型半挂牵引车',
|
||
'重型仓栅式货车',
|
||
'重型半挂牵引车',
|
||
'重型仓栅式货车',
|
||
'重型半挂牵引车',
|
||
'重型半挂牵引车'
|
||
];
|
||
var brands = ['飞驰', '飞驰', '羚牛', '飞驰', '飞驰', '羚牛', '飞驰', '羚牛', '飞驰', '飞驰'];
|
||
var models = [
|
||
'49吨牵引车头',
|
||
'49吨牵引车头',
|
||
'31吨载货车',
|
||
'49吨牵引车头',
|
||
'49吨牵引车头',
|
||
'31吨载货车',
|
||
'49吨牵引车头',
|
||
'31吨载货车',
|
||
'49吨牵引车头',
|
||
'49吨牵引车头'
|
||
];
|
||
var arr = [];
|
||
for (var i = 1; i <= 10; i++) {
|
||
var pr = provinces[(i - 1) % provinces.length];
|
||
var cs = PROVINCE_CITY_MAP[pr];
|
||
var city = cs[(i - 1) % cs.length];
|
||
arr.push({
|
||
id: 'veh-' + i,
|
||
plate: plates[i - 1],
|
||
status: statuses[i - 1],
|
||
province: pr,
|
||
city: city,
|
||
vehicleType: vehicleTypes[i - 1],
|
||
brand: brands[i - 1],
|
||
model: models[i - 1]
|
||
});
|
||
}
|
||
return arr;
|
||
})();
|
||
|
||
function iconTruck() {
|
||
var c = COLOR_PRIMARY;
|
||
return h(
|
||
'svg',
|
||
{
|
||
width: 22,
|
||
height: 22,
|
||
viewBox: '0 0 24 24',
|
||
fill: 'none',
|
||
xmlns: 'http://www.w3.org/2000/svg'
|
||
},
|
||
h('path', {
|
||
d: 'M2 17h13V7H8L6 17',
|
||
stroke: c,
|
||
strokeWidth: 1.7,
|
||
strokeLinecap: 'round',
|
||
strokeLinejoin: 'round'
|
||
}),
|
||
h('path', {
|
||
d: 'M15 17h8l2-6h-8v6zm0-6v2h6',
|
||
stroke: c,
|
||
strokeWidth: 1.7,
|
||
strokeLinecap: 'round',
|
||
strokeLinejoin: 'round'
|
||
}),
|
||
h('circle', { cx: 7.5, cy: 18.5, r: 1.8, fill: c }),
|
||
h('circle', { cx: 18.5, cy: 18.5, r: 1.8, fill: c }),
|
||
h('circle', { cx: 7.5, cy: 18.5, r: 0.8, fill: '#FFFFFF' }),
|
||
h('circle', { cx: 18.5, cy: 18.5, r: 0.8, fill: '#FFFFFF' })
|
||
);
|
||
}
|
||
|
||
function kvRow(label, value) {
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'flex-start',
|
||
gap: 12,
|
||
paddingTop: 8,
|
||
paddingBottom: 8,
|
||
borderTop: '1px solid ' + COLOR_LINE,
|
||
fontSize: 14,
|
||
lineHeight: 1.45
|
||
}
|
||
},
|
||
h('span', { style: { color: COLOR_MUTED, flexShrink: 0 } }, label),
|
||
h(
|
||
'span',
|
||
{
|
||
style: { color: COLOR_TEXT, textAlign: 'right', wordBreak: 'break-all', fontWeight: 500 }
|
||
},
|
||
value
|
||
)
|
||
);
|
||
}
|
||
|
||
function renderTaskCard(task) {
|
||
return h(
|
||
'div',
|
||
{
|
||
key: task.id,
|
||
style: {
|
||
background: COLOR_BG,
|
||
borderRadius: 12,
|
||
padding: '14px 16px',
|
||
marginBottom: 12,
|
||
boxShadow: '0 1px 4px rgba(0,0,0,0.06)'
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
gap: 10,
|
||
marginBottom: 4
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{ style: { display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 } },
|
||
iconTruck(),
|
||
h(
|
||
'span',
|
||
{ style: { fontSize: 16, fontWeight: 700, color: COLOR_TEXT } },
|
||
task.taskName
|
||
)
|
||
),
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: function () {
|
||
message.info('去处理:可跳转任务详情');
|
||
},
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
color: COLOR_PRIMARY,
|
||
fontSize: 15,
|
||
fontWeight: 600,
|
||
padding: '4px 0',
|
||
cursor: 'pointer',
|
||
flexShrink: 0
|
||
}
|
||
},
|
||
'去处理'
|
||
)
|
||
),
|
||
kvRow('项目名称', task.project),
|
||
kvRow('客户名称', task.customer),
|
||
kvRow('交车地点', task.place),
|
||
kvRow('交车时间', task.time)
|
||
);
|
||
}
|
||
|
||
function renderBellWithBadge(count) {
|
||
return h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
'aria-label': '通知',
|
||
onClick: function () {
|
||
message.info('通知占位');
|
||
},
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
padding: 4,
|
||
cursor: 'pointer',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'flex-start',
|
||
justifySelf: 'start'
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{ style: { position: 'relative', width: 28, height: 28 } },
|
||
h(
|
||
'svg',
|
||
{
|
||
width: 26,
|
||
height: 26,
|
||
viewBox: '0 0 24 24',
|
||
fill: 'none',
|
||
xmlns: 'http://www.w3.org/2000/svg'
|
||
},
|
||
h('path', {
|
||
d: 'M12 21a2 2 0 002-2H10a2 2 0 002 2zm7-9a6 6 0 10-12 0c0 6-3 6.5-3 7h18c0-.5-3-1-3-7z',
|
||
stroke: COLOR_TEXT,
|
||
strokeWidth: 1.5,
|
||
strokeLinejoin: 'round'
|
||
})
|
||
),
|
||
h(
|
||
'span',
|
||
{
|
||
style: {
|
||
position: 'absolute',
|
||
top: -4,
|
||
right: -10,
|
||
minWidth: 18,
|
||
height: 18,
|
||
borderRadius: 9,
|
||
background: COLOR_PRIMARY,
|
||
color: '#FFFFFF',
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
padding: '0 5px',
|
||
boxSizing: 'border-box'
|
||
}
|
||
},
|
||
String(count || 99)
|
||
)
|
||
)
|
||
);
|
||
}
|
||
|
||
function bizBadge() {
|
||
return h(
|
||
'span',
|
||
{
|
||
style: {
|
||
position: 'absolute',
|
||
top: -6,
|
||
right: -8,
|
||
minWidth: 18,
|
||
height: 18,
|
||
borderRadius: 9,
|
||
background: COLOR_PRIMARY,
|
||
color: '#FFFFFF',
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
padding: '0 5px',
|
||
boxSizing: 'border-box'
|
||
}
|
||
},
|
||
'99'
|
||
);
|
||
}
|
||
|
||
function bizMenuCell(spec) {
|
||
var label = typeof spec === 'string' ? spec : spec.label;
|
||
var badge = typeof spec === 'object' ? spec.badge : false;
|
||
var bizEntry = typeof spec === 'object' ? spec.bizEntry : '';
|
||
|
||
return h(
|
||
'button',
|
||
{
|
||
key: label,
|
||
type: 'button',
|
||
onClick: function () {
|
||
if (bizEntry === 'vehicleList') {
|
||
setVehicleSearch('');
|
||
setBizNav('vehicleList');
|
||
return;
|
||
}
|
||
message.info(label + ':功能入口占位');
|
||
},
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
padding: '8px 4px',
|
||
cursor: 'pointer',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
gap: 8,
|
||
width: '100%',
|
||
boxSizing: 'border-box'
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
position: 'relative',
|
||
width: 52,
|
||
height: 52,
|
||
borderRadius: 14,
|
||
background: '#EEEEEE',
|
||
boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.04)'
|
||
}
|
||
},
|
||
badge ? bizBadge() : null
|
||
),
|
||
h(
|
||
'span',
|
||
{
|
||
style: {
|
||
fontSize: 12,
|
||
color: COLOR_TEXT,
|
||
lineHeight: 1.35,
|
||
textAlign: 'center',
|
||
maxWidth: '100%',
|
||
wordBreak: 'break-all'
|
||
}
|
||
},
|
||
label
|
||
)
|
||
);
|
||
}
|
||
|
||
function bizGrid(rows) {
|
||
return rows.map(function (row, ri) {
|
||
return h(
|
||
'div',
|
||
{
|
||
key: 'row-' + ri,
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: 'repeat(3, 1fr)',
|
||
columnGap: 6,
|
||
rowGap: 4,
|
||
marginBottom: ri < rows.length - 1 ? 6 : 0
|
||
}
|
||
},
|
||
row.map(function (cell) {
|
||
return bizMenuCell(cell);
|
||
})
|
||
);
|
||
});
|
||
}
|
||
|
||
function sectionTitle(text) {
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
fontSize: 13,
|
||
color: COLOR_MUTED,
|
||
marginBottom: 8,
|
||
paddingLeft: 4,
|
||
fontWeight: 500,
|
||
textAlign: 'left'
|
||
}
|
||
},
|
||
text
|
||
);
|
||
}
|
||
|
||
function whiteCard(children) {
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
background: COLOR_BG,
|
||
borderRadius: 12,
|
||
padding: '14px 10px',
|
||
boxShadow: '0 1px 4px rgba(0,0,0,0.06)',
|
||
marginBottom: 14
|
||
}
|
||
},
|
||
children
|
||
);
|
||
}
|
||
|
||
function getFilteredVehicles() {
|
||
return MOCK_VEHICLES.filter(function (v) {
|
||
var st = vehicleFilterApplied.status;
|
||
if (st !== '全部' && v.status !== st) {
|
||
return false;
|
||
}
|
||
if (st === '全部' && v.status === '退出运营') {
|
||
return false;
|
||
}
|
||
if (vehicleFilterApplied.province !== '全部' && v.province !== vehicleFilterApplied.province) {
|
||
return false;
|
||
}
|
||
if (vehicleFilterApplied.city !== '全部' && v.city !== vehicleFilterApplied.city) {
|
||
return false;
|
||
}
|
||
if (vehicleSearch.trim()) {
|
||
if (v.plate.indexOf(vehicleSearch.trim()) === -1) return false;
|
||
}
|
||
return true;
|
||
});
|
||
}
|
||
|
||
function openVehicleFilterDrawer() {
|
||
setVehicleFilterDraft(
|
||
normalizeFilterDraft({
|
||
status: vehicleFilterApplied.status,
|
||
province: vehicleFilterApplied.province,
|
||
city: vehicleFilterApplied.city
|
||
})
|
||
);
|
||
setFilterDrawerOpen(true);
|
||
}
|
||
|
||
function detailLine(label, value, withTopBorder) {
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'flex-start',
|
||
gap: 12,
|
||
padding: '12px 0',
|
||
borderTop: withTopBorder ? '1px solid ' + COLOR_LINE : 'none',
|
||
fontSize: 14
|
||
}
|
||
},
|
||
h('span', { style: { color: COLOR_MUTED, flexShrink: 0 } }, label),
|
||
h(
|
||
'span',
|
||
{
|
||
style: { color: COLOR_TEXT, textAlign: 'right', fontWeight: 500, wordBreak: 'break-all' }
|
||
},
|
||
value
|
||
)
|
||
);
|
||
}
|
||
|
||
function renderSubNavHeader(title, onBack) {
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
position: 'sticky',
|
||
top: 0,
|
||
zIndex: 20,
|
||
background: COLOR_PAGE_BG,
|
||
paddingTop: 'calc(8px + env(safe-area-inset-top, 0px))',
|
||
paddingBottom: 10,
|
||
paddingLeft: 8,
|
||
paddingRight: 8
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: '40px minmax(0, 1fr) 88px',
|
||
alignItems: 'center',
|
||
minHeight: 44,
|
||
columnGap: 4
|
||
}
|
||
},
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: onBack,
|
||
'aria-label': '返回',
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
padding: 8,
|
||
cursor: 'pointer',
|
||
color: COLOR_TEXT,
|
||
fontSize: 20,
|
||
lineHeight: 1,
|
||
justifySelf: 'start'
|
||
}
|
||
},
|
||
'‹'
|
||
),
|
||
h(
|
||
'h1',
|
||
{
|
||
style: {
|
||
margin: 0,
|
||
fontSize: 17,
|
||
fontWeight: 700,
|
||
color: COLOR_TEXT,
|
||
textAlign: 'center'
|
||
}
|
||
},
|
||
title
|
||
),
|
||
h('div', { style: { justifySelf: 'end' } }, renderCapsuleCompact())
|
||
)
|
||
);
|
||
}
|
||
|
||
function iconFilterFunnel() {
|
||
return h(
|
||
'svg',
|
||
{
|
||
width: 22,
|
||
height: 22,
|
||
viewBox: '0 0 24 24',
|
||
fill: 'none',
|
||
xmlns: 'http://www.w3.org/2000/svg'
|
||
},
|
||
h('path', {
|
||
d: 'M5 4h14l-5.5 7.2V18l-3 1.2v-8L5 4z',
|
||
stroke: COLOR_MUTED,
|
||
strokeWidth: 1.5,
|
||
strokeLinejoin: 'round'
|
||
})
|
||
);
|
||
}
|
||
|
||
function iconChevronRight() {
|
||
return h(
|
||
'svg',
|
||
{
|
||
width: 18,
|
||
height: 18,
|
||
viewBox: '0 0 24 24',
|
||
fill: 'none',
|
||
xmlns: 'http://www.w3.org/2000/svg'
|
||
},
|
||
h('path', {
|
||
d: 'M9 6l6 6-6 6',
|
||
stroke: '#C9CDD4',
|
||
strokeWidth: 2,
|
||
strokeLinecap: 'round',
|
||
strokeLinejoin: 'round'
|
||
})
|
||
);
|
||
}
|
||
|
||
function vehicleStatusBadgeEl(status) {
|
||
var bg;
|
||
var fg;
|
||
if (status === '退出运营') {
|
||
bg = '#E8EAED';
|
||
fg = '#86909C';
|
||
} else if (status === '待运营') {
|
||
bg = '#FFF7E6';
|
||
fg = '#D48806';
|
||
} else {
|
||
bg = 'rgba(22,209,161,0.16)';
|
||
fg = COLOR_PRIMARY;
|
||
}
|
||
return h(
|
||
'span',
|
||
{
|
||
style: {
|
||
flexShrink: 0,
|
||
padding: '3px 10px',
|
||
borderRadius: 999,
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
lineHeight: 1.2,
|
||
background: bg,
|
||
color: fg
|
||
}
|
||
},
|
||
status
|
||
);
|
||
}
|
||
|
||
function vehicleListMetaRow(label, value) {
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'flex-start',
|
||
gap: 10,
|
||
paddingTop: 4,
|
||
paddingBottom: 4,
|
||
fontSize: 13,
|
||
lineHeight: 1.45
|
||
}
|
||
},
|
||
h('span', { style: { color: COLOR_MUTED, flexShrink: 0 } }, label),
|
||
h(
|
||
'span',
|
||
{
|
||
style: {
|
||
color: COLOR_TEXT,
|
||
fontWeight: 500,
|
||
textAlign: 'right',
|
||
wordBreak: 'break-all'
|
||
}
|
||
},
|
||
value
|
||
)
|
||
);
|
||
}
|
||
|
||
function vehicleListCard(v) {
|
||
return h(
|
||
'button',
|
||
{
|
||
key: v.id,
|
||
type: 'button',
|
||
onClick: function () {
|
||
setSelectedVehicle(v);
|
||
setVehicleDetailTab('basic');
|
||
setBizNav('vehicleDetail');
|
||
},
|
||
style: {
|
||
width: '100%',
|
||
display: 'flex',
|
||
alignItems: 'stretch',
|
||
flexDirection: 'row',
|
||
gap: 4,
|
||
border: 'none',
|
||
borderRadius: 10,
|
||
background: COLOR_BG,
|
||
padding: '14px 10px 14px 14px',
|
||
marginBottom: 10,
|
||
cursor: 'pointer',
|
||
boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
|
||
textAlign: 'left',
|
||
boxSizing: 'border-box'
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{ style: { flex: 1, minWidth: 0 } },
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
gap: 10
|
||
}
|
||
},
|
||
h(
|
||
'span',
|
||
{
|
||
style: {
|
||
fontSize: 17,
|
||
fontWeight: 700,
|
||
color: COLOR_TEXT
|
||
}
|
||
},
|
||
v.plate
|
||
),
|
||
vehicleStatusBadgeEl(v.status)
|
||
),
|
||
h('div', { style: { height: 1, background: COLOR_LINE, margin: '12px 0 6px' } }),
|
||
h(
|
||
'div',
|
||
null,
|
||
vehicleListMetaRow('车辆类型', v.vehicleType || '—'),
|
||
vehicleListMetaRow('品牌', v.brand || '—'),
|
||
vehicleListMetaRow('型号', v.model || '—')
|
||
)
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
alignSelf: 'stretch',
|
||
paddingLeft: 2,
|
||
flexShrink: 0
|
||
}
|
||
},
|
||
iconChevronRight()
|
||
)
|
||
);
|
||
}
|
||
|
||
function filterTag(label, active, onSelect) {
|
||
return h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
key: label,
|
||
onClick: onSelect,
|
||
style: {
|
||
border:
|
||
'1px solid ' + (active ? COLOR_PRIMARY : COLOR_LINE),
|
||
background: active ? 'rgba(22,209,161,0.1)' : COLOR_BG,
|
||
color: active ? COLOR_PRIMARY : COLOR_TEXT,
|
||
borderRadius: 6,
|
||
padding: '10px 8px',
|
||
fontSize: 13,
|
||
cursor: 'pointer',
|
||
textAlign: 'center',
|
||
lineHeight: 1.3
|
||
}
|
||
},
|
||
label
|
||
);
|
||
}
|
||
|
||
function filterSection(title, options, field) {
|
||
var cur = vehicleFilterDraft[field];
|
||
return h(
|
||
'div',
|
||
{ key: field, style: { marginBottom: 20 } },
|
||
h(
|
||
'div',
|
||
{ style: { fontSize: 13, color: COLOR_MUTED, marginBottom: 10 } },
|
||
title
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: 'repeat(3, 1fr)',
|
||
gap: 8
|
||
}
|
||
},
|
||
options.map(function (opt) {
|
||
return filterTag(opt, cur === opt, function () {
|
||
var next = Object.assign({}, vehicleFilterDraft);
|
||
next[field] = opt;
|
||
setVehicleFilterDraft(next);
|
||
});
|
||
})
|
||
)
|
||
);
|
||
}
|
||
|
||
function filterProvinceSection() {
|
||
var cur = vehicleFilterDraft.province;
|
||
return h(
|
||
'div',
|
||
{ key: 'province', style: { marginBottom: 20 } },
|
||
h(
|
||
'div',
|
||
{ style: { fontSize: 13, color: COLOR_MUTED, marginBottom: 10 } },
|
||
'车辆运营省份'
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: 'repeat(3, 1fr)',
|
||
gap: 8
|
||
}
|
||
},
|
||
VEHICLE_FILTER_PROVINCES.map(function (opt) {
|
||
return filterTag(opt, cur === opt, function () {
|
||
setVehicleFilterDraft(
|
||
normalizeFilterDraft({
|
||
status: vehicleFilterDraft.status,
|
||
province: opt,
|
||
city: '全部'
|
||
})
|
||
);
|
||
});
|
||
})
|
||
)
|
||
);
|
||
}
|
||
|
||
function filterCitySectionDynamic() {
|
||
var opts = getCityFilterOptionsByProvince(vehicleFilterDraft.province);
|
||
var raw = vehicleFilterDraft.city;
|
||
var cur = opts.indexOf(raw) >= 0 ? raw : '全部';
|
||
return h(
|
||
'div',
|
||
{ key: 'city', style: { marginBottom: 20 } },
|
||
h(
|
||
'div',
|
||
{ style: { fontSize: 13, color: COLOR_MUTED, marginBottom: 10 } },
|
||
'车辆运营城市'
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: 'repeat(3, 1fr)',
|
||
gap: 8
|
||
}
|
||
},
|
||
opts.map(function (opt) {
|
||
return filterTag(opt, cur === opt, function () {
|
||
var next = Object.assign({}, vehicleFilterDraft);
|
||
next.city = opt;
|
||
setVehicleFilterDraft(normalizeFilterDraft(next));
|
||
});
|
||
})
|
||
)
|
||
);
|
||
}
|
||
|
||
function renderVehicleFilterDrawer() {
|
||
if (!filterDrawerOpen) return null;
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
position: 'fixed',
|
||
inset: 0,
|
||
zIndex: 200,
|
||
display: 'flex',
|
||
justifyContent: 'flex-end'
|
||
}
|
||
},
|
||
h('div', {
|
||
style: {
|
||
flex: 1,
|
||
background: 'rgba(0,0,0,0.45)',
|
||
minWidth: 0
|
||
},
|
||
onClick: function () {
|
||
setFilterDrawerOpen(false);
|
||
}
|
||
}),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
width: 'min(86vw, 340px)',
|
||
height: '100%',
|
||
background: COLOR_BG,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
boxShadow: '-4px 0 16px rgba(0,0,0,0.12)',
|
||
boxSizing: 'border-box'
|
||
},
|
||
onClick: function (e) {
|
||
e.stopPropagation();
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
padding: '16px 14px 12px',
|
||
borderBottom: '1px solid ' + COLOR_LINE,
|
||
flexShrink: 0
|
||
}
|
||
},
|
||
h('span', { style: { fontSize: 17, fontWeight: 700, color: COLOR_TEXT } }, '筛选'),
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: function () {
|
||
setFilterDrawerOpen(false);
|
||
},
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
fontSize: 22,
|
||
color: COLOR_MUTED,
|
||
padding: 6,
|
||
cursor: 'pointer',
|
||
lineHeight: 1
|
||
}
|
||
},
|
||
'—'
|
||
)
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
flex: 1,
|
||
overflowY: 'auto',
|
||
padding: '16px 14px',
|
||
WebkitOverflowScrolling: 'touch'
|
||
}
|
||
},
|
||
filterSection('车辆运营状态', VEHICLE_FILTER_STATUS, 'status'),
|
||
filterProvinceSection(),
|
||
filterCitySectionDynamic()
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
gap: 10,
|
||
padding: '12px 14px calc(12px + env(safe-area-inset-bottom, 0px))',
|
||
borderTop: '1px solid ' + COLOR_LINE,
|
||
flexShrink: 0
|
||
}
|
||
},
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: function () {
|
||
setVehicleFilterDraft({
|
||
status: '全部',
|
||
province: '全部',
|
||
city: '全部'
|
||
});
|
||
},
|
||
style: {
|
||
flex: 1,
|
||
height: 46,
|
||
borderRadius: 8,
|
||
border: 'none',
|
||
background: '#ECEFF2',
|
||
color: COLOR_TEXT,
|
||
fontSize: 16,
|
||
fontWeight: 600,
|
||
cursor: 'pointer'
|
||
}
|
||
},
|
||
'重置'
|
||
),
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: function () {
|
||
setVehicleFilterApplied(normalizeFilterDraft(vehicleFilterDraft));
|
||
setFilterDrawerOpen(false);
|
||
},
|
||
style: {
|
||
flex: 1,
|
||
height: 46,
|
||
borderRadius: 8,
|
||
border: 'none',
|
||
background: COLOR_PRIMARY,
|
||
color: '#FFFFFF',
|
||
fontSize: 16,
|
||
fontWeight: 600,
|
||
cursor: 'pointer'
|
||
}
|
||
},
|
||
'确定'
|
||
)
|
||
)
|
||
)
|
||
);
|
||
}
|
||
|
||
function renderVehicleListPage() {
|
||
var list = getFilteredVehicles();
|
||
var count = list.length;
|
||
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
minHeight: '100dvh',
|
||
boxSizing: 'border-box',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
background: COLOR_PAGE_BG,
|
||
fontFamily: FONT_FAMILY,
|
||
color: COLOR_TEXT,
|
||
position: 'relative'
|
||
}
|
||
},
|
||
renderSubNavHeader('车辆管理', function () {
|
||
setBizNav('menu');
|
||
}),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
padding: '10px 14px 8px',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 10
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
background: COLOR_BG,
|
||
borderRadius: 22,
|
||
paddingLeft: 14,
|
||
paddingRight: 14,
|
||
minHeight: 42,
|
||
boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.06)'
|
||
}
|
||
},
|
||
h('input', {
|
||
value: vehicleSearch,
|
||
onChange: function (e) {
|
||
setVehicleSearch((e.target && e.target.value) || '');
|
||
},
|
||
placeholder: '请输入车牌号',
|
||
'aria-label': '车牌号搜索',
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0,
|
||
border: 'none',
|
||
outline: 'none',
|
||
fontSize: 15,
|
||
background: 'transparent',
|
||
color: COLOR_TEXT
|
||
}
|
||
})
|
||
),
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: openVehicleFilterDrawer,
|
||
'aria-label': '筛选',
|
||
style: {
|
||
border: 'none',
|
||
background: COLOR_BG,
|
||
width: 42,
|
||
height: 42,
|
||
borderRadius: 22,
|
||
cursor: 'pointer',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
boxShadow: '0 1px 4px rgba(0,0,0,0.06)',
|
||
flexShrink: 0
|
||
}
|
||
},
|
||
iconFilterFunnel()
|
||
)
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
padding: '4px 14px 12px',
|
||
fontSize: 14,
|
||
color: COLOR_TEXT
|
||
}
|
||
},
|
||
h('span', { style: { color: COLOR_MUTED } }, '全国'),
|
||
h('span', { style: { fontWeight: 500 } }, '车辆数:' + count)
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
flex: 1,
|
||
overflowY: 'auto',
|
||
paddingLeft: 14,
|
||
paddingRight: 14,
|
||
paddingBottom: 16,
|
||
WebkitOverflowScrolling: 'touch'
|
||
}
|
||
},
|
||
list.map(function (v) {
|
||
return vehicleListCard(v);
|
||
})
|
||
),
|
||
renderVehicleFilterDrawer()
|
||
);
|
||
}
|
||
|
||
function photoPlaceholder() {
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
height: 120,
|
||
borderRadius: 8,
|
||
background: '#F7F8FA',
|
||
border: '1px dashed ' + COLOR_LINE,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
color: COLOR_MUTED,
|
||
fontSize: 14,
|
||
marginTop: 8
|
||
}
|
||
},
|
||
'PHOTO'
|
||
);
|
||
}
|
||
|
||
function renderVehicleDetailPage() {
|
||
if (!selectedVehicle) {
|
||
return h('div', { style: { minHeight: 100 } });
|
||
}
|
||
var v = selectedVehicle;
|
||
var vt = vehicleDetailTab;
|
||
|
||
var basicBlock = h(
|
||
'div',
|
||
null,
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
marginBottom: 8
|
||
}
|
||
},
|
||
h('span', { style: { fontSize: 18, fontWeight: 700 } }, v.plate),
|
||
vehicleStatusBadgeEl(v.status)
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
background: COLOR_BG,
|
||
borderRadius: 10,
|
||
overflow: 'hidden',
|
||
paddingLeft: 14,
|
||
paddingRight: 14
|
||
}
|
||
},
|
||
detailLine('运营城市', v.province + ' ' + v.city, false),
|
||
detailLine('停车区域', v.province + ' ' + v.city, true),
|
||
detailLine('车辆类型', v.vehicleType || '氢能厢式车', true),
|
||
detailLine('品牌', v.brand || '羚牛', true),
|
||
detailLine('型号', v.model || 'LN-H2-01', true),
|
||
detailLine('车辆识别代码', 'LNVIN' + v.id + 'XXXXX', true),
|
||
detailLine('合同编号', 'HT-2026-' + v.id.slice(-3), true),
|
||
detailLine('客户名称', '示例客户', true),
|
||
detailLine('出厂年份', '2024', true),
|
||
detailLine('行驶公里数', '12345.67 KM', true),
|
||
detailLine('业务部门', '华东业务部', true),
|
||
detailLine('业务负责人', '张三', true),
|
||
detailLine('登记所有权', '羚牛氢能科技有限公司', true)
|
||
)
|
||
);
|
||
|
||
var specBlock = h(
|
||
'div',
|
||
null,
|
||
h(
|
||
'div',
|
||
{ style: { fontSize: 15, fontWeight: 700, marginBottom: 8, color: COLOR_TEXT } },
|
||
'型号参数'
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
background: COLOR_BG,
|
||
borderRadius: 10,
|
||
paddingLeft: 14,
|
||
paddingRight: 14
|
||
}
|
||
},
|
||
detailLine('车辆类型', v.vehicleType || '氢能厢式车', false),
|
||
detailLine('品牌', v.brand || '羚牛', true),
|
||
detailLine('型号', v.model || 'LN-H2-01', true),
|
||
detailLine('燃料种类', '氢', true),
|
||
detailLine('车牌颜色', '绿牌', true),
|
||
detailLine('整车尺寸', '长×宽×高(mm)', true)
|
||
),
|
||
h('div', { style: { height: 12 } }),
|
||
h(
|
||
'div',
|
||
{ style: { fontSize: 15, fontWeight: 700, marginBottom: 8 } },
|
||
'轮胎情况'
|
||
),
|
||
h(
|
||
'div',
|
||
{ style: { background: COLOR_BG, borderRadius: 10, padding: '0 14px' } },
|
||
detailLine('轮胎数量', '6', false),
|
||
detailLine('轮胎规格', '12R22.5', true)
|
||
),
|
||
h('div', { style: { height: 12 } }),
|
||
h(
|
||
'div',
|
||
{ style: { fontSize: 15, fontWeight: 700, marginBottom: 8 } },
|
||
'电气系统'
|
||
),
|
||
h(
|
||
'div',
|
||
{ style: { background: COLOR_BG, borderRadius: 10, padding: '0 14px' } },
|
||
detailLine('电池类型', 'LFP', false),
|
||
detailLine('电池厂家', '某某动力', true),
|
||
detailLine('电量', '150 kWh', true),
|
||
detailLine('续航', '300 KM', true)
|
||
),
|
||
h('div', { style: { height: 12 } }),
|
||
h(
|
||
'div',
|
||
{ style: { fontSize: 15, fontWeight: 700, marginBottom: 8 } },
|
||
'供氢系统'
|
||
),
|
||
h(
|
||
'div',
|
||
{ style: { background: COLOR_BG, borderRadius: 10, padding: '0 14px' } },
|
||
detailLine('气瓶容积', '320 L', false),
|
||
detailLine('仪表盘', '35 MPa', true),
|
||
detailLine('续航里程', '800 KM', true),
|
||
detailLine('厂家', '某某氢能', true)
|
||
)
|
||
);
|
||
|
||
var certBlock = h(
|
||
'div',
|
||
null,
|
||
h(
|
||
'div',
|
||
{ style: { fontSize: 15, fontWeight: 700, marginBottom: 8 } },
|
||
'证照信息'
|
||
),
|
||
h('div', { style: { color: COLOR_MUTED, fontSize: 13, marginBottom: 4 } }, '行驶证'),
|
||
photoPlaceholder(),
|
||
h('div', { style: { color: COLOR_MUTED, fontSize: 13, marginTop: 12, marginBottom: 4 } }, '营运证'),
|
||
photoPlaceholder(),
|
||
h('div', { style: { color: COLOR_MUTED, fontSize: 13, marginTop: 12, marginBottom: 4 } }, '加氢证'),
|
||
photoPlaceholder(),
|
||
photoPlaceholder(),
|
||
h('div', { style: { height: 16 } }),
|
||
h(
|
||
'div',
|
||
{
|
||
style: { background: COLOR_BG, borderRadius: 10, padding: '0 14px', marginTop: 8 }
|
||
},
|
||
detailLine('商业险', '到期日期:2027-01-15', false),
|
||
detailLine('超赔险', '到期日期:2027-01-15', true)
|
||
)
|
||
);
|
||
|
||
var body =
|
||
vt === 'basic' ? basicBlock : vt === 'spec' ? specBlock : certBlock;
|
||
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
minHeight: '100dvh',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
background: COLOR_PAGE_BG,
|
||
fontFamily: FONT_FAMILY,
|
||
color: COLOR_TEXT
|
||
}
|
||
},
|
||
renderSubNavHeader('车辆详情', function () {
|
||
setBizNav('vehicleList');
|
||
}),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
justifyContent: 'space-around',
|
||
background: COLOR_BG,
|
||
borderBottom: '1px solid ' + COLOR_LINE,
|
||
padding: '0 8px',
|
||
flexShrink: 0
|
||
}
|
||
},
|
||
['basic', 'spec', 'cert'].map(function (key) {
|
||
var labels = { basic: '基本信息', spec: '型号参数', cert: '证照及保险' };
|
||
var active = vehicleDetailTab === key;
|
||
return h(
|
||
'button',
|
||
{
|
||
key: key,
|
||
type: 'button',
|
||
onClick: function () {
|
||
setVehicleDetailTab(key);
|
||
},
|
||
style: {
|
||
flex: 1,
|
||
border: 'none',
|
||
background: 'transparent',
|
||
padding: '12px 6px',
|
||
fontSize: 15,
|
||
fontWeight: active ? 700 : 400,
|
||
color: active ? COLOR_PRIMARY : COLOR_MUTED,
|
||
borderBottom: active ? '3px solid ' + COLOR_PRIMARY : '3px solid transparent',
|
||
marginBottom: -1,
|
||
cursor: 'pointer'
|
||
}
|
||
},
|
||
labels[key]
|
||
);
|
||
})
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
flex: 1,
|
||
overflowY: 'auto',
|
||
padding: '12px 14px calc(20px + env(safe-area-inset-bottom, 0px))',
|
||
WebkitOverflowScrolling: 'touch'
|
||
}
|
||
},
|
||
body
|
||
)
|
||
);
|
||
}
|
||
|
||
function renderBusinessMenu() {
|
||
var opsRows = [
|
||
[{ label: '车辆管理', bizEntry: 'vehicleList' }, { label: '备车' }, { label: '交车', badge: true }],
|
||
[{ label: '还车' }, { label: '替换车' }, { label: '异动' }],
|
||
[{ label: '调拨', badge: true }, { label: '年审', badge: true }, { label: '故障' }],
|
||
[{ label: '司机安全培训' }]
|
||
];
|
||
|
||
var dataRows = [
|
||
[{ label: '车辆统计' }, { label: '氢费统计' }, { label: '氢量汇总' }],
|
||
[{ label: '电量汇总' }, { label: '里程查询' }, { label: '里程考核' }]
|
||
];
|
||
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
flex: 1,
|
||
overflowY: 'auto',
|
||
WebkitOverflowScrolling: 'touch',
|
||
paddingLeft: 16,
|
||
paddingRight: 16,
|
||
paddingBottom: 16,
|
||
boxSizing: 'border-box'
|
||
}
|
||
},
|
||
h('div', { style: { height: 4 } }),
|
||
h('div', null, sectionTitle('运维管理'), whiteCard(bizGrid(opsRows))),
|
||
h(
|
||
'div',
|
||
null,
|
||
sectionTitle('审批管理'),
|
||
whiteCard(
|
||
h('div', { style: { maxWidth: 120 } }, bizMenuCell({ label: '审核中心', badge: true }))
|
||
)
|
||
),
|
||
h('div', null, sectionTitle('数据可视化'), whiteCard(bizGrid(dataRows)))
|
||
);
|
||
}
|
||
|
||
function renderMainHeader() {
|
||
var titles = { todo: '待办', biz: '业务', map: '地图', mine: '我的' };
|
||
var title = titles[mainTab] || '羚牛氢能';
|
||
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
position: 'sticky',
|
||
top: 0,
|
||
zIndex: 10,
|
||
background: COLOR_PAGE_BG,
|
||
paddingTop: 'calc(8px + env(safe-area-inset-top, 0px))',
|
||
paddingBottom: 10,
|
||
paddingLeft: 16,
|
||
paddingRight: 16
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: '42px minmax(0, 1fr) 76px',
|
||
alignItems: 'center',
|
||
minHeight: 44,
|
||
columnGap: 6
|
||
}
|
||
},
|
||
mainTab === 'biz'
|
||
? renderBellWithBadge(99)
|
||
: h('div', { 'aria-hidden': true, style: { width: 42, flexShrink: 0 } }),
|
||
h(
|
||
'h1',
|
||
{
|
||
style: {
|
||
margin: 0,
|
||
fontSize: 18,
|
||
fontWeight: 700,
|
||
color: COLOR_TEXT,
|
||
textAlign: 'center'
|
||
}
|
||
},
|
||
title
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'flex-end',
|
||
gap: 10
|
||
}
|
||
},
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
'aria-label': '更多',
|
||
onClick: function () {
|
||
message.info('更多菜单占位');
|
||
},
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
padding: 6,
|
||
cursor: 'pointer',
|
||
color: COLOR_TEXT,
|
||
fontSize: 18,
|
||
lineHeight: 1
|
||
}
|
||
},
|
||
'⋯'
|
||
),
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
'aria-label': '关闭',
|
||
onClick: function () {
|
||
setRoute('login');
|
||
},
|
||
style: {
|
||
width: 26,
|
||
height: 26,
|
||
borderRadius: 13,
|
||
border: '1.5px solid ' + COLOR_LINE,
|
||
background: COLOR_BG,
|
||
padding: 0,
|
||
cursor: 'pointer',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
fontSize: 14,
|
||
color: COLOR_MUTED,
|
||
flexShrink: 0
|
||
}
|
||
},
|
||
'○'
|
||
)
|
||
)
|
||
)
|
||
);
|
||
}
|
||
|
||
function renderTodoHeader() {
|
||
return renderMainHeader();
|
||
}
|
||
|
||
function renderBottomTabs() {
|
||
var items = [
|
||
{ key: 'todo', label: '待办', active: mainTab === 'todo' },
|
||
{ key: 'biz', label: '业务', active: mainTab === 'biz' },
|
||
{ key: 'map', label: '地图', active: mainTab === 'map' },
|
||
{ key: 'mine', label: '我的', active: mainTab === 'mine' }
|
||
];
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
flexShrink: 0,
|
||
display: 'flex',
|
||
alignItems: 'stretch',
|
||
justifyContent: 'space-around',
|
||
background: COLOR_BG,
|
||
borderTop: '1px solid ' + COLOR_LINE,
|
||
paddingTop: 6,
|
||
paddingBottom: 'calc(8px + env(safe-area-inset-bottom, 0px))',
|
||
paddingLeft: 4,
|
||
paddingRight: 4
|
||
}
|
||
},
|
||
items.map(function (it) {
|
||
var active = it.active;
|
||
var color = active ? COLOR_PRIMARY : COLOR_MUTED;
|
||
return h(
|
||
'button',
|
||
{
|
||
key: it.key,
|
||
type: 'button',
|
||
onClick: function () {
|
||
var k = it.key;
|
||
if (k !== 'biz') {
|
||
setBizNav('menu');
|
||
}
|
||
setMainTab(k);
|
||
},
|
||
style: {
|
||
flex: 1,
|
||
border: 'none',
|
||
background: 'transparent',
|
||
padding: '6px 4px',
|
||
cursor: 'pointer',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
gap: 4,
|
||
color: color,
|
||
fontSize: 11,
|
||
fontWeight: active ? 600 : 400
|
||
}
|
||
},
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
width: 26,
|
||
height: 26,
|
||
borderRadius: 6,
|
||
border: active ? '2px solid ' + COLOR_PRIMARY : '2px solid transparent',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
fontSize: 14
|
||
}
|
||
},
|
||
it.key === 'todo'
|
||
? '□'
|
||
: it.key === 'biz'
|
||
? '◧'
|
||
: it.key === 'map'
|
||
? '⌖'
|
||
: '☺'
|
||
),
|
||
it.label
|
||
);
|
||
})
|
||
);
|
||
}
|
||
|
||
function renderTabBody() {
|
||
if (mainTab === 'biz') {
|
||
return renderBusinessMenu();
|
||
}
|
||
if (mainTab !== 'todo') {
|
||
var titles = { map: '地图', mine: '我的' };
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
color: COLOR_MUTED,
|
||
fontSize: 15,
|
||
padding: 24
|
||
}
|
||
},
|
||
titles[mainTab] + '(占位)'
|
||
);
|
||
}
|
||
return h(
|
||
'div',
|
||
{ style: { flex: 1, overflowY: 'auto', WebkitOverflowScrolling: 'touch', padding: '0 16px 16px' } },
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
fontSize: 16,
|
||
fontWeight: 700,
|
||
color: COLOR_TEXT,
|
||
marginBottom: 12,
|
||
textAlign: 'left'
|
||
}
|
||
},
|
||
'待办任务'
|
||
),
|
||
MOCK_TODO_TASKS.map(function (t) {
|
||
return renderTaskCard(t);
|
||
})
|
||
);
|
||
}
|
||
|
||
function renderTodoApp() {
|
||
if (route === 'todo' && mainTab === 'biz' && bizNav === 'vehicleList') {
|
||
return renderVehicleListPage();
|
||
}
|
||
if (route === 'todo' && mainTab === 'biz' && bizNav === 'vehicleDetail') {
|
||
return renderVehicleDetailPage();
|
||
}
|
||
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
minHeight: '100dvh',
|
||
boxSizing: 'border-box',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
background: COLOR_PAGE_BG,
|
||
fontFamily: FONT_FAMILY,
|
||
color: COLOR_TEXT
|
||
}
|
||
},
|
||
renderTodoHeader(),
|
||
renderTabBody(),
|
||
renderBottomTabs()
|
||
);
|
||
}
|
||
|
||
function renderLogin() {
|
||
var canSubmit = !!account && !!password && !!agree && !submitting;
|
||
|
||
return h(
|
||
'div',
|
||
{
|
||
style: {
|
||
minHeight: '100dvh',
|
||
boxSizing: 'border-box',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
background: COLOR_BG,
|
||
fontFamily: FONT_FAMILY,
|
||
position: 'relative',
|
||
overflowY: 'auto',
|
||
paddingLeft: 24,
|
||
paddingRight: 24,
|
||
paddingBottom: 'calc(20px + env(safe-area-inset-bottom, 0px))',
|
||
paddingTop: 'calc(12px + env(safe-area-inset-top, 0px))'
|
||
}
|
||
},
|
||
renderCapsule(),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
justifyContent: 'center',
|
||
maxWidth: 400,
|
||
width: '100%',
|
||
marginLeft: 'auto',
|
||
marginRight: 'auto',
|
||
paddingTop: 'max(36px, 7vh)'
|
||
}
|
||
},
|
||
renderBrandLogo(),
|
||
h(
|
||
'div',
|
||
{ style: pillInputStyle({ marginBottom: 16 }) },
|
||
renderIconUser(),
|
||
h('input', {
|
||
value: account,
|
||
onChange: function (e) {
|
||
setAccount((e && e.target && e.target.value) || '');
|
||
},
|
||
placeholder: '请输入用户名',
|
||
autoComplete: 'username',
|
||
'aria-label': '用户名',
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0,
|
||
border: 'none',
|
||
outline: 'none',
|
||
fontSize: 15,
|
||
color: COLOR_TEXT,
|
||
background: 'transparent',
|
||
padding: '14px 0',
|
||
lineHeight: 1.4
|
||
}
|
||
})
|
||
),
|
||
h(
|
||
'div',
|
||
{ style: pillInputStyle() },
|
||
renderIconLock(),
|
||
h('input', {
|
||
value: password,
|
||
type: 'password',
|
||
onChange: function (e) {
|
||
setPassword((e && e.target && e.target.value) || '');
|
||
},
|
||
placeholder: '请输入密码',
|
||
autoComplete: 'current-password',
|
||
'aria-label': '密码',
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0,
|
||
border: 'none',
|
||
outline: 'none',
|
||
fontSize: 15,
|
||
color: COLOR_TEXT,
|
||
background: 'transparent',
|
||
padding: '14px 0',
|
||
lineHeight: 1.4
|
||
}
|
||
})
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'flex-start',
|
||
gap: 10,
|
||
marginTop: 18,
|
||
fontSize: 13,
|
||
color: COLOR_TEXT,
|
||
lineHeight: 1.5,
|
||
userSelect: 'none'
|
||
}
|
||
},
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: function () {
|
||
setAgree(!agree);
|
||
},
|
||
'aria-checked': agree,
|
||
role: 'checkbox',
|
||
style: {
|
||
width: 18,
|
||
height: 18,
|
||
borderRadius: 9,
|
||
border: '1.5px solid ' + (agree ? COLOR_PRIMARY : COLOR_LINE),
|
||
background: agree ? COLOR_PRIMARY : '#FFFFFF',
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
padding: 0,
|
||
cursor: 'pointer',
|
||
flexShrink: 0,
|
||
marginTop: 2,
|
||
boxSizing: 'border-box'
|
||
}
|
||
},
|
||
agree
|
||
? h('div', { style: { width: 5, height: 5, borderRadius: 2.5, background: '#FFFFFF' } })
|
||
: null
|
||
),
|
||
h(
|
||
'div',
|
||
{ style: { flex: 1 } },
|
||
'我同意',
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: function () {
|
||
message.info('用户协议占位');
|
||
},
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
padding: 0,
|
||
color: COLOR_PRIMARY,
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
cursor: 'pointer',
|
||
verticalAlign: 'baseline'
|
||
}
|
||
},
|
||
'《用户协议》'
|
||
),
|
||
'和',
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: function () {
|
||
message.info('隐私政策占位');
|
||
},
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
padding: 0,
|
||
color: COLOR_PRIMARY,
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
cursor: 'pointer'
|
||
}
|
||
},
|
||
'《隐私政策》'
|
||
),
|
||
'规定'
|
||
)
|
||
),
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: handleLogin,
|
||
disabled: !canSubmit,
|
||
style: {
|
||
width: '100%',
|
||
height: 52,
|
||
borderRadius: 26,
|
||
border: 'none',
|
||
marginTop: 28,
|
||
background: canSubmit
|
||
? 'linear-gradient(180deg, ' + COLOR_PRIMARY + ' 0%, ' + COLOR_PRIMARY_DEEP + ' 100%)'
|
||
: '#D0D5DD',
|
||
color: '#FFFFFF',
|
||
fontSize: 17,
|
||
fontWeight: 700,
|
||
boxShadow: canSubmit ? '0 10px 28px rgba(22,209,161,0.36)' : 'none',
|
||
cursor: canSubmit ? 'pointer' : 'not-allowed',
|
||
opacity: submitting ? 0.92 : 1
|
||
}
|
||
},
|
||
submitting ? '登录中…' : '立即登录'
|
||
),
|
||
h(
|
||
'div',
|
||
{
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
marginTop: 20,
|
||
fontSize: 14
|
||
}
|
||
},
|
||
h(
|
||
'button',
|
||
{
|
||
type: 'button',
|
||
onClick: handlePhoneAuthLogin,
|
||
'aria-label': '手机号授权登录',
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
padding: 0,
|
||
color: COLOR_PRIMARY,
|
||
cursor: 'pointer',
|
||
fontSize: 14,
|
||
fontWeight: 600
|
||
}
|
||
},
|
||
'手机号授权登录'
|
||
)
|
||
),
|
||
h('div', { style: { flexShrink: 0, minHeight: 'max(28px, 4vh)' } })
|
||
)
|
||
);
|
||
}
|
||
|
||
|
||
return h(App, null, route === 'login' ? renderLogin() : renderTodoApp());
|
||
};
|