mirror of
https://github.com/34892002/edgeKey.git
synced 2026-09-03 06:47:41 +08:00
3
.gitignore
vendored
3
.gitignore
vendored
@@ -151,3 +151,6 @@ cdk.out
|
||||
|
||||
# prisma部署d1的客户端产物
|
||||
/generated/prisma/
|
||||
|
||||
# 本地SQLite数据库
|
||||
prisma/db.sqlite
|
||||
|
||||
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,5 +1,15 @@
|
||||
# Change Log
|
||||
|
||||
## v1.4.7 (2026-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
- **site:** 新增页头/页脚自定义代码注入功能,支持在后台添加统计 JS、Meta 标签、客服插件等自定义代码
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **payment:** 修复 TypeScript 类型错误
|
||||
|
||||
## v1.4.6 (2026-06-21)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -71,6 +71,14 @@ const defaultPaymentConfigs: Record<PaymentProvider, PaymentConfigValue> = {
|
||||
notifyUrl: "/api/payments/stripe/notify",
|
||||
returnUrl: "/order/{orderNo}?token={token}",
|
||||
},
|
||||
FREE_PAY: {
|
||||
provider: "FREE_PAY",
|
||||
name: "免费下单",
|
||||
isEnabled: false,
|
||||
baseUrl: "",
|
||||
notifyUrl: "/api/payments/free/notify",
|
||||
returnUrl: "/order/{orderNo}?token={token}",
|
||||
},
|
||||
};
|
||||
|
||||
function getPaymentContext() {
|
||||
|
||||
@@ -18,6 +18,8 @@ export function upsertSiteSettingRecord(
|
||||
supportContact?: string | null;
|
||||
footerText?: string | null;
|
||||
orderNotice?: string | null;
|
||||
headCode?: string | null;
|
||||
footerCode?: string | null;
|
||||
},
|
||||
) {
|
||||
return prisma.siteSetting.upsert({
|
||||
@@ -33,6 +35,8 @@ export function upsertSiteSettingRecord(
|
||||
supportContact: input.supportContact ?? null,
|
||||
footerText: input.footerText ?? null,
|
||||
orderNotice: input.orderNotice ?? null,
|
||||
headCode: input.headCode ?? null,
|
||||
footerCode: input.footerCode ?? null,
|
||||
},
|
||||
update: {
|
||||
siteName: input.siteName,
|
||||
@@ -44,6 +48,8 @@ export function upsertSiteSettingRecord(
|
||||
supportContact: input.supportContact ?? null,
|
||||
footerText: input.footerText ?? null,
|
||||
orderNotice: input.orderNotice ?? null,
|
||||
headCode: input.headCode ?? null,
|
||||
footerCode: input.footerCode ?? null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ const defaultSiteSetting = {
|
||||
supportContact: null,
|
||||
footerText: null,
|
||||
orderNotice: null,
|
||||
headCode: null,
|
||||
footerCode: null,
|
||||
};
|
||||
|
||||
function normalizeSetting(record: Awaited<ReturnType<typeof getSiteSettingRecord>>) {
|
||||
@@ -32,6 +34,8 @@ function normalizeSetting(record: Awaited<ReturnType<typeof getSiteSettingRecord
|
||||
supportContact: record.supportContact,
|
||||
footerText: record.footerText,
|
||||
orderNotice: record.orderNotice,
|
||||
headCode: record.headCode,
|
||||
footerCode: record.footerCode,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,6 +65,8 @@ export async function saveSiteSetting(input: SiteSettingInput) {
|
||||
supportContact: input.supportContact?.trim() || null,
|
||||
footerText: input.footerText?.trim() || null,
|
||||
orderNotice: input.orderNotice?.trim() || null,
|
||||
headCode: input.headCode ?? null,
|
||||
footerCode: input.footerCode ?? null,
|
||||
});
|
||||
|
||||
await logAdminOperation(
|
||||
|
||||
@@ -8,4 +8,6 @@ export interface SiteSettingInput {
|
||||
supportContact?: string | null;
|
||||
footerText?: string | null;
|
||||
orderNotice?: string | null;
|
||||
headCode?: string | null;
|
||||
footerCode?: string | null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "1.4.6",
|
||||
"scripts": {
|
||||
"version": "1.4.7",
|
||||
"scripts": {
|
||||
"dev": "vike dev",
|
||||
"build": "bun run db:generate && vike build",
|
||||
"preview": "vike build && vike preview",
|
||||
|
||||
@@ -66,11 +66,13 @@
|
||||
</details>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- footerCode 通过 onMounted 动态注入 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { computed, onMounted } from "vue";
|
||||
import AppButton from "../components/AppButton.vue";
|
||||
import { usePageContext } from "vike-vue/usePageContext";
|
||||
|
||||
@@ -89,8 +91,24 @@ const supportContactItems = computed(() => {
|
||||
});
|
||||
});
|
||||
const footerText = computed(() => pageContext.site?.footerText || "");
|
||||
const headCode = computed(() => pageContext.site?.headCode || "");
|
||||
const footerCode = computed(() => pageContext.site?.footerCode || "");
|
||||
|
||||
const isAdminRoute = computed(() => pageContext.urlPathname?.startsWith("/admin"));
|
||||
|
||||
function injectCode(html: string, target: Element) {
|
||||
const doc = new DOMParser().parseFromString(html, "text/html");
|
||||
doc.querySelectorAll("script").forEach((old) => {
|
||||
const s = document.createElement("script");
|
||||
s.textContent = old.textContent;
|
||||
target.appendChild(s);
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (headCode.value) injectCode(headCode.value, document.body);
|
||||
if (footerCode.value) injectCode(footerCode.value, document.body);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -66,6 +66,23 @@
|
||||
<textarea v-model="form.orderNotice" class="textarea textarea-bordered w-full" rows="4"></textarea>
|
||||
</label>
|
||||
|
||||
<div class="divider">自定义代码</div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<label class="flex flex-col gap-1.5">
|
||||
<span class="label-text font-medium">页头代码 (head)</span>
|
||||
<textarea v-model="form.headCode" class="textarea textarea-bordered w-full font-mono text-sm" rows="6" placeholder="<meta name="..." content="...">
|
||||
<script>...</script>"></textarea>
|
||||
<span class="text-xs text-base-content/50">注入到 <head> 标签内,可用于添加统计代码、Meta 标签等</span>
|
||||
</label>
|
||||
<label class="flex flex-col gap-1.5">
|
||||
<span class="label-text font-medium">页脚代码 (body)</span>
|
||||
<textarea v-model="form.footerCode" class="textarea textarea-bordered w-full font-mono text-sm" rows="6" placeholder="<script>...</script>
|
||||
<div>...</div>"></textarea>
|
||||
<span class="text-xs text-base-content/50">注入到 </body> 标签前,可用于添加客服插件、JS 脚本等</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<AppButton variant="primary" :loading="saving" @click="handleSave">保存设置</AppButton>
|
||||
<span v-if="errorMessage" class="text-sm text-error">{{ errorMessage }}</span>
|
||||
@@ -103,6 +120,8 @@ const form = reactive({
|
||||
supportContact: site.supportContact ?? "",
|
||||
footerText: site.footerText ?? "",
|
||||
orderNotice: site.orderNotice ?? "",
|
||||
headCode: site.headCode ?? "",
|
||||
footerCode: site.footerCode ?? "",
|
||||
});
|
||||
|
||||
const saving = ref(false);
|
||||
@@ -128,6 +147,8 @@ async function handleSave() {
|
||||
form.supportContact = result.supportContact ?? "";
|
||||
form.footerText = result.footerText ?? "";
|
||||
form.orderNotice = result.orderNotice ?? "";
|
||||
form.headCode = result.headCode ?? "";
|
||||
form.footerCode = result.footerCode ?? "";
|
||||
saved.value = true;
|
||||
} catch (error) {
|
||||
errorMessage.value = normalizeTelefuncError(error, "保存失败");
|
||||
|
||||
@@ -11,6 +11,8 @@ export async function onSaveSiteSettings(input: {
|
||||
supportContact?: string;
|
||||
footerText?: string;
|
||||
orderNotice?: string;
|
||||
headCode?: string;
|
||||
footerCode?: string;
|
||||
}) {
|
||||
assertAdminAccess();
|
||||
return saveSiteSetting(input);
|
||||
|
||||
3
prisma/migrations/0006_add_head_footer_code.sql
Normal file
3
prisma/migrations/0006_add_head_footer_code.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
-- AlterTable: Add head and footer custom code fields
|
||||
ALTER TABLE "SiteSetting" ADD COLUMN "headCode" TEXT;
|
||||
ALTER TABLE "SiteSetting" ADD COLUMN "footerCode" TEXT;
|
||||
@@ -156,6 +156,8 @@ model SiteSetting {
|
||||
supportContact String?
|
||||
footerText String?
|
||||
orderNotice String?
|
||||
headCode String?
|
||||
footerCode String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user