You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
8.4 KiB
235 lines
8.4 KiB
<template>
|
|
<div>
|
|
<!-- 列表视图 -->
|
|
<div v-show="view === 'list'">
|
|
<div class="flex items-center justify-between mb-5">
|
|
<h2 class="text-lg font-semibold text-slate-800">品牌商列表</h2>
|
|
<button class="btn btn-primary btn-sm text-white" @click="showForm()">+ 新增品牌商</button>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-sm border border-slate-100">
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-zebra">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th><th>品牌名称</th><th>归属方案商</th><th>联系邮箱</th><th>产品</th><th>操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-if="loading">
|
|
<td colspan="6" class="text-center py-8"><span class="loading loading-spinner loading-sm"></span></td>
|
|
</tr>
|
|
<tr v-else-if="!rows.length">
|
|
<td colspan="6" class="text-center text-slate-400 py-8">暂无数据</td>
|
|
</tr>
|
|
<tr v-for="b in rows" :key="b.id">
|
|
<td class="font-mono text-sm">{{ hexId(b.id) }}</td>
|
|
<td class="font-medium">{{ b.brand }}</td>
|
|
<td><span class="badge badge-ghost badge-sm">{{ providerName(b.providerid) }}</span></td>
|
|
<td class="text-sm">
|
|
<template v-if="mailList(b.contactmails).length">
|
|
<span v-for="(e, i) in mailList(b.contactmails)" :key="i" class="badge badge-ghost badge-sm mr-1">{{ e }}</span>
|
|
</template>
|
|
<span v-else class="text-slate-300">-</span>
|
|
</td>
|
|
<td class="text-sm text-slate-500">{{ b.productlists || '' }}</td>
|
|
<td>
|
|
<div class="flex gap-1">
|
|
<button class="btn btn-ghost btn-xs" @click="showForm(b)">编辑</button>
|
|
<button class="btn btn-ghost btn-xs text-red-500" @click="delBrand(b)">删除</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 表单视图 -->
|
|
<div v-show="view === 'form'">
|
|
<div class="flex items-center gap-3 mb-5">
|
|
<button class="btn btn-ghost btn-sm" @click="showList()">← 返回列表</button>
|
|
<h2 class="text-lg font-semibold text-slate-800">{{ isEditing ? '编辑品牌商' : '新增品牌商' }}</h2>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-6 max-w-2xl">
|
|
<form @submit.prevent="saveBrand">
|
|
<div class="form-control mb-4">
|
|
<label class="label"><span class="label-text font-medium">品牌名称 *</span></label>
|
|
<input v-model="form.brand" type="text" class="input input-bordered w-full" required />
|
|
</div>
|
|
<div class="form-control mb-4">
|
|
<label class="label"><span class="label-text font-medium">归属方案商</span></label>
|
|
<select v-model.number="form.providerid" class="select select-bordered w-full">
|
|
<option :value="0">请选择方案商</option>
|
|
<option v-for="p in providers" :key="p.id" :value="p.id">{{ p.name }}</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-control mb-4">
|
|
<label class="label"><span class="label-text font-medium">描述</span></label>
|
|
<textarea v-model="form.description" class="textarea textarea-bordered w-full" rows="3"></textarea>
|
|
</div>
|
|
|
|
<!-- 多邮箱管理 -->
|
|
<div class="form-control mb-6">
|
|
<label class="label"><span class="label-text font-medium">联系邮箱</span></label>
|
|
<div class="space-y-2">
|
|
<p v-if="!emails.length" class="text-xs text-slate-400">暂无邮箱</p>
|
|
<div v-for="(e, i) in emails" :key="i" class="flex items-center gap-2">
|
|
<span class="badge badge-lg gap-1 bg-slate-100 border-slate-200">
|
|
<svg class="h-3 w-3 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
</svg>
|
|
{{ e }}
|
|
</span>
|
|
<button type="button" class="btn btn-ghost btn-xs text-red-400 hover:text-red-600" @click="removeEmail(i)">×</button>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-2 mt-2">
|
|
<input v-model="newEmail" type="email" class="input input-bordered input-sm flex-1" placeholder="输入邮箱地址" @keydown.enter.prevent="addEmail" />
|
|
<button type="button" class="btn btn-outline btn-sm btn-primary" @click="addEmail">添加</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex gap-3">
|
|
<button type="button" class="btn btn-ghost" @click="showList()">取消</button>
|
|
<button type="submit" class="btn btn-primary text-white" :disabled="submitting">保存</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
import { useApi } from '~/composables/useApi'
|
|
import { useToast } from '~/composables/useToast'
|
|
|
|
definePageMeta({ title: '品牌商管理' })
|
|
|
|
interface Brand {
|
|
id: number
|
|
brand: string
|
|
providerid: number
|
|
description: string
|
|
contactmails: string
|
|
productlists: string
|
|
}
|
|
interface Provider { id: number; name: string }
|
|
|
|
const { webApi } = useApi()
|
|
const { success, error, warn } = useToast()
|
|
|
|
const view = ref<'list' | 'form'>('list')
|
|
const rows = ref<Brand[]>([])
|
|
const providers = ref<Provider[]>([])
|
|
const loading = ref(false)
|
|
const submitting = ref(false)
|
|
const isEditing = ref(false)
|
|
const emails = ref<string[]>([])
|
|
const newEmail = ref('')
|
|
|
|
const form = reactive({ id: 0, brand: '', providerid: 0, description: '' })
|
|
|
|
function hexId(id: number) {
|
|
return id ? Number(id).toString(16).toUpperCase() : ''
|
|
}
|
|
function mailList(s?: string) {
|
|
return (s || '').split(',').map((e) => e.trim()).filter(Boolean)
|
|
}
|
|
function providerName(pid?: number) {
|
|
if (!pid) return '-'
|
|
const p = providers.value.find((x) => x.id === pid)
|
|
return p ? p.name : ('方案商#' + pid)
|
|
}
|
|
|
|
async function loadProviders() {
|
|
try {
|
|
const d = await webApi<{ providers: Provider[] }>('getsolutionproviders', {})
|
|
providers.value = d?.providers ?? []
|
|
} catch {
|
|
providers.value = []
|
|
}
|
|
}
|
|
|
|
async function loadData() {
|
|
loading.value = true
|
|
try {
|
|
await loadProviders()
|
|
const data = await webApi<{ brands: Brand[] }>('getbrands', {})
|
|
rows.value = data?.brands ?? []
|
|
} catch (e: any) {
|
|
error(e?.message ?? '获取品牌商列表失败')
|
|
rows.value = []
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function showList() {
|
|
view.value = 'list'
|
|
loadData()
|
|
}
|
|
|
|
function showForm(b?: Brand) {
|
|
if (b) {
|
|
isEditing.value = true
|
|
form.id = b.id
|
|
form.brand = b.brand || ''
|
|
form.providerid = b.providerid || 0
|
|
form.description = b.description || ''
|
|
emails.value = mailList(b.contactmails)
|
|
} else {
|
|
isEditing.value = false
|
|
form.id = 0
|
|
form.brand = ''
|
|
form.providerid = 0
|
|
form.description = ''
|
|
emails.value = []
|
|
}
|
|
newEmail.value = ''
|
|
view.value = 'form'
|
|
}
|
|
|
|
function addEmail() {
|
|
const val = newEmail.value.trim()
|
|
if (!val) return
|
|
if (emails.value.includes(val)) { warn('邮箱已存在'); return }
|
|
emails.value.push(val)
|
|
newEmail.value = ''
|
|
}
|
|
function removeEmail(idx: number) {
|
|
emails.value.splice(idx, 1)
|
|
}
|
|
|
|
async function saveBrand() {
|
|
submitting.value = true
|
|
try {
|
|
const payload: { brand: Record<string, unknown> } = {
|
|
brand: {
|
|
brand: form.brand,
|
|
providerid: Number(form.providerid) || 0,
|
|
description: form.description,
|
|
contactmails: emails.value.join(','),
|
|
},
|
|
}
|
|
if (isEditing.value) payload.brand.id = Number(form.id)
|
|
await webApi(isEditing.value ? 'updatebrand' : 'addbrand', payload)
|
|
success('保存成功')
|
|
showList()
|
|
} catch (e: any) {
|
|
error(e?.message ?? '保存失败')
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
}
|
|
|
|
function delBrand(b: Brand) {
|
|
if (!window.confirm(`确定删除品牌商 "${b.brand}" 吗?`)) return
|
|
webApi('delbrand', { id: b.id })
|
|
.then(() => { success('删除成功'); loadData() })
|
|
.catch((e: any) => error(e?.message ?? '删除失败'))
|
|
}
|
|
|
|
onMounted(loadData)
|
|
</script>
|
|
|