Initial commit: One Pipe System

完整的管理系统,包含账户管理、卡片管理、套餐管理、财务管理等功能模块。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
sexygoat
2026-01-22 16:35:33 +08:00
commit 222e5bb11a
495 changed files with 145440 additions and 0 deletions

View File

@@ -0,0 +1,430 @@
<template>
<div class="page-content">
<!-- 搜索和操作区 -->
<ElRow :gutter="12">
<ElCol :xs="24" :sm="12" :lg="6">
<ElInput v-model="searchQuery" placeholder="产品名称/ICCID" clearable />
</ElCol>
<ElCol :xs="24" :sm="12" :lg="6">
<ElSelect v-model="operatorFilter" placeholder="运营商筛选" clearable style="width: 100%">
<ElOption label="全部" value="" />
<ElOption label="中国移动" value="CMCC" />
<ElOption label="中国联通" value="CUCC" />
<ElOption label="中国电信" value="CTCC" />
</ElSelect>
</ElCol>
<ElCol :xs="24" :sm="12" :lg="6">
<ElSelect v-model="statusFilter" placeholder="分配状态" clearable style="width: 100%">
<ElOption label="全部" value="" />
<ElOption label="已分配" value="assigned" />
<ElOption label="未分配" value="unassigned" />
</ElSelect>
</ElCol>
<ElCol :xs="24" :sm="12" :lg="6" class="el-col2">
<ElButton v-ripple @click="handleSearch">搜索</ElButton>
<ElButton v-ripple type="primary" @click="showAssignDialog">批量分配</ElButton>
</ElCol>
</ElRow>
<!-- 号卡产品列表 -->
<ArtTable :data="filteredData" index style="margin-top: 20px" @selection-change="handleSelectionChange">
<template #default>
<ElTableColumn type="selection" width="55" />
<ElTableColumn label="产品名称" prop="productName" min-width="180" show-overflow-tooltip />
<ElTableColumn label="运营商" prop="operator" width="100">
<template #default="scope">
<ElTag :type="getOperatorTagType(scope.row.operator)">
{{ getOperatorText(scope.row.operator) }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="套餐规格" prop="packageSpec" min-width="150" />
<ElTableColumn label="产品价格" prop="price" width="120">
<template #default="scope"> ¥{{ scope.row.price.toFixed(2) }} </template>
</ElTableColumn>
<ElTableColumn label="库存数量" prop="stock" width="100" align="center">
<template #default="scope">
<span :style="{ color: scope.row.stock < 100 ? 'var(--el-color-danger)' : '' }">
{{ scope.row.stock }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="已分配数量" prop="assignedCount" width="120" align="center">
<template #default="scope">
<span style="color: var(--el-color-primary)">{{ scope.row.assignedCount }}</span>
</template>
</ElTableColumn>
<ElTableColumn label="分配状态" prop="assignStatus" width="100">
<template #default="scope">
<ElTag v-if="scope.row.assignedCount > 0" type="success">已分配</ElTag>
<ElTag v-else type="info">未分配</ElTag>
</template>
</ElTableColumn>
<ElTableColumn fixed="right" label="操作" width="200">
<template #default="scope">
<el-button link :icon="View" @click="viewAssignDetail(scope.row)">分配记录</el-button>
<el-button link type="primary" @click="assignToAgent(scope.row)">分配</el-button>
</template>
</ElTableColumn>
</template>
</ArtTable>
<!-- 分配对话框 -->
<ElDialog v-model="assignDialogVisible" title="分配号卡产品" width="600px" align-center>
<ElForm ref="formRef" :model="assignForm" :rules="assignRules" label-width="120px">
<ElFormItem label="选择代理商" prop="agentId">
<ElSelect
v-model="assignForm.agentId"
placeholder="请选择代理商"
filterable
style="width: 100%"
@change="handleAgentChange"
>
<ElOption
v-for="agent in agentList"
:key="agent.id"
:label="`${agent.agentName} (${agent.phone})`"
:value="agent.id"
/>
</ElSelect>
</ElFormItem>
<ElFormItem label="分配数量" prop="quantity">
<ElInputNumber
v-model="assignForm.quantity"
:min="1"
:max="currentProduct.stock"
style="width: 100%"
/>
<div style="color: var(--el-text-color-secondary); font-size: 12px; margin-top: 4px">
当前库存{{ currentProduct.stock }}
</div>
</ElFormItem>
<ElFormItem label="分佣模式" prop="commissionMode">
<ElRadioGroup v-model="assignForm.commissionMode">
<ElRadio value="fixed">固定佣金</ElRadio>
<ElRadio value="percent">比例佣金</ElRadio>
<ElRadio value="template">使用模板</ElRadio>
</ElRadioGroup>
</ElFormItem>
<ElFormItem v-if="assignForm.commissionMode === 'fixed'" label="固定金额" prop="fixedAmount">
<ElInputNumber v-model="assignForm.fixedAmount" :min="0" :precision="2" style="width: 100%" />
<span style="margin-left: 8px">/</span>
</ElFormItem>
<ElFormItem v-if="assignForm.commissionMode === 'percent'" label="佣金比例" prop="percent">
<ElInputNumber v-model="assignForm.percent" :min="0" :max="100" :precision="2" style="width: 100%" />
<span style="margin-left: 8px">%</span>
</ElFormItem>
<ElFormItem v-if="assignForm.commissionMode === 'template'" label="分佣模板" prop="templateId">
<ElSelect v-model="assignForm.templateId" placeholder="请选择分佣模板" style="width: 100%">
<ElOption
v-for="template in commissionTemplates"
:key="template.id"
:label="template.templateName"
:value="template.id"
>
<span>{{ template.templateName }}</span>
<span style="float: right; color: var(--el-text-color-secondary); font-size: 12px">
{{ template.mode === 'fixed' ? `¥${template.value}元/张` : `${template.value}%` }}
</span>
</ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="特殊折扣" prop="discount">
<ElInputNumber v-model="assignForm.discount" :min="0" :max="100" :precision="2" style="width: 100%" />
<span style="margin-left: 8px">%</span>
<div style="color: var(--el-text-color-secondary); font-size: 12px; margin-top: 4px">
0表示无折扣设置后代理商可以此折扣价格销售
</div>
</ElFormItem>
<ElFormItem label="备注" prop="remark">
<ElInput v-model="assignForm.remark" type="textarea" :rows="3" placeholder="请输入备注信息" />
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="assignDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleAssignSubmit">确认分配</ElButton>
</div>
</template>
</ElDialog>
<!-- 分配记录对话框 -->
<ElDialog v-model="detailDialogVisible" title="分配记录" width="900px" align-center>
<ArtTable :data="assignRecords" index>
<template #default>
<ElTableColumn label="代理商名称" prop="agentName" min-width="150" />
<ElTableColumn label="分配数量" prop="quantity" width="100" align="center" />
<ElTableColumn label="分佣模式" prop="commissionMode" width="120">
<template #default="scope">
<ElTag v-if="scope.row.commissionMode === 'fixed'" type="warning">固定佣金</ElTag>
<ElTag v-else-if="scope.row.commissionMode === 'percent'" type="success">比例佣金</ElTag>
<ElTag v-else>模板佣金</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="佣金规则" prop="commissionRule" width="150" />
<ElTableColumn label="特殊折扣" prop="discount" width="100">
<template #default="scope">
{{ scope.row.discount > 0 ? `${scope.row.discount}%` : '无' }}
</template>
</ElTableColumn>
<ElTableColumn label="分配时间" prop="assignTime" width="180" />
<ElTableColumn label="操作人" prop="operator" width="100" />
<ElTableColumn fixed="right" label="操作" width="120">
<template #default="scope">
<el-button link type="danger" @click="handleCancelAssign(scope.row)">取消分配</el-button>
</template>
</ElTableColumn>
</template>
</ArtTable>
<template #footer>
<ElButton @click="detailDialogVisible = false">关闭</ElButton>
</template>
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { View } from '@element-plus/icons-vue'
import type { FormInstance, FormRules } from 'element-plus'
defineOptions({ name: 'SimCardAssign' })
interface SimCardProduct {
id: string
productName: string
operator: string
packageSpec: string
price: number
stock: number
assignedCount: number
}
const searchQuery = ref('')
const operatorFilter = ref('')
const statusFilter = ref('')
const assignDialogVisible = ref(false)
const detailDialogVisible = ref(false)
const formRef = ref<FormInstance>()
const selectedRows = ref<SimCardProduct[]>([])
const currentProduct = ref<SimCardProduct>({
id: '',
productName: '',
operator: '',
packageSpec: '',
price: 0,
stock: 0,
assignedCount: 0
})
const assignForm = reactive({
agentId: '',
quantity: 1,
commissionMode: 'percent',
fixedAmount: 0,
percent: 10,
templateId: '',
discount: 0,
remark: ''
})
const assignRules = reactive<FormRules>({
agentId: [{ required: true, message: '请选择代理商', trigger: 'change' }],
quantity: [{ required: true, message: '请输入分配数量', trigger: 'blur' }],
commissionMode: [{ required: true, message: '请选择分佣模式', trigger: 'change' }]
})
const agentList = ref([
{ id: '1', agentName: '华东区总代理', phone: '13800138000', level: 1 },
{ id: '2', agentName: '华南区代理', phone: '13900139000', level: 2 },
{ id: '3', agentName: '华北区代理', phone: '13700137000', level: 1 }
])
const commissionTemplates = ref([
{ id: '1', templateName: '标准代理商佣金', mode: 'percent', value: 10 },
{ id: '2', templateName: '特殊套餐固定佣金', mode: 'fixed', value: 50 },
{ id: '3', templateName: '高端代理商佣金', mode: 'percent', value: 15 }
])
const mockData = ref<SimCardProduct[]>([
{
id: '1',
productName: '移动4G流量卡-月包100GB',
operator: 'CMCC',
packageSpec: '100GB/月有效期1年',
price: 80.00,
stock: 1000,
assignedCount: 500
},
{
id: '2',
productName: '联通5G流量卡-季包300GB',
operator: 'CUCC',
packageSpec: '300GB/季有效期1年',
price: 220.00,
stock: 500,
assignedCount: 200
},
{
id: '3',
productName: '电信物联网卡-年包1TB',
operator: 'CTCC',
packageSpec: '1TB/年有效期2年',
price: 800.00,
stock: 80,
assignedCount: 0
}
])
const assignRecords = ref([
{
id: '1',
agentName: '华东区总代理',
quantity: 200,
commissionMode: 'percent',
commissionRule: '10%',
discount: 5,
assignTime: '2026-01-08 10:00:00',
operator: 'admin'
},
{
id: '2',
agentName: '华南区代理',
quantity: 150,
commissionMode: 'fixed',
commissionRule: '¥5.00/张',
discount: 0,
assignTime: '2026-01-07 14:30:00',
operator: 'admin'
}
])
const filteredData = computed(() => {
let data = mockData.value
if (searchQuery.value) {
data = data.filter((item) => item.productName.includes(searchQuery.value))
}
if (operatorFilter.value) {
data = data.filter((item) => item.operator === operatorFilter.value)
}
if (statusFilter.value) {
if (statusFilter.value === 'assigned') {
data = data.filter((item) => item.assignedCount > 0)
} else if (statusFilter.value === 'unassigned') {
data = data.filter((item) => item.assignedCount === 0)
}
}
return data
})
const getOperatorText = (operator: string) => {
const map: Record<string, string> = {
CMCC: '中国移动',
CUCC: '中国联通',
CTCC: '中国电信'
}
return map[operator] || operator
}
const getOperatorTagType = (operator: string) => {
const map: Record<string, any> = {
CMCC: 'success',
CUCC: 'primary',
CTCC: 'warning'
}
return map[operator] || ''
}
const handleSearch = () => {}
const handleSelectionChange = (rows: SimCardProduct[]) => {
selectedRows.value = rows
}
const showAssignDialog = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要分配的产品')
return
}
if (selectedRows.value.length > 1) {
ElMessage.warning('批量分配功能开发中,请单个选择')
return
}
currentProduct.value = selectedRows.value[0]
assignDialogVisible.value = true
}
const assignToAgent = (row: SimCardProduct) => {
currentProduct.value = row
assignDialogVisible.value = true
}
const handleAgentChange = () => {
// 可以根据代理商自动填充默认佣金设置
}
const handleAssignSubmit = async () => {
if (!formRef.value) return
await formRef.value.validate((valid) => {
if (valid) {
if (assignForm.quantity > currentProduct.value.stock) {
ElMessage.error('分配数量不能超过库存数量')
return
}
// 更新分配数量
currentProduct.value.assignedCount += assignForm.quantity
currentProduct.value.stock -= assignForm.quantity
assignDialogVisible.value = false
formRef.value.resetFields()
ElMessage.success('分配成功')
}
})
}
const viewAssignDetail = (row: SimCardProduct) => {
currentProduct.value = row
detailDialogVisible.value = true
}
const handleCancelAssign = (row: any) => {
ElMessageBox.confirm('取消分配后,该代理商将无法继续销售此产品,确定取消吗?', '取消分配', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 恢复库存
currentProduct.value.stock += row.quantity
currentProduct.value.assignedCount -= row.quantity
const index = assignRecords.value.findIndex((item) => item.id === row.id)
if (index !== -1) assignRecords.value.splice(index, 1)
ElMessage.success('取消分配成功')
})
}
</script>
<style lang="scss" scoped>
.page-content {
:deep(.el-select-dropdown__item) {
display: flex;
justify-content: space-between;
}
}
</style>