diff --git a/README.md b/README.md index e82aff0..b7a1653 100644 --- a/README.md +++ b/README.md @@ -422,4 +422,20 @@ watch(loading, (load) => { load ? spinner.value?.open() : spinner.value?.close(); }); -``` \ No newline at end of file +``` + + +### 关于 tooltip + +kuc 没有实现插槽,所以应该当成一个普通组件使用: + +```vue + + 购买 + + + + +``` diff --git a/components.d.ts b/components.d.ts index f0e951b..f2fe8d8 100644 --- a/components.d.ts +++ b/components.d.ts @@ -8,6 +8,7 @@ export {} declare module 'vue' { export interface GlobalComponents { Config: typeof import('./src/components/Config.vue')['default'] + LicenseStatus: typeof import('./src/components/LicenseStatus.vue')['default'] PluginInput: typeof import('./src/components/basic/PluginInput.vue')['default'] PluginLabel: typeof import('./src/components/basic/PluginLabel.vue')['default'] } diff --git a/src/components/Config.vue b/src/components/Config.vue index d33589c..ff3c475 100644 --- a/src/components/Config.vue +++ b/src/components/Config.vue @@ -1,4 +1,7 @@ + + + {{ $t('config.title') }} {{ $t('config.desc') }} @@ -9,7 +12,7 @@ - + @@ -19,10 +22,8 @@ import type { Setting } from '@/types'; import { useI18n } from 'vue-i18n'; import { useKintoneClient } from '@/composables/useKintoneClient'; -import { useLicenseManager } from '@/composables/useLicenseManager'; import type { Spinner } from 'kintone-ui-component'; -import LicenseModal from '@/components/LicenseModal.vue'; -import LicenseNotification from '@/components/LicenseNotification.vue'; +import LicenseStatus from './LicenseStatus.vue'; import { nextTick, onMounted, reactive, ref, shallowRef, watch } from 'vue'; @@ -44,21 +45,11 @@ const loading = ref(false); const mainArea = shallowRef(null); const spinner = shallowRef(null); -const licenseManager = useLicenseManager(); - onMounted(async () => { // 等待页面完全渲染后再显示加载状态,实现更平滑的用户体验 nextTick(async () => { loading.value = true; - // 检查许可证 - if (!licenseManager.canAccessFeatures()) { - // 没有权限或许可证无效,显示弹框或错误提示 - showLicenseError(); - loading.value = false; - return; - } - // 获取已保存的插件配置 const savedSetting = kintone.plugin.app.getConfig(props.pluginId); setting.buttonName = savedSetting?.buttonName || $t('config.button.default'); @@ -70,10 +61,8 @@ onMounted(async () => { console.log('pluginsInfo', pluginsInfo); // 模拟加载时间,展示 spinner 效果 await new Promise((resolve) => setTimeout(resolve, 500)); - loading.value = false; - // 检查许可证状态,显示相关信息 - checkLicenseStatus(); + loading.value = false; }); }); @@ -115,54 +104,6 @@ async function save() { }); } -/** - * 显示许可证错误 - */ -function showLicenseError() { - // 添加许可证状态显示区域 - const licenseStatusElement = document.createElement('div'); - licenseStatusElement.className = 'license-status'; - licenseStatusElement.innerHTML = ` - - 许可证无效 - 您的插件许可证已过期或无效,请联系管理员或购买许可证。 - 重新检查 - - `; - - // 插入到页面顶部 - const mainArea = document.getElementById('main-area'); - if (mainArea && mainArea.parentNode) { - mainArea.parentNode.insertBefore(licenseStatusElement, mainArea); - } -} - -/** - * 检查许可证状态并显示相关信息 - */ -function checkLicenseStatus() { - const licenseInfo = licenseManager.getLicenseDisplayInfo(); - - if (licenseInfo) { - // 添加许可证状态显示 - const statusElement = document.createElement('div'); - statusElement.className = 'license-status-info'; - statusElement.innerHTML = ` - - 许可证状态 - 到期时间: ${licenseInfo.expiryDate} - 状态: ${licenseInfo.isExpired ? '已过期' : licenseInfo.remainingDays === Infinity ? '永久' : `剩余 ${licenseInfo.remainingDays} 天`} - - `; - - // 插入到设置区域上方 - const mainArea = document.getElementById('main-area'); - if (mainArea && mainArea.parentNode) { - mainArea.parentNode.insertBefore(statusElement, mainArea); - } - } -} - /** * 取消操作并返回插件列表 * 重定向到插件管理页面 diff --git a/src/components/LicenseStatus.vue b/src/components/LicenseStatus.vue new file mode 100644 index 0000000..8789e08 --- /dev/null +++ b/src/components/LicenseStatus.vue @@ -0,0 +1,151 @@ + + + + + 许可证状态: + ... + + + + 检查 + + 购买 + + + 不再显示 + + + + + + diff --git a/src/css/config.css b/src/css/config.css index 449d29d..572088b 100644 --- a/src/css/config.css +++ b/src/css/config.css @@ -15,7 +15,7 @@ /* 最上面的说明 */ .settings-heading { padding: 1em 0; - margin-bottom: 8px; + margin: 8px 0; } /* label 样式 */ diff --git a/src/css/license.css b/src/css/license.css index b0bb6b2..61f0dda 100644 --- a/src/css/license.css +++ b/src/css/license.css @@ -271,27 +271,99 @@ } .license-status-info { - margin-bottom: 20px; - padding: 16px; - border-radius: 6px; - border: 1px solid #e5e5e5; - background-color: #f7fafc; + padding: 12px 16px; + margin-right: 24px; + border-radius: 4px; + border: 1px solid #ddd; + background-color: #f9f9f9; + font-size: 13px; } -.license-status-info .license-info h4 { - margin: 0 0 12px 0; - color: #2d3748; - font-size: 16px; - font-weight: 600; +.license-status-info .license-content { + display: flex; + justify-content: space-between; + align-items: baseline; } -.license-status-info .license-info p { - margin: 0 0 8px 0; - font-size: 14px; - color: #4a5568; - line-height: 1.4; +.license-status-info .license-status-text { + color: #333; + flex: 1; } -.license-status-info .license-info p:last-child { - margin-bottom: 0; +.license-status-info .license-actions { + display: flex; + gap: 8px; + flex-shrink: 0; +} + +.license-status-info .action-btn { + padding: 5px 10px; + border-radius: 3px; + font-size: 12px; + cursor: pointer; + transition: all 0.2s; + min-width: 65px; +} + +.license-status-info .action-btn:not(.main) { + background: transparent; + color: #666; + border: 1px solid #ccc; +} + +.license-status-info .action-btn:not(.main):disabled { + background: #efefef; + color: #a0a0a0; +} + +.license-status-info .action-btn:not(.main):not(:disabled):hover { + background: #eee; + border-color: #999; +} + +.license-status-info .action-btn:not(.main):not(:disabled):active { + background: #BDBDBD; + border-color: #757575; +} + +.license-status-info .action-btn.main { + background: #4CAF50; + color: white; + border: 1px solid #4CAF50; +} + +.license-status-info .action-btn.main:hover { + background: #388E3C; + border-color: #388E3C; +} + +.license-status-info .action-btn.main:active { + background: #33691E; + border-color: #33691E; +} + +.notification-link { + color: #fff; + text-decoration: underline; +} + +.notification-link:hover { + color: #daf0ff; +} + +.text-red { + color: #B71C1C; +} + +.text-green { + color: #4CAF50; +} + +#license-label { + background-color: #4CAF50; + color: white; + padding: 2px 8px; + font-size: 12px; + border-radius: 9999px; + margin-left: 8px; } diff --git a/src/services/licenseService.ts b/src/services/licenseService.ts index 6459359..9c8e40d 100644 --- a/src/services/licenseService.ts +++ b/src/services/licenseService.ts @@ -7,7 +7,7 @@ import { MobileNotification } from 'kintone-ui-component/lib/mobile/notification export class LicenseService { // 常量定义 private static readonly WARNING_DAYS_BEFORE_EXPIRY = 7; - private static readonly TRIAL_DATE = 1; + private static readonly TRIAL_DATE = 30; private static PLUGIN_ID: string = ''; // ============ 基础工具函数 ============ @@ -22,7 +22,7 @@ export class LicenseService { * 获取插件ID */ static getPluginId(): string { - return this.PLUGIN_ID; + return this.PLUGIN_ID || new URLSearchParams(window.location.search).get('pluginId') || ''; } /** @@ -122,7 +122,7 @@ export class LicenseService { */ static formatExpiryDate(timestamp: number): string { if (timestamp === -1) return '永久'; - return new Date(timestamp).toLocaleDateString('zh-CN'); + return new Date(timestamp).toLocaleDateString(kintone.getLoginUser().language); } /** @@ -132,7 +132,7 @@ export class LicenseService { if (timestamp === -1) return Infinity; const now = new Date().getTime(); const remainingMs = timestamp - now; - return Math.ceil(remainingMs / (24 * 60 * 60 * 1000)); + return Math.floor(remainingMs / (24 * 60 * 60 * 1000)); } /** @@ -167,28 +167,32 @@ export class LicenseService { warningDialogMessage?: string; }) { const remainingDays = this.getDaysRemaining(license.expiredTime); - const msg = remainingDays > 0 ? ` ${remainingDays} 天后` : '今天' - const defaultMessage = `您的插件许可证将在${msg}到期,请及时续期以避免服务中断。`; + if (remainingDays <= 0) { + return + } + const msg = remainingDays > 1 ? ` ${remainingDays} 天后` : (remainingDays > 0 ? '明天' : '今天') + const link = `https://alicorn.cybozu.com/k/admin/app/${kintone.app.getId()}/plugin/config?pluginId=${this.getPluginId()}` + const defaultMessage = `您的插件将在${msg}试用结束如需使用请进入「プラグインの設定」查看详情。`; // 使用自定义消息或默认消息 const message = options?.warningDialogMessage || defaultMessage; // 检查是否已设置不再提醒 - const settings = LicenseStorage.getSettings(this.getPluginId()); - if (settings.suppressMsgTime && this.isToday(settings.suppressMsgTime)) { - return - } - delete settings.suppressMsgTime - LicenseStorage.saveSetting(settings, this.getPluginId()); + // const settings = LicenseStorage.getSettings(this.getPluginId()); + // if (settings.suppressMsgTime && this.isToday(settings.suppressMsgTime)) { + // return + // } + // delete settings.suppressMsgTime + // LicenseStorage.saveSetting(settings, this.getPluginId()); if (await kintone.isMobilePage()) { const notification = new MobileNotification({ - text: message, + content: message, }); notification.open(); } else { const notification = new Notification({ - text: message, + content: message, type: 'info', }); notification.open(); @@ -279,6 +283,7 @@ export class LicenseService { private static mockCreateTrialLicense(): LicenseInfo { const expiryDate = new Date(); expiryDate.setDate(expiryDate.getDate() + this.TRIAL_DATE); + expiryDate.setHours(23, 59, 59, 999); return { expiredTime: expiryDate.getTime(), diff --git a/src/types/license.d.ts b/src/types/license.d.ts index 0b6ba5d..7cdf7a2 100644 --- a/src/types/license.d.ts +++ b/src/types/license.d.ts @@ -17,6 +17,7 @@ export interface LicenseInfo { export interface LicenseSetting { suppressMsgTime?: number; + hideLicenseAreaIfPaid?: boolean; } export interface LicenseCheckResult {
{{ $t('config.desc') }}
您的插件许可证已过期或无效,请联系管理员或购买许可证。
到期时间: ${licenseInfo.expiryDate}
状态: ${licenseInfo.isExpired ? '已过期' : licenseInfo.remainingDays === Infinity ? '永久' : `剩余 ${licenseInfo.remainingDays} 天`}