feat(miniapp): 新增小羚羚小程序主体与审批中心等页面,并重构年审管理为支持嵌入XLL主题
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2152,6 +2152,43 @@ const PAGE_STYLE = `
|
||||
}
|
||||
`;
|
||||
|
||||
const XLL_GREEN = '#7AB929';
|
||||
const XLL_GREEN_DEEP = '#6AA322';
|
||||
const XLL_GREEN_SOFT = 'rgba(122, 185, 41, 0.14)';
|
||||
|
||||
const EMBED_STYLE = `
|
||||
.ar-embed-root {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
background: ${COLOR_PAGE};
|
||||
position: relative;
|
||||
}
|
||||
.ar-embed-root .ar-tabs,
|
||||
.ar-embed-root .ar-search-row { flex-shrink: 0; }
|
||||
.ar-embed-root .ar-list,
|
||||
.ar-embed-root .ar-operate-scroll,
|
||||
.ar-embed-root .ar-history-scroll { flex: 1; min-height: 0; }
|
||||
`;
|
||||
|
||||
const XLL_THEME_PATCH = `
|
||||
.xll-module-theme .ar-tab.active { color: ${XLL_GREEN}; }
|
||||
.xll-module-theme .ar-tab.active::after { background: ${XLL_GREEN}; }
|
||||
.xll-module-theme .ar-filter-btn:active { border-color: ${XLL_GREEN}; color: ${XLL_GREEN}; }
|
||||
.xll-module-theme .ar-card-action { color: ${XLL_GREEN}; }
|
||||
.xll-module-theme .ar-add-btn { color: ${XLL_GREEN_DEEP}; }
|
||||
.xll-module-theme .ar-mp-link { color: ${XLL_GREEN_DEEP}; }
|
||||
.xll-module-theme .ar-form-control .ant-select-focused .ant-select-selector {
|
||||
background: ${XLL_GREEN_SOFT} !important;
|
||||
}
|
||||
.xll-module-theme .ar-operate-foot .ant-btn-primary {
|
||||
background: linear-gradient(135deg, ${XLL_GREEN} 0%, ${XLL_GREEN_DEEP} 100%) !important;
|
||||
border: none !important;
|
||||
}
|
||||
`;
|
||||
|
||||
const IconFilter = () => (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={COLOR_PRIMARY_DEEP} strokeWidth="2" strokeLinecap="round">
|
||||
<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" />
|
||||
@@ -3310,7 +3347,7 @@ const AnnualReviewPrdDoc = () => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const Component = function () {
|
||||
const AnnualReviewPanel = function AnnualReviewPanel({ embedded = false, theme = 'default', onBack, onOpenPrd }) {
|
||||
const [mainTab, setMainTab] = useState('pending');
|
||||
const [searchPlate, setSearchPlate] = useState('');
|
||||
const [plateKbOpen, setPlateKbOpen] = useState(false);
|
||||
@@ -3336,6 +3373,8 @@ const Component = function () {
|
||||
const [licenseForm, setLicenseForm] = useState({ ...EMPTY_LICENSE_FORM });
|
||||
const [operateDrafts, setOperateDrafts] = useState(() => loadOperateDrafts());
|
||||
const licenseOcrTimerRef = useRef(null);
|
||||
const themeClass = theme === 'xll' ? ' xll-module-theme' : '';
|
||||
const embedStyles = `${PAGE_STYLE}${EMBED_STYLE}${theme === 'xll' ? XLL_THEME_PATCH : ''}`;
|
||||
|
||||
const resetOperateForms = () => {
|
||||
if (licenseOcrTimerRef.current) {
|
||||
@@ -3431,6 +3470,22 @@ const Component = function () {
|
||||
setHistoryViewTask(null);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!embedded || typeof window === 'undefined') return undefined;
|
||||
window.__xllInspectionBack = () => {
|
||||
if (historyViewTask) {
|
||||
closeHistoryViewPage();
|
||||
return true;
|
||||
}
|
||||
if (operateTask) {
|
||||
closeOperatePage();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return () => { delete window.__xllInspectionBack; };
|
||||
}, [embedded, historyViewTask, operateTask]);
|
||||
|
||||
const setInspection = (key, val) => setInspectionForm((p) => ({ ...p, [key]: val }));
|
||||
const setM2 = (key, val) => setM2Form((p) => ({ ...p, [key]: val }));
|
||||
const setZb = (key, val) => setZbForm((p) => ({ ...p, [key]: val }));
|
||||
@@ -4078,92 +4133,85 @@ const Component = function () {
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ar-mini-root">
|
||||
<style>{PAGE_STYLE}</style>
|
||||
<div className="ar-phone">
|
||||
<MiniProgramChrome
|
||||
title={historyViewTask ? '年审记录' : operateTask ? '年审操作' : '年审'}
|
||||
showBack={!!operateTask || !!historyViewTask}
|
||||
onBack={historyViewTask ? closeHistoryViewPage : closeOperatePage}
|
||||
showPrdLink={!operateTask && !historyViewTask}
|
||||
onPrdClick={() => setPrdOpen(true)}
|
||||
/>
|
||||
const phoneMain = (
|
||||
<>
|
||||
{historyViewTask ? (
|
||||
renderHistoryDetailPage()
|
||||
) : operateTask ? (
|
||||
renderOperatePage()
|
||||
) : (
|
||||
<>
|
||||
<div className="ar-tabs" role="tablist">
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={mainTab === 'pending'}
|
||||
className={`ar-tab${mainTab === 'pending' ? ' active' : ''}`}
|
||||
onClick={() => setMainTab('pending')}
|
||||
>
|
||||
待处理
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={mainTab === 'history'}
|
||||
className={`ar-tab${mainTab === 'history' ? ' active' : ''}`}
|
||||
onClick={() => setMainTab('history')}
|
||||
>
|
||||
历史记录
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{historyViewTask ? (
|
||||
renderHistoryDetailPage()
|
||||
) : operateTask ? (
|
||||
renderOperatePage()
|
||||
) : (
|
||||
<>
|
||||
<div className="ar-tabs" role="tablist">
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={mainTab === 'pending'}
|
||||
className={`ar-tab${mainTab === 'pending' ? ' active' : ''}`}
|
||||
onClick={() => setMainTab('pending')}
|
||||
>
|
||||
待处理
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={mainTab === 'history'}
|
||||
className={`ar-tab${mainTab === 'history' ? ' active' : ''}`}
|
||||
onClick={() => setMainTab('history')}
|
||||
>
|
||||
历史记录
|
||||
</button>
|
||||
<div className="ar-search-row">
|
||||
<div
|
||||
className="ar-search-box"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={openPlateKeyboard}
|
||||
onKeyDown={(e) => e.key === 'Enter' && openPlateKeyboard()}
|
||||
>
|
||||
{!searchPlate ? (
|
||||
<span className="ar-search-placeholder" aria-hidden="true">
|
||||
请输入车牌号
|
||||
</span>
|
||||
) : null}
|
||||
<input
|
||||
readOnly
|
||||
value={searchPlate}
|
||||
aria-label="车牌号搜索"
|
||||
onFocus={(e) => {
|
||||
e.target.blur();
|
||||
openPlateKeyboard();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<button type="button" className="ar-filter-btn" aria-label="打开筛选" onClick={openFilter}>
|
||||
<IconFilter />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="ar-search-row">
|
||||
<div
|
||||
className="ar-search-box"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={openPlateKeyboard}
|
||||
onKeyDown={(e) => e.key === 'Enter' && openPlateKeyboard()}
|
||||
>
|
||||
{!searchPlate ? (
|
||||
<span className="ar-search-placeholder" aria-hidden="true">
|
||||
请输入车牌号
|
||||
</span>
|
||||
) : null}
|
||||
<input
|
||||
readOnly
|
||||
value={searchPlate}
|
||||
aria-label="车牌号搜索"
|
||||
onFocus={(e) => {
|
||||
e.target.blur();
|
||||
openPlateKeyboard();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<button type="button" className="ar-filter-btn" aria-label="打开筛选" onClick={openFilter}>
|
||||
<IconFilter />
|
||||
</button>
|
||||
</div>
|
||||
<div className="ar-list">
|
||||
{filteredTasks.length === 0 ? (
|
||||
<div className="ar-empty">暂无符合条件的年审任务<br />请调整搜索或筛选条件</div>
|
||||
) : (
|
||||
filteredTasks.map(renderTaskCard)
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="ar-list">
|
||||
{filteredTasks.length === 0 ? (
|
||||
<div className="ar-empty">暂无符合条件的年审任务<br />请调整搜索或筛选条件</div>
|
||||
) : (
|
||||
filteredTasks.map(renderTaskCard)
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<PlateKeyboardPanel
|
||||
open={plateKbOpen && !operateTask && !historyViewTask}
|
||||
value={plateKbDraft}
|
||||
onChange={setPlateKbDraft}
|
||||
onConfirm={confirmPlateKeyboard}
|
||||
onClose={closePlateKeyboard}
|
||||
/>
|
||||
</div>
|
||||
<PlateKeyboardPanel
|
||||
open={plateKbOpen && !operateTask && !historyViewTask}
|
||||
value={plateKbDraft}
|
||||
onChange={setPlateKbDraft}
|
||||
onConfirm={confirmPlateKeyboard}
|
||||
onClose={closePlateKeyboard}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
const moduleOverlays = (
|
||||
<>
|
||||
<Drawer
|
||||
title="筛选"
|
||||
placement="right"
|
||||
@@ -4184,7 +4232,9 @@ const Component = function () {
|
||||
style={{
|
||||
height: 46,
|
||||
borderRadius: 8,
|
||||
background: `linear-gradient(135deg, ${COLOR_PRIMARY} 0%, ${COLOR_PRIMARY_DEEP} 100%)`,
|
||||
background: theme === 'xll'
|
||||
? `linear-gradient(135deg, ${XLL_GREEN} 0%, ${XLL_GREEN_DEEP} 100%)`
|
||||
: `linear-gradient(135deg, ${COLOR_PRIMARY} 0%, ${COLOR_PRIMARY_DEEP} 100%)`,
|
||||
border: 'none'
|
||||
}}
|
||||
>
|
||||
@@ -4236,7 +4286,7 @@ const Component = function () {
|
||||
</Drawer>
|
||||
|
||||
<Modal
|
||||
open={prdOpen}
|
||||
open={embedded ? false : prdOpen}
|
||||
title="年审管理 · 产品需求说明"
|
||||
onCancel={() => setPrdOpen(false)}
|
||||
footer={[
|
||||
@@ -4244,7 +4294,10 @@ const Component = function () {
|
||||
key="ok"
|
||||
type="primary"
|
||||
onClick={() => setPrdOpen(false)}
|
||||
style={{ background: COLOR_PRIMARY_DEEP, borderColor: COLOR_PRIMARY_DEEP }}
|
||||
style={{
|
||||
background: theme === 'xll' ? XLL_GREEN_DEEP : COLOR_PRIMARY_DEEP,
|
||||
borderColor: theme === 'xll' ? XLL_GREEN_DEEP : COLOR_PRIMARY_DEEP,
|
||||
}}
|
||||
>
|
||||
知道了
|
||||
</Button>
|
||||
@@ -4255,8 +4308,45 @@ const Component = function () {
|
||||
>
|
||||
<AnnualReviewPrdDoc />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
|
||||
if (embedded) {
|
||||
return (
|
||||
<div className={`ar-embed-root${themeClass}`}>
|
||||
<style>{embedStyles}</style>
|
||||
{phoneMain}
|
||||
{moduleOverlays}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ar-mini-root">
|
||||
<style>{PAGE_STYLE}</style>
|
||||
<div className="ar-phone">
|
||||
<MiniProgramChrome
|
||||
title={historyViewTask ? '年审记录' : operateTask ? '年审操作' : '年审'}
|
||||
showBack={!!operateTask || !!historyViewTask}
|
||||
onBack={historyViewTask ? closeHistoryViewPage : closeOperatePage}
|
||||
showPrdLink={!operateTask && !historyViewTask}
|
||||
onPrdClick={() => (onOpenPrd ? onOpenPrd() : setPrdOpen(true))}
|
||||
/>
|
||||
<div className="ar-embed-root">{phoneMain}</div>
|
||||
</div>
|
||||
{moduleOverlays}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Component = function AnnualReviewMiniApp() {
|
||||
return <AnnualReviewPanel embedded={false} />;
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Component = Component;
|
||||
window.ONEOS_MP_EMBED = window.ONEOS_MP_EMBED || {};
|
||||
window.ONEOS_MP_EMBED.AnnualReviewPanel = AnnualReviewPanel;
|
||||
}
|
||||
|
||||
export default Component;
|
||||
|
||||
Reference in New Issue
Block a user