mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-07 02:46:55 +08:00
更新项目配置,删除不必要的文件,优化依赖项,修复类型定义,添加新歌单样式
This commit is contained in:
@@ -9,8 +9,8 @@ import {
|
||||
export interface SongListConfigType {
|
||||
userInfo: UserInfo | undefined
|
||||
biliInfo: any | undefined
|
||||
liveRequestSettings: Setting_LiveRequest
|
||||
liveRequestActive: SongRequestInfo[]
|
||||
liveRequestSettings?: Setting_LiveRequest
|
||||
liveRequestActive?: SongRequestInfo[]
|
||||
data: SongsInfo[] | undefined
|
||||
config?: any
|
||||
}
|
||||
|
||||
@@ -1,60 +1,204 @@
|
||||
import { VNode } from 'vue'
|
||||
import { VNode, h } from 'vue'; // 导入 Vue 的 VNode 类型和 h 函数(用于示例)
|
||||
|
||||
// --- 基础和通用类型 ---
|
||||
|
||||
export type TemplateConfig<T> = {
|
||||
name: string
|
||||
items: (
|
||||
| TemplateConfigStringItem<T>
|
||||
| TemplateConfigNumberItem<T>
|
||||
| TemplateConfigStringArrayItem<T>
|
||||
| TemplateConfigNumberArrayItem<T>
|
||||
| TemplateConfigImageItem<T>
|
||||
| TemplateConfigRenderItem<T>
|
||||
)[]
|
||||
onConfirm?: (arg0: T) => void
|
||||
}
|
||||
interface TemplateConfigBase {
|
||||
name: string | VNode
|
||||
key: string //将被保存到指定key中
|
||||
name: string | VNode; // 名称,可以是字符串或 VNode
|
||||
key: string; // 唯一标识符,用于数据对象的键
|
||||
/**
|
||||
* 可选的默认值。
|
||||
* 其具体类型在更具体的项类型中被细化。
|
||||
* TemplateConfigRenderItem 会使用其是否存在来进行类型推断。
|
||||
*/
|
||||
default?: any;
|
||||
}
|
||||
|
||||
type CommonProps = TemplateConfigBase
|
||||
// 大多数项类型共享的通用属性 (暂时排除 RenderItem)
|
||||
// 我们使用 'unknown' 作为 T 的默认值,表示在定义单个项时
|
||||
// 完整的配置对象类型是未知的。
|
||||
type CommonProps<T = unknown> = TemplateConfigBase & {
|
||||
// 注意:T 代表 *整个* 配置对象的数据类型。
|
||||
// 由于类型推断的方式(T 依赖于完整的 items 数组),
|
||||
// 在单个项定义内部的回调函数/访问器中的 'config' 参数
|
||||
// 通常会是 'unknown' 类型。如果在实现中需要访问
|
||||
// 完整配置对象的特定属性,你可能需要进行类型断言
|
||||
// (例如:config as MyConfigType)。
|
||||
};
|
||||
|
||||
// 数据访问器类型
|
||||
type DataAccessor<T, V> = {
|
||||
get: (config: T) => V
|
||||
set: (config: T, value: V) => void
|
||||
}
|
||||
|
||||
// 扩展 CommonProps 以包含额外的共有属性
|
||||
export type TemplateConfigItemWithType<T, V> = CommonProps & { data?: DataAccessor<T, V> }
|
||||
|
||||
export type TemplateConfigStringItem<T> = TemplateConfigItemWithType<T, string> & {
|
||||
type: 'string'
|
||||
}
|
||||
export type TemplateConfigStringArrayItem<T> = TemplateConfigItemWithType<T, string[]> & {
|
||||
type: 'stringArray'
|
||||
}
|
||||
export type TemplateConfigNumberItem<T> = TemplateConfigItemWithType<T, number> & {
|
||||
type: 'number'
|
||||
}
|
||||
export type TemplateConfigNumberArrayItem<T> = TemplateConfigItemWithType<T, number[]> & {
|
||||
type: 'numberArray'
|
||||
}
|
||||
|
||||
export type TemplateConfigRenderItem<T> = TemplateConfigBase & {
|
||||
type: 'render'
|
||||
render: (arg0: T) => VNode
|
||||
}
|
||||
get: (config: T) => V;
|
||||
set: (config: T, value: V) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @template T - The type of the associated data model.
|
||||
* @description 带有特定值类型 'V' 的配置项的基础类型。
|
||||
* @template T - 完整配置对象的类型 (默认为 unknown)。
|
||||
* @template V - 此特定配置项的值的类型。
|
||||
*/
|
||||
export type TemplateConfigImageItem<T> = TemplateConfigBase & {
|
||||
type: 'image' // Specifies the type of configuration item as 'image'.
|
||||
imageLimit: number // The maximum number of images allowed.
|
||||
export type TemplateConfigItemWithType<T = unknown, V = unknown> = CommonProps<T> & {
|
||||
type: string; // 类型判别属性
|
||||
data?: DataAccessor<T, V>; // 可选的数据访问器
|
||||
/**
|
||||
* Callback function triggered upon image upload.
|
||||
* @param {string[]} uploadedImages - The uploaded image or array of images.
|
||||
* @param {T} config - The configuration data model.
|
||||
* @description 可选的上传/更新回调函数。
|
||||
* @param data - 当前项更新的数据,类型为 V。
|
||||
* @param config - 整个配置数据对象,类型为 T (通常是 unknown)。
|
||||
*/
|
||||
onUploaded?: (uploadedImages: string[], config: T) => void
|
||||
onUploaded?: (data: V, config: T) => void;
|
||||
/**
|
||||
* 可选的默认值,约束为类型 V。
|
||||
* 覆盖了 TemplateConfigBase 中的 'any' 类型。
|
||||
*/
|
||||
default?: V;
|
||||
};
|
||||
|
||||
// --- Widen 工具类型 (保持不变) ---
|
||||
// 递归地将类型拓宽为其基础类型。
|
||||
type Widen<T> =
|
||||
T extends string ? string :
|
||||
T extends number ? number :
|
||||
T extends boolean ? boolean :
|
||||
T extends bigint ? bigint :
|
||||
T extends symbol ? symbol :
|
||||
T extends undefined ? undefined :
|
||||
T extends null ? null :
|
||||
T extends Function ? T :
|
||||
T extends Date ? Date :
|
||||
T extends readonly (infer U)[] ? Widen<U>[] :
|
||||
T extends object ? { -readonly [K in keyof T]: Widen<T[K]> } :
|
||||
T;
|
||||
|
||||
// --- 具体配置项类型定义 ---
|
||||
// T 在所有具体类型中默认为 unknown
|
||||
|
||||
export type TemplateConfigStringItem<T = unknown> = TemplateConfigItemWithType<T, string> & {
|
||||
type: 'string';
|
||||
placeholder?: string; // 可选的占位符
|
||||
inputType?: 'text' | 'password' | 'textarea'; // 可选的输入类型
|
||||
};
|
||||
|
||||
export type TemplateConfigStringArrayItem<T = unknown> = TemplateConfigItemWithType<T, string[]> & {
|
||||
type: 'stringArray';
|
||||
};
|
||||
|
||||
export type TemplateConfigNumberItem<T = unknown> = TemplateConfigItemWithType<T, number> & {
|
||||
type: 'number';
|
||||
min?: number;
|
||||
max?: number;
|
||||
|
||||
};
|
||||
|
||||
export type TemplateConfigColorItem<T = unknown> = TemplateConfigItemWithType<T, number> & {
|
||||
type: 'color';
|
||||
showAlpha?: boolean;
|
||||
};
|
||||
|
||||
export type TemplateConfigSliderNumberItem<T = unknown> = TemplateConfigItemWithType<T, number> & {
|
||||
type: 'sliderNumber';
|
||||
step?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
};
|
||||
|
||||
export type TemplateConfigNumberArrayItem<T = unknown> = TemplateConfigItemWithType<T, number[]> & {
|
||||
type: 'numberArray';
|
||||
};
|
||||
|
||||
export type TemplateConfigImageItem<T = unknown> = TemplateConfigItemWithType<T, string[]> & {
|
||||
type: 'image';
|
||||
imageLimit: number; // 图片数量限制
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 自定义渲染项的配置。使用 'this' 类型实现动态参数类型。
|
||||
* @template T - 完整配置对象的类型 (默认为 unknown)。
|
||||
*/
|
||||
export interface TemplateConfigRenderItem<T = unknown> extends TemplateConfigBase { // 继承基础接口以获取 key, name, default 检查
|
||||
type: 'render';
|
||||
|
||||
/**
|
||||
* @description 渲染此项的自定义 VNode。
|
||||
* @param this 当前的 TemplateConfigRenderItem 实例。
|
||||
* @param config 整个配置对象 (类型为 T, 默认为 unknown)。
|
||||
* 在实现内部可能需要类型断言 (例如 `config as MyConfig`)。
|
||||
* @param defaultData 从此项的 'default' 属性派生的数据。
|
||||
* 如果 `default` 存在,其类型为 `Widen<D>` (D 是 default 的类型),否则为 `unknown`。
|
||||
* @returns 表示渲染输出的 VNode。
|
||||
*
|
||||
* @importantUsage 调用此方法时,如果项定义了 `default` 属性,
|
||||
* 则 **必须** 将该项的默认值 (或与其 Widen 后的类型兼容的值)
|
||||
* 作为第二个参数传递。如果不存在 `default`,则传递 `undefined` 或 `null`。
|
||||
* 示例: `item.render(config, item.default)`
|
||||
*/
|
||||
render(this: this, config: T): VNode;
|
||||
|
||||
/**
|
||||
* @description 可选的回调函数,当自定义渲染的组件发出更新信号时调用。
|
||||
* @param this 当前的 TemplateConfigRenderItem 实例。
|
||||
* @param data 更新后的数据。如果 `default` 存在,其类型为 `Widen<D>`,否则为 `unknown`。
|
||||
* @param config 整个配置对象 (类型为 T, 默认为 unknown)。
|
||||
* 在实现内部可能需要类型断言 (例如 `config as MyConfig`)。
|
||||
*/
|
||||
onUploaded?(this: this, data: this extends { default: infer D; } ? Widen<D> : unknown, config: T): void;
|
||||
|
||||
// 继承自 TemplateConfigBase 的 'default?: any',这对于
|
||||
// 'this extends { default: infer D }' 类型检查能正确工作至关重要。
|
||||
}
|
||||
|
||||
// --- 联合类型和核心提取逻辑 ---
|
||||
|
||||
/**
|
||||
* @description 所有可能的配置项定义类型的联合类型。
|
||||
* 使用 `<unknown>` 作为完整配置类型 T 的占位符。
|
||||
*/
|
||||
export type ConfigItemDefinition =
|
||||
| TemplateConfigStringItem<any>
|
||||
| TemplateConfigNumberItem<any>
|
||||
| TemplateConfigStringArrayItem<any>
|
||||
| TemplateConfigNumberArrayItem<any>
|
||||
| TemplateConfigImageItem<any>
|
||||
| TemplateConfigRenderItem<any> // 包含优化后的 render/onUploaded 方法
|
||||
| TemplateConfigSliderNumberItem<any>
|
||||
| TemplateConfigColorItem<any>;
|
||||
|
||||
/**
|
||||
* @description 从只读的配置项数组中提取最终的数据结构类型。
|
||||
* @template Items - 通过 `defineItems([...])` 推断出的只读元组类型。
|
||||
*/
|
||||
export type ExtractConfigData<
|
||||
Items extends readonly ConfigItemDefinition[]
|
||||
> = {
|
||||
// 遍历联合类型 Items[number] 中所有项的 'key' 属性
|
||||
[K in Extract<Items[number], { key: string; }>['key']]:
|
||||
// 找到与当前键 K 匹配的具体项定义
|
||||
Extract<Items[number], { key: K; }> extends infer ItemWithKeyK
|
||||
// 检查匹配到的项是否有 'default' 属性
|
||||
? ItemWithKeyK extends { default: infer DefaultType; }
|
||||
// 如果有,使用 default 值的 Widen 处理后的类型
|
||||
? Widen<DefaultType>
|
||||
// 如果没有 default,则根据 'type' 属性确定类型
|
||||
: ItemWithKeyK extends { type: 'string'; } ? string
|
||||
: ItemWithKeyK extends { type: 'stringArray'; } ? string[]
|
||||
: ItemWithKeyK extends { type: 'number' | 'sliderNumber' | 'color'; } ? number
|
||||
: ItemWithKeyK extends { type: 'numberArray'; } ? number[]
|
||||
: ItemWithKeyK extends { type: 'image'; } ? string[]
|
||||
// *** 优化应用:无 default 的 render 类型回退到 'unknown' ***
|
||||
: ItemWithKeyK extends { type: 'render'; } ? unknown
|
||||
// 其他意外情况的回退类型
|
||||
: unknown
|
||||
: never // 如果 K 正确派生,则不应发生
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 定义并验证配置项数组。
|
||||
* 使用 `const Items` 可以在与 `as const` 结合使用时保留字面量类型和元组结构。
|
||||
* @param items 一个只读的配置项定义数组。
|
||||
* @returns 类型被保留的同一个只读数组。
|
||||
*/
|
||||
export function defineItems<
|
||||
const Items extends readonly ConfigItemDefinition[] // 使用 'const' 泛型进行推断
|
||||
>(items: Items): Items {
|
||||
// 如果需要,可以在此处添加基本的运行时验证。
|
||||
// 类型检查主要由 TypeScript 根据约束完成。
|
||||
return items;
|
||||
}
|
||||
@@ -1,101 +1,118 @@
|
||||
import DefaultIndexTemplateVue from '@/views/view/indexTemplate/DefaultIndexTemplate.vue'
|
||||
import { defineAsyncComponent, ref } from 'vue'
|
||||
import DefaultIndexTemplateVue from '@/views/view/indexTemplate/DefaultIndexTemplate.vue';
|
||||
import { defineAsyncComponent, ref } from 'vue';
|
||||
|
||||
const debugAPI =
|
||||
import.meta.env.VITE_API == 'dev'
|
||||
? import.meta.env.VITE_DEBUG_DEV_API
|
||||
: import.meta.env.VITE_DEBUG_RELEASE_API
|
||||
const releseAPI = `https://vtsuru.suki.club/`
|
||||
const failoverAPI = `https://failover-api.vtsuru.suki.club/`
|
||||
: import.meta.env.VITE_DEBUG_RELEASE_API;
|
||||
const releseAPI = `https://vtsuru.suki.club/`;
|
||||
const failoverAPI = `https://failover-api.vtsuru.suki.club/`;
|
||||
|
||||
export const isBackendUsable = ref(true)
|
||||
export const isDev = import.meta.env.MODE === 'development'
|
||||
export const isBackendUsable = ref(true);
|
||||
export const isDev = import.meta.env.MODE === 'development';
|
||||
|
||||
export const AVATAR_URL = 'https://workers.vrp.moe/api/bilibili/avatar/'
|
||||
export const FILE_BASE_URL = 'https://files.vtsuru.live'
|
||||
export const IMGUR_URL = FILE_BASE_URL + '/imgur/'
|
||||
export const THINGS_URL = FILE_BASE_URL + '/things/'
|
||||
export const apiFail = ref(false)
|
||||
export const AVATAR_URL = 'https://workers.vrp.moe/api/bilibili/avatar/';
|
||||
export const FILE_BASE_URL = 'https://files.vtsuru.live';
|
||||
export const IMGUR_URL = FILE_BASE_URL + '/imgur/';
|
||||
export const THINGS_URL = FILE_BASE_URL + '/things/';
|
||||
export const apiFail = ref(false);
|
||||
|
||||
export const BASE_URL =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? debugAPI
|
||||
: apiFail.value
|
||||
? failoverAPI
|
||||
: releseAPI
|
||||
export const BASE_API_URL = BASE_URL + 'api/'
|
||||
export const FETCH_API = 'https://fetch.vtsuru.live/'
|
||||
: releseAPI;
|
||||
export const BASE_API_URL = BASE_URL + 'api/';
|
||||
export const FETCH_API = 'https://fetch.vtsuru.live/';
|
||||
export const BASE_HUB_URL =
|
||||
(process.env.NODE_ENV === 'development'
|
||||
? debugAPI
|
||||
: apiFail.value
|
||||
? failoverAPI
|
||||
: releseAPI) + 'hub/'
|
||||
: releseAPI) + 'hub/';
|
||||
|
||||
export const TURNSTILE_KEY = '0x4AAAAAAAETUSAKbds019h0'
|
||||
export const TURNSTILE_KEY = '0x4AAAAAAAETUSAKbds019h0';
|
||||
|
||||
export const CURRENT_HOST = `${window.location.protocol}//${window.location.host}/`
|
||||
export const CN_HOST = 'https://vtsuru.suki.club/'
|
||||
export const CURRENT_HOST = `${window.location.protocol}//${window.location.host}/`;
|
||||
export const CN_HOST = 'https://vtsuru.suki.club/';
|
||||
|
||||
export const USER_API_URL = BASE_API_URL + 'user/'
|
||||
export const ACCOUNT_API_URL = BASE_API_URL + 'account/'
|
||||
export const BILI_API_URL = BASE_API_URL + 'bili/'
|
||||
export const SONG_API_URL = BASE_API_URL + 'song-list/'
|
||||
export const NOTIFACTION_API_URL = BASE_API_URL + 'notification/'
|
||||
export const QUESTION_API_URL = BASE_API_URL + 'qa/'
|
||||
export const LOTTERY_API_URL = BASE_API_URL + 'lottery/'
|
||||
export const HISTORY_API_URL = BASE_API_URL + 'history/'
|
||||
export const SCHEDULE_API_URL = BASE_API_URL + 'schedule/'
|
||||
export const VIDEO_COLLECT_API_URL = BASE_API_URL + 'video-collect/'
|
||||
export const OPEN_LIVE_API_URL = BASE_API_URL + 'open-live/'
|
||||
export const SONG_REQUEST_API_URL = BASE_API_URL + 'live-request/'
|
||||
export const QUEUE_API_URL = BASE_API_URL + 'queue/'
|
||||
export const EVENT_API_URL = BASE_API_URL + 'event/'
|
||||
export const LIVE_API_URL = BASE_API_URL + 'live/'
|
||||
export const FEEDBACK_API_URL = BASE_API_URL + 'feedback/'
|
||||
export const MUSIC_REQUEST_API_URL = BASE_API_URL + 'music-request/'
|
||||
export const VTSURU_API_URL = BASE_API_URL + 'vtsuru/'
|
||||
export const POINT_API_URL = BASE_API_URL + 'point/'
|
||||
export const BILI_AUTH_API_URL = BASE_API_URL + 'bili-auth/'
|
||||
export const FORUM_API_URL = BASE_API_URL + 'forum/'
|
||||
export const USER_INDEX_API_URL = BASE_API_URL + 'user-index/'
|
||||
export const ANALYZE_API_URL = BASE_API_URL + 'analyze/'
|
||||
export const USER_API_URL = BASE_API_URL + 'user/';
|
||||
export const ACCOUNT_API_URL = BASE_API_URL + 'account/';
|
||||
export const BILI_API_URL = BASE_API_URL + 'bili/';
|
||||
export const SONG_API_URL = BASE_API_URL + 'song-list/';
|
||||
export const NOTIFACTION_API_URL = BASE_API_URL + 'notification/';
|
||||
export const QUESTION_API_URL = BASE_API_URL + 'qa/';
|
||||
export const LOTTERY_API_URL = BASE_API_URL + 'lottery/';
|
||||
export const HISTORY_API_URL = BASE_API_URL + 'history/';
|
||||
export const SCHEDULE_API_URL = BASE_API_URL + 'schedule/';
|
||||
export const VIDEO_COLLECT_API_URL = BASE_API_URL + 'video-collect/';
|
||||
export const OPEN_LIVE_API_URL = BASE_API_URL + 'open-live/';
|
||||
export const SONG_REQUEST_API_URL = BASE_API_URL + 'live-request/';
|
||||
export const QUEUE_API_URL = BASE_API_URL + 'queue/';
|
||||
export const EVENT_API_URL = BASE_API_URL + 'event/';
|
||||
export const LIVE_API_URL = BASE_API_URL + 'live/';
|
||||
export const FEEDBACK_API_URL = BASE_API_URL + 'feedback/';
|
||||
export const MUSIC_REQUEST_API_URL = BASE_API_URL + 'music-request/';
|
||||
export const VTSURU_API_URL = BASE_API_URL + 'vtsuru/';
|
||||
export const POINT_API_URL = BASE_API_URL + 'point/';
|
||||
export const BILI_AUTH_API_URL = BASE_API_URL + 'bili-auth/';
|
||||
export const FORUM_API_URL = BASE_API_URL + 'forum/';
|
||||
export const USER_INDEX_API_URL = BASE_API_URL + 'user-index/';
|
||||
export const ANALYZE_API_URL = BASE_API_URL + 'analyze/';
|
||||
|
||||
export const ScheduleTemplateMap = {
|
||||
export type TemplateMapType = {
|
||||
[key: string]: {
|
||||
name: string;
|
||||
settingName?: string;
|
||||
component: any;
|
||||
};
|
||||
};
|
||||
export const ScheduleTemplateMap: TemplateMapType = {
|
||||
'': {
|
||||
name: '默认',
|
||||
compoent: defineAsyncComponent(
|
||||
//settingName: 'Template.Schedule.Default',
|
||||
component: defineAsyncComponent(
|
||||
() => import('@/views/view/scheduleTemplate/DefaultScheduleTemplate.vue')
|
||||
)
|
||||
},
|
||||
pinky: {
|
||||
name: '粉粉',
|
||||
compoent: defineAsyncComponent(
|
||||
//settingName: 'Template.Schedule.Pinky',
|
||||
component: defineAsyncComponent(
|
||||
() => import('@/views/view/scheduleTemplate/PinkySchedule.vue')
|
||||
)
|
||||
}
|
||||
} as { [key: string]: { name: string; compoent: any } }
|
||||
export const SongListTemplateMap = {
|
||||
};
|
||||
export const SongListTemplateMap: TemplateMapType = {
|
||||
'': {
|
||||
name: '默认',
|
||||
compoent: defineAsyncComponent(
|
||||
//settingName: 'Template.SongList.Default',
|
||||
component: defineAsyncComponent(
|
||||
() => import('@/views/view/songListTemplate/DefaultSongListTemplate.vue')
|
||||
)
|
||||
},
|
||||
simple: {
|
||||
name: '简单',
|
||||
compoent: defineAsyncComponent(
|
||||
//settingName: 'Template.SongList.Simple',
|
||||
component: defineAsyncComponent(
|
||||
() => import('@/views/view/songListTemplate/SimpleSongListTemplate.vue')
|
||||
)
|
||||
},
|
||||
traditional: {
|
||||
name: '传统',
|
||||
compoent: defineAsyncComponent(
|
||||
name: '列表',
|
||||
settingName: 'Template.SongList.Traditional',
|
||||
component: defineAsyncComponent(
|
||||
() =>
|
||||
import('@/views/view/songListTemplate/TraditionalSongListTemplate.vue')
|
||||
)
|
||||
}
|
||||
} as { [key: string]: { name: string; compoent: any } }
|
||||
export const IndexTemplateMap = {
|
||||
'': { name: '默认', compoent: DefaultIndexTemplateVue }
|
||||
} as { [key: string]: { name: string; compoent: any } }
|
||||
};
|
||||
|
||||
export const IndexTemplateMap: TemplateMapType = {
|
||||
'': {
|
||||
name: '默认',
|
||||
//settingName: 'Template.Index.Default',
|
||||
component: DefaultIndexTemplateVue
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user