refactor: standardize code formatting and linting configuration

- Replace .eslintrc.js with .eslintrc.cjs for better ES module compatibility
- Add .prettierrc configuration for consistent code formatting
- Update package.json with new lint and format scripts
- Add nodemon.json for development hot reloading configuration
- Standardize code formatting across all JavaScript and Vue files
- Update web admin SPA with improved linting rules and formatting
- Add prettier configuration to web admin SPA

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
千羽
2025-08-07 18:19:31 +09:00
parent 4a0eba117c
commit 8a74bf5afe
124 changed files with 20878 additions and 18757 deletions

View File

@@ -2,76 +2,58 @@
<div class="settings-container">
<div class="card p-4 sm:p-6">
<div class="mb-4 sm:mb-6">
<h3 class="text-lg sm:text-xl font-bold text-gray-900 mb-1 sm:mb-2">
其他设置
</h3>
<p class="text-sm sm:text-base text-gray-600">
自定义网站名称和图标
</p>
<h3 class="mb-1 text-lg font-bold text-gray-900 sm:mb-2 sm:text-xl">其他设置</h3>
<p class="text-sm text-gray-600 sm:text-base">自定义网站名称和图标</p>
</div>
<div
v-if="loading"
class="text-center py-12"
>
<div v-if="loading" class="py-12 text-center">
<div class="loading-spinner mx-auto mb-4" />
<p class="text-gray-500">
正在加载设置...
</p>
<p class="text-gray-500">正在加载设置...</p>
</div>
<!-- 桌面端表格视图 -->
<div
v-else
class="hidden sm:block table-container"
>
<div v-else class="table-container hidden sm:block">
<table class="min-w-full">
<tbody class="divide-y divide-gray-200/50">
<!-- 网站名称 -->
<tr class="table-row">
<td class="px-6 py-4 whitespace-nowrap w-48">
<td class="w-48 whitespace-nowrap px-6 py-4">
<div class="flex items-center">
<div class="w-8 h-8 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center mr-3">
<i class="fas fa-font text-white text-xs" />
<div
class="mr-3 flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-blue-600"
>
<i class="fas fa-font text-xs text-white" />
</div>
<div>
<div class="text-sm font-semibold text-gray-900">
网站名称
</div>
<div class="text-xs text-gray-500">
品牌标识
</div>
<div class="text-sm font-semibold text-gray-900">网站名称</div>
<div class="text-xs text-gray-500">品牌标识</div>
</div>
</div>
</td>
<td class="px-6 py-4">
<input
<input
v-model="oemSettings.siteName"
type="text"
class="form-input w-full max-w-md"
placeholder="Claude Relay Service"
maxlength="100"
>
<p class="text-xs text-gray-500 mt-1">
将显示在浏览器标题和页面头部
</p>
placeholder="Claude Relay Service"
type="text"
/>
<p class="mt-1 text-xs text-gray-500">将显示在浏览器标题和页面头部</p>
</td>
</tr>
<!-- 网站图标 -->
<tr class="table-row">
<td class="px-6 py-4 whitespace-nowrap w-48">
<td class="w-48 whitespace-nowrap px-6 py-4">
<div class="flex items-center">
<div class="w-8 h-8 bg-gradient-to-br from-purple-500 to-purple-600 rounded-lg flex items-center justify-center mr-3">
<i class="fas fa-image text-white text-xs" />
<div
class="mr-3 flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-purple-500 to-purple-600"
>
<i class="fas fa-image text-xs text-white" />
</div>
<div>
<div class="text-sm font-semibold text-gray-900">
网站图标
</div>
<div class="text-xs text-gray-500">
Favicon
</div>
<div class="text-sm font-semibold text-gray-900">网站图标</div>
<div class="text-xs text-gray-500">Favicon</div>
</div>
</div>
</td>
@@ -80,72 +62,62 @@
<!-- 图标预览 -->
<div
v-if="oemSettings.siteIconData || oemSettings.siteIcon"
class="inline-flex items-center gap-3 p-3 bg-gray-50 rounded-lg"
class="inline-flex items-center gap-3 rounded-lg bg-gray-50 p-3"
>
<img
:src="oemSettings.siteIconData || oemSettings.siteIcon"
alt="图标预览"
class="w-8 h-8"
<img
alt="图标预览"
class="h-8 w-8"
:src="oemSettings.siteIconData || oemSettings.siteIcon"
@error="handleIconError"
>
/>
<span class="text-sm text-gray-600">当前图标</span>
<button
class="text-red-600 hover:text-red-900 font-medium hover:bg-red-50 px-3 py-1 rounded-lg transition-colors"
<button
class="rounded-lg px-3 py-1 font-medium text-red-600 transition-colors hover:bg-red-50 hover:text-red-900"
@click="removeIcon"
>
<i class="fas fa-trash mr-1" />删除
</button>
</div>
<!-- 文件上传 -->
<div>
<input
ref="iconFileInput"
type="file"
<input
ref="iconFileInput"
accept=".ico,.png,.jpg,.jpeg,.svg"
class="hidden"
type="file"
@change="handleIconUpload"
>
<button
class="btn btn-success px-4 py-2"
@click="$refs.iconFileInput.click()"
>
/>
<button class="btn btn-success px-4 py-2" @click="$refs.iconFileInput.click()">
<i class="fas fa-upload mr-2" />
上传图标
</button>
<span class="text-xs text-gray-500 ml-3">支持 .ico, .png, .jpg, .svg 格式最大 350KB</span>
<span class="ml-3 text-xs text-gray-500"
>支持 .ico, .png, .jpg, .svg 格式最大 350KB</span
>
</div>
</div>
</td>
</tr>
<!-- 操作按钮 -->
<tr>
<td
class="px-6 py-6"
colspan="2"
>
<td class="px-6 py-6" colspan="2">
<div class="flex items-center justify-between">
<div class="flex gap-3">
<button
:disabled="saving"
<button
class="btn btn-primary px-6 py-3"
:class="{ 'opacity-50 cursor-not-allowed': saving }"
:class="{ 'cursor-not-allowed opacity-50': saving }"
:disabled="saving"
@click="saveOemSettings"
>
<div
v-if="saving"
class="loading-spinner mr-2"
/>
<i
v-else
class="fas fa-save mr-2"
/>
<div v-if="saving" class="loading-spinner mr-2" />
<i v-else class="fas fa-save mr-2" />
{{ saving ? '保存中...' : '保存设置' }}
</button>
<button
class="btn bg-gray-100 text-gray-700 hover:bg-gray-200 px-6 py-3"
<button
class="btn bg-gray-100 px-6 py-3 text-gray-700 hover:bg-gray-200"
:disabled="saving"
@click="resetOemSettings"
>
@@ -153,11 +125,8 @@
重置为默认
</button>
</div>
<div
v-if="oemSettings.updatedAt"
class="text-sm text-gray-500"
>
<div v-if="oemSettings.updatedAt" class="text-sm text-gray-500">
<i class="fas fa-clock mr-1" />
最后更新{{ formatDateTime(oemSettings.updatedAt) }}
</div>
@@ -167,140 +136,118 @@
</tbody>
</table>
</div>
<!-- 移动端卡片视图 -->
<div
v-if="!loading"
class="sm:hidden space-y-4"
>
<div v-if="!loading" class="space-y-4 sm:hidden">
<!-- 网站名称设置卡片 -->
<div class="bg-gray-50 rounded-lg p-4">
<div class="flex items-center gap-3 mb-3">
<div class="w-10 h-10 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center flex-shrink-0">
<i class="fas fa-font text-white text-sm" />
<div class="rounded-lg bg-gray-50 p-4">
<div class="mb-3 flex items-center gap-3">
<div
class="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-blue-600"
>
<i class="fas fa-font text-sm text-white" />
</div>
<div>
<h4 class="text-sm font-semibold text-gray-900">
网站名称
</h4>
<p class="text-xs text-gray-500">
品牌标识
</p>
<h4 class="text-sm font-semibold text-gray-900">网站名称</h4>
<p class="text-xs text-gray-500">品牌标识</p>
</div>
</div>
<div class="space-y-2">
<input
<input
v-model="oemSettings.siteName"
type="text"
class="form-input w-full text-sm"
placeholder="Claude Relay Service"
maxlength="100"
>
<p class="text-xs text-gray-500">
将显示在浏览器标题和页面头部
</p>
placeholder="Claude Relay Service"
type="text"
/>
<p class="text-xs text-gray-500">将显示在浏览器标题和页面头部</p>
</div>
</div>
<!-- 网站图标设置卡片 -->
<div class="bg-gray-50 rounded-lg p-4">
<div class="flex items-center gap-3 mb-3">
<div class="w-10 h-10 bg-gradient-to-br from-purple-500 to-purple-600 rounded-lg flex items-center justify-center flex-shrink-0">
<i class="fas fa-image text-white text-sm" />
<div class="rounded-lg bg-gray-50 p-4">
<div class="mb-3 flex items-center gap-3">
<div
class="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-purple-500 to-purple-600"
>
<i class="fas fa-image text-sm text-white" />
</div>
<div>
<h4 class="text-sm font-semibold text-gray-900">
网站图标
</h4>
<p class="text-xs text-gray-500">
Favicon
</p>
<h4 class="text-sm font-semibold text-gray-900">网站图标</h4>
<p class="text-xs text-gray-500">Favicon</p>
</div>
</div>
<div class="space-y-3">
<!-- 图标预览 -->
<div
v-if="oemSettings.siteIconData || oemSettings.siteIcon"
class="inline-flex items-center gap-3 p-3 bg-white rounded-lg border border-gray-200 w-full"
class="inline-flex w-full items-center gap-3 rounded-lg border border-gray-200 bg-white p-3"
>
<img
:src="oemSettings.siteIconData || oemSettings.siteIcon"
alt="图标预览"
class="w-8 h-8"
<img
alt="图标预览"
class="h-8 w-8"
:src="oemSettings.siteIconData || oemSettings.siteIcon"
@error="handleIconError"
>
<span class="text-sm text-gray-600 flex-1">当前图标</span>
<button
class="text-red-600 hover:text-red-900 text-sm font-medium hover:bg-red-50 px-3 py-1 rounded-lg transition-colors"
/>
<span class="flex-1 text-sm text-gray-600">当前图标</span>
<button
class="rounded-lg px-3 py-1 text-sm font-medium text-red-600 transition-colors hover:bg-red-50 hover:text-red-900"
@click="removeIcon"
>
<i class="fas fa-trash mr-1" />删除
</button>
</div>
<!-- 上传按钮 -->
<input
<input
ref="iconFileInput"
type="file"
accept="image/*"
style="display: none;"
accept="image/*"
style="display: none"
type="file"
@change="handleIconUpload"
>
/>
<div class="flex flex-col gap-2">
<button
class="w-full px-4 py-2 bg-white border border-gray-200 rounded-lg text-sm text-gray-700 hover:bg-gray-50 transition-colors flex items-center justify-center gap-2"
<button
class="flex w-full items-center justify-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm text-gray-700 transition-colors hover:bg-gray-50"
@click="$refs.iconFileInput.click()"
>
<i class="fas fa-upload" />
上传图标
</button>
<div class="text-xs text-gray-500">
或者输入图标URL
</div>
<input
<div class="text-xs text-gray-500">或者输入图标URL</div>
<input
v-model="oemSettings.siteIcon"
type="url"
class="form-input w-full text-sm"
placeholder="https://example.com/icon.png"
>
type="url"
/>
</div>
<p class="text-xs text-gray-500">
支持 PNGJPEGGIF 格式建议使用正方形图片
</p>
<p class="text-xs text-gray-500">支持 PNGJPEGGIF 格式建议使用正方形图片</p>
</div>
</div>
<!-- 操作按钮 -->
<div class="space-y-3 pt-2">
<button
<button
class="btn btn-primary w-full py-3 text-sm font-semibold"
:disabled="saving"
@click="saveOemSettings"
>
<div
v-if="saving"
class="loading-spinner mr-2"
/>
<i
v-else
class="fas fa-save mr-2"
/>
<div v-if="saving" class="loading-spinner mr-2" />
<i v-else class="fas fa-save mr-2" />
{{ saving ? '保存中...' : '保存设置' }}
</button>
<button
class="btn bg-gray-100 text-gray-700 hover:bg-gray-200 w-full py-3 text-sm"
<button
class="btn w-full bg-gray-100 py-3 text-sm text-gray-700 hover:bg-gray-200"
:disabled="saving"
@click="resetOemSettings"
>
<i class="fas fa-undo mr-2" />
重置为默认
</button>
<div
v-if="oemSettings.updatedAt"
class="text-center text-xs text-gray-500"
>
<div v-if="oemSettings.updatedAt" class="text-center text-xs text-gray-500">
<i class="fas fa-clock mr-1" />
最后更新{{ formatDateTime(oemSettings.updatedAt) }}
</div>
@@ -354,7 +301,7 @@ const saveOemSettings = async () => {
// 重置OEM设置
const resetOemSettings = async () => {
if (!confirm('确定要重置为默认设置吗?\n\n这将清除所有自定义的网站名称和图标设置。')) return
try {
const result = await settingsStore.resetOemSettings()
if (result && result.success) {
@@ -371,14 +318,14 @@ const resetOemSettings = async () => {
const handleIconUpload = async (event) => {
const file = event.target.files[0]
if (!file) return
// 验证文件
const validation = settingsStore.validateIconFile(file)
if (!validation.isValid) {
validation.errors.forEach(error => showToast(error, 'error'))
validation.errors.forEach((error) => showToast(error, 'error'))
return
}
try {
// 转换为Base64
const base64Data = await settingsStore.fileToBase64(file)
@@ -386,7 +333,7 @@ const handleIconUpload = async (event) => {
} catch (error) {
showToast('文件读取失败', 'error')
}
// 清除input的值允许重复选择同一文件
event.target.value = ''
}
@@ -433,11 +380,11 @@ const formatDateTime = settingsStore.formatDateTime
}
.form-input {
@apply w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200;
@apply w-full rounded-lg border border-gray-300 px-4 py-2 transition-all duration-200 focus:border-transparent focus:ring-2 focus:ring-blue-500;
}
.btn {
@apply inline-flex items-center justify-center px-4 py-2 text-sm font-semibold rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2;
@apply inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-semibold transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2;
}
.btn-primary {
@@ -449,6 +396,6 @@ const formatDateTime = settingsStore.formatDateTime
}
.loading-spinner {
@apply w-5 h-5 border-2 border-gray-300 border-t-blue-600 rounded-full animate-spin;
@apply h-5 w-5 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600;
}
</style>
</style>