75b1d16453
Made-with: Cursor
487 lines
12 KiB
Vue
487 lines
12 KiB
Vue
<template>
|
|
<div class="model-container">
|
|
<el-card class="search-card">
|
|
<el-form :inline="true" :model="searchForm" class="search-form">
|
|
<el-form-item label="品牌名称">
|
|
<el-select
|
|
v-model="searchForm.brand_name"
|
|
placeholder="请选择品牌"
|
|
filterable
|
|
clearable
|
|
style="width: 200px"
|
|
>
|
|
<el-option
|
|
v-for="brand in brandOptions"
|
|
:key="brand.id"
|
|
:label="brand.name"
|
|
:value="brand.name"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="型号名称">
|
|
<el-input
|
|
v-model="searchForm.name"
|
|
placeholder="请输入型号名称"
|
|
clearable
|
|
style="width: 200px"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="handleSearch">
|
|
<el-icon><Search /></el-icon>
|
|
搜索
|
|
</el-button>
|
|
<el-button @click="handleReset">
|
|
<el-icon><Refresh /></el-icon>
|
|
重置
|
|
</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</el-card>
|
|
|
|
<el-card class="table-card">
|
|
<div class="card-header">
|
|
<span class="title">型号列表</span>
|
|
<div class="header-buttons">
|
|
<el-button
|
|
type="warning"
|
|
:disabled="selectedIds.length === 0"
|
|
@click="handlePushToSearch"
|
|
:loading="pushLoading"
|
|
>
|
|
<el-icon><Upload /></el-icon>
|
|
推送到搜索 ({{ selectedIds.length }})
|
|
</el-button>
|
|
<el-button type="primary" @click="handleAdd">
|
|
<el-icon><Plus /></el-icon>
|
|
新型号
|
|
</el-button>
|
|
</div>
|
|
</div>
|
|
|
|
<el-table
|
|
v-loading="loading"
|
|
:data="tableData"
|
|
border
|
|
stripe
|
|
style="width: 100%"
|
|
@selection-change="handleSelectionChange"
|
|
>
|
|
<el-table-column type="selection" width="55" />
|
|
<el-table-column prop="id" label="ID" width="80" />
|
|
<el-table-column prop="brand_name" label="品牌名称" width="150" />
|
|
<el-table-column prop="name" label="型号名称" />
|
|
<el-table-column prop="form" label="佩戴方式" width="120">
|
|
<template #default="scope">
|
|
<el-tag v-if="scope.row.form === 'in-ear'" type="success">入耳式</el-tag>
|
|
<el-tag v-else-if="scope.row.form === 'over-ear'" type="primary">头戴式</el-tag>
|
|
<el-tag v-else-if="scope.row.form === 'earbud'" type="info">耳塞式</el-tag>
|
|
<span v-else>{{ scope.row.form || '-' }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="rig" label="阻抗" width="120" />
|
|
<el-table-column prop="eq_key" label="EQ 配置" width="150" show-overflow-tooltip />
|
|
<el-table-column prop="create_at" label="创建时间" width="180" />
|
|
<el-table-column label="操作" width="200" fixed="right">
|
|
<template #default="scope">
|
|
<el-button
|
|
type="primary"
|
|
size="small"
|
|
@click="handleEdit(scope.row)"
|
|
>
|
|
编辑
|
|
</el-button>
|
|
<el-button
|
|
type="danger"
|
|
size="small"
|
|
@click="handleDelete(scope.row)"
|
|
>
|
|
删除
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<div class="pagination">
|
|
<el-pagination
|
|
v-model:current-page="pagination.page"
|
|
v-model:page-size="pagination.pageSize"
|
|
:total="pagination.total"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
@size-change="handleSizeChange"
|
|
@current-change="handlePageChange"
|
|
/>
|
|
</div>
|
|
</el-card>
|
|
|
|
<!-- 新增/编辑对话框 -->
|
|
<el-dialog
|
|
v-model="dialogVisible"
|
|
:title="dialogTitle"
|
|
width="600px"
|
|
@close="handleDialogClose"
|
|
>
|
|
<el-form
|
|
ref="formRef"
|
|
:model="formData"
|
|
:rules="formRules"
|
|
label-width="100px"
|
|
>
|
|
<el-form-item label="品牌名称" prop="brand_name">
|
|
<el-select
|
|
v-model="formData.brand_name"
|
|
placeholder="请选择品牌"
|
|
filterable
|
|
allow-create
|
|
default-first-option
|
|
style="width: 100%"
|
|
>
|
|
<el-option
|
|
v-for="brand in brandOptions"
|
|
:key="brand.id"
|
|
:label="brand.name"
|
|
:value="brand.name"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="型号名称" prop="name">
|
|
<el-input
|
|
v-model="formData.name"
|
|
placeholder="请输入型号名称"
|
|
maxlength="100"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="形式" prop="form">
|
|
<el-select
|
|
v-model="formData.form"
|
|
placeholder="请选择佩戴方式"
|
|
style="width: 100%"
|
|
>
|
|
<el-option label="入耳式" value="in-ear" />
|
|
<el-option label="头戴式" value="over-ear" />
|
|
<el-option label="耳塞式" value="earbud" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="阻抗" prop="rig">
|
|
<el-input
|
|
v-model="formData.rig"
|
|
placeholder="例如:32Ω"
|
|
maxlength="100"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="来源" prop="source">
|
|
<el-input
|
|
v-model="formData.source"
|
|
placeholder="型号来源"
|
|
maxlength="100"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="EQ 键" prop="eq_key">
|
|
<el-input
|
|
v-model="formData.eq_key"
|
|
placeholder="EQ 配置键值"
|
|
maxlength="255"
|
|
/>
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">
|
|
确定
|
|
</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import { Search, Refresh, Plus, Upload } from '@element-plus/icons-vue'
|
|
import { getModels, createModel, updateModel, deleteModel, pushToMeilisearch } from '@/api/model'
|
|
import { getBrands } from '@/api/brand'
|
|
|
|
const loading = ref(false)
|
|
const submitLoading = ref(false)
|
|
const pushLoading = ref(false)
|
|
const dialogVisible = ref(false)
|
|
const dialogTitle = ref('新型号')
|
|
const formRef = ref(null)
|
|
const brandOptions = ref([])
|
|
const selectedIds = ref([])
|
|
|
|
const searchForm = reactive({
|
|
brand_name: '',
|
|
name: ''
|
|
})
|
|
|
|
const pagination = reactive({
|
|
page: 1,
|
|
pageSize: 10,
|
|
total: 0
|
|
})
|
|
|
|
const tableData = ref([])
|
|
|
|
const formData = reactive({
|
|
id: null,
|
|
brand_name: '',
|
|
name: '',
|
|
form: '',
|
|
rig: '',
|
|
source: '',
|
|
eq_key: ''
|
|
})
|
|
|
|
const formRules = {
|
|
brand_name: [
|
|
{ required: true, message: '请选择品牌名称', trigger: 'change' }
|
|
],
|
|
name: [
|
|
{ required: true, message: '请输入型号名称', trigger: 'blur' },
|
|
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
|
|
]
|
|
}
|
|
|
|
// 加载品牌列表
|
|
const loadBrands = async () => {
|
|
try {
|
|
const res = await getBrands({ skip: 0, limit: 1000 })
|
|
if (res.code === 1 && res.data) {
|
|
brandOptions.value = res.data.items || []
|
|
}
|
|
} catch (error) {
|
|
console.error('加载品牌列表失败:', error)
|
|
}
|
|
}
|
|
|
|
// 加载数据
|
|
const loadData = async () => {
|
|
loading.value = true
|
|
try {
|
|
const res = await getModels({
|
|
skip: (pagination.page - 1) * pagination.pageSize,
|
|
limit: pagination.pageSize,
|
|
brand_name: searchForm.brand_name || undefined,
|
|
name: searchForm.name || undefined
|
|
})
|
|
|
|
if (res.code === 1 && res.data) {
|
|
tableData.value = res.data.items || []
|
|
pagination.total = res.data.total || 0
|
|
} else if (res.code === 2) {
|
|
tableData.value = []
|
|
pagination.total = 0
|
|
}
|
|
} catch (error) {
|
|
console.error('加载数据失败:', error)
|
|
ElMessage.error('加载数据失败')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// 搜索
|
|
const handleSearch = () => {
|
|
pagination.page = 1
|
|
loadData()
|
|
}
|
|
|
|
// 重置
|
|
const handleReset = () => {
|
|
searchForm.brand_name = ''
|
|
searchForm.name = ''
|
|
pagination.page = 1
|
|
loadData()
|
|
}
|
|
|
|
// 新增
|
|
const handleAdd = () => {
|
|
dialogTitle.value = '新型号'
|
|
dialogVisible.value = true
|
|
}
|
|
|
|
// 编辑
|
|
const handleEdit = (row) => {
|
|
dialogTitle.value = '编辑型号'
|
|
formData.id = row.id
|
|
formData.brand_name = row.brand_name
|
|
formData.name = row.name
|
|
formData.form = row.form || ''
|
|
formData.rig = row.rig || ''
|
|
formData.source = row.source || ''
|
|
formData.eq_key = row.eq_key || ''
|
|
dialogVisible.value = true
|
|
}
|
|
|
|
// 删除
|
|
const handleDelete = (row) => {
|
|
ElMessageBox.confirm(
|
|
`确定要删除型号 "${row.brand_name} - ${row.name}" 吗?`,
|
|
'警告',
|
|
{
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
}
|
|
).then(async () => {
|
|
try {
|
|
submitLoading.value = true
|
|
const res = await deleteModel(row.id)
|
|
|
|
if (res.code === 1) {
|
|
ElMessage.success('删除成功')
|
|
loadData()
|
|
} else {
|
|
ElMessage.error(res.msg || '删除失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('删除失败:', error)
|
|
ElMessage.error('删除失败')
|
|
} finally {
|
|
submitLoading.value = false
|
|
}
|
|
}).catch(() => {})
|
|
}
|
|
|
|
// 提交表单
|
|
const handleSubmit = async () => {
|
|
if (!formRef.value) return
|
|
|
|
await formRef.value.validate(async (valid) => {
|
|
if (valid) {
|
|
submitLoading.value = true
|
|
try {
|
|
const api = formData.id ? updateModel : createModel
|
|
const res = await api(formData)
|
|
|
|
if (res.code === 1) {
|
|
ElMessage.success(formData.id ? '更新成功' : '创建成功')
|
|
dialogVisible.value = false
|
|
loadData()
|
|
} else {
|
|
ElMessage.error(res.msg || '操作失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('操作失败:', error)
|
|
ElMessage.error('操作失败')
|
|
} finally {
|
|
submitLoading.value = false
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 对话框关闭
|
|
const handleDialogClose = () => {
|
|
formData.id = null
|
|
formData.brand_name = ''
|
|
formData.name = ''
|
|
formData.form = ''
|
|
formData.rig = ''
|
|
formData.source = ''
|
|
formData.eq_key = ''
|
|
if (formRef.value) {
|
|
formRef.value.resetFields()
|
|
}
|
|
}
|
|
|
|
// 分页变化
|
|
const handleSizeChange = () => {
|
|
loadData()
|
|
}
|
|
|
|
const handlePageChange = () => {
|
|
loadData()
|
|
}
|
|
|
|
// 处理选择变化
|
|
const handleSelectionChange = (selection) => {
|
|
selectedIds.value = selection.map(row => row.id)
|
|
}
|
|
|
|
// 推送到 Meilisearch
|
|
const handlePushToSearch = () => {
|
|
if (selectedIds.value.length === 0) {
|
|
ElMessage.warning('请选择要推送的型号')
|
|
return
|
|
}
|
|
|
|
ElMessageBox.confirm(
|
|
`确定要将选中的 ${selectedIds.value.length} 个型号推送到搜索引擎吗?`,
|
|
'推送确认',
|
|
{
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
}
|
|
).then(async () => {
|
|
pushLoading.value = true
|
|
try {
|
|
const res = await pushToMeilisearch(selectedIds.value)
|
|
|
|
if (res.code === 1) {
|
|
ElMessage.success(`成功推送 ${res.data.pushed_count} 条数据到搜索引擎`)
|
|
console.log('推送的数据:', res.data.models)
|
|
} else {
|
|
ElMessage.error(res.msg || '推送失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('推送失败:', error)
|
|
ElMessage.error('推送失败')
|
|
} finally {
|
|
pushLoading.value = false
|
|
}
|
|
}).catch(() => {})
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadBrands()
|
|
loadData()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.model-container {
|
|
height: 100%;
|
|
}
|
|
|
|
.search-card {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.search-form {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.table-card {
|
|
min-height: 500px;
|
|
}
|
|
|
|
.card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.header-buttons {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
.title {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
|
|
.pagination {
|
|
margin-top: 20px;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
</style>
|