add About
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 timi
|
||||
Copyright (c) 2026 imyeyu.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="about-page">
|
||||
<section class="brand" v-entrance-transition>
|
||||
<logo />
|
||||
<h1 v-text="appName" />
|
||||
<p v-text="appDescription" />
|
||||
</section>
|
||||
<t-cell-group title="协议" theme="card" v-entrance-transition="70">
|
||||
<t-cell
|
||||
v-for="item in agreementItemList"
|
||||
:key="item.code"
|
||||
:title="item.title"
|
||||
arrow
|
||||
hover
|
||||
@click="openAgreement(item)"
|
||||
>
|
||||
<template #leftIcon>
|
||||
<t-icon :name="item.icon" size="1.75rem" />
|
||||
</template>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
<footer class="footer" v-entrance-transition="140">
|
||||
<p>技术支持:夜雨</p>
|
||||
<p>Copyright © 2026 imyeyu.com</p>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type Setting, SettingAPI } from "timi-web-api";
|
||||
import Logo from "@/pages/user/component/Logo.vue";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
type AgreementCode = "SERVICE_LICENSE" | "PRIVACY_LICENSE" | "OPEN_SOURCE_LICENSE";
|
||||
type AgreementRouteName = "ServiceAgreement" | "PrivacyAgreement" | "OpenSourceAgreement";
|
||||
type AgreementItem = {
|
||||
code: AgreementCode;
|
||||
icon: string;
|
||||
routeName: AgreementRouteName;
|
||||
title: string;
|
||||
};
|
||||
|
||||
defineOptions({
|
||||
name: "About"
|
||||
});
|
||||
|
||||
const appName = "糕店客户管理";
|
||||
const appDescription = "面向门店的客户、登记、积分与经营管理工具";
|
||||
const router = useRouter();
|
||||
|
||||
const agreementItemList: AgreementItem[] = [
|
||||
{
|
||||
code: "SERVICE_LICENSE",
|
||||
icon: "file-attachment",
|
||||
routeName: "ServiceAgreement",
|
||||
title: "服务协议"
|
||||
},
|
||||
{
|
||||
code: "PRIVACY_LICENSE",
|
||||
icon: "secured",
|
||||
routeName: "PrivacyAgreement",
|
||||
title: "隐私协议"
|
||||
},
|
||||
{
|
||||
code: "OPEN_SOURCE_LICENSE",
|
||||
icon: "code",
|
||||
routeName: "OpenSourceAgreement",
|
||||
title: "开源软件协议"
|
||||
}
|
||||
];
|
||||
|
||||
const settingMap = ref<Record<string, Setting>>({});
|
||||
|
||||
function getAgreementId(code: AgreementCode): string {
|
||||
return settingMap.value[code]?.value?.trim() || "";
|
||||
}
|
||||
|
||||
async function openAgreement(item: AgreementItem): Promise<void> {
|
||||
const articleId = getAgreementId(item.code);
|
||||
if (!articleId) {
|
||||
Message.warning(`${item.title}暂未配置`);
|
||||
return;
|
||||
}
|
||||
await router.push({
|
||||
name: item.routeName,
|
||||
params: {
|
||||
id: articleId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function loadSettings(): Promise<void> {
|
||||
try {
|
||||
settingMap.value = await SettingAPI.map("GAO_SYSTEM");
|
||||
} catch (error) {
|
||||
Message.error(getErrorMessage(error, "协议配置加载失败"));
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadSettings();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.about-page {
|
||||
gap: 1rem;
|
||||
display: flex;
|
||||
padding: calc(var(--app-nav-height) + 1rem) 0 5rem;
|
||||
flex-direction: column;
|
||||
|
||||
.brand {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
|
||||
.logo {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: .875rem 0 .5rem;
|
||||
color: #0f172a;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: #52607a;
|
||||
font-size: .875rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 1.25rem 1rem calc(1rem + env(safe-area-inset-bottom, 0));
|
||||
text-align: center;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: #52607a;
|
||||
font-size: .8125rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-dark) {
|
||||
.about-page {
|
||||
.brand {
|
||||
h1 {
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #94a3b8;
|
||||
}
|
||||
}
|
||||
|
||||
.footer p {
|
||||
color: #94a3b8;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="agreement-page">
|
||||
<template v-if="article">
|
||||
<header class="header" v-entrance-transition>
|
||||
<h3 v-text="article.title || '协议'" />
|
||||
<markdown-view
|
||||
v-if="article.description"
|
||||
class="description"
|
||||
:content="article.description"
|
||||
/>
|
||||
</header>
|
||||
<article-view
|
||||
:article="article"
|
||||
empty-text="暂无协议正文"
|
||||
:resolve-attachment-url="resolveAttachmentUrl"
|
||||
/>
|
||||
</template>
|
||||
<t-empty
|
||||
v-else
|
||||
:description="isLoading ? '正在加载协议' : '协议不存在'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type Article, ArticleAPI, AttachmentAPI } from "timi-web-api";
|
||||
import { ArticleView } from "timi-tdesign-mobile";
|
||||
import { MarkdownView } from "timi-web";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
defineOptions({
|
||||
name: "Agreement"
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const article = ref<Article>();
|
||||
const isLoading = ref(false);
|
||||
let loadToken = 0;
|
||||
|
||||
function resolveAttachmentUrl(attachmentId: string): string {
|
||||
return AttachmentAPI.getReadURL(attachmentId);
|
||||
}
|
||||
|
||||
async function loadArticle(): Promise<void> {
|
||||
const requestToken = loadToken + 1;
|
||||
loadToken = requestToken;
|
||||
article.value = undefined;
|
||||
const articleId = String(route.params.id || "").trim();
|
||||
if (!articleId) {
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const result = await ArticleAPI.detail(articleId);
|
||||
if (requestToken !== loadToken) {
|
||||
return;
|
||||
}
|
||||
article.value = result;
|
||||
} catch (error) {
|
||||
Message.error(getErrorMessage(error, "协议加载失败"));
|
||||
} finally {
|
||||
if (requestToken === loadToken) {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => route.params.id, async () => {
|
||||
await loadArticle();
|
||||
}, { immediate: true });
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.agreement-page {
|
||||
padding: var(--app-nav-height) 0 1rem 0;
|
||||
|
||||
.header {
|
||||
padding: .5rem 1rem;
|
||||
|
||||
h3 {
|
||||
color: #0f172a;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #52607a;
|
||||
font-size: .875rem;
|
||||
margin-top: .5rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.content) {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-dark) {
|
||||
.agreement-page {
|
||||
.header {
|
||||
h1 {
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #94a3b8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -87,7 +87,7 @@ type MenuItem = {
|
||||
|
||||
type MenuGroup = {
|
||||
items: MenuItem[];
|
||||
title: string;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
defineOptions({
|
||||
@@ -234,6 +234,16 @@ const menuGroupList = computed<MenuGroup[]>(() => [
|
||||
path: "/management/qr-code",
|
||||
title: "生成客户二维码",
|
||||
visible: userStore.hasPermissions(permissionCodes.qrCode.create)
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{
|
||||
icon: "info-circle",
|
||||
path: "/management/about",
|
||||
title: "关于",
|
||||
visible: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
import About from "@/pages/setting/About.vue";
|
||||
import Agreement from "@/pages/setting/Agreement.vue";
|
||||
import AppSettings from "@/pages/setting/AppSettings.vue";
|
||||
import NotifyList from "@/pages/notify/NotifyList.vue";
|
||||
import NotifyCreate from "@/pages/notify/NotifyCreate.vue";
|
||||
@@ -146,6 +148,62 @@ export const storeRoutes: RouteRecordRaw[] = [
|
||||
];
|
||||
|
||||
export const managementSettingRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/management/about",
|
||||
name: "About",
|
||||
component: About,
|
||||
meta: {
|
||||
depth: 3,
|
||||
navBarVisible: true,
|
||||
navBarCanBack: true,
|
||||
navBarTitle: "关于",
|
||||
tabBarVisible: false,
|
||||
bodyBackground: "#F5F5F8",
|
||||
darkBodyBackground: "#10161f"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/management/about/service-agreement/:id",
|
||||
name: "ServiceAgreement",
|
||||
component: Agreement,
|
||||
meta: {
|
||||
depth: 4,
|
||||
navBarVisible: true,
|
||||
navBarCanBack: true,
|
||||
navBarTitle: "服务协议",
|
||||
tabBarVisible: false,
|
||||
bodyBackground: "#FFF",
|
||||
darkBodyBackground: "#10161f"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/management/about/privacy-agreement/:id",
|
||||
name: "PrivacyAgreement",
|
||||
component: Agreement,
|
||||
meta: {
|
||||
depth: 4,
|
||||
navBarVisible: true,
|
||||
navBarCanBack: true,
|
||||
navBarTitle: "隐私协议",
|
||||
tabBarVisible: false,
|
||||
bodyBackground: "#FFF",
|
||||
darkBodyBackground: "#10161f"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/management/about/open-source-agreement/:id",
|
||||
name: "OpenSourceAgreement",
|
||||
component: Agreement,
|
||||
meta: {
|
||||
depth: 4,
|
||||
navBarVisible: true,
|
||||
navBarCanBack: true,
|
||||
navBarTitle: "开源软件协议",
|
||||
tabBarVisible: false,
|
||||
bodyBackground: "#F5F5F8",
|
||||
darkBodyBackground: "#10161f"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/management/settings",
|
||||
name: "AppSettings",
|
||||
|
||||
Reference in New Issue
Block a user