+
-
-
-
-
-
-
+
+
+
+
+
- {{ tag }}
-
-
-
+
+
+ {{ tag }}
+
+
+
+
-
-
-
-
+
+
+
+
+
+
- + 上传图片
-
-
+
+ 只有注册用户才能够上传图片
+
+
-
-
-
-
-
+
- 只有注册用户才能够上传图片
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+ 发送
+
+
+ 我发送的
+
+
-
-
-
+
+
+
+
+
+
- 发送
-
-
- 我发送的
-
+
+ 不能给自己提问
+
+
-
-
-
-
-
-
- 不能给自己提问
-
-
-
+
+
-
公开回复
-
-
-
+
+
+
+ 公开回复
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ item.question.message }}
-
-
-
-
-
-
-
-
-
-
- {{ item.answer?.message }}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {{ item.question.message }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.answer?.message }}
+
+
+
+
+
+
+
+
+
+
+
@@ -408,5 +500,380 @@ onUnmounted(() => {
margin: 0 auto;
position: relative;
width: 100%;
+ padding: 0 16px;
+}
+
+/* 卡片样式 */
+.question-form-card {
+ border-radius: 12px;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
+ transition: all 0.3s ease;
+ overflow: hidden;
+}
+
+.question-form-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
+}
+
+.self-user {
+ border-left: 4px solid #f5222d;
+}
+
+/* 话题选择卡片 */
+.topic-card {
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.tag-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.tag-item {
+ cursor: pointer;
+ transition: all 0.2s ease;
+ position: relative;
+ overflow: hidden;
+ border-radius: 16px;
+}
+
+.tag-item:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+/* 提问输入区域 */
+.question-textarea {
+ flex: 1;
+ border-radius: 8px;
+ transition: all 0.3s ease;
+ width: 100% !important; /* 使用 !important 确保不被其他样式覆盖 */
+ min-height: 100px; /* 设置最小高度 */
+ resize: vertical; /* 允许垂直调整大小 */
+}
+
+/* 设置 naive-ui 内部元素样式 */
+.question-textarea :deep(.n-input__textarea) {
+ min-width: 100% !important;
+ width: 100% !important;
+}
+
+.question-textarea :deep(.n-input__textarea-el) {
+ min-width: 100% !important;
+ width: 100% !important;
+}
+
+/* 确保输入框容器占满可用空间 */
+.question-input-area {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ margin: 16px 0;
+ width: 100%;
+}
+
+@media (min-width: 640px) {
+ .question-input-area {
+ flex-direction: row;
+ align-items: flex-start;
+ }
+
+ /* 在水平布局中设置输入框区域的最小宽度 */
+ .question-textarea {
+ min-width: 75%; /* 占据至少75%的宽度 */
+ }
+}
+
+.image-upload {
+ min-width: 112px;
+}
+
+.upload-trigger {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ width: 100%;
+ opacity: 0.8;
+ transition: all 0.3s ease;
+}
+
+.upload-trigger:hover {
+ opacity: 1;
+ transform: scale(1.05);
+}
+
+.upload-icon {
+ font-size: 24px;
+ margin-bottom: 4px;
+}
+
+/* 分隔线 */
+.form-divider {
+ margin: 10px 0;
+ opacity: 0.6;
+}
+
+/* 警告提示 */
+.login-alert,
+.self-alert {
+ border-radius: 8px;
+ animation: pulse 2s infinite;
+}
+
+@keyframes pulse {
+ 0% {
+ box-shadow: 0 0 0 0 rgba(255, 213, 79, 0.4);
+ }
+ 70% {
+ box-shadow: 0 0 0 6px rgba(255, 213, 79, 0);
+ }
+ 100% {
+ box-shadow: 0 0 0 0 rgba(255, 213, 79, 0);
+ }
+}
+
+/* 匿名选项 */
+.anonymous-option {
+ margin: 8px 0;
+}
+
+/* 操作按钮 */
+.action-buttons {
+ display: flex;
+ justify-content: center;
+ gap: 16px;
+ margin: 8px 0 16px 0;
+}
+
+.send-button,
+.my-questions-button {
+ min-width: 100px;
+ transition: all 0.3s ease;
+ border-radius: 20px;
+}
+
+.send-button:not(:disabled):hover,
+.my-questions-button:not(:disabled):hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+/* 验证码容器 */
+.turnstile-container {
+ display: flex;
+ justify-content: center;
+ margin: 8px 0;
+}
+
+/* 公开回复部分 */
+.public-divider {
+ margin: 32px 0 24px;
+ position: relative;
+}
+
+.divider-content {
+ font-weight: bold;
+ color: #36ad6a;
+ background-image: linear-gradient(90deg, #36ad6a, #18a058);
+ background-clip: text;
+ -webkit-background-clip: text;
+ color: transparent;
+ display: inline-block;
+ padding: 0 8px;
+ position: relative;
+}
+
+.questions-list-container {
+ position: relative;
+}
+
+.questions-list {
+ border-radius: 12px;
+ overflow: hidden;
+}
+
+.question-list-item {
+ margin-bottom: 16px;
+}
+
+.question-card {
+ border-radius: 8px;
+ overflow: hidden;
+ transition: all 0.3s ease;
+}
+
+.question-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
+}
+
+.unread {
+ box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.3);
+}
+
+.question-header {
+ padding: 8px 0;
+}
+
+.time-text {
+ font-size: 12px;
+ opacity: 0.8;
+}
+
+.question-tag {
+ margin-left: 8px;
+}
+
+.question-content {
+ text-align: center;
+ padding: 16px;
+ background-color: rgba(0, 0, 0, 0.02);
+ border-radius: 6px;
+}
+
+.question-message {
+ white-space: pre-wrap;
+ word-break: break-word;
+ margin-bottom: 12px;
+}
+
+.question-image-container {
+ display: flex;
+ justify-content: center;
+ margin-top: 12px;
+}
+
+.question-image {
+ max-height: 200px;
+ border-radius: 8px;
+ transition: all 0.3s ease;
+}
+
+.question-image:hover {
+ transform: scale(1.02);
+}
+
+.answer-container {
+ padding: 12px;
+ background-color: rgba(24, 160, 88, 0.06);
+ border-radius: 8px;
+ margin-top: 8px;
+}
+
+.answer-content {
+ gap: 12px;
+}
+
+.answer-avatar {
+ flex-shrink: 0;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+ border: 2px solid #fff;
+ transition: all 0.3s ease;
+}
+
+.answer-avatar:hover {
+ transform: scale(1.05);
+}
+
+.answer-divider {
+ height: 24px;
+ margin: 0 4px;
+}
+
+.answer-text {
+ font-size: 16px;
+ line-height: 1.6;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+.empty-state {
+ padding: 24px;
+ opacity: 0.7;
+}
+
+/* 过渡动效 */
+/* 淡入淡出 */
+.fade-enter-active,
+.fade-leave-active {
+ transition: opacity 0.5s ease;
+}
+
+.fade-enter-from,
+.fade-leave-to {
+ opacity: 0;
+}
+
+/* 下滑淡入 */
+.fade-slide-down-enter-active,
+.fade-slide-down-leave-active {
+ transition: all 0.5s ease;
+}
+
+.fade-slide-down-enter-from,
+.fade-slide-down-leave-to {
+ opacity: 0;
+ transform: translateY(-20px);
+}
+
+/* 上滑淡入 */
+.fade-slide-up-enter-active,
+.fade-slide-up-leave-active {
+ transition: all 0.5s ease;
+}
+
+.fade-slide-up-enter-from,
+.fade-slide-up-leave-to {
+ opacity: 0;
+ transform: translateY(20px);
+}
+
+/* 缩放淡入 */
+.fade-scale-enter-active,
+.fade-scale-leave-active {
+ transition: all 0.5s ease;
+}
+
+.fade-scale-enter-from,
+.fade-scale-leave-to {
+ opacity: 0;
+ transform: scale(0.9);
+}
+
+/* 标签列表过渡 */
+.tag-list-move {
+ transition: all 0.5s ease;
+}
+
+.tag-list-enter-active,
+.tag-list-leave-active {
+ transition: all 0.5s ease;
+}
+
+.tag-list-enter-from,
+.tag-list-leave-to {
+ opacity: 0;
+ transform: translateX(30px);
+}
+
+/* 问题列表过渡 */
+.list-fade-move {
+ transition: transform 0.5s ease;
+}
+
+.list-fade-enter-active,
+.list-fade-leave-active {
+ transition: all 0.5s ease;
+}
+
+.list-fade-enter-from,
+.list-fade-leave-to {
+ opacity: 0;
+ transform: translateY(30px);
}
diff --git a/src/views/view/songListTemplate/TraditionalSongListTemplate.vue b/src/views/view/songListTemplate/TraditionalSongListTemplate.vue
index 732761b..e2900e5 100644
--- a/src/views/view/songListTemplate/TraditionalSongListTemplate.vue
+++ b/src/views/view/songListTemplate/TraditionalSongListTemplate.vue
@@ -31,6 +31,8 @@ const selectedLanguage = ref
();
const selectedTag = ref(); // Renamed from activeTab for clarity
const searchQuery = ref('');
const selectedArtist = ref(null);
+// 添加点歌条件筛选状态
+const selectedOption = ref();
// --- New: Sorting State ---
type SortKey = 'name' | 'author' | 'language' | 'tags' | 'options' | 'description' | null;
@@ -42,6 +44,10 @@ const sortOrder = ref<'asc' | 'desc'>('asc'); // 当前排序顺序
// Extract unique languages
const allUniqueLanguages = computed(() => {
const languages = new Set();
+
+ // 添加"未设定"语言选项
+ languages.add('未设定');
+
props.data?.forEach(song => {
song.language?.forEach(lang => {
if (lang?.trim()) {
@@ -60,6 +66,10 @@ const languageButtons = computed(() =>
// Extract unique tags (similar to original 'tabs' logic)
const allUniqueTags = computed(() => {
const tags = new Set();
+
+ // 添加"未设定"标签选项
+ tags.add('未设定');
+
props.data?.forEach(song => {
song.tags?.forEach(tag => {
if (tag?.trim()) {
@@ -75,6 +85,28 @@ const tagButtons = computed(() =>
allUniqueTags.value.map((tag, index) => ({ id: index, name: tag }))
);
+// --- 添加点歌条件筛选按钮 ---
+// 提取所有唯一的点歌条件类型
+const allOptionTypes = computed(() => {
+ const optionTypes = new Set();
+
+ // 添加"未设定"选项
+ optionTypes.add('未设定');
+ // 添加基本选项类型
+ optionTypes.add('舰长');
+ optionTypes.add('提督');
+ optionTypes.add('总督');
+ optionTypes.add('粉丝牌');
+ optionTypes.add('SC');
+
+ return Array.from(optionTypes);
+});
+
+// 创建点歌条件筛选按钮
+const optionButtons = computed(() =>
+ allOptionTypes.value.map((option, index) => ({ id: index, name: option }))
+);
+
// --- Computed Properties for Data ---
@@ -105,21 +137,52 @@ const filteredAndSortedSongs = computed(() => {
// 1. Filter by Selected Language
if (selectedLanguage.value) {
const lang = selectedLanguage.value;
- query = query.Where(song => song.language?.includes(lang));
+ if (lang === '未设定') {
+ // 筛选没有设置语言或语言数组为空的歌曲
+ query = query.Where(song => !song.language || song.language.length === 0);
+ } else {
+ query = query.Where(song => song.language?.includes(lang));
+ }
}
// 2. Filter by Selected Tag
if (selectedTag.value) {
const tag = selectedTag.value;
- query = query.Where(song => song.tags?.includes(tag) ?? false);
+ if (tag === '未设定') {
+ // 筛选没有设置标签或标签数组为空的歌曲
+ query = query.Where(song => !song.tags || song.tags.length === 0);
+ } else {
+ query = query.Where(song => song.tags?.includes(tag) ?? false);
+ }
}
// 3. Filter by Selected Artist
if (selectedArtist.value) {
const artist = selectedArtist.value;
- query = query.Where(song => song.author?.includes(artist));
+ query = query.Where(song => song.author?.includes(artist) ?? false);
}
+ // 新增: 4. 根据点歌条件筛选
+ if (selectedOption.value) {
+ const option = selectedOption.value;
+
+ if (option === '未设定') {
+ // 筛选没有设置点歌条件的歌曲
+ query = query.Where(song => !song.options);
+ } else if (option === '舰长') {
+ query = query.Where(song => song.options?.needJianzhang === true);
+ } else if (option === '提督') {
+ query = query.Where(song => song.options?.needTidu === true);
+ } else if (option === '总督') {
+ query = query.Where(song => song.options?.needZongdu === true);
+ } else if (option === '粉丝牌') {
+ query = query.Where(song => (song.options?.fanMedalMinLevel ?? 0) > 0);
+ } else if (option === 'SC') {
+ query = query.Where(song => (song.options?.scMinPrice ?? 0) > 0);
+ }
+ }
+
+ // 原有的搜索逻辑
// 4. Filter by Search Query (case-insensitive, including tags)
if (searchQuery.value.trim()) {
const lowerSearch = searchQuery.value.toLowerCase().trim();
@@ -190,6 +253,15 @@ const selectTag = (tagName: string) => {
}
};
+// 新增: 选择/取消选择点歌条件
+const selectOption = (optionName: string) => {
+ if (optionName === selectedOption.value) {
+ selectedOption.value = undefined; // 点击已激活的按钮则取消筛选
+ } else {
+ selectedOption.value = optionName;
+ }
+};
+
// Select Artist (from table click, updated to allow deselect)
const selectArtistFromTable = (artist: string) => {
if (selectedArtist.value === artist) {
@@ -213,6 +285,7 @@ const clearFilters = () => {
selectedLanguage.value = undefined;
selectedTag.value = undefined;
selectedArtist.value = null; // Reset NSelect value
+ selectedOption.value = undefined; // 清除点歌条件筛选
searchQuery.value = '';
};
@@ -361,8 +434,8 @@ function GetPlayButton(song: SongsInfo) {
// --- New: Helper function for Song Request Options ---
function getOptionDisplay(options?: SongRequestOption) {
if (!options) {
- // 为"无特殊要求"添加 'empty-placeholder' 类
- return h('span', { class: 'empty-placeholder' }, '无特殊要求');
+ // 直接返回空元素,不显示"无特殊要求"
+ return h('span', {});
}
const conditions: VNode[] = [];
@@ -384,8 +457,8 @@ function getOptionDisplay(options?: SongRequestOption) {
}
if (conditions.length === 0) {
- // 为"无特殊要求"添加 'empty-placeholder' 类
- return h('span', { class: 'empty-placeholder' }, '无特殊要求');
+ // 如果没有条件,直接返回空元素,不显示"无特殊要求"
+ return h('span', {});
}
// Use NFlex for better wrapping
@@ -697,6 +770,23 @@ export const Config = defineTemplateConfig([
+
+