mirror of
https://github.com/ZeroCatDev/Classworks.git
synced 2025-07-02 09:19:23 +00:00
Refactor index.vue and settings.vue for improved layout and functionality. Added URL configuration dialog and settings link generator component. Enhanced button formatting and code readability.
This commit is contained in:
parent
94149d0381
commit
eefd9eb054
458
src/components/SettingsLinkGenerator.vue
Normal file
458
src/components/SettingsLinkGenerator.vue
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
<template>
|
||||||
|
<v-card border class="settings-link-generator mb-4">
|
||||||
|
<v-card-title class="text-h6">
|
||||||
|
<v-icon start icon="mdi-link-variant" class="mr-2" />
|
||||||
|
设置分享
|
||||||
|
</v-card-title>
|
||||||
|
|
||||||
|
<v-card-text>
|
||||||
|
<!-- 快速选择按钮 -->
|
||||||
|
<div class="d-flex mb-3 gap-2 flex-wrap">
|
||||||
|
<v-btn
|
||||||
|
size="small"
|
||||||
|
variant="tonal"
|
||||||
|
color="primary"
|
||||||
|
prepend-icon="mdi-select-all"
|
||||||
|
@click="selectAll"
|
||||||
|
>
|
||||||
|
全选
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
size="small"
|
||||||
|
variant="tonal"
|
||||||
|
color="primary"
|
||||||
|
prepend-icon="mdi-server-network"
|
||||||
|
@click="selectDataSourceSettings"
|
||||||
|
>
|
||||||
|
数据源设置
|
||||||
|
</v-btn>
|
||||||
|
<v-btn
|
||||||
|
size="small"
|
||||||
|
variant="tonal"
|
||||||
|
color="primary"
|
||||||
|
prepend-icon="mdi-compare"
|
||||||
|
@click="selectChangedSettings"
|
||||||
|
>
|
||||||
|
已变更设置
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
|
<v-btn
|
||||||
|
size="small"
|
||||||
|
variant="tonal"
|
||||||
|
color="error"
|
||||||
|
prepend-icon="mdi-select-remove"
|
||||||
|
@click="resetSelection"
|
||||||
|
>
|
||||||
|
取消选择
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 选择摘要和链接 -->
|
||||||
|
<div class="d-flex align-center mt-3 mb-3 flex-wrap gap-2">
|
||||||
|
<v-chip color="primary" class="mr-2">
|
||||||
|
已选 {{ selectedItems.length }} 项设置
|
||||||
|
</v-chip>
|
||||||
|
|
||||||
|
<template v-if="selectedItems.length > 0">
|
||||||
|
<v-chip
|
||||||
|
v-for="item in selectedItems"
|
||||||
|
:key="item"
|
||||||
|
size="small"
|
||||||
|
class="mr-1"
|
||||||
|
variant="text"
|
||||||
|
>
|
||||||
|
{{ getSettingDescription(item) }}
|
||||||
|
</v-chip>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
v-model="generatedLink"
|
||||||
|
label="生成的链接"
|
||||||
|
readonly
|
||||||
|
variant="outlined"
|
||||||
|
class="mb-2"
|
||||||
|
:append-inner-icon="linkCopied ? 'mdi-check' : 'mdi-content-copy'"
|
||||||
|
@click:append-inner="copyLink"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 设置列表折叠面板 -->
|
||||||
|
<v-expansion-panels variant="accordion">
|
||||||
|
<v-expansion-panel>
|
||||||
|
<v-expansion-panel-title> 显示设置列表详情 </v-expansion-panel-title>
|
||||||
|
|
||||||
|
<v-expansion-panel-text>
|
||||||
|
<v-data-table
|
||||||
|
:items-per-page="settingItems.length"
|
||||||
|
:headers="headers"
|
||||||
|
:items="filteredItems"
|
||||||
|
item-value="key"
|
||||||
|
v-model="selectedItems"
|
||||||
|
show-select
|
||||||
|
density="compact"
|
||||||
|
class="rounded setting-table"
|
||||||
|
@update:selected="handleSelectionChange"
|
||||||
|
:sort-by="[{ key: 'isChanged', order: 'desc' }]"
|
||||||
|
>
|
||||||
|
<template v-slot:top>
|
||||||
|
<v-text-field
|
||||||
|
v-model="search"
|
||||||
|
label="搜索设置"
|
||||||
|
prepend-inner-icon="mdi-magnify"
|
||||||
|
single-line
|
||||||
|
hide-details
|
||||||
|
class="mb-4"
|
||||||
|
></v-text-field>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #[`item.description`]="{ item }">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon size="small" :icon="item.icon" class="mr-2"></v-icon>
|
||||||
|
{{ item.description }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #[`item.value`]="{ item }">
|
||||||
|
<span v-if="typeof item.value === 'boolean'">
|
||||||
|
{{ item.value ? "是" : "否" }}
|
||||||
|
</span>
|
||||||
|
<span v-else>{{ item.value }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #[`item.key`]="{ item }">
|
||||||
|
<span class="text-caption text-grey">{{ item.key }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #[`item.isChanged`]="{ item }">
|
||||||
|
<v-chip
|
||||||
|
size="x-small"
|
||||||
|
:color="item.isChanged ? 'warning' : 'success'"
|
||||||
|
:text="item.isChanged ? '已修改' : '默认'"
|
||||||
|
density="compact"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</v-data-table>
|
||||||
|
</v-expansion-panel-text>
|
||||||
|
</v-expansion-panel>
|
||||||
|
</v-expansion-panels>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
exportSettingsAsKeyValue,
|
||||||
|
settingsDefinitions,
|
||||||
|
} from "@/utils/settings";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置链接生成器组件
|
||||||
|
*
|
||||||
|
* 提供一个界面,让用户选择需要包含在链接中的设置项,
|
||||||
|
* 然后生成一个包含这些设置的URL,可以分享给其他用户。
|
||||||
|
*
|
||||||
|
* 当其他用户打开生成的链接时,这些设置将自动应用。
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: "SettingsLinkGenerator",
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 选择的设置项键名列表
|
||||||
|
selectedItems: [],
|
||||||
|
|
||||||
|
// 生成的链接
|
||||||
|
generatedLink: "",
|
||||||
|
|
||||||
|
// 是否已复制链接
|
||||||
|
linkCopied: false,
|
||||||
|
search: "",
|
||||||
|
headers: [
|
||||||
|
{ title: "", key: "data-table-select" },
|
||||||
|
{ title: "设置项", key: "description", sortable: true },
|
||||||
|
{ title: "当前值", key: "value", sortable: true },
|
||||||
|
{
|
||||||
|
title: "键名",
|
||||||
|
key: "key",
|
||||||
|
class: "d-none d-sm-table-cell",
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{ title: "状态", key: "isChanged", sortable: true },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* 获取处理后的设置项列表
|
||||||
|
*/
|
||||||
|
settingItems() {
|
||||||
|
const currentSettings = exportSettingsAsKeyValue();
|
||||||
|
const items = [];
|
||||||
|
|
||||||
|
for (const [key, definition] of Object.entries(settingsDefinitions)) {
|
||||||
|
// 如果是需要开发者模式的设置且未启用开发者模式,则跳过
|
||||||
|
if (
|
||||||
|
definition.requireDeveloper &&
|
||||||
|
!currentSettings["developer.enabled"]
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已修改(与默认值不同)
|
||||||
|
const isChanged = currentSettings[key] !== definition.default;
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
key: key,
|
||||||
|
description: definition.description || key,
|
||||||
|
value: currentSettings[key],
|
||||||
|
icon: definition.icon || "mdi-cog",
|
||||||
|
isChanged: isChanged,
|
||||||
|
defaultValue: definition.default,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按键名排序
|
||||||
|
return items.sort((a, b) => a.key.localeCompare(b.key));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据搜索条件筛选设置项
|
||||||
|
*/
|
||||||
|
filteredItems() {
|
||||||
|
if (!this.search) return this.settingItems;
|
||||||
|
|
||||||
|
const searchText = this.search.toLowerCase();
|
||||||
|
|
||||||
|
// 特殊关键词处理
|
||||||
|
if (searchText === "已修改") {
|
||||||
|
return this.settingItems.filter((item) => item.isChanged);
|
||||||
|
}
|
||||||
|
if (searchText === "是" || searchText === "否") {
|
||||||
|
return this.settingItems.filter(
|
||||||
|
(item) =>
|
||||||
|
typeof item.value === "boolean" &&
|
||||||
|
(searchText === "是" ? item.value : !item.value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 常规文本搜索
|
||||||
|
return this.settingItems.filter((item) => {
|
||||||
|
const description = item.description.toLowerCase();
|
||||||
|
const key = item.key.toLowerCase();
|
||||||
|
const value = String(item.value).toLowerCase();
|
||||||
|
const status = item.isChanged ? "已修改" : "默认";
|
||||||
|
|
||||||
|
return (
|
||||||
|
description.includes(searchText) ||
|
||||||
|
key.includes(searchText) ||
|
||||||
|
value.includes(searchText) ||
|
||||||
|
status.includes(searchText)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有显示相关设置
|
||||||
|
*/
|
||||||
|
hasDisplaySettings() {
|
||||||
|
return this.selectedItems.some((key) => key.startsWith("display."));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有编辑相关设置
|
||||||
|
*/
|
||||||
|
hasEditSettings() {
|
||||||
|
return this.selectedItems.some((key) => key.startsWith("edit."));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有服务器相关设置
|
||||||
|
*/
|
||||||
|
hasServerSettings() {
|
||||||
|
return this.selectedItems.some((key) => key.startsWith("server."));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否已选择已修改的设置
|
||||||
|
*/
|
||||||
|
hasChangedSettings() {
|
||||||
|
const currentSettings = exportSettingsAsKeyValue();
|
||||||
|
|
||||||
|
return this.selectedItems.some((key) => {
|
||||||
|
const definition = settingsDefinitions[key];
|
||||||
|
return definition && currentSettings[key] !== definition.default;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 处理表格选择变化
|
||||||
|
*/
|
||||||
|
handleSelectionChange(items) {
|
||||||
|
this.selectedItems = items.map((item) => item.key);
|
||||||
|
this.generateLink();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成包含所选设置的链接
|
||||||
|
*/
|
||||||
|
generateLink() {
|
||||||
|
// 获取当前网址的基础部分
|
||||||
|
const baseUrl = `${window.location.protocol}//${window.location.host}/`;
|
||||||
|
|
||||||
|
// 获取当前的所有设置
|
||||||
|
const allSettings = exportSettingsAsKeyValue();
|
||||||
|
|
||||||
|
// 创建只包含选中设置的对象
|
||||||
|
const configObj = {};
|
||||||
|
for (const key of this.selectedItems) {
|
||||||
|
configObj[key] = allSettings[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有选择任何设置,则生成不带参数的链接
|
||||||
|
if (Object.keys(configObj).length === 0) {
|
||||||
|
this.generatedLink = baseUrl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 转换为JSON并进行base64编码
|
||||||
|
const jsonString = JSON.stringify(configObj);
|
||||||
|
const utf8Encoder = new TextEncoder();
|
||||||
|
const utf8Bytes = utf8Encoder.encode(jsonString);
|
||||||
|
const base64String = btoa(
|
||||||
|
Array.from(utf8Bytes)
|
||||||
|
.map((byte) => String.fromCharCode(byte))
|
||||||
|
.join("")
|
||||||
|
);
|
||||||
|
|
||||||
|
// 构建查询参数
|
||||||
|
const queryParams = { config: base64String };
|
||||||
|
|
||||||
|
// 添加当前日期到查询参数,如果URL中存在
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const currentDate = urlParams.get("date");
|
||||||
|
if (currentDate) {
|
||||||
|
queryParams.date = currentDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建查询字符串
|
||||||
|
const queryString = new URLSearchParams(queryParams).toString();
|
||||||
|
|
||||||
|
// 生成完整URL
|
||||||
|
this.generatedLink = `${baseUrl}?${queryString}`;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("生成链接失败:", err);
|
||||||
|
this.generatedLink = "链接生成失败,请重试";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置复制状态
|
||||||
|
this.linkCopied = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制生成的链接到剪贴板
|
||||||
|
*/
|
||||||
|
async copyLink() {
|
||||||
|
if (!this.generatedLink) {
|
||||||
|
this.generateLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(this.generatedLink);
|
||||||
|
this.linkCopied = true;
|
||||||
|
|
||||||
|
// 3秒后重置复制状态
|
||||||
|
setTimeout(() => {
|
||||||
|
this.linkCopied = false;
|
||||||
|
}, 3000);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("复制链接失败:", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置所有选择
|
||||||
|
*/
|
||||||
|
resetSelection() {
|
||||||
|
this.selectedItems = [];
|
||||||
|
this.generatedLink = "";
|
||||||
|
this.linkCopied = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择所有设置
|
||||||
|
*/
|
||||||
|
selectAll() {
|
||||||
|
this.selectedItems = this.settingItems.map((item) => item.key);
|
||||||
|
this.generateLink();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择数据源相关设置
|
||||||
|
*/
|
||||||
|
selectDataSourceSettings() {
|
||||||
|
const dataSourceKeys = this.settingItems
|
||||||
|
.filter((item) => item.key.startsWith("server."))
|
||||||
|
.map((item) => item.key);
|
||||||
|
|
||||||
|
this.selectedItems = dataSourceKeys;
|
||||||
|
this.generateLink();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择已修改的设置
|
||||||
|
*/
|
||||||
|
selectChangedSettings() {
|
||||||
|
const changedKeys = this.settingItems
|
||||||
|
.filter((item) => item.isChanged)
|
||||||
|
.map((item) => item.key);
|
||||||
|
|
||||||
|
this.selectedItems = changedKeys;
|
||||||
|
this.generateLink();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据前缀选择设置
|
||||||
|
*/
|
||||||
|
selectByPrefix(prefix) {
|
||||||
|
const keys = this.settingItems
|
||||||
|
.filter((item) => item.key.startsWith(`${prefix}.`))
|
||||||
|
.map((item) => item.key);
|
||||||
|
|
||||||
|
this.selectedItems = keys;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动生成链接(当选择变化时)
|
||||||
|
*/
|
||||||
|
autoGenerateLink() {
|
||||||
|
if (this.selectedItems.length > 0) {
|
||||||
|
this.generateLink();
|
||||||
|
} else {
|
||||||
|
this.generatedLink = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设置描述
|
||||||
|
*/
|
||||||
|
getSettingDescription(key) {
|
||||||
|
const setting = this.settingItems.find((item) => item.key === key);
|
||||||
|
return setting ? setting.description : key;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
// 监听选择变化,自动生成链接
|
||||||
|
selectedItems: {
|
||||||
|
handler() {
|
||||||
|
this.autoGenerateLink();
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
File diff suppressed because it is too large
Load Diff
@ -40,7 +40,9 @@
|
|||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<theme-settings-card border />
|
<theme-settings-card border />
|
||||||
</v-col>
|
</v-col>
|
||||||
|
<v-col cols="12">
|
||||||
|
<settings-link-generator border />
|
||||||
|
</v-col>
|
||||||
<!-- 开发者选项卡片 -->
|
<!-- 开发者选项卡片 -->
|
||||||
<v-col :cols="12" :md="settings.developer.enabled ? 12 : 6">
|
<v-col :cols="12" :md="settings.developer.enabled ? 12 : 6">
|
||||||
<settings-card
|
<settings-card
|
||||||
@ -187,7 +189,7 @@ import AboutCard from '@/components/settings/AboutCard.vue';
|
|||||||
import '../styles/settings.scss';
|
import '../styles/settings.scss';
|
||||||
import dataProvider from '@/utils/dataProvider';
|
import dataProvider from '@/utils/dataProvider';
|
||||||
import SettingsExplorer from '@/components/settings/SettingsExplorer.vue';
|
import SettingsExplorer from '@/components/settings/SettingsExplorer.vue';
|
||||||
|
import SettingsLinkGenerator from '@/components/SettingsLinkGenerator.vue';
|
||||||
export default {
|
export default {
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
components: {
|
components: {
|
||||||
@ -202,7 +204,8 @@ export default {
|
|||||||
DataProviderSettingsCard,
|
DataProviderSettingsCard,
|
||||||
ThemeSettingsCard,
|
ThemeSettingsCard,
|
||||||
EchoChamberCard,
|
EchoChamberCard,
|
||||||
SettingsExplorer
|
SettingsExplorer,
|
||||||
|
SettingsLinkGenerator
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const { mobile } = useDisplay();
|
const { mobile } = useDisplay();
|
||||||
|
33
src/utils/settingsLinkParser.js
Normal file
33
src/utils/settingsLinkParser.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 处理新的base64编码的config参数
|
||||||
|
const encodedConfig = urlParams.get('config');
|
||||||
|
if (encodedConfig) {
|
||||||
|
try {
|
||||||
|
// 解码base64
|
||||||
|
const base64Decoded = atob(encodedConfig);
|
||||||
|
|
||||||
|
// 转换回UTF-8字节数组
|
||||||
|
const utf8Bytes = new Uint8Array(base64Decoded.length);
|
||||||
|
for (let i = 0; i < base64Decoded.length; i++) {
|
||||||
|
utf8Bytes[i] = base64Decoded.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解码UTF-8字节数组为字符串
|
||||||
|
const utf8Decoder = new TextDecoder();
|
||||||
|
const jsonString = utf8Decoder.decode(utf8Bytes);
|
||||||
|
|
||||||
|
// 解析JSON
|
||||||
|
const configObj = JSON.parse(jsonString);
|
||||||
|
|
||||||
|
// 应用每个设置
|
||||||
|
for (const [key, value] of Object.entries(configObj)) {
|
||||||
|
const success = setSetting(key, value);
|
||||||
|
if (success) {
|
||||||
|
appliedSettings[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return appliedSettings;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析配置参数失败:', error);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user