附录A全接入:7新页面+路由菜单+库存大盘抽屉 / P1-2真机非法值拦截全绿 / 文档终版510+514

This commit is contained in:
seagull
2026-09-12 02:32:25 +08:00
parent 3809774a6f
commit 6c0693d160
12 changed files with 820 additions and 8 deletions
+149
View File
@@ -0,0 +1,149 @@
<template>
<el-card shadow="never">
<el-tabs v-model="tab">
<el-tab-pane label="价格等级" name="levels">
<div class="toolbar">
<el-button type="primary" @click="openLevelCreate">新建等级</el-button>
</div>
<el-table :data="levels" v-loading="levelsLoading" size="small" stripe>
<el-table-column prop="code" label="等级编码" width="140" />
<el-table-column prop="name" label="等级名称" min-width="160" />
<el-table-column prop="discount_rate" label="折扣率" width="120" />
<el-table-column prop="sort_order" label="排序" width="100" />
<el-table-column label="操作" width="150">
<template #default="{ row }">
<el-button link type="primary" size="small" @click="openLevelEdit(row)">编辑</el-button>
<el-button link type="danger" size="small" @click="removeLevel(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="联系人" name="contacts">
<div class="toolbar">
<el-input v-model="contactSearch" placeholder="搜索联系人姓名" style="width: 240px" clearable @keyup.enter="loadContacts" />
<el-button type="primary" @click="loadContacts">查询</el-button>
</div>
<el-table :data="contacts" v-loading="contactsLoading" size="small" stripe>
<el-table-column prop="name" label="姓名" width="140" />
<el-table-column prop="phone" label="电话" width="150" />
<el-table-column prop="email" label="邮箱" min-width="160" />
<el-table-column prop="title" label="职位" width="140" />
</el-table>
<el-pagination
style="margin-top: 14px"
layout="prev, pager, next, total"
:total="contactsCount"
:page-size="20"
v-model:current-page="contactsPage"
@current-change="loadContacts"
/>
</el-tab-pane>
</el-tabs>
<el-dialog v-model="dialogVisible" :title="editing ? '编辑价格等级' : '新建价格等级'" width="480px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="等级编码" prop="code"><el-input v-model="form.code" /></el-form-item>
<el-form-item label="等级名称" prop="name"><el-input v-model="form.name" /></el-form-item>
<el-form-item label="折扣率" prop="discount_rate">
<el-input-number v-model="form.discount_rate" :min="0" :max="1" :precision="4" style="width: 100%" />
</el-form-item>
<el-form-item label="排序" prop="sort_order">
<el-input-number v-model="form.sort_order" :precision="0" style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="saveLevel">保存</el-button>
</template>
</el-dialog>
</el-card>
</template>
<script setup>
import { onMounted, ref, reactive } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import api from '../api/client'
const tab = ref('levels')
const levels = ref([])
const levelsLoading = ref(false)
const contacts = ref([])
const contactsCount = ref(0)
const contactsPage = ref(1)
const contactSearch = ref('')
const contactsLoading = ref(false)
const dialogVisible = ref(false)
const editing = ref(null)
const saving = ref(false)
const formRef = ref(null)
const form = reactive({ code: '', name: '', discount_rate: 0, sort_order: 0 })
const rules = {
code: [{ required: true, message: '请输入等级编码', trigger: 'blur' }],
name: [{ required: true, message: '请输入等级名称', trigger: 'blur' }],
}
async function loadLevels() {
levelsLoading.value = true
try {
const data = await api.get('/partner/price-levels/', { params: { page_size: 100 } })
levels.value = data.results || []
} finally {
levelsLoading.value = false
}
}
async function loadContacts() {
contactsLoading.value = true
try {
const data = await api.get('/partner/contacts/', {
params: { page: contactsPage.value, page_size: 20, search: contactSearch.value },
})
contacts.value = data.results || []
contactsCount.value = data.count || 0
} finally {
contactsLoading.value = false
}
}
function openLevelCreate() {
editing.value = null
Object.assign(form, { code: '', name: '', discount_rate: 0, sort_order: 0 })
dialogVisible.value = true
}
function openLevelEdit(row) {
editing.value = row
Object.assign(form, { code: row.code, name: row.name, discount_rate: Number(row.discount_rate), sort_order: row.sort_order })
dialogVisible.value = true
}
async function saveLevel() {
const ok = await formRef.value.validate().catch(() => false)
if (!ok) return
saving.value = true
try {
if (editing.value) await api.patch(`/partner/price-levels/${editing.value.id}/`, { ...form })
else await api.post('/partner/price-levels/', { ...form })
ElMessage.success('已保存')
dialogVisible.value = false
await loadLevels()
} finally {
saving.value = false
}
}
async function removeLevel(row) {
try {
await ElMessageBox.confirm(`删除等级 ${row.code}(${row.name})?`, '删除确认', { type: 'warning' })
} catch (e) { return }
await api.delete(`/partner/price-levels/${row.id}/`)
ElMessage.success('已删除')
await loadLevels()
}
onMounted(() => { loadLevels(); loadContacts() })
</script>
<style scoped>
.toolbar { display: flex; gap: 10px; margin-bottom: 14px; align-items: center; }
</style>