Merge branch 'feature/refactor' of github.com:AUTO-MAS-Project/AUTO-MAS into feature/refactor
This commit is contained in:
@@ -11,21 +11,32 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<a-table
|
<!-- 使用vuedraggable替换a-table实现拖拽功能 -->
|
||||||
:columns="queueColumns"
|
<div class="draggable-table-container">
|
||||||
:data-source="queueItems"
|
<!-- 表头 -->
|
||||||
:pagination="false"
|
<div class="draggable-table-header">
|
||||||
size="middle"
|
<div class="header-cell index-cell">序号</div>
|
||||||
:scroll="{ x: false, y: false }"
|
<div class="header-cell script-cell">脚本任务</div>
|
||||||
table-layout="auto"
|
<div class="header-cell actions-cell">操作</div>
|
||||||
class="queue-table"
|
</div>
|
||||||
|
|
||||||
|
<!-- 拖拽内容区域 -->
|
||||||
|
<draggable
|
||||||
|
v-model="queueItems"
|
||||||
|
group="queueItems"
|
||||||
|
item-key="id"
|
||||||
|
:animation="200"
|
||||||
|
:disabled="loading"
|
||||||
|
ghost-class="ghost"
|
||||||
|
chosen-class="chosen"
|
||||||
|
drag-class="drag"
|
||||||
|
@end="onDragEnd"
|
||||||
|
class="draggable-container"
|
||||||
>
|
>
|
||||||
<template #emptyText>
|
<template #item="{ element: record, index }">
|
||||||
<span>暂无任务</span>
|
<div class="draggable-row" :class="{ 'row-dragging': loading }">
|
||||||
</template>
|
<div class="row-cell index-cell">{{ index + 1 }}</div>
|
||||||
<template #bodyCell="{ column, record, index }">
|
<div class="row-cell script-cell">
|
||||||
<template v-if="column.key === 'index'"> {{ index + 1 }} </template>
|
|
||||||
<template v-else-if="column.key === 'script'">
|
|
||||||
<a-select
|
<a-select
|
||||||
v-model:value="record.script"
|
v-model:value="record.script"
|
||||||
@change="updateQueueItemScript(record)"
|
@change="updateQueueItemScript(record)"
|
||||||
@@ -36,8 +47,8 @@
|
|||||||
:options="scriptOptions"
|
:options="scriptOptions"
|
||||||
allow-clear
|
allow-clear
|
||||||
/>
|
/>
|
||||||
</template>
|
</div>
|
||||||
<template v-else-if="column.key === 'actions'">
|
<div class="row-cell actions-cell">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-popconfirm
|
<a-popconfirm
|
||||||
title="确定要删除这个任务吗?"
|
title="确定要删除这个任务吗?"
|
||||||
@@ -51,6 +62,8 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
@@ -83,6 +96,7 @@
|
|||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
import { Service } from '@/api'
|
import { Service } from '@/api'
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
@@ -248,6 +262,44 @@ const deleteQueueItem = async (itemId: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 拖拽结束处理函数
|
||||||
|
const onDragEnd = async (evt: any) => {
|
||||||
|
// 如果位置没有变化,直接返回
|
||||||
|
if (evt.oldIndex === evt.newIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
// 构造排序后的ID列表
|
||||||
|
const sortedIds = queueItems.value.map(item => item.id)
|
||||||
|
|
||||||
|
// 调用排序API
|
||||||
|
const response = await Service.reorderItemApiQueueItemOrderPost({
|
||||||
|
queueId: props.queueId,
|
||||||
|
indexList: sortedIds,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.code === 200) {
|
||||||
|
message.success('任务顺序已更新')
|
||||||
|
// 刷新数据以确保与服务器同步
|
||||||
|
emit('refresh')
|
||||||
|
} else {
|
||||||
|
message.error('更新任务顺序失败: ' + (response.message || '未知错误'))
|
||||||
|
// 如果失败,刷新数据恢复原状态
|
||||||
|
emit('refresh')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('拖拽排序失败:', error)
|
||||||
|
message.error('更新任务顺序失败: ' + (error?.message || '网络错误'))
|
||||||
|
// 如果失败,刷新数据恢复原状态
|
||||||
|
emit('refresh')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadOptions()
|
loadOptions()
|
||||||
@@ -511,6 +563,124 @@ onMounted(() => {
|
|||||||
padding: 40px 0;
|
padding: 40px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 拖拽表格样式 */
|
||||||
|
.draggable-table-container {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--ant-color-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-table-header {
|
||||||
|
display: flex;
|
||||||
|
background-color: var(--ant-color-fill-quaternary);
|
||||||
|
border-bottom: 1px solid var(--ant-color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-cell {
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
text-align: center;
|
||||||
|
border-right: 1px solid var(--ant-color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-cell:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-cell {
|
||||||
|
width: 80px;
|
||||||
|
min-width: 80px;
|
||||||
|
max-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.script-cell {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-cell {
|
||||||
|
width: 180px;
|
||||||
|
min-width: 180px;
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-container {
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--ant-color-bg-container);
|
||||||
|
border-bottom: 1px solid var(--ant-color-border);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-row:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-row:hover {
|
||||||
|
background-color: var(--ant-color-fill-quaternary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-row.row-dragging {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell {
|
||||||
|
padding: 12px 16px;
|
||||||
|
text-align: center;
|
||||||
|
border-right: 1px solid var(--ant-color-border);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell.index-cell {
|
||||||
|
width: 80px;
|
||||||
|
min-width: 80px;
|
||||||
|
max-width: 80px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell.script-cell {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell.actions-cell {
|
||||||
|
width: 180px;
|
||||||
|
min-width: 180px;
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 拖拽状态样式 */
|
||||||
|
.ghost {
|
||||||
|
opacity: 0.5;
|
||||||
|
background: var(--ant-color-primary-bg);
|
||||||
|
border: 2px dashed var(--ant-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chosen {
|
||||||
|
background: var(--ant-color-primary-bg-hover);
|
||||||
|
transform: scale(1.02);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag {
|
||||||
|
transform: rotate(5deg);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
/* 响应式设计 */
|
/* 响应式设计 */
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.queue-items-grid {
|
.queue-items-grid {
|
||||||
@@ -526,6 +696,30 @@ onMounted(() => {
|
|||||||
.queue-item-card-item {
|
.queue-item-card-item {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.draggable-row {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell,
|
||||||
|
.header-cell {
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 1px solid var(--ant-color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell:last-child,
|
||||||
|
.header-cell:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-cell,
|
||||||
|
.script-cell,
|
||||||
|
.actions-cell {
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: auto !important;
|
||||||
|
max-width: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 标签样式 */
|
/* 标签样式 */
|
||||||
|
|||||||
@@ -16,20 +16,33 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<a-table
|
<!-- 使用vuedraggable替换a-table实现拖拽功能 -->
|
||||||
:columns="timeColumns"
|
<div class="draggable-table-container">
|
||||||
:data-source="timeSets"
|
<!-- 表头 -->
|
||||||
:pagination="false"
|
<div class="draggable-table-header">
|
||||||
size="middle"
|
<div class="header-cell index-cell">序号</div>
|
||||||
:scroll="{ x: false, y: false }"
|
<div class="header-cell status-cell">状态</div>
|
||||||
table-layout="auto"
|
<div class="header-cell time-cell">执行时间</div>
|
||||||
class="time-set-table"
|
<div class="header-cell actions-cell">操作</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 拖拽内容区域 -->
|
||||||
|
<draggable
|
||||||
|
v-model="timeSets"
|
||||||
|
group="timeSets"
|
||||||
|
item-key="id"
|
||||||
|
:animation="200"
|
||||||
|
:disabled="loading"
|
||||||
|
ghost-class="ghost"
|
||||||
|
chosen-class="chosen"
|
||||||
|
drag-class="drag"
|
||||||
|
@end="onDragEnd"
|
||||||
|
class="draggable-container"
|
||||||
>
|
>
|
||||||
<template #emptyText>
|
<template #item="{ element: record, index }">
|
||||||
<span>暂无定时</span>
|
<div class="draggable-row" :class="{ 'row-dragging': loading }">
|
||||||
</template>
|
<div class="row-cell index-cell">{{ index + 1 }}</div>
|
||||||
<template #bodyCell="{ column, record }">
|
<div class="row-cell status-cell">
|
||||||
<template v-if="column.key === 'enabled'">
|
|
||||||
<a-select
|
<a-select
|
||||||
v-model:value="record.enabled"
|
v-model:value="record.enabled"
|
||||||
@change="updateTimeSetStatus(record)"
|
@change="updateTimeSetStatus(record)"
|
||||||
@@ -40,8 +53,8 @@
|
|||||||
<a-select-option :value="true">启用</a-select-option>
|
<a-select-option :value="true">启用</a-select-option>
|
||||||
<a-select-option :value="false">禁用</a-select-option>
|
<a-select-option :value="false">禁用</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</template>
|
</div>
|
||||||
<template v-else-if="column.key === 'time'">
|
<div class="row-cell time-cell">
|
||||||
<a-time-picker
|
<a-time-picker
|
||||||
v-model:value="record.timeValue"
|
v-model:value="record.timeValue"
|
||||||
format="HH:mm"
|
format="HH:mm"
|
||||||
@@ -50,8 +63,8 @@
|
|||||||
@change="updateTimeSetTime(record)"
|
@change="updateTimeSetTime(record)"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
/>
|
/>
|
||||||
</template>
|
</div>
|
||||||
<template v-else-if="column.key === 'actions'">
|
<div class="row-cell actions-cell">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-popconfirm
|
<a-popconfirm
|
||||||
title="确定要删除这个定时吗?"
|
title="确定要删除这个定时吗?"
|
||||||
@@ -65,11 +78,16 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</draggable>
|
||||||
</a-table>
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-if="timeSets.length === 0" class="empty-state">
|
||||||
|
<span>暂无定时</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -77,6 +95,7 @@
|
|||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
import { Service } from '@/api'
|
import { Service } from '@/api'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
@@ -159,7 +178,7 @@ const timeSets = ref([...props.timeSets])
|
|||||||
const processTimeSets = (rawTimeSets: any[]) => {
|
const processTimeSets = (rawTimeSets: any[]) => {
|
||||||
return rawTimeSets.map(item => ({
|
return rawTimeSets.map(item => ({
|
||||||
...item,
|
...item,
|
||||||
timeValue: parseTimeString(item.time)
|
timeValue: parseTimeString(item.time),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,6 +316,44 @@ const deleteTimeSet = async (timeSetId: string) => {
|
|||||||
message.error('删除定时项失败: ' + (error?.message || '网络错误'))
|
message.error('删除定时项失败: ' + (error?.message || '网络错误'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 拖拽结束处理函数
|
||||||
|
const onDragEnd = async (evt: any) => {
|
||||||
|
// 如果位置没有变化,直接返回
|
||||||
|
if (evt.oldIndex === evt.newIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
// 构造排序后的ID列表
|
||||||
|
const sortedIds = timeSets.value.map(item => item.id)
|
||||||
|
|
||||||
|
// 调用排序API
|
||||||
|
const response = await Service.reorderTimeSetApiQueueTimeOrderPost({
|
||||||
|
queueId: props.queueId,
|
||||||
|
indexList: sortedIds,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.code === 200) {
|
||||||
|
message.success('定时顺序已更新')
|
||||||
|
// 刷新数据以确保与服务器同步
|
||||||
|
emit('refresh')
|
||||||
|
} else {
|
||||||
|
message.error('更新定时顺序失败: ' + (response.message || '未知错误'))
|
||||||
|
// 如果失败,刷新数据恢复原状态
|
||||||
|
emit('refresh')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('拖拽排序失败:', error)
|
||||||
|
message.error('更新定时顺序失败: ' + (error?.message || '网络错误'))
|
||||||
|
// 如果失败,刷新数据恢复原状态
|
||||||
|
emit('refresh')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -771,6 +828,172 @@ const deleteTimeSet = async (timeSetId: string) => {
|
|||||||
:deep(.ant-picker-time-panel-cell:hover) {
|
:deep(.ant-picker-time-panel-cell:hover) {
|
||||||
background: var(--ant-color-fill-tertiary);
|
background: var(--ant-color-fill-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 拖拽表格样式 */
|
||||||
|
.draggable-table-container {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--ant-color-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-table-header {
|
||||||
|
display: flex;
|
||||||
|
background-color: var(--ant-color-fill-quaternary);
|
||||||
|
border-bottom: 1px solid var(--ant-color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-cell {
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
text-align: center;
|
||||||
|
border-right: 1px solid var(--ant-color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-cell:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-cell {
|
||||||
|
width: 80px;
|
||||||
|
min-width: 80px;
|
||||||
|
max-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-cell {
|
||||||
|
width: 120px;
|
||||||
|
min-width: 120px;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-cell {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-cell {
|
||||||
|
width: 180px;
|
||||||
|
min-width: 180px;
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-container {
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--ant-color-bg-container);
|
||||||
|
border-bottom: 1px solid var(--ant-color-border);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-row:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-row:hover {
|
||||||
|
background-color: var(--ant-color-fill-quaternary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-row.row-dragging {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell {
|
||||||
|
padding: 12px 16px;
|
||||||
|
text-align: center;
|
||||||
|
border-right: 1px solid var(--ant-color-border);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell.index-cell {
|
||||||
|
width: 80px;
|
||||||
|
min-width: 80px;
|
||||||
|
max-width: 80px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell.status-cell {
|
||||||
|
width: 120px;
|
||||||
|
min-width: 120px;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell.time-cell {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell.actions-cell {
|
||||||
|
width: 180px;
|
||||||
|
min-width: 180px;
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 拖拽状态样式 */
|
||||||
|
.ghost {
|
||||||
|
opacity: 0.5;
|
||||||
|
background: var(--ant-color-primary-bg);
|
||||||
|
border: 2px dashed var(--ant-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chosen {
|
||||||
|
background: var(--ant-color-primary-bg-hover);
|
||||||
|
transform: scale(1.02);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag {
|
||||||
|
transform: rotate(5deg);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 空状态样式 */
|
||||||
|
.empty-state {
|
||||||
|
padding: 40px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--ant-color-text-tertiary);
|
||||||
|
background: var(--ant-color-bg-container);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.draggable-row {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell,
|
||||||
|
.header-cell {
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 1px solid var(--ant-color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-cell:last-child,
|
||||||
|
.header-cell:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-cell,
|
||||||
|
.status-cell,
|
||||||
|
.time-cell,
|
||||||
|
.actions-cell {
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: auto !important;
|
||||||
|
max-width: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- 全局样式 - 用于时间选择器弹出面板 -->
|
<!-- 全局样式 - 用于时间选择器弹出面板 -->
|
||||||
@@ -831,7 +1054,9 @@ const deleteTimeSet = async (timeSetId: string) => {
|
|||||||
transition: background 0.2s ease;
|
transition: background 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme='dark'] .ant-picker-dropdown .ant-picker-time-panel-column::-webkit-scrollbar-thumb:hover {
|
[data-theme='dark']
|
||||||
|
.ant-picker-dropdown
|
||||||
|
.ant-picker-time-panel-column::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(255, 255, 255, 0.45);
|
background: rgba(255, 255, 255, 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,65 @@
|
|||||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
|
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
|
||||||
import type { SettingsData } from '@/types/settings'
|
import type { SettingsData } from '@/types/settings'
|
||||||
|
|
||||||
const { settings, historyRetentionOptions, handleSettingChange } = defineProps<{
|
const { settings, historyRetentionOptions, updateSourceOptions, voiceTypeOptions, handleSettingChange, checkUpdate } = defineProps<{
|
||||||
settings: SettingsData
|
settings: SettingsData
|
||||||
historyRetentionOptions: { label: string; value: number }[]
|
historyRetentionOptions: { label: string; value: number }[]
|
||||||
|
updateSourceOptions: { label: string; value: string }[]
|
||||||
|
voiceTypeOptions: { label: string; value: string }[]
|
||||||
handleSettingChange: (category: keyof SettingsData, key: string, value: any) => Promise<void>
|
handleSettingChange: (category: keyof SettingsData, key: string, value: any) => Promise<void>
|
||||||
|
checkUpdate: () => Promise<void>
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
<!-- 启动设置 - 移到最上方 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<h3>启动配置</h3>
|
||||||
|
</div>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="form-item-vertical">
|
||||||
|
<div class="form-label-wrapper">
|
||||||
|
<span class="form-label">开机自启</span>
|
||||||
|
<a-tooltip title="在系统启动时自动启动应用">
|
||||||
|
<QuestionCircleOutlined class="help-icon" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<a-select
|
||||||
|
v-model:value="settings.Start.IfSelfStart"
|
||||||
|
@change="(checked: any) => handleSettingChange('Start', 'IfSelfStart', checked)"
|
||||||
|
size="large"
|
||||||
|
style="width:100%"
|
||||||
|
>
|
||||||
|
<a-select-option :value="true">是</a-select-option>
|
||||||
|
<a-select-option :value="false">否</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="form-item-vertical">
|
||||||
|
<div class="form-label-wrapper">
|
||||||
|
<span class="form-label">启动后直接最小化</span>
|
||||||
|
<a-tooltip title="启动后直接最小化">
|
||||||
|
<QuestionCircleOutlined class="help-icon" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<a-select
|
||||||
|
v-model:value="settings.Start.IfMinimizeDirectly"
|
||||||
|
@change="(checked: any) => handleSettingChange('Start', 'IfMinimizeDirectly', checked)"
|
||||||
|
size="large"
|
||||||
|
style="width:100%"
|
||||||
|
>
|
||||||
|
<a-select-option :value="true">是</a-select-option>
|
||||||
|
<a-select-option :value="false">否</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 原有的功能设置 -->
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h3>功能设置</h3>
|
<h3>功能设置</h3>
|
||||||
@@ -147,5 +198,147 @@ const { settings, historyRetentionOptions, handleSettingChange } = defineProps<{
|
|||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 更新设置 - 移到最下方 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<h3>更新配置</h3>
|
||||||
|
<a-button type="primary" @click="checkUpdate" size="small" class="section-update-button">
|
||||||
|
<template #icon>
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
检查更新
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="form-item-vertical">
|
||||||
|
<div class="form-label-wrapper">
|
||||||
|
<span class="form-label">自动检查更新</span>
|
||||||
|
<a-tooltip title="启动时自动检测软件更新">
|
||||||
|
<QuestionCircleOutlined class="help-icon" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<a-select
|
||||||
|
v-model:value="settings.Update.IfAutoUpdate"
|
||||||
|
@change="(checked: any) => handleSettingChange('Update', 'IfAutoUpdate', checked)"
|
||||||
|
size="large"
|
||||||
|
style="width:100%"
|
||||||
|
>
|
||||||
|
<a-select-option :value="true">是</a-select-option>
|
||||||
|
<a-select-option :value="false">否</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="form-item-vertical">
|
||||||
|
<div class="form-label-wrapper">
|
||||||
|
<span class="form-label">更新源</span>
|
||||||
|
<a-tooltip title="选择下载软件更新的来源">
|
||||||
|
<QuestionCircleOutlined class="help-icon" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<a-select
|
||||||
|
v-model:value="settings.Update.Source"
|
||||||
|
@change="(value: any) => handleSettingChange('Update', 'Source', value)"
|
||||||
|
:options="updateSourceOptions"
|
||||||
|
size="large"
|
||||||
|
style="width:100%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="form-item-vertical">
|
||||||
|
<div class="form-label-wrapper">
|
||||||
|
<span class="form-label">网络代理地址</span>
|
||||||
|
<a-tooltip title="使用网络代理软件时,若出现网络连接问题,请尝试设置代理地址,此设置全局生效">
|
||||||
|
<QuestionCircleOutlined class="help-icon" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<a-input
|
||||||
|
v-model:value="settings.Update.ProxyAddress"
|
||||||
|
@blur="handleSettingChange('Update', 'ProxyAddress', settings.Update.ProxyAddress)"
|
||||||
|
placeholder="请输入网络代理地址"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="form-item-vertical">
|
||||||
|
<div class="form-label-wrapper">
|
||||||
|
<span class="form-label">Mirror酱 CDK</span>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>
|
||||||
|
<div>
|
||||||
|
Mirror酱CDK是使用Mirror源进行高速下载的凭证,可前往
|
||||||
|
<a href="https://mirrorchyan.com/zh/get-start?source=auto-mas-setting" target="_blank" class="tooltip-link" @click.stop>Mirror酱官网</a>
|
||||||
|
获取
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<QuestionCircleOutlined class="help-icon" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="settings.Update.MirrorChyanCDK"
|
||||||
|
@blur="handleSettingChange('Update','MirrorChyanCDK', settings.Update.MirrorChyanCDK)"
|
||||||
|
:disabled="settings.Update.Source !== 'MirrorChyan'"
|
||||||
|
placeholder="使用Mirror源时请输入Mirror酱CDK"
|
||||||
|
:visibilityToggle="true"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 语音设置 - 移到最下方 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<h3>语音配置</h3>
|
||||||
|
</div>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="form-item-vertical">
|
||||||
|
<div class="form-label-wrapper">
|
||||||
|
<span class="form-label">启用语音提示</span>
|
||||||
|
<a-tooltip title="开启后将在特定时刻播放语音提示">
|
||||||
|
<QuestionCircleOutlined class="help-icon" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<a-select
|
||||||
|
v-model:value="settings.Voice.Enabled"
|
||||||
|
@change="(checked: any) => handleSettingChange('Voice', 'Enabled', checked)"
|
||||||
|
size="large"
|
||||||
|
style="width:100%"
|
||||||
|
>
|
||||||
|
<a-select-option :value="true">是</a-select-option>
|
||||||
|
<a-select-option :value="false">否</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="form-item-vertical">
|
||||||
|
<div class="form-label-wrapper">
|
||||||
|
<span class="form-label">语音类型</span>
|
||||||
|
<a-tooltip title="选择语音提示的详细程度">
|
||||||
|
<QuestionCircleOutlined class="help-icon" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<a-select
|
||||||
|
v-model:value="settings.Voice.Type"
|
||||||
|
@change="(value: any) => handleSettingChange('Voice', 'Type', value)"
|
||||||
|
:options="voiceTypeOptions"
|
||||||
|
:disabled="!settings.Voice.Enabled"
|
||||||
|
size="large"
|
||||||
|
style="width:100%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
|
|
||||||
import type { SettingsData } from '@/types/settings'
|
|
||||||
|
|
||||||
const { settings, handleSettingChange } = defineProps<{
|
|
||||||
settings: SettingsData
|
|
||||||
handleSettingChange: (category: keyof SettingsData, key: string, value: any) => Promise<void>
|
|
||||||
}>()
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="tab-content">
|
|
||||||
<div class="form-section">
|
|
||||||
<div class="section-header">
|
|
||||||
<h3>启动配置</h3>
|
|
||||||
</div>
|
|
||||||
<a-row :gutter="24">
|
|
||||||
<a-col :span="12">
|
|
||||||
<div class="form-item-vertical">
|
|
||||||
<div class="form-label-wrapper">
|
|
||||||
<span class="form-label">开机自启</span>
|
|
||||||
<a-tooltip title="在系统启动时自动启动应用">
|
|
||||||
<QuestionCircleOutlined class="help-icon" />
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<a-select
|
|
||||||
v-model:value="settings.Start.IfSelfStart"
|
|
||||||
@change="(checked: any) => handleSettingChange('Start', 'IfSelfStart', checked)"
|
|
||||||
size="large"
|
|
||||||
style="width:100%"
|
|
||||||
>
|
|
||||||
<a-select-option :value="true">是</a-select-option>
|
|
||||||
<a-select-option :value="false">否</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</div>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<div class="form-item-vertical">
|
|
||||||
<div class="form-label-wrapper">
|
|
||||||
<span class="form-label">启动后直接最小化</span>
|
|
||||||
<a-tooltip title="启动后直接最小化">
|
|
||||||
<QuestionCircleOutlined class="help-icon" />
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<a-select
|
|
||||||
v-model:value="settings.Start.IfMinimizeDirectly"
|
|
||||||
@change="(checked: any) => handleSettingChange('Start', 'IfMinimizeDirectly', checked)"
|
|
||||||
size="large"
|
|
||||||
style="width:100%"
|
|
||||||
>
|
|
||||||
<a-select-option :value="true">是</a-select-option>
|
|
||||||
<a-select-option :value="false">否</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</div>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
|
|
||||||
import type { SettingsData } from '@/types/settings'
|
|
||||||
|
|
||||||
const { settings, updateSourceOptions, handleSettingChange, checkUpdate } = defineProps<{
|
|
||||||
settings: SettingsData
|
|
||||||
updateSourceOptions: { label: string; value: string }[]
|
|
||||||
handleSettingChange: (category: keyof SettingsData, key: string, value: any) => Promise<void>
|
|
||||||
checkUpdate: () => Promise<void>
|
|
||||||
}>()
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="tab-content">
|
|
||||||
<div class="form-section">
|
|
||||||
<div class="section-header">
|
|
||||||
<h3>更新配置</h3>
|
|
||||||
<a-button type="primary" @click="checkUpdate" size="medium" class="section-update-button">
|
|
||||||
<template #icon>
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
|
||||||
<path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z" />
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
检查更新
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
<a-row :gutter="24">
|
|
||||||
<a-col :span="12">
|
|
||||||
<div class="form-item-vertical">
|
|
||||||
<div class="form-label-wrapper">
|
|
||||||
<span class="form-label">自动检查更新</span>
|
|
||||||
<a-tooltip title="启动时自动检测软件更新">
|
|
||||||
<QuestionCircleOutlined class="help-icon" />
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<a-select
|
|
||||||
v-model:value="settings.Update.IfAutoUpdate"
|
|
||||||
@change="(checked: any) => handleSettingChange('Update', 'IfAutoUpdate', checked)"
|
|
||||||
size="large"
|
|
||||||
style="width:100%"
|
|
||||||
>
|
|
||||||
<a-select-option :value="true">是</a-select-option>
|
|
||||||
<a-select-option :value="false">否</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</div>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<div class="form-item-vertical">
|
|
||||||
<div class="form-label-wrapper">
|
|
||||||
<span class="form-label">更新源</span>
|
|
||||||
<a-tooltip title="选择下载软件更新的来源">
|
|
||||||
<QuestionCircleOutlined class="help-icon" />
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<a-select
|
|
||||||
v-model:value="settings.Update.Source"
|
|
||||||
@change="(value: any) => handleSettingChange('Update', 'Source', value)"
|
|
||||||
:options="updateSourceOptions"
|
|
||||||
size="large"
|
|
||||||
style="width:100%"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
<a-row :gutter="24">
|
|
||||||
<a-col :span="12">
|
|
||||||
<div class="form-item-vertical">
|
|
||||||
<div class="form-label-wrapper">
|
|
||||||
<span class="form-label">网络代理地址</span>
|
|
||||||
<a-tooltip title="使用网络代理软件时,若出现网络连接问题,请尝试设置代理地址,此设置全局生效">
|
|
||||||
<QuestionCircleOutlined class="help-icon" />
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<a-input
|
|
||||||
v-model:value="settings.Update.ProxyAddress"
|
|
||||||
@blur="handleSettingChange('Update', 'ProxyAddress', settings.Update.ProxyAddress)"
|
|
||||||
placeholder="请输入网络代理地址"
|
|
||||||
size="large"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<div class="form-item-vertical">
|
|
||||||
<div class="form-label-wrapper">
|
|
||||||
<span class="form-label">Mirror酱 CDK</span>
|
|
||||||
<a-tooltip>
|
|
||||||
<template #title>
|
|
||||||
<div>
|
|
||||||
Mirror酱CDK是使用Mirror源进行高速下载的凭证,可前往
|
|
||||||
<a href="https://mirrorchyan.com/zh/get-start?source=auto-mas-setting" target="_blank" class="tooltip-link" @click.stop>Mirror酱官网</a>
|
|
||||||
获取
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<QuestionCircleOutlined class="help-icon" />
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<a-input
|
|
||||||
v-model:value="settings.Update.MirrorChyanCDK"
|
|
||||||
@blur="handleSettingChange('Update','MirrorChyanCDK', settings.Update.MirrorChyanCDK)"
|
|
||||||
:disabled="settings.Update.Source !== 'MirrorChyan'"
|
|
||||||
placeholder="使用Mirror源时请输入Mirror酱CDK"
|
|
||||||
size="large"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
|
|
||||||
import type { SettingsData } from '@/types/settings'
|
|
||||||
|
|
||||||
const { settings, voiceTypeOptions, handleSettingChange } = defineProps<{
|
|
||||||
settings: SettingsData
|
|
||||||
voiceTypeOptions: { label: string; value: string }[]
|
|
||||||
handleSettingChange: (category: keyof SettingsData, key: string, value: any) => Promise<void>
|
|
||||||
}>()
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="tab-content">
|
|
||||||
<div class="form-section">
|
|
||||||
<div class="section-header">
|
|
||||||
<h3>语音配置</h3>
|
|
||||||
</div>
|
|
||||||
<a-row :gutter="24">
|
|
||||||
<a-col :span="12">
|
|
||||||
<div class="form-item-vertical">
|
|
||||||
<div class="form-label-wrapper">
|
|
||||||
<span class="form-label">启用音效</span>
|
|
||||||
<a-tooltip title="是否启用音效功能">
|
|
||||||
<QuestionCircleOutlined class="help-icon" />
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<a-select
|
|
||||||
v-model:value="settings.Voice.Enabled"
|
|
||||||
@change="(checked: any) => handleSettingChange('Voice', 'Enabled', checked)"
|
|
||||||
size="large"
|
|
||||||
style="width:100%"
|
|
||||||
>
|
|
||||||
<a-select-option :value="true">是</a-select-option>
|
|
||||||
<a-select-option :value="false">否</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</div>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<div class="form-item-vertical">
|
|
||||||
<div class="form-label-wrapper">
|
|
||||||
<span class="form-label">音效模式</span>
|
|
||||||
<a-tooltip title="选择音效的播报模式">
|
|
||||||
<QuestionCircleOutlined class="help-icon" />
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
<a-select
|
|
||||||
v-model:value="settings.Voice.Type"
|
|
||||||
@change="(value: any) => handleSettingChange('Voice', 'Type', value)"
|
|
||||||
:options="voiceTypeOptions"
|
|
||||||
:disabled="!settings.Voice.Enabled"
|
|
||||||
size="large"
|
|
||||||
style="width:100%"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -16,9 +16,6 @@ import { mirrorManager } from '@/utils/mirrorManager'
|
|||||||
import TabBasic from './TabBasic.vue'
|
import TabBasic from './TabBasic.vue'
|
||||||
import TabFunction from './TabFunction.vue'
|
import TabFunction from './TabFunction.vue'
|
||||||
import TabNotify from './TabNotify.vue'
|
import TabNotify from './TabNotify.vue'
|
||||||
import TabUpdate from './TabUpdate.vue'
|
|
||||||
import TabStart from './TabStart.vue'
|
|
||||||
import TabVoice from './TabVoice.vue'
|
|
||||||
import TabAdvanced from './TabAdvanced.vue'
|
import TabAdvanced from './TabAdvanced.vue'
|
||||||
import TabOthers from './TabOthers.vue'
|
import TabOthers from './TabOthers.vue'
|
||||||
|
|
||||||
@@ -193,7 +190,10 @@ const openDevTools = () => (window as any).electronAPI?.openDevTools?.()
|
|||||||
// 更新检查
|
// 更新检查
|
||||||
const checkUpdate = async () => {
|
const checkUpdate = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await Service.checkUpdateApiUpdateCheckPost({ current_version: version, if_force: true })
|
const response = await Service.checkUpdateApiUpdateCheckPost({
|
||||||
|
current_version: version,
|
||||||
|
if_force: true,
|
||||||
|
})
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
if (response.if_need_update) {
|
if (response.if_need_update) {
|
||||||
updateData.value = response.update_info
|
updateData.value = response.update_info
|
||||||
@@ -232,7 +232,9 @@ const refreshMirrorConfig = async () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('刷新镜像配置失败', e)
|
console.error('刷新镜像配置失败', e)
|
||||||
message.error('刷新镜像配置失败')
|
message.error('刷新镜像配置失败')
|
||||||
} finally { refreshingConfig.value = false }
|
} finally {
|
||||||
|
refreshingConfig.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const goToMirrorTest = () => router.push('/mirror-test')
|
const goToMirrorTest = () => router.push('/mirror-test')
|
||||||
|
|
||||||
@@ -247,7 +249,9 @@ const testNotify = async () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('测试通知发送失败', e)
|
console.error('测试通知发送失败', e)
|
||||||
message.error('测试通知发送失败')
|
message.error('测试通知发送失败')
|
||||||
} finally { testingNotify.value = false }
|
} finally {
|
||||||
|
testingNotify.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -278,7 +282,10 @@ onMounted(() => {
|
|||||||
<TabFunction
|
<TabFunction
|
||||||
:settings="settings"
|
:settings="settings"
|
||||||
:history-retention-options="historyRetentionOptions"
|
:history-retention-options="historyRetentionOptions"
|
||||||
|
:update-source-options="updateSourceOptions"
|
||||||
|
:voice-type-options="voiceTypeOptions"
|
||||||
:handle-setting-change="handleSettingChange"
|
:handle-setting-change="handleSettingChange"
|
||||||
|
:check-update="checkUpdate"
|
||||||
/>
|
/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="notify" tab="通知设置">
|
<a-tab-pane key="notify" tab="通知设置">
|
||||||
@@ -290,20 +297,6 @@ onMounted(() => {
|
|||||||
:testing-notify="testingNotify"
|
:testing-notify="testingNotify"
|
||||||
/>
|
/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="update" tab="更新设置">
|
|
||||||
<TabUpdate
|
|
||||||
:settings="settings"
|
|
||||||
:update-source-options="updateSourceOptions"
|
|
||||||
:handle-setting-change="handleSettingChange"
|
|
||||||
:check-update="checkUpdate"
|
|
||||||
/>
|
|
||||||
</a-tab-pane>
|
|
||||||
<a-tab-pane key="start" tab="启动设置">
|
|
||||||
<TabStart :settings="settings" :handle-setting-change="handleSettingChange" />
|
|
||||||
</a-tab-pane>
|
|
||||||
<a-tab-pane key="voice" tab="语音设置">
|
|
||||||
<TabVoice :settings="settings" :voice-type-options="voiceTypeOptions" :handle-setting-change="handleSettingChange" />
|
|
||||||
</a-tab-pane>
|
|
||||||
<a-tab-pane key="advanced" tab="高级设置">
|
<a-tab-pane key="advanced" tab="高级设置">
|
||||||
<TabAdvanced
|
<TabAdvanced
|
||||||
:go-to-logs="goToLogs"
|
:go-to-logs="goToLogs"
|
||||||
@@ -314,53 +307,249 @@ onMounted(() => {
|
|||||||
:go-to-mirror-test="goToMirrorTest"
|
:go-to-mirror-test="goToMirrorTest"
|
||||||
/>
|
/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="others" tab="其他">
|
<a-tab-pane key="others" tab="其他设置">
|
||||||
<TabOthers :version="version" :backend-update-info="backendUpdateInfo" />
|
<TabOthers :version="version" :backend-update-info="backendUpdateInfo" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</div>
|
</div>
|
||||||
<UpdateModal v-if="updateVisible" v-model:visible="updateVisible" :update-data="updateData" @confirmed="onUpdateConfirmed" />
|
<UpdateModal
|
||||||
|
v-if="updateVisible"
|
||||||
|
v-model:visible="updateVisible"
|
||||||
|
:update-data="updateData"
|
||||||
|
@confirmed="onUpdateConfirmed"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 统一样式,使用 :deep 作用到子组件内部 */
|
/* 统一样式,使用 :deep 作用到子组件内部 */
|
||||||
.settings-container { max-width: 1200px; margin: 0 auto; padding: 20px; }
|
.settings-container {
|
||||||
.settings-header { margin-bottom: 24px; }
|
max-width: 1200px;
|
||||||
.page-title { margin: 0; font-size: 32px; font-weight: 600; color: var(--ant-color-text); }
|
margin: 0 auto;
|
||||||
.settings-content { background: var(--ant-color-bg-container); border-radius: 12px; }
|
padding: 20px;
|
||||||
.settings-tabs { margin: 0; }
|
}
|
||||||
.settings-tabs :deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab) { background: transparent; border: 1px solid var(--ant-color-border); border-radius: 8px 8px 0 0; margin-right: 8px; }
|
.settings-header {
|
||||||
.settings-tabs :deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active) { background: var(--ant-color-bg-container); border-bottom-color: var(--ant-color-bg-container); }
|
margin-bottom: 24px;
|
||||||
:deep(.tab-content) { padding: 24px; }
|
}
|
||||||
:deep(.form-section) { margin-bottom: 32px; }
|
.page-title {
|
||||||
:deep(.form-section:last-child) { margin-bottom: 0; }
|
margin: 0;
|
||||||
:deep(.section-header) { margin-bottom: 20px; padding-bottom: 8px; border-bottom: 2px solid var(--ant-color-border-secondary); display: flex; justify-content: space-between; align-items: center; }
|
font-size: 32px;
|
||||||
:deep(.section-header h3) { margin: 0; font-size: 20px; font-weight: 700; color: var(--ant-color-text); display: flex; align-items: center; gap: 12px; }
|
font-weight: 600;
|
||||||
:deep(.section-header h3::before) { content: ''; width: 4px; height: 24px; background: linear-gradient(135deg, var(--ant-color-primary), var(--ant-color-primary-hover)); border-radius: 2px; }
|
color: var(--ant-color-text);
|
||||||
:deep(.section-description) { margin: 4px 0 0; font-size: 13px; color: var(--ant-color-text-secondary); }
|
}
|
||||||
:deep(.section-doc-link) { color: var(--ant-color-primary) !important; text-decoration: none; font-size: 14px; font-weight: 500; padding: 4px 8px; border-radius: 4px; border: 1px solid var(--ant-color-primary); transition: all .2s ease; display: flex; align-items: center; gap: 4px; }
|
.settings-content {
|
||||||
:deep(.section-doc-link:hover) { color: var(--ant-color-primary-hover) !important; background-color: var(--ant-color-primary-bg); border-color: var(--ant-color-primary-hover); text-decoration: none; }
|
background: var(--ant-color-bg-container);
|
||||||
:deep(.section-update-button) { height: 32px; padding: 0 12px; font-size: 13px; font-weight: 600; border-radius: 6px; box-shadow: 0 2px 6px rgba(22,119,255,.2); transition: all .3s cubic-bezier(.4,0,.2,1); display: flex; align-items: center; gap: 6px; background: linear-gradient(135deg, var(--ant-color-primary), var(--ant-color-primary-hover)) !important; border: none !important; color: #fff !important; }
|
border-radius: 12px;
|
||||||
:deep(.section-update-button:hover) { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(22,119,255,.3); background: linear-gradient(135deg,#4096ff,#1677ff) !important; color: #fff !important; }
|
}
|
||||||
:deep(.section-update-button:active) { transform: translateY(0); color: #fff !important; }
|
.settings-tabs {
|
||||||
:deep(.section-update-button svg) { transition: transform .3s ease; }
|
margin: 0;
|
||||||
:deep(.section-update-button:hover svg) { transform: rotate(180deg); }
|
}
|
||||||
:deep(.form-item-vertical) { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
|
.settings-tabs :deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab) {
|
||||||
:deep(.form-label-wrapper) { display: flex; align-items: center; gap: 8px; }
|
background: transparent;
|
||||||
:deep(.form-label) { font-weight: 600; color: var(--ant-color-text); font-size: 14px; }
|
border: 1px solid var(--ant-color-border);
|
||||||
:deep(.help-icon) { color: #8c8c8c; font-size: 14px; }
|
border-radius: 8px 8px 0 0;
|
||||||
:deep(.tooltip-link) { color: var(--ant-color-primary) !important; text-decoration: underline; transition: color .2s ease; }
|
margin-right: 8px;
|
||||||
:deep(.tooltip-link:hover) { color: var(--ant-color-primary-hover) !important; text-decoration: underline; }
|
}
|
||||||
:deep(.link-card) { background: var(--ant-color-bg-container); border: 1px solid var(--ant-color-border); border-radius: 8px; padding: 20px; text-align: center; transition: all .3s ease; height: 100%; display: flex; flex-direction: column; align-items: center; }
|
.settings-tabs :deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active) {
|
||||||
:deep(.link-card:hover) { border-color: var(--ant-color-primary); box-shadow: 0 4px 12px rgba(0,0,0,.1); transform: translateY(-2px); }
|
background: var(--ant-color-bg-container);
|
||||||
:deep(.link-icon) { font-size: 48px; margin-bottom: 16px; line-height: 1; color: var(--ant-color-primary); display: flex; justify-content: center; align-items: center; }
|
border-bottom-color: var(--ant-color-bg-container);
|
||||||
:deep(.link-content) { flex: 1; display: flex; flex-direction: column; }
|
}
|
||||||
:deep(.link-content h4) { margin: 0 0 8px; font-size: 18px; font-weight: 600; color: var(--ant-color-text); }
|
:deep(.tab-content) {
|
||||||
:deep(.link-content p) { margin: 0 0 16px; font-size: 14px; color: var(--ant-color-text-secondary); line-height: 1.5; flex: 1; }
|
padding: 24px;
|
||||||
:deep(.link-button) { display: inline-block; padding: 8px 16px; background: var(--ant-color-primary); color: #fff !important; text-decoration: none; border-radius: 4px; font-size: 14px; font-weight: 500; transition: background-color .2s ease; margin-top: auto; }
|
}
|
||||||
:deep(.link-button:hover) { background: var(--ant-color-primary-hover); color: #fff !important; text-decoration: none; }
|
:deep(.form-section) {
|
||||||
:deep(.info-item) { display: flex; align-items: center; margin-bottom: 12px; line-height: 1.5; }
|
margin-bottom: 32px;
|
||||||
:deep(.info-label) { font-weight: 600; color: var(--ant-color-text); min-width: 100px; flex-shrink: 0; }
|
}
|
||||||
:deep(.info-value) { color: var(--ant-color-text-secondary); margin-left: 8px; }
|
:deep(.form-section:last-child) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
:deep(.section-header) {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--ant-color-border-secondary);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:deep(.section-header h3) {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
:deep(.section-header h3::before) {
|
||||||
|
content: '';
|
||||||
|
width: 4px;
|
||||||
|
height: 24px;
|
||||||
|
background: linear-gradient(135deg, var(--ant-color-primary), var(--ant-color-primary-hover));
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
:deep(.section-description) {
|
||||||
|
margin: 4px 0 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
}
|
||||||
|
:deep(.section-doc-link) {
|
||||||
|
color: var(--ant-color-primary) !important;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--ant-color-primary);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
:deep(.section-doc-link:hover) {
|
||||||
|
color: var(--ant-color-primary-hover) !important;
|
||||||
|
background-color: var(--ant-color-primary-bg);
|
||||||
|
border-color: var(--ant-color-primary-hover);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
:deep(.section-update-button) {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 2px 6px rgba(22, 119, 255, 0.2);
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--ant-color-primary),
|
||||||
|
var(--ant-color-primary-hover)
|
||||||
|
) !important;
|
||||||
|
border: none !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
:deep(.section-update-button:hover) {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba(22, 119, 255, 0.3);
|
||||||
|
background: linear-gradient(135deg, #4096ff, #1677ff) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
:deep(.section-update-button:active) {
|
||||||
|
transform: translateY(0);
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
:deep(.section-update-button svg) {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
:deep(.section-update-button:hover svg) {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
:deep(.form-item-vertical) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
:deep(.form-label-wrapper) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
:deep(.form-label) {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
:deep(.help-icon) {
|
||||||
|
color: #8c8c8c;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
:deep(.tooltip-link) {
|
||||||
|
color: var(--ant-color-primary) !important;
|
||||||
|
text-decoration: underline;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
:deep(.tooltip-link:hover) {
|
||||||
|
color: var(--ant-color-primary-hover) !important;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
:deep(.link-card) {
|
||||||
|
background: var(--ant-color-bg-container);
|
||||||
|
border: 1px solid var(--ant-color-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:deep(.link-card:hover) {
|
||||||
|
border-color: var(--ant-color-primary);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
:deep(.link-icon) {
|
||||||
|
font-size: 48px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--ant-color-primary);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:deep(.link-content) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
:deep(.link-content h4) {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
}
|
||||||
|
:deep(.link-content p) {
|
||||||
|
margin: 0 0 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
line-height: 1.5;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
:deep(.link-button) {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: var(--ant-color-primary);
|
||||||
|
color: #fff !important;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
:deep(.link-button:hover) {
|
||||||
|
background: var(--ant-color-primary-hover);
|
||||||
|
color: #fff !important;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
:deep(.info-item) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
:deep(.info-label) {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
min-width: 100px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
:deep(.info-value) {
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user