统一授权提示私有化口径
This commit is contained in:
parent
7b9260d8d7
commit
ec440c5c60
@ -2011,7 +2011,7 @@ export default function TemplateManager({ showToast }: { showToast: ToastFn }) {
|
||||
onClose={() => setShowUpgradeModal(false)}
|
||||
featureKey="customTemplates"
|
||||
featureName="自定义模板"
|
||||
featureDescription="创建、复制、编辑和迁移自定义模板需要升级后才能使用。"
|
||||
featureDescription="当前许可证未包含创建、复制、编辑和迁移自定义模板的授权。"
|
||||
/>
|
||||
|
||||
{/* 素材迁移弹窗 */}
|
||||
|
||||
@ -117,12 +117,12 @@ export default function BuiltInTemplateGrid({
|
||||
'inline-flex items-center gap-1.5 shadow-none',
|
||||
!canQuickCreate && 'opacity-75'
|
||||
)}
|
||||
title={canQuickCreate ? '快速创建副本' : '需要升级后才能创建自定义模板'}
|
||||
title={canQuickCreate ? '快速创建副本' : '当前许可证未包含自定义模板功能'}
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M10 3.5a.75.75 0 01.75.75v5h5a.75.75 0 010 1.5h-5v5a.75.75 0 01-1.5 0v-5h-5a.75.75 0 010-1.5h5v-5A.75.75 0 0110 3.5z" />
|
||||
</svg>
|
||||
{canQuickCreate ? '快速创建副本' : '升级后创建'}
|
||||
{canQuickCreate ? '快速创建副本' : '授权后创建'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -58,7 +58,7 @@ export default function CustomTemplateSection({
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 mt-0.5">
|
||||
{canManageCustomTemplates
|
||||
? '这里列出你自己创建的模板,可继续编辑、复制或删除'
|
||||
: '当前许可证未开放自定义模板功能,升级后可新建、复制、编辑和迁移模板'}
|
||||
: '当前许可证未包含自定义模板功能,请联系管理员处理授权'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -90,12 +90,12 @@ export default function CustomTemplateSection({
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="text-sm font-semibold text-slate-800 dark:text-slate-200">
|
||||
{canManageCustomTemplates ? '新建自定义模板' : '升级后新建模板'}
|
||||
{canManageCustomTemplates ? '新建自定义模板' : '授权后可新建'}
|
||||
</p>
|
||||
<p className="text-[11px] text-slate-500 dark:text-slate-400 mt-1 leading-snug">
|
||||
{canManageCustomTemplates
|
||||
? '从空白模板起步,按照业务需求配置字段'
|
||||
: '升级后可新建、复制和迁移自定义模板'}
|
||||
: '当前不可新建、复制或迁移自定义模板'}
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@ -79,10 +79,10 @@ describe('CustomTemplateSection 授权提示', () => {
|
||||
it('许可证不包含自定义模板时应显示前置授权提示并拦截新建入口', () => {
|
||||
renderSection(false)
|
||||
|
||||
expect(screen.getByText('当前许可证未开放自定义模板功能,升级后可新建、复制、编辑和迁移模板')).toBeInTheDocument()
|
||||
expect(screen.getByText('当前许可证未包含自定义模板功能,请联系管理员处理授权')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('pro-badge')).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(screen.getByText('升级后新建模板'))
|
||||
fireEvent.click(screen.getByText('授权后可新建'))
|
||||
|
||||
expect(callbacks.onRequestUpgrade).toHaveBeenCalledTimes(1)
|
||||
expect(callbacks.onCreate).not.toHaveBeenCalled()
|
||||
|
||||
@ -321,7 +321,7 @@ export async function POST(
|
||||
|
||||
if (!(await canUseCustomTemplates())) {
|
||||
return NextResponse.json({
|
||||
error: '自定义模板功能需要标准版或专业版许可证,请升级后使用',
|
||||
error: '当前许可证未包含自定义模板功能,请联系管理员处理授权',
|
||||
errorCode: 'LICENSE_REQUIRED',
|
||||
}, { status: 403 })
|
||||
}
|
||||
|
||||
@ -384,7 +384,7 @@ export async function PUT(req: NextRequest, { params }: { params: { id: string }
|
||||
|
||||
if (!(await canUseCustomTemplates())) {
|
||||
return NextResponse.json({
|
||||
error: '自定义模板功能需要标准版或专业版许可证,请升级后使用',
|
||||
error: '当前许可证未包含自定义模板功能,请联系管理员处理授权',
|
||||
errorCode: 'LICENSE_REQUIRED',
|
||||
}, { status: 403 })
|
||||
}
|
||||
@ -713,7 +713,7 @@ export async function DELETE(_: NextRequest, { params }: { params: { id: string
|
||||
|
||||
if (!(await canUseCustomTemplates())) {
|
||||
return NextResponse.json({
|
||||
error: '自定义模板功能需要标准版或专业版许可证,请升级后使用',
|
||||
error: '当前许可证未包含自定义模板功能,请联系管理员处理授权',
|
||||
errorCode: 'LICENSE_REQUIRED',
|
||||
}, { status: 403 })
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ export async function POST(req: NextRequest) {
|
||||
// 检查自定义模板功能是否可用
|
||||
if (!(await canUseCustomTemplates())) {
|
||||
return NextResponse.json({
|
||||
error: '自定义模板功能需要标准版或专业版许可证,请升级后使用',
|
||||
error: '当前许可证未包含自定义模板功能,请联系管理员处理授权',
|
||||
errorCode: 'LICENSE_REQUIRED',
|
||||
}, { status: 403 })
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ describe('departments route license guard', () => {
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(403)
|
||||
expect(data.error).toContain('部门隔离功能需要专业版许可证')
|
||||
expect(data.error).toContain('当前许可证未包含部门隔离功能')
|
||||
expect(prisma.department.findUnique).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@ -128,7 +128,7 @@ describe('departments route license guard', () => {
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(403)
|
||||
expect(data.error).toContain('部门隔离功能需要专业版许可证')
|
||||
expect(data.error).toContain('当前许可证未包含部门隔离功能')
|
||||
expect(prisma.department.findUnique).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ export async function POST(req: NextRequest) {
|
||||
// 检查部门隔离功能是否可用
|
||||
if (!(await canUseDepartmentIsolation())) {
|
||||
return NextResponse.json(
|
||||
{ error: '部门隔离功能需要专业版许可证,请升级后使用' },
|
||||
{ error: '当前许可证未包含部门隔离功能,请联系管理员处理授权' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
@ -93,7 +93,7 @@ export async function PATCH(req: NextRequest) {
|
||||
|
||||
if (!(await canUseDepartmentIsolation())) {
|
||||
return NextResponse.json(
|
||||
{ error: '部门隔离功能需要专业版许可证,请升级后使用' },
|
||||
{ error: '当前许可证未包含部门隔离功能,请联系管理员处理授权' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
@ -257,7 +257,7 @@ export async function DELETE(req: NextRequest) {
|
||||
|
||||
if (!(await canUseDepartmentIsolation())) {
|
||||
return NextResponse.json(
|
||||
{ error: '部门隔离功能需要专业版许可证,请升级后使用' },
|
||||
{ error: '当前许可证未包含部门隔离功能,请联系管理员处理授权' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
@ -209,7 +209,7 @@ export async function PUT(request: NextRequest) {
|
||||
// 检查系统设置功能是否可用(免费版只能查看,不能修改)
|
||||
if (!(await canUseSystemSettings())) {
|
||||
return NextResponse.json(
|
||||
{ error: '系统设置功能需要标准版或专业版许可证,请升级后使用' },
|
||||
{ error: '当前许可证未包含系统设置功能,请联系管理员处理授权' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* RestrictedToggle 组件
|
||||
*
|
||||
* 受限功能开关组件,集成 PRO 徽章和点击拦截逻辑
|
||||
* 当功能受限时显示禁用状态,点击时触发升级弹窗
|
||||
* 当功能受限时显示禁用状态,点击时触发授权提示弹窗
|
||||
*
|
||||
* @created 2025-12-16
|
||||
* @component
|
||||
@ -96,7 +96,7 @@ export default function RestrictedToggle({
|
||||
)}
|
||||
data-testid="restricted-hint"
|
||||
>
|
||||
升级到付费版解锁此功能
|
||||
当前许可证未包含此功能
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@ -142,7 +142,7 @@ export default function RestrictedToggle({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 升级弹窗 */}
|
||||
{/* 授权提示弹窗 */}
|
||||
<UpgradeModal
|
||||
isOpen={showUpgradeModal}
|
||||
onClose={() => setShowUpgradeModal(false)}
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
/**
|
||||
* UpgradeModal 组件
|
||||
*
|
||||
* 升级提示弹窗,当免费版用户点击受限功能时显示
|
||||
* 基于现有 Modal 组件构建,显示功能名称、限制说明和升级好处
|
||||
* 授权提示弹窗,当当前许可证不包含某个功能时显示
|
||||
* 基于现有 Modal 组件构建,显示功能名称和限制说明
|
||||
*
|
||||
* @created 2025-12-16
|
||||
* @component
|
||||
@ -41,9 +41,8 @@ export default function UpgradeModal({
|
||||
const displayName = featureName || feature.name
|
||||
const displayDescription = featureDescription || feature.description
|
||||
|
||||
const handleUpgradeClick = () => {
|
||||
// TODO: 跳转到升级页面或显示联系方式
|
||||
// 目前先关闭弹窗
|
||||
const handleContactAdminClick = () => {
|
||||
// 目前先关闭弹窗,后续可接入客户自己的授权处理入口。
|
||||
onClose()
|
||||
}
|
||||
|
||||
@ -112,7 +111,7 @@ export default function UpgradeModal({
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleUpgradeClick}
|
||||
onClick={handleContactAdminClick}
|
||||
className={cn(
|
||||
'flex-1 px-4 py-2 rounded-xl text-sm font-medium transition-all',
|
||||
'bg-slate-900 text-white hover:bg-slate-800',
|
||||
|
||||
@ -181,13 +181,13 @@ describe('RestrictedToggle 组件', () => {
|
||||
expect(toggleButton.className).not.toContain('opacity-50')
|
||||
})
|
||||
|
||||
it('受限时应该显示升级提示文字', () => {
|
||||
it('受限时应该显示授权提示文字', () => {
|
||||
render(<RestrictedToggle {...defaultProps} isRestricted={true} />)
|
||||
expect(screen.getByTestId('restricted-hint')).toBeInTheDocument()
|
||||
expect(screen.getByText('升级到付费版解锁此功能')).toBeInTheDocument()
|
||||
expect(screen.getByText('当前许可证未包含此功能')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('不受限时不应该显示升级提示文字', () => {
|
||||
it('不受限时不应该显示授权提示文字', () => {
|
||||
render(<RestrictedToggle {...defaultProps} isRestricted={false} />)
|
||||
expect(screen.queryByTestId('restricted-hint')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
@ -160,9 +160,9 @@
|
||||
- 预制模板是否被禁用
|
||||
- 是否是管理员草稿模板
|
||||
|
||||
### 为什么保存自定义模板提示需要升级?
|
||||
### 为什么保存自定义模板提示许可证未包含?
|
||||
|
||||
说明当前许可证没有开启 `customTemplates`。需要激活标准版或专业版,或调整许可证能力。
|
||||
说明当前许可证没有开启 `customTemplates`。请联系管理员确认当前授权范围,必要时激活包含自定义模板能力的许可证。
|
||||
|
||||
### 能不能删除正在使用的模板?
|
||||
|
||||
|
||||
@ -854,17 +854,17 @@ AI 改图以后会消耗外部额度、CPU、存储和客户素材数据。即
|
||||
- 已完成:监控页按管理员能力收口。
|
||||
- 后续如果要给普通成员开放部分统计,应单独做低敏概览接口,不复用当前系统状态接口。
|
||||
|
||||
### P2-02 模板管理免费版缺少前置提示(已修复)
|
||||
### P2-02 模板管理许可证限制缺少前置提示(已修复)
|
||||
|
||||
客户影响:
|
||||
|
||||
历史风险是免费版用户在后台看到新建自定义模板入口,点击后才收到后端许可证错误,体验不完整。当前前端已读取许可证状态,在模板管理页提前展示授权限制提示;受限动作会打开中性的授权提示弹窗,后端仍保留强制拦截。
|
||||
历史风险是当前许可证不包含自定义模板时,用户在后台仍看到新建入口,点击后才收到后端许可证错误,体验不完整。当前前端已读取许可证状态,在模板管理页提前展示授权限制提示;受限动作会打开中性的授权提示弹窗,后端仍保留强制拦截。
|
||||
|
||||
证据:
|
||||
|
||||
- `hooks/useLicenseStatus.ts`:前端通过 `/api/license/info` 读取当前许可证和功能开关。
|
||||
- `app/admin/_components/TemplateManager.tsx`:模板管理页用 `isFree + customTemplates` 计算自定义模板能力;新建、编辑、复制和从预制模板快速创建都会先检查能力,未授权时打开 `UpgradeModal`。
|
||||
- `app/admin/_components/template-manager/CustomTemplateSection.tsx`:自定义模板区免费版显示 `ProBadge` 和“当前许可证未开放自定义模板功能”提示;点击受限动作只触发授权提示,不直接打开编辑器。
|
||||
- `app/admin/_components/TemplateManager.tsx`:模板管理页用许可证能力计算自定义模板能力;新建、编辑、复制和从预制模板快速创建都会先检查能力,未授权时打开授权提示弹窗。
|
||||
- `app/admin/_components/template-manager/CustomTemplateSection.tsx`:自定义模板区在未授权时显示“当前许可证未包含自定义模板功能,请联系管理员处理授权”提示;点击受限动作只触发授权提示,不直接打开编辑器。
|
||||
- `components/ui/UpgradeModal.tsx`:授权提示已收口为“当前许可证未包含此功能,请联系管理员处理授权”的私有化工作提示。
|
||||
- `app/api/admin/upload-templates/route.ts`:后端创建模板前仍调用 `canUseCustomTemplates()`,失败返回 `403 + LICENSE_REQUIRED`。
|
||||
- `app/admin/_components/template-manager/__tests__/CustomTemplateSection.test.tsx`:新增测试覆盖未授权时显示前置提示并拦截新建、编辑、复制和启停动作;授权允许时新建动作正常执行。
|
||||
@ -872,9 +872,9 @@ AI 改图以后会消耗外部额度、CPU、存储和客户素材数据。即
|
||||
|
||||
建议动作:
|
||||
|
||||
- 已完成:免费版自定义模板入口有前置授权提示,受限动作不会直接进入编辑流程。
|
||||
- 已完成:后端创建、更新、删除和迁移入口继续保留许可证强制。
|
||||
- 仍需继续:后续统一把“升级后”话术改成“当前许可证未包含/联系管理员处理授权”,保持私有化交付语气一致。
|
||||
- 已完成:自定义模板入口有前置授权提示,受限动作不会直接进入编辑流程。
|
||||
- 已完成:后端创建、更新、删除和迁移入口继续保留许可证强制,错误信息统一为“当前许可证未包含/联系管理员处理授权”口径。
|
||||
- 已完成:前端授权提示、按钮、通用受限开关、数量限制和系统设置/部门隔离/模板 API 的“升级后”话术已统一成私有化交付口径。
|
||||
|
||||
### P2-03 必填 checkbox 前后端语义不一致(已修复)
|
||||
|
||||
@ -1069,25 +1069,26 @@ NAS 私有化系统可能在平板或窄屏浏览器里使用。模板编辑器
|
||||
- 拖拽只保留虚线占位和轻背景,不使用旋转、放大、bounce、pulse、强阴影。
|
||||
- 实时预览默认可以折叠,避免占据过多编辑空间。
|
||||
|
||||
### P2-11 授权/付费提示偏公网 SaaS 营销话术(升级弹窗已先收口)
|
||||
### P2-11 授权/付费提示偏公网 SaaS 营销话术(已完成第一轮收口)
|
||||
|
||||
客户影响:
|
||||
|
||||
当前项目目标是私有化交付。许可证限制应该像“当前许可证不包含此能力”的工作提示,而不是“升级后可享受”的营销弹窗。否则客户会感觉产品不是为私有部署准备的。升级弹窗已先改成中性限制提示,但 PRO 徽章、其它授权入口和部分页面样式仍需继续统一。
|
||||
当前项目目标是私有化交付。许可证限制应该像“当前许可证不包含此能力”的工作提示,而不是“升级后可享受”的营销弹窗。否则客户会感觉产品不是为私有部署准备的。当前第一轮已把前端授权提示、通用受限开关、数量限制和核心授权 API 的可见话术统一成“当前许可证未包含/联系管理员处理授权”。
|
||||
|
||||
证据:
|
||||
|
||||
- `components/ui/ProBadge.tsx:6`:组件说明是付费功能 PRO 徽章。
|
||||
- `components/ui/ProBadge.tsx:38`:PRO 徽章使用紫蓝渐变。
|
||||
- `components/ui/UpgradeModal.tsx:62`、`components/ui/UpgradeModal.tsx:86`、`components/ui/UpgradeModal.tsx:111`、`components/ui/UpgradeModal.tsx:122`:升级弹窗已改成中性图标、限制提示文案和普通按钮,不再显示“升级后可享受”。
|
||||
- `components/ui/__tests__/UpgradeModal.test.tsx`:测试已改为锁定限制提示和中性按钮文案。
|
||||
- `components/ui/UpgradeModal.tsx`:授权提示弹窗已改成中性图标、限制提示文案和普通按钮,不再显示“升级后可享受”。
|
||||
- `components/ui/RestrictedToggle.tsx`:受限开关提示已改为“当前许可证未包含此功能”。
|
||||
- `lib/license/check-limits.ts`:用户数和素材数超限提示已改为联系管理员处理授权。
|
||||
- `app/api/admin/upload-templates/route.ts`、`app/api/admin/upload-templates/[id]/route.ts`、`app/api/admin/upload-templates/[id]/migrate/route.ts`、`app/api/departments/route.ts`、`app/api/system-settings/route.ts`:授权不足错误已统一成私有化口径。
|
||||
- `components/ui/ProBadge.tsx`:PRO 徽章仍使用紫色渐变,属于后续统一风格的一部分。
|
||||
- `components/ui/__tests__/UpgradeModal.test.tsx`:测试已改为锁定限制提示和中性按钮文案。
|
||||
|
||||
建议动作:
|
||||
|
||||
- 改成许可证状态提示:功能不可用、当前版本、需要的版本、联系管理员/服务商。
|
||||
- 去掉营销化“升级好处”列表。
|
||||
- PRO 徽章改为中性许可证标签,不用紫蓝渐变。
|
||||
- 已完成:可见“升级后/请升级”话术第一轮清理,改成许可证状态提示和联系管理员处理授权。
|
||||
- 已完成:通用授权弹窗不再展示营销化“升级好处”。
|
||||
- 仍需继续:PRO 徽章改为中性许可证标签,不用紫蓝渐变。
|
||||
|
||||
### P2-12 登录、激活、回调页风格和后台不统一
|
||||
|
||||
|
||||
@ -54,16 +54,16 @@ describe('canCreateUser', () => {
|
||||
expect(mockCheckLimit).toHaveBeenCalledWith('users');
|
||||
});
|
||||
|
||||
it('当达到限制时(FREE 版本)应该返回升级提示', async () => {
|
||||
it('当达到限制时(FREE 版本)应该返回授权处理提示', async () => {
|
||||
mockCheckLimit.mockResolvedValue({ allowed: false, current: 10, max: 10 });
|
||||
mockGetLicenseType.mockResolvedValue('FREE');
|
||||
|
||||
const result = await canCreateUser();
|
||||
|
||||
expect(result.allowed).toBe(false);
|
||||
expect(result.message).toContain('免费版');
|
||||
expect(result.message).toContain('当前许可证');
|
||||
expect(result.message).toContain('10');
|
||||
expect(result.message).toContain('升级');
|
||||
expect(result.message).toContain('联系管理员');
|
||||
});
|
||||
|
||||
it('当达到限制时(非 FREE 版本)应该返回联系管理员提示', async () => {
|
||||
@ -94,16 +94,16 @@ describe('canUploadMaterial', () => {
|
||||
expect(mockCheckLimit).toHaveBeenCalledWith('materials');
|
||||
});
|
||||
|
||||
it('当达到限制时(FREE 版本)应该返回升级提示', async () => {
|
||||
it('当达到限制时(FREE 版本)应该返回授权处理提示', async () => {
|
||||
mockCheckLimit.mockResolvedValue({ allowed: false, current: 100, max: 100 });
|
||||
mockGetLicenseType.mockResolvedValue('FREE');
|
||||
|
||||
const result = await canUploadMaterial();
|
||||
|
||||
expect(result.allowed).toBe(false);
|
||||
expect(result.message).toContain('免费版');
|
||||
expect(result.message).toContain('当前许可证');
|
||||
expect(result.message).toContain('素材');
|
||||
expect(result.message).toContain('升级');
|
||||
expect(result.message).toContain('联系管理员');
|
||||
});
|
||||
|
||||
it('当达到限制时(非 FREE 版本)应该返回联系管理员提示', async () => {
|
||||
|
||||
@ -25,7 +25,7 @@ export async function canCreateUser(): Promise<LimitCheckResult> {
|
||||
...result,
|
||||
message:
|
||||
licenseType === 'FREE'
|
||||
? `免费版最多支持 ${result.max} 个用户,当前已有 ${result.current} 个用户。请升级到标准版或专业版以解除限制。`
|
||||
? `当前许可证最多支持 ${result.max} 个用户,当前已有 ${result.current} 个用户。请联系管理员处理授权。`
|
||||
: `已达到用户数量上限(${result.max}),请联系管理员。`,
|
||||
};
|
||||
}
|
||||
@ -45,7 +45,7 @@ export async function canUploadMaterial(): Promise<LimitCheckResult> {
|
||||
...result,
|
||||
message:
|
||||
licenseType === 'FREE'
|
||||
? `免费版最多支持 ${result.max} 个素材,当前已有 ${result.current} 个素材。请升级到标准版或专业版以解除限制。`
|
||||
? `当前许可证最多支持 ${result.max} 个素材,当前已有 ${result.current} 个素材。请联系管理员处理授权。`
|
||||
: `已达到素材数量上限(${result.max}),请联系管理员。`,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 功能描述映射
|
||||
*
|
||||
* 定义每个付费功能的名称、描述和升级好处
|
||||
* 定义每个授权功能的名称和描述
|
||||
* 用于 UpgradeModal 和其他需要展示功能信息的地方
|
||||
*/
|
||||
|
||||
@ -12,7 +12,7 @@ export interface FeatureDescription {
|
||||
name: string;
|
||||
/** 功能描述 */
|
||||
description: string;
|
||||
/** 升级好处列表 */
|
||||
/** 功能说明要点 */
|
||||
benefits: string[];
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ export const FEATURE_DESCRIPTIONS: Record<FeatureKey, FeatureDescription> = {
|
||||
export function getFeatureDescription(featureKey: FeatureKey): FeatureDescription {
|
||||
return FEATURE_DESCRIPTIONS[featureKey] || {
|
||||
name: '高级功能',
|
||||
description: '此功能需要升级许可证才能使用',
|
||||
benefits: ['解锁更多功能', '获得技术支持', '享受更新服务'],
|
||||
description: '当前许可证未包含此功能,请联系管理员处理授权',
|
||||
benefits: ['联系管理员处理授权', '确认当前许可证范围', '完成后重新尝试操作'],
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user