前端-胡立永 1 week ago
parent
commit
47718e2d49
6 changed files with 287 additions and 108 deletions
  1. +70
    -79
      src/components/author/WorkItem.vue
  2. +1
    -1
      src/components/common/BookCard.vue
  3. +65
    -12
      src/views/author/WorkEdit.vue
  4. +149
    -14
      src/views/author/components/ReadersManagement.vue
  5. +1
    -1
      src/views/book/index.vue
  6. +1
    -1
      src/views/home/Home.vue

+ 70
- 79
src/components/author/WorkItem.vue View File

@ -1,12 +1,12 @@
<template>
<div class="work-item">
<div class="work-image">
<img :src="work.image || defaultCover" :alt="work.bookName || work.title" />
<img :src="work.image && work.image.split(',')[0] || defaultCover" :alt="work.name || '未命名作品'" />
<!-- 原创标签 -->
<div v-if="work.isOriginal == 'Y'" class="tag original">原创</div>
</div>
<div class="work-info">
<h3 class="work-title">{{ work.name|| '未命名作品' }}</h3>
<h3 class="work-title">{{ work.name || '未命名作品' }}</h3>
<!-- 读者信息 -->
<div class="readers">
@ -16,18 +16,18 @@
<!-- 状态标签区域 -->
<div class="status-wrapper">
<!-- 连载状态 -->
<div class="status-tag" :class="getStatusClass(work.status)">
{{ getStatusText(work.status) }}
<div class="status-tag" :class="statusClass">
{{ statusText }}
</div>
<!-- 发布审核状态 -->
<div v-if="work.bookStatus !== undefined || work.publishStatus !== undefined" class="publish-status" :class="getPublishStatusClass(work.bookStatus || work.publishStatus)">
{{ getPublishStatusText(work.bookStatus || work.publishStatus) }}
<!-- 发布状态标签 -->
<div v-if="bookStatusText" class="publish-status" :class="bookStatusClass">
{{ bookStatusText }}
</div>
<!-- 设置审核状态 -->
<div v-if="work.auditStatus" class="publish-status" :class="getAuditStatusClass(work.auditStatus)">
{{ getAuditStatusText(work.auditStatus) }}
<!-- 设置状态标签 -->
<div v-if="toolStatusText" class="publish-status" :class="toolStatusClass">
{{ toolStatusText }}
</div>
</div>
@ -59,82 +59,73 @@ export default defineComponent({
});
//
const getStatusClass = (status) => {
const statusClass = computed(() => {
const statusMap = {
0: 'ongoing', // 稿 ->
1: 'completed', // ->
2: 'ongoing', // ->
3: 'ongoing', // ->
'draft': 'ongoing',
'published': 'completed',
'publishing': 'ongoing'
'0': 'ongoing',
'1': 'completed'
};
return statusMap[status] || 'ongoing';
};
return statusMap[props.work.status] || 'ongoing';
});
//
const getStatusText = (status) => {
const statusText = computed(() => {
// 使 dictText使
if (props.work.status_dictText) {
return props.work.status_dictText;
}
const textMap = {
0: '连载中', // 稿
1: '已完结', //
2: '连载中', //
3: '连载中', //
'draft': '连载中',
'published': '已完结',
'publishing': '连载中'
'0': '连载中',
'1': '已完结'
};
return textMap[status] || '连载中';
};
return textMap[props.work.status] || '连载中';
});
//
const getPublishStatusClass = (publishStatus) => {
if (publishStatus === undefined || publishStatus === null) return '';
const statusMap = {
0: 'ongoing', //
1: 'completed', //
2: 'error' //
//
const toolStatusClass = computed(() => {
const toolStatusMap = {
'0': 'error',
'1': 'completed',
// '2': 'error',
};
return statusMap[publishStatus] || '';
};
return toolStatusMap[props.work.toolStatus] || '';
});
//
const getPublishStatusText = (publishStatus) => {
if (publishStatus === undefined || publishStatus === null) return '';
//
const toolStatusText = computed(() => {
// 使 dictText使
if (props.work.toolStatus_dictText) {
return props.work.toolStatus_dictText;
}
const textMap = {
0: '发布审核中',
1: '发布审核通过',
2: '发布审核不通过'
'0': '设置审核中',
'1': '设置审核通过',
'2': '设置审核不通过'
};
return textMap[publishStatus] || '';
};
return textMap[props.work.toolStatus] || '';
});
//
const getAuditStatusClass = (auditStatus) => {
if (!auditStatus) return '';
const auditMap = {
0: 'ongoing', //
1: 'completed', //
2: 'error', //
'pending': 'ongoing',
'passed': 'completed',
'rejected': 'error'
//
const bookStatusClass = computed(() => {
const bookStatusMap = {
'0': 'completed',
'1': 'error',
};
return auditMap[auditStatus] || '';
};
return bookStatusMap[props.work.bookStatus] || '';
});
//
const getAuditStatusText = (auditStatus) => {
if (!auditStatus) return '';
const auditMap = {
0: '设置审核中',
1: '设置审核通过',
2: '设置审核不通过',
'pending': '设置审核中',
'passed': '设置审核通过',
'rejected': '设置审核不通过'
//
const bookStatusText = computed(() => {
// 使 dictText使
if (props.work.bookStatus_dictText) {
return props.work.bookStatus_dictText;
}
const textMap = {
'0': '发布审核中',
'1': '发布审核通过',
'2': '发布审核不通过'
};
return auditMap[auditStatus] || '';
};
return textMap[props.work.bookStatus] || '';
});
const handleSetup = () => {
emit('setup', props.work.id);
@ -150,12 +141,12 @@ export default defineComponent({
return {
defaultCover,
getStatusClass,
getStatusText,
getPublishStatusClass,
getPublishStatusText,
getAuditStatusClass,
getAuditStatusText,
statusClass,
statusText,
toolStatusClass,
toolStatusText,
bookStatusClass,
bookStatusText,
handleSetup,
handleEdit,
handleDelete
@ -273,8 +264,8 @@ export default defineComponent({
white-space: nowrap;
&.error {
color: #F56C6C;
background-color: #fef0f0;
color: #666;
background-color: #f5f5f5;
}
&.completed {


+ 1
- 1
src/components/common/BookCard.vue View File

@ -1,7 +1,7 @@
<template>
<div class="book-card" @click="goToDetail">
<div class="book-cover">
<img :src="book.image" :alt="book.title">
<img :src="book.image && book.image.split(',')[0]" :alt="book.title">
<!-- 原创标签 -->
<div v-if="book.isOriginal == 'Y'" class="tag original">原创</div>
</div>


+ 65
- 12
src/views/author/WorkEdit.vue View File

@ -181,6 +181,12 @@ export default defineComponent({
const response = await myBookApi.saveOrUpdateShopNovel(chapterData);
if (response.success) {
// id
if (!currentChapter.value.id && response.result && response.result.id) {
currentChapter.value.id = response.result.id;
chapters.value[chapters.value.length - 1].id = currentChapter.value.id; // id
}
//
const index = chapters.value.findIndex(c => c.id == currentChapter.value.id);
if (index !== -1) {
@ -190,12 +196,11 @@ export default defineComponent({
currentChapter.value.status = status;
currentChapter.value._status = status;
chapters.value[index].title = chapterTitle.value;
chapters.value[index].details = chapterContent.value;
chapters.value[index].num = num.value;
chapters.value[index].status = status
chapters.value[index]._status = status
chapters.value[index].status = status;
chapters.value[index]._status = status;
}
lastSaved.value = status == 1 ? '已发布' : new Date().toLocaleTimeString();
@ -232,6 +237,14 @@ export default defineComponent({
//
const createNewChapter = async () => {
// id
const hasUnsavedChapter = chapters.value.some(chapter => !chapter.id);
if (hasUnsavedChapter) {
ElMessage.warning('请先保存当前章节,再创建新章节');
return;
}
if (await checkSaveStatus()) {
createEmptyChapter();
}
@ -353,9 +366,10 @@ export default defineComponent({
// sort
sortChapters();
//
if (chapters.value.length > 0) {
await selectChapter(chapters.value[chapters.value.length - 1]);
//
const savedChapters = chapters.value.filter(chapter => chapter.id);
if (savedChapters.length > 0) {
await selectChapter(savedChapters[savedChapters.length - 1]);
}
} else {
throw new Error(chaptersResponse.message || '获取章节列表失败');
@ -377,6 +391,43 @@ export default defineComponent({
}
});
//
const handleKeydown = (event) => {
// Ctrl+S
if ((event.ctrlKey || event.metaKey) && event.key === 's') {
event.preventDefault(); //
event.stopPropagation(); //
//
if (chapterTitle.value && chapterContent.value) {
saveChapter();
}
}
};
//
const addKeyboardListener = () => {
document.addEventListener('keydown', handleKeydown);
};
//
const removeKeyboardListener = () => {
document.removeEventListener('keydown', handleKeydown);
};
//
initData();
setupAutoSave();
addKeyboardListener();
//
onBeforeUnmount(() => {
if (autoSaveInterval) {
clearInterval(autoSaveInterval);
}
removeKeyboardListener();
});
//
const sortChapters = () => {
chapters.value.sort((a, b) => (a.sort || 0) - (b.sort || 0));
@ -459,10 +510,11 @@ export default defineComponent({
chapters.value.splice(index, 1);
}
//
//
if (currentChapter.value === chapter) {
if (chapters.value.length > 0) {
await selectChapter(chapters.value[0]);
const savedChapters = chapters.value.filter(c => c.id);
if (savedChapters.length > 0) {
await selectChapter(savedChapters[savedChapters.length - 1]);
} else {
currentChapter.value = null;
chapterTitle.value = '';
@ -487,10 +539,11 @@ export default defineComponent({
chapters.value.splice(index, 1);
}
//
//
if (currentChapter.value && currentChapter.value.id === chapter.id) {
if (chapters.value.length > 0) {
await selectChapter(chapters.value[0]);
const savedChapters = chapters.value.filter(c => c.id);
if (savedChapters.length > 0) {
await selectChapter(savedChapters[savedChapters.length - 1]);
} else {
currentChapter.value = null;
chapterTitle.value = '';


+ 149
- 14
src/views/author/components/ReadersManagement.vue View File

@ -2,8 +2,21 @@
<div class="author-content">
<div class="page-header">
<div class="header-title">读者成就设置</div>
<el-button type="primary" class="submit-btn" :disabled="isPending" @click="handleSubmit">
{{ isPending ? '设置审核中' : '提交申请' }}
<div class="status-info" v-if="form.status !== undefined">
<el-tag
:type="getStatusType(form.status)"
:class="'status-tag status-' + form.status"
>
{{ getStatusText(form.status) }}
</el-tag>
</div>
<el-button
type="primary"
class="submit-btn"
:disabled="!canSubmit"
@click="handleSubmit"
>
{{ getSubmitButtonText() }}
</el-button>
</div>
@ -18,7 +31,7 @@
<el-input
v-model="form[keys[index] + 'Name']"
placeholder="请输入成就名称"
:disabled="isPending"
:disabled="!canEdit"
/>
</div>
<div class="achievement-stats" v-if="form[keys[index] + 'Num']">
@ -27,23 +40,84 @@
</div>
</div>
</div>
<!-- 驳回原因显示 -->
<div v-if="form.status === 2 && form.rejectReason" class="reject-reason">
<el-alert
title="驳回原因"
:description="form.rejectReason"
type="error"
show-icon
:closable="false"
/>
</div>
</div>
</div>
</template>
<script>
import { defineComponent, ref, reactive, onMounted } from 'vue';
import { defineComponent, ref, reactive, onMounted, computed } from 'vue';
import { ElMessage } from 'element-plus';
import { achievementApi } from '@/api/bookshelf';
export default defineComponent({
name: 'ReadersManagement',
setup() {
const isPending = ref(false);
const keys = ['one', 'two', 'three'];
const form = reactive({});
const form = reactive({
status: undefined
});
const achievements = ref([]);
//
const canEdit = computed(() => {
return form.status === undefined || form.status === 2; //
});
//
const canSubmit = computed(() => {
return canEdit.value && !isSubmitting.value;
});
const isSubmitting = ref(false);
//
const getStatusType = (status) => {
switch (status) {
case 0: return 'warning'; //
case 1: return 'success'; //
case 2: return 'danger'; //
default: return 'info';
}
};
//
const getStatusText = (status) => {
switch (status) {
case 0: return '待审核';
case 1: return '已通过';
case 2: return '已驳回';
default: return '未设置';
}
};
//
const getSubmitButtonText = () => {
if (isSubmitting.value) {
return '提交中...';
}
if (form.status === 0) {
return '审核中';
}
if (form.status === 1) {
return '已通过';
}
if (form.status === 2) {
return '重新提交';
}
return '提交申请';
};
//
const getAchievementList = async () => {
try {
@ -61,9 +135,9 @@ export default defineComponent({
const getAchievementDetail = async () => {
try {
const response = await achievementApi.getAchievement();
if (response.success) {
if (response.success && response.result) {
response.result.status = parseInt(response.result.status)
Object.assign(form, response.result);
isPending.value = response.result.status === 0;
}
} catch (error) {
console.error('获取成就设置失败:', error);
@ -80,17 +154,38 @@ export default defineComponent({
return;
}
//
if (form.status === 1) {
ElMessage.warning('成就设置已通过,无需重复提交');
return;
}
try {
const response = await achievementApi.setAchievementName(form);
isSubmitting.value = true;
let data = {
oneName: form.oneName,
twoName: form.twoName,
threeName: form.threeName,
}
if(form.id){
data.id = form.id;
}
const response = await achievementApi.setAchievementName(data);
if (response.success) {
ElMessage.success('提交成功');
isPending.value = true;
ElMessage.success('提交成功,请等待审核');
//
await getAchievementDetail();
} else {
ElMessage.error(response.message || '提交失败');
}
} catch (error) {
console.error('提交成就设置失败:', error);
ElMessage.error('提交失败,请稍后重试');
} finally {
isSubmitting.value = false;
}
};
@ -103,7 +198,12 @@ export default defineComponent({
achievements,
form,
keys,
isPending,
canEdit,
canSubmit,
isSubmitting,
getStatusType,
getStatusText,
getSubmitButtonText,
handleSubmit
};
}
@ -129,6 +229,31 @@ export default defineComponent({
font-weight: bold;
color: #333;
}
.status-info {
.status-tag {
font-size: 14px;
padding: 6px 12px;
&.status-0 {
background-color: #fdf6ec;
color: #e6a23c;
border-color: #f5dab1;
}
&.status-1 {
background-color: #f0f9ff;
color: #67c23a;
border-color: #b3e19d;
}
&.status-2 {
background-color: #fef0f0;
color: #f56c6c;
border-color: #fbc4c4;
}
}
}
.submit-btn {
background-color: #0A2463;
@ -140,8 +265,9 @@ export default defineComponent({
}
&.is-disabled {
background-color: #807a7a;
border-color: #807a7a;
background-color: #c0c4cc;
border-color: #c0c4cc;
color: #fff;
}
}
}
@ -157,6 +283,7 @@ export default defineComponent({
flex-wrap: wrap;
justify-content: space-around;
gap: 40px;
margin-bottom: 20px;
.achievement-item {
display: flex;
@ -208,6 +335,14 @@ export default defineComponent({
}
}
}
.reject-reason {
margin-top: 20px;
padding: 16px;
background-color: #fef0f0;
border-radius: 6px;
border-left: 4px solid #f56c6c;
}
}
}
</style>

+ 1
- 1
src/views/book/index.vue View File

@ -11,7 +11,7 @@
<div class="book-info-wrapper">
<div class="book-info">
<div class="book-cover">
<img :src="book.cover || book.image" :alt="book.title || book.name">
<img :src="book.image && book.image.split(',')[0]" :alt="book.title || book.name">
</div>
<div class="book-details">
<h1 class="book-title">{{ book.title || book.name }}</h1>


+ 1
- 1
src/views/home/Home.vue View File

@ -106,7 +106,7 @@ export default {
const [bannerRes, noticeRes, recommendRes, newBooksRes] = await Promise.all([
homeApi.getBanner(),
homeApi.getNoticePage({ pageNo: 1, pageSize: 4 }),
homeApi.getRecommendList(),
homeApi.getRecommendList({ pageNo: 1, pageSize: 4 }),
homeApi.getNewList({ pageNo: 1, pageSize: 4 })
]);


Loading…
Cancel
Save