v4.0.20版本迭代推广码增加渠道负责人
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@dukang/admin-web",
|
||||
"version": "4.0.19",
|
||||
"version": "4.0.20",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Button, Form, Input, Modal, Select, Space, Table, Tag, Tooltip, Typography, message,
|
||||
Button, Form, Input, Modal, Select, Space, Table, Tag, Tooltip, message,
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import {
|
||||
@@ -9,25 +9,37 @@ import {
|
||||
PROMO_CODE_STATUS_LABELS,
|
||||
promoConversion,
|
||||
type PromoCodeItem,
|
||||
type PromoCodePartnerBrief,
|
||||
type PromoCodeScene,
|
||||
} from '@dukang/shared-types';
|
||||
import { request } from '../lib/api';
|
||||
import { fmtTime } from '../lib/constants';
|
||||
import { request, type Paginated } from '../lib/api';
|
||||
import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
type Row = PromoCodeItem;
|
||||
|
||||
type PartnerOption = { id: string; companyName?: string | null; name?: string; phone?: string };
|
||||
type SceneOption = { value: PromoCodeScene; label: string };
|
||||
|
||||
function formatPartnerLabel(p?: PromoCodePartnerBrief | PartnerOption | null) {
|
||||
if (!p) return '—';
|
||||
const title = p.companyName || p.name || p.id;
|
||||
return p.phone ? `${title} · ${p.phone}` : title;
|
||||
}
|
||||
|
||||
function formatPartnerNames(partners?: PromoCodePartnerBrief[] | null) {
|
||||
if (!partners?.length) return '—';
|
||||
return partners.map((p) => p.companyName || p.name || p.id).join('、');
|
||||
}
|
||||
|
||||
export default function PromoCodesPage() {
|
||||
const navigate = useNavigate();
|
||||
const [filterForm] = Form.useForm();
|
||||
const [createForm] = Form.useForm();
|
||||
const [filters, setFilters] = useState<Record<string, string>>({});
|
||||
const [partners, setPartners] = useState<PartnerOption[]>([]);
|
||||
const [scenes, setScenes] = useState<SceneOption[]>(
|
||||
Object.entries(PROMO_CODE_SCENE_LABELS).map(([value, label]) => ({
|
||||
value: value as PromoCodeScene,
|
||||
@@ -37,6 +49,11 @@ export default function PromoCodesPage() {
|
||||
const [createOpen, setCreateOpen] = useState(false);
|
||||
const [creating, setCreating] = useState(false);
|
||||
|
||||
const partnerSelectOptions = useMemo(
|
||||
() => partners.map((p) => ({ value: p.id, label: formatPartnerLabel(p) })),
|
||||
[partners],
|
||||
);
|
||||
|
||||
const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList<Row>(
|
||||
'/admin/promo-codes',
|
||||
() => {
|
||||
@@ -45,6 +62,8 @@ export default function PromoCodesPage() {
|
||||
if (filters.code) qs.set('code', filters.code);
|
||||
if (filters.status) qs.set('status', filters.status);
|
||||
if (filters.scene) qs.set('scene', filters.scene);
|
||||
if (filters.channelOwnerPartnerId) qs.set('channelOwnerPartnerId', filters.channelOwnerPartnerId);
|
||||
if (filters.assocPartnerAccountId) qs.set('assocPartnerAccountId', filters.assocPartnerAccountId);
|
||||
return qs;
|
||||
},
|
||||
[filters],
|
||||
@@ -63,6 +82,12 @@ export default function PromoCodesPage() {
|
||||
void loadScenes();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void request<Paginated<PartnerOption>>(`/admin/partners?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`)
|
||||
.then((res) => setPartners(res.items ?? []))
|
||||
.catch(() => setPartners([]));
|
||||
}, []);
|
||||
|
||||
const baseColumns: ColumnsType<Row> = [
|
||||
{
|
||||
title: '名称',
|
||||
@@ -116,8 +141,13 @@ export default function PromoCodesPage() {
|
||||
},
|
||||
{
|
||||
title: '渠道负责人',
|
||||
width: 120,
|
||||
render: (_, row) => row.ownerUser?.userNo || row.ownerUser?.phone || '—',
|
||||
width: 160,
|
||||
render: (_, row) => formatPartnerNames(row.channelOwners),
|
||||
},
|
||||
{
|
||||
title: '关联合伙人',
|
||||
width: 140,
|
||||
render: (_, row) => row.assocPartner?.companyName || row.assocPartner?.name || '—',
|
||||
},
|
||||
{ title: '创建', dataIndex: 'createdAt', width: 160, render: fmtTime },
|
||||
{
|
||||
@@ -137,7 +167,15 @@ export default function PromoCodesPage() {
|
||||
},
|
||||
];
|
||||
|
||||
async function handleCreate(values: Record<string, string>) {
|
||||
async function handleCreate(values: {
|
||||
name: string;
|
||||
code?: string;
|
||||
scene: PromoCodeScene;
|
||||
page?: string;
|
||||
remark?: string;
|
||||
channelOwnerPartnerIds?: string[];
|
||||
assocPartnerAccountId?: string;
|
||||
}) {
|
||||
setCreating(true);
|
||||
try {
|
||||
const created = await request<PromoCodeItem>('/admin/promo-codes', {
|
||||
@@ -146,7 +184,8 @@ export default function PromoCodesPage() {
|
||||
name: values.name,
|
||||
code: values.code?.trim() || undefined,
|
||||
scene: values.scene,
|
||||
ownerUserId: values.ownerUserId?.trim() || undefined,
|
||||
channelOwnerPartnerIds: values.channelOwnerPartnerIds ?? [],
|
||||
assocPartnerAccountId: values.assocPartnerAccountId || null,
|
||||
remark: values.remark?.trim() || undefined,
|
||||
page: values.page?.trim() || undefined,
|
||||
}),
|
||||
@@ -192,6 +231,24 @@ export default function PromoCodesPage() {
|
||||
options={Object.entries(PROMO_CODE_STATUS_LABELS).map(([value, label]) => ({ value, label }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="channelOwnerPartnerId" label="渠道负责人">
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
style={{ minWidth: 200 }}
|
||||
options={partnerSelectOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="assocPartnerAccountId" label="关联合伙人">
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
style={{ minWidth: 200 }}
|
||||
options={partnerSelectOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item><Button type="primary" htmlType="submit">查询</Button></Form.Item>
|
||||
</Form>
|
||||
|
||||
@@ -235,8 +292,32 @@ export default function PromoCodesPage() {
|
||||
>
|
||||
<Input placeholder="pages/home/index" />
|
||||
</Form.Item>
|
||||
<Form.Item name="ownerUserId" label="关联用户 ID(选填)">
|
||||
<Input placeholder="渠道负责人,填写用户数据库 ID" />
|
||||
<Form.Item
|
||||
name="channelOwnerPartnerIds"
|
||||
label="渠道负责人"
|
||||
extra="可空、可多选。被指定的主合伙人可在 H5 查看扫码/归因/订单数量,看不到用户或订单明细。"
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder="选择主合伙人"
|
||||
options={partnerSelectOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="assocPartnerAccountId"
|
||||
label="关联合伙人"
|
||||
extra="可空。登录用户扫该码且尚未关联任何人时,将绑定到该主合伙人;已关联他人不换绑。"
|
||||
>
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder="选择主合伙人"
|
||||
options={partnerSelectOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="remark" label="备注">
|
||||
<Input.TextArea rows={2} placeholder="渠道说明、活动备注等" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, type CSSProperties } from 'react';
|
||||
import { useEffect, useMemo, useState, type CSSProperties } from 'react';
|
||||
import { useNavigate, useOutletContext } from 'react-router-dom';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
@@ -23,8 +23,8 @@ import {
|
||||
PROMO_CODE_STATUS_LABELS,
|
||||
promoConversion,
|
||||
} from '@dukang/shared-types';
|
||||
import { request } from '../../lib/api';
|
||||
import { fmtTime } from '../../lib/constants';
|
||||
import { request, type Paginated } from '../../lib/api';
|
||||
import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../../lib/constants';
|
||||
import type { PromoCodeDetailContext } from './PromoCodeDetailLayout';
|
||||
import PromoCodeMetricsPanel from './PromoCodeMetricsPanel';
|
||||
|
||||
@@ -51,6 +51,19 @@ const ORDER_HINT = '下单时绑定当时的归因推广码,仅统计已完成
|
||||
const CONVERSION_HINT =
|
||||
'已完成订单数 ÷ 扫码进入次数(人次)。同一人多次扫码会放大分母,未登录扫码也计入。';
|
||||
|
||||
type PartnerOption = { id: string; companyName?: string | null; name?: string | null; phone?: string | null };
|
||||
|
||||
function formatPartnerLabel(p?: { companyName?: string | null; name?: string | null; phone?: string | null; id: string } | null) {
|
||||
if (!p) return '—';
|
||||
const title = p.companyName || p.name || p.id;
|
||||
return p.phone ? `${title} · ${p.phone}` : title;
|
||||
}
|
||||
|
||||
function formatPartnerNames(partners?: Array<{ companyName?: string | null; name?: string | null; id: string }> | null) {
|
||||
if (!partners?.length) return '—';
|
||||
return partners.map((p) => p.companyName || p.name || p.id).join('、');
|
||||
}
|
||||
|
||||
function StatHint({ label, hint }: { label: string; hint: string }) {
|
||||
return (
|
||||
<span>
|
||||
@@ -86,10 +99,30 @@ export default function PromoCodeDetailPage() {
|
||||
const [editForm] = Form.useForm();
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [partners, setPartners] = useState<PartnerOption[]>([]);
|
||||
|
||||
const stats = detail.stats;
|
||||
|
||||
async function handleEdit(values: Record<string, string>) {
|
||||
const partnerSelectOptions = useMemo(() => {
|
||||
const map = new Map(partners.map((p) => [p.id, p]));
|
||||
for (const p of detail.channelOwners ?? []) map.set(p.id, p);
|
||||
if (detail.assocPartner) map.set(detail.assocPartner.id, detail.assocPartner);
|
||||
return [...map.values()].map((p) => ({ value: p.id, label: formatPartnerLabel(p) }));
|
||||
}, [partners, detail.channelOwners, detail.assocPartner]);
|
||||
|
||||
useEffect(() => {
|
||||
void request<Paginated<PartnerOption>>(`/admin/partners?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`)
|
||||
.then((res) => setPartners(res.items ?? []))
|
||||
.catch(() => setPartners([]));
|
||||
}, []);
|
||||
|
||||
async function handleEdit(values: {
|
||||
name: string;
|
||||
scene: string;
|
||||
remark?: string;
|
||||
channelOwnerPartnerIds?: string[];
|
||||
assocPartnerAccountId?: string;
|
||||
}) {
|
||||
setSaving(true);
|
||||
try {
|
||||
await request(`/admin/promo-codes/${detail.id}`, {
|
||||
@@ -97,7 +130,8 @@ export default function PromoCodeDetailPage() {
|
||||
body: JSON.stringify({
|
||||
name: values.name,
|
||||
scene: values.scene,
|
||||
ownerUserId: values.ownerUserId?.trim() || null,
|
||||
channelOwnerPartnerIds: values.channelOwnerPartnerIds ?? [],
|
||||
assocPartnerAccountId: values.assocPartnerAccountId || null,
|
||||
remark: values.remark?.trim() || null,
|
||||
}),
|
||||
});
|
||||
@@ -128,7 +162,8 @@ export default function PromoCodeDetailPage() {
|
||||
name: detail.name,
|
||||
scene: detail.scene,
|
||||
remark: detail.remark,
|
||||
ownerUserId: detail.ownerUser?.id,
|
||||
channelOwnerPartnerIds: (detail.channelOwners ?? []).map((p) => p.id),
|
||||
assocPartnerAccountId: detail.assocPartner?.id,
|
||||
});
|
||||
setEditOpen(true);
|
||||
}}
|
||||
@@ -194,7 +229,12 @@ export default function PromoCodeDetailPage() {
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="渠道负责人">
|
||||
<span style={{ whiteSpace: 'nowrap' }}>
|
||||
{detail.ownerUser?.userNo || detail.ownerUser?.phone || '—'}
|
||||
{formatPartnerNames(detail.channelOwners)}
|
||||
</span>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="关联合伙人">
|
||||
<span style={{ whiteSpace: 'nowrap' }}>
|
||||
{formatPartnerLabel(detail.assocPartner)}
|
||||
</span>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="备注">{detail.remark || '—'}</Descriptions.Item>
|
||||
@@ -309,8 +349,32 @@ export default function PromoCodeDetailPage() {
|
||||
options={Object.entries(PROMO_CODE_SCENE_LABELS).map(([value, label]) => ({ value, label }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="ownerUserId" label="关联用户 ID">
|
||||
<Input placeholder="留空表示解除关联" />
|
||||
<Form.Item
|
||||
name="channelOwnerPartnerIds"
|
||||
label="渠道负责人"
|
||||
extra="可空、可多选。被指定的主合伙人可在 H5 查看扫码/归因/订单数量。"
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder="选择主合伙人"
|
||||
options={partnerSelectOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="assocPartnerAccountId"
|
||||
label="关联合伙人"
|
||||
extra="可空。扫该码且尚未关联的用户将绑定该合伙人;已关联他人不换绑,不回刷历史。"
|
||||
>
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder="选择主合伙人"
|
||||
options={partnerSelectOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="remark" label="备注">
|
||||
<Input.TextArea rows={2} />
|
||||
|
||||
@@ -2,7 +2,7 @@ import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
|
||||
const apiTarget = process.env.VITE_API_TARGET ?? 'http://localhost:3010';
|
||||
const apiTarget = process.env.VITE_API_TARGET ?? 'http://127.0.0.1:3010';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@dukang/h5-partner",
|
||||
"version": "4.0.19",
|
||||
"version": "4.0.20",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -26,6 +26,7 @@ import UsersManagePage from './pages/UsersManagePage';
|
||||
import AssocOrdersPage from './pages/AssocOrdersPage';
|
||||
import CommissionOrdersPage from './pages/CommissionOrdersPage';
|
||||
import ActivityPostersPage from './pages/ActivityPostersPage';
|
||||
import PromoCodesPage from './pages/PromoCodesPage';
|
||||
import BankAccountPage from './pages/BankAccountPage';
|
||||
|
||||
function PrimaryRoutes() {
|
||||
@@ -45,6 +46,7 @@ function PrimaryRoutes() {
|
||||
<Route path="/center/staff/new" element={<StaffCreatePage />} />
|
||||
<Route path="/center/bank" element={<BankAccountPage />} />
|
||||
<Route path="/center/activity-posters" element={<ActivityPostersPage />} />
|
||||
<Route path="/center/promo-codes" element={<PromoCodesPage />} />
|
||||
<Route path="/center/assoc" element={<Navigate to="/users" replace />} />
|
||||
<Route path="/center/assoc/users" element={<Navigate to="/users" replace />} />
|
||||
<Route path="/users/orders" element={<AssocOrdersPage />} />
|
||||
|
||||
@@ -258,6 +258,15 @@ export default function CenterPage({ variant = 'primary', roleLabel }: CenterPag
|
||||
</div>
|
||||
<span className="material-symbols-outlined text-muted">chevron_right</span>
|
||||
</Link>
|
||||
<Link to="/center/promo-codes" className="partner-menu-item">
|
||||
<div className="partner-menu-item-left">
|
||||
<div className="partner-menu-icon">
|
||||
<span className="material-symbols-outlined">qr_code_2</span>
|
||||
</div>
|
||||
<span className="body-md" style={{ fontSize: 16 }}>推广码数据</span>
|
||||
</div>
|
||||
<span className="material-symbols-outlined text-muted">chevron_right</span>
|
||||
</Link>
|
||||
<Link to="/users" className="partner-menu-item">
|
||||
<div className="partner-menu-item-left">
|
||||
<div className="partner-menu-icon">
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import PageHeader from '@dukang/shared-ui/PageHeader';
|
||||
import PullToRefresh from '@dukang/shared-ui/PullToRefresh';
|
||||
import { PROMO_CODE_STATUS_LABELS, type PartnerPromoCodeItem } from '@dukang/shared-types';
|
||||
import { request } from '../lib/api';
|
||||
import { toastError } from '../lib/toast';
|
||||
import { usePartnerPageView } from '../lib/usePageView';
|
||||
|
||||
type ListRes = { items: PartnerPromoCodeItem[] };
|
||||
|
||||
export default function PromoCodesPage() {
|
||||
usePartnerPageView('partner_promo_codes_view');
|
||||
const navigate = useNavigate();
|
||||
const [items, setItems] = useState<PartnerPromoCodeItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await request<ListRes>('PARTNER_H5', '/partner/promo-codes');
|
||||
setItems(res.items ?? []);
|
||||
} catch (e) {
|
||||
toastError(e instanceof Error ? e.message : '加载失败');
|
||||
setItems([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = '推广码数据';
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void load();
|
||||
}, [load]);
|
||||
|
||||
return (
|
||||
<PullToRefresh onRefresh={load}>
|
||||
<PageHeader title="推广码数据" onBack={() => navigate(-1)} />
|
||||
<div style={{ padding: '0 20px 24px' }}>
|
||||
<p className="label-md text-muted" style={{ margin: '12px 0' }}>
|
||||
{loading ? '加载中…' : `仅展示扫码人数、归因人数、订单数量`}
|
||||
</p>
|
||||
{!loading && items.length === 0 && <div className="empty">暂无负责的推广码</div>}
|
||||
{items.map((row) => (
|
||||
<div key={row.id} className="partner-store-card" style={{ marginBottom: 12 }}>
|
||||
<div className="partner-store-card-header">
|
||||
<div>
|
||||
<p className="body-md">{row.name}</p>
|
||||
<p className="label-md text-muted">
|
||||
{row.code}
|
||||
{row.status !== 'ACTIVE' ? ` · ${PROMO_CODE_STATUS_LABELS[row.status] || row.status}` : ''}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 16, marginTop: 8 }}>
|
||||
<div>
|
||||
<p className="label-md text-muted">扫码人数</p>
|
||||
<p className="body-md">{row.scanCount}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="label-md text-muted">归因人数</p>
|
||||
<p className="body-md">{row.attributionCount}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="label-md text-muted">订单数量</p>
|
||||
<p className="body-md">{row.orderCount}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</PullToRefresh>
|
||||
);
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export default defineConfig({
|
||||
host: true,
|
||||
port: 5175,
|
||||
proxy: {
|
||||
'/api': process.env.VITE_API_TARGET ?? 'http://localhost:3010',
|
||||
'/api': process.env.VITE_API_TARGET ?? 'http://127.0.0.1:3010',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@dukang/h5-shop",
|
||||
"version": "4.0.19",
|
||||
"version": "4.0.20",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -14,6 +14,6 @@ export default defineConfig({
|
||||
server: {
|
||||
host: true,
|
||||
port: 5174,
|
||||
proxy: { '/api': process.env.VITE_API_TARGET ?? 'http://localhost:3010' },
|
||||
proxy: { '/api': process.env.VITE_API_TARGET ?? 'http://127.0.0.1:3010' },
|
||||
},
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@dukang/mini-user",
|
||||
"version": "4.0.19",
|
||||
"version": "4.0.20",
|
||||
"private": true,
|
||||
"description": "杜康好客 · C 端用户微信小程序(Taro)",
|
||||
"scripts": {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { fetchClientConfig } from './pay-wechat';
|
||||
|
||||
/** 与 package.json version 同步(Taro defineConstants 注入),供 minClientVersion 比对 */
|
||||
export const APP_VERSION =
|
||||
(typeof TARO_APP_VERSION !== 'undefined' && String(TARO_APP_VERSION).trim()) || '4.0.19';
|
||||
(typeof TARO_APP_VERSION !== 'undefined' && String(TARO_APP_VERSION).trim()) || '4.0.20';
|
||||
|
||||
export const APP_VERSION_LABEL = `v${APP_VERSION.replace(/^v/i, '')}`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user