refactor(eslint): 重构 ESLint 配置并优化组件样式

- 将 ESLint 配置从 .eslintrc.js 迁移到 eslint.config.js
- 优化 Scripts.vue 和 ScriptTable.vue 组件的样式
- 移除了不必要的模拟数据
- 调整了按钮样式和布局
This commit is contained in:
2025-08-04 15:23:20 +08:00
parent 0b1ed48471
commit 19aab99398
4 changed files with 94 additions and 274 deletions

View File

@@ -1,23 +0,0 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
es2021: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 'latest',
sourceType: 'module',
},
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
}

34
frontend/eslint.config.js Normal file
View File

@@ -0,0 +1,34 @@
const vue = require('eslint-plugin-vue');
const ts = require('@typescript-eslint/eslint-plugin');
const tsParser = require('@typescript-eslint/parser');
const prettier = require('eslint-plugin-prettier');
module.exports = [
// 推荐的 vue3 配置
vue.configs['vue3-recommended'],
// 推荐的 ts 配置
ts.configs.recommended,
// 推荐的 prettier 配置
prettier.configs.recommended,
// 自定义规则和文件范围
{
files: ['**/*.js', '**/*.ts', '**/*.vue'],
ignores: ['dist/**', 'node_modules/**'],
languageOptions: {
parser: tsParser,
ecmaVersion: 2021,
sourceType: 'module',
},
plugins: {
vue,
'@typescript-eslint': ts,
prettier,
},
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off',
// 如果你希望 prettier 报错,取消注释下面一行
// 'prettier/prettier': 'error',
},
},
];

View File

@@ -40,33 +40,21 @@
</template>
<template v-if="column.key === 'action'">
<a-space size="small">
<a-tooltip title="编辑脚本配置">
<a-button
type="primary"
size="small"
@click="handleEdit(record)"
class="action-button edit-button"
>
<template #icon>
<EditOutlined />
</template>
编辑
</a-button>
</a-tooltip>
<a-tooltip title="为此脚本添加用户">
<a-button
type="default"
size="small"
@click="handleAddUser(record)"
class="action-button add-user-button"
>
<template #icon>
<UserAddOutlined />
</template>
添加用户
</a-button>
</a-tooltip>
<a-space size="middle">
<a-button type="primary" size="middle" @click="handleEdit(record)" shape="round">
<template #icon>
<EditOutlined />
</template>
编辑
</a-button>
<a-button type="primary" size="middle" @click="handleAddUser(record)" shape="round">
<template #icon>
<UserAddOutlined />
</template>
添加用户
</a-button>
<a-popconfirm
title="确定要删除这个脚本吗"
description="删除后将无法恢复请谨慎操作"
@@ -74,14 +62,12 @@
ok-text="确定"
cancel-text="取消"
>
<a-tooltip title="删除脚本">
<a-button danger size="small" class="action-button delete-button">
<template #icon>
<DeleteOutlined />
</template>
删除
</a-button>
</a-tooltip>
<a-button danger size="middle" type="primary" shape="round">
<template #icon>
<DeleteOutlined />
</template>
删除
</a-button>
</a-popconfirm>
</a-space>
</template>
@@ -189,9 +175,13 @@ interface Props {
interface Emits {
(e: 'edit', script: Script): void
(e: 'delete', script: Script): void
(e: 'addUser', script: Script): void
(e: 'editUser', user: User): void
(e: 'deleteUser', user: User): void
}
@@ -564,4 +554,4 @@ const handleDeleteUser = (user: User) => {
padding: 4px 8px;
}
}
</style>
</style>

View File

@@ -2,26 +2,16 @@
<div class="scripts-container">
<div class="scripts-header">
<div class="header-title">
<FileTextOutlined class="title-icon" />
<h1>脚本管理</h1>
</div>
<a-space size="middle">
<a-button
type="primary"
size="large"
@click="handleAddScript"
class="add-button"
>
<a-button type="primary" size="large" @click="handleAddScript" class="link">
<template #icon>
<PlusOutlined />
</template>
添加脚本
</a-button>
<a-button
size="large"
@click="handleRefresh"
class="refresh-button"
>
<a-button size="large" @click="handleRefresh" class="default">
<template #icon>
<ReloadOutlined />
</template>
@@ -30,16 +20,14 @@
</a-space>
</div>
<div class="scripts-content">
<ScriptTable
:scripts="scripts"
@edit="handleEditScript"
@delete="handleDeleteScript"
@add-user="handleAddUser"
@edit-user="handleEditUser"
@delete-user="handleDeleteUser"
/>
</div>
<ScriptTable
:scripts="scripts"
@edit="handleEditScript"
@delete="handleDeleteScript"
@add-user="handleAddUser"
@edit-user="handleEditUser"
@delete-user="handleDeleteUser"
/>
<!-- 脚本类型选择弹窗 -->
<a-modal
@@ -50,12 +38,11 @@
@cancel="typeSelectVisible = false"
class="type-select-modal"
width="500px"
ok-text="确定"
cancel-text="取消"
>
<div class="type-selection">
<a-radio-group
v-model:value="selectedType"
class="type-radio-group"
>
<a-radio-group v-model:value="selectedType" class="type-radio-group">
<a-radio-button value="MAA" class="type-option">
<div class="type-content">
<div class="type-logo-container">
@@ -85,12 +72,12 @@
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
import { PlusOutlined, ReloadOutlined, FileTextOutlined } from '@ant-design/icons-vue'
import { PlusOutlined, ReloadOutlined } from '@ant-design/icons-vue'
import ScriptTable from '@/components/ScriptTable.vue'
import type { Script, User, ScriptType, MAAScriptConfig, GeneralScriptConfig, ScriptDetail } from '@/types/script'
import type { Script, ScriptType, User } from '@/types/script'
import { useScriptApi } from '@/composables/useScriptApi'
const router = useRouter()
@@ -101,144 +88,6 @@ const typeSelectVisible = ref(false)
const selectedType = ref<ScriptType>('MAA')
const addLoading = ref(false)
// 模拟数据
const mockScripts: Script[] = [
{
id: '1',
type: 'MAA',
name: 'MAA自动化脚本',
config: {
Info: {
Name: 'MAA自动化脚本',
Path: 'D:/MAA_For_AutoMAA'
},
Run: {
ADBSearchRange: 3,
AnnihilationTimeLimit: 40,
AnnihilationWeeklyLimit: true,
ProxyTimesLimit: 0,
RoutineTimeLimit: 10,
RunTimesLimit: 5,
TaskTransitionMethod: 'NoAction'
},
SubConfigsInfo: {
UserData: {
instances: []
}
}
} as MAAScriptConfig,
users: [
{
id: 'user1',
name: 'aoxuan',
Data: {
CustomInfrastPlanIndex: '0',
IfPassCheck: true,
LastAnnihilationDate: '2025-07-28',
LastProxyDate: '2025-08-03',
LastSklandDate: '2000-01-01',
ProxyTimes: 2
},
Info: {
Annihilation: 'Annihilation',
Id: '8668',
IfSkland: false,
InfrastMode: 'Normal',
MedicineNumb: 0,
Mode: '简洁',
Name: 'aoxuan',
Notes: '无',
Password: 'BVd/Y56Mts0gLywaz5kqT5lU',
RemainedDay: -1,
Routine: false,
SeriesNumb: '0',
Server: 'Official',
SklandToken: '',
Stage: 'AT-8',
StageMode: '固定',
Stage_1: '-',
Stage_2: '-',
Stage_3: '-',
Stage_Remain: '-',
Status: true
},
Notify: {
CompanyWebHookBotUrl: '',
Enabled: false,
IfCompanyWebHookBot: false,
IfSendMail: false,
IfSendSixStar: false,
IfSendStatistic: false,
IfServerChan: false,
ServerChanChannel: '',
ServerChanKey: '',
ServerChanTag: '',
ToAddress: ''
},
Task: {
IfAutoRoguelike: false,
IfBase: true,
IfCombat: true,
IfMall: true,
IfMission: true,
IfReclamation: false,
IfRecruiting: true,
IfWakeUp: true
},
QFluentWidgets: {
ThemeColor: '#ff009faa',
ThemeMode: 'Auto'
}
}
]
},
{
id: '2',
type: 'General',
name: '通用自动化脚本',
config: {
Game: {
Arguments: '',
Enabled: false,
IfForceClose: false,
Path: '.',
Style: 'Emulator',
WaitTime: 0
},
Info: {
Name: '通用自动化脚本',
RootPath: '.'
},
Run: {
ProxyTimesLimit: 0,
RunTimeLimit: 10,
RunTimesLimit: 3
},
Script: {
Arguments: '',
ConfigPath: '.',
ConfigPathMode: '所有文件 (*)',
ErrorLog: '',
IfTrackProcess: false,
LogPath: '.',
LogPathFormat: '%Y-%m-%d',
LogTimeEnd: 1,
LogTimeStart: 1,
LogTimeFormat: '%Y-%m-%d %H:%M:%S',
ScriptPath: '.',
SuccessLog: '',
UpdateConfigMode: 'Never'
},
SubConfigsInfo: {
UserData: {
instances: []
}
}
} as GeneralScriptConfig,
users: []
}
]
onMounted(() => {
loadScripts()
})
@@ -246,7 +95,7 @@ onMounted(() => {
const loadScripts = async () => {
try {
const scriptDetails = await getScripts()
// 将 ScriptDetail 转换为 Script 格式(为了兼容现有的表格组件)
scripts.value = scriptDetails.map(detail => ({
id: detail.uid,
@@ -254,7 +103,7 @@ const loadScripts = async () => {
name: detail.name,
config: detail.config,
users: [], // 暂时为空后续可以从其他API获取用户数据
createTime: detail.createTime || new Date().toLocaleString()
createTime: detail.createTime || new Date().toLocaleString(),
}))
} catch (error) {
console.error('加载脚本列表失败:', error)
@@ -281,9 +130,9 @@ const handleConfirmAddScript = async () => {
scriptData: {
id: result.scriptId,
type: selectedType.value,
config: result.data
}
}
config: result.data,
},
},
})
}
} catch (error) {
@@ -325,8 +174,6 @@ const handleRefresh = () => {
loadScripts()
message.success('刷新成功')
}
</script>
<style scoped>
@@ -396,31 +243,6 @@ const handleRefresh = () => {
transition: all 0.3s ease;
}
.refresh-button:hover {
border-color: var(--ant-color-primary);
color: var(--ant-color-primary);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.scripts-content {
flex: 1;
background: var(--ant-color-bg-container);
border-radius: 16px;
padding: 32px;
box-shadow:
0 4px 20px rgba(0, 0, 0, 0.08),
0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid var(--ant-color-border-secondary);
transition: all 0.3s ease;
}
.scripts-content:hover {
box-shadow:
0 8px 30px rgba(0, 0, 0, 0.12),
0 2px 6px rgba(0, 0, 0, 0.15);
}
/* 脚本类型选择弹窗样式 */
.type-select-modal :deep(.ant-modal-content) {
border-radius: 16px;
@@ -509,9 +331,6 @@ const handleRefresh = () => {
transition: all 0.3s ease;
}
.type-info {
flex: 1;
}
@@ -532,25 +351,25 @@ const handleRefresh = () => {
/* 深色模式适配 */
@media (prefers-color-scheme: dark) {
.scripts-content {
box-shadow:
box-shadow:
0 4px 20px rgba(0, 0, 0, 0.3),
0 1px 3px rgba(0, 0, 0, 0.4);
}
.scripts-content:hover {
box-shadow:
box-shadow:
0 8px 30px rgba(0, 0, 0, 0.4),
0 2px 6px rgba(0, 0, 0, 0.5);
}
.add-button {
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.4);
}
.add-button:hover {
box-shadow: 0 6px 16px rgba(24, 144, 255, 0.5);
}
.refresh-button:hover {
box-shadow: 0 4px 12px rgba(255, 255, 255, 0.1);
}
@@ -561,31 +380,31 @@ const handleRefresh = () => {
.scripts-container {
padding: 16px;
}
.scripts-header {
flex-direction: column;
gap: 16px;
align-items: stretch;
}
.header-title h1 {
font-size: 24px;
}
.scripts-content {
padding: 16px;
}
.type-content {
padding: 16px;
}
.type-icon {
font-size: 24px;
}
.type-title {
font-size: 16px;
}
}
</style>
</style>