提交价格套餐功能
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function listPriceSet(query) {
|
||||
return request({
|
||||
url: '/price/set/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getPriceSet(priceSetId) {
|
||||
return request({
|
||||
url: '/price/set/' + priceSetId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function savePriceSet(data) {
|
||||
return request({
|
||||
url: '/price/set',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updatePriceSet(data) {
|
||||
return request({
|
||||
url: '/price/set',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function delPriceSet(priceSetIds) {
|
||||
return request({
|
||||
url: '/price/set/' + priceSetIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function submitPriceSet(priceSetId) {
|
||||
return request({
|
||||
url: '/price/set/submit/' + priceSetId,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
export function approvePriceSet(priceSetId) {
|
||||
return request({
|
||||
url: '/price/set/approve/' + priceSetId,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
export function rejectPriceSet(priceSetId) {
|
||||
return request({
|
||||
url: '/price/set/reject/' + priceSetId,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
export function disablePriceSet(priceSetId) {
|
||||
return request({
|
||||
url: '/price/set/disable/' + priceSetId,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
export function copyPriceSet(priceSetId) {
|
||||
return request({
|
||||
url: '/price/set/copy/' + priceSetId,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function validatePriceSet(data) {
|
||||
return request({
|
||||
url: '/price/set/validate',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function listSetDetail(query) {
|
||||
return request({
|
||||
url: '/price/set/detail/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addSetDetail(data) {
|
||||
return request({
|
||||
url: '/price/set/detail',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateSetDetail(data) {
|
||||
return request({
|
||||
url: '/price/set/detail',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function delSetDetail(psdIds) {
|
||||
return request({
|
||||
url: '/price/set/detail/' + psdIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function listSetCustomer(query) {
|
||||
return request({
|
||||
url: '/price/set/customer/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addSetCustomer(data) {
|
||||
return request({
|
||||
url: '/price/set/customer',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateSetCustomer(data) {
|
||||
return request({
|
||||
url: '/price/set/customer',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function delSetCustomer(ids) {
|
||||
return request({
|
||||
url: '/price/set/customer/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// ========== 重量段模板 ==========
|
||||
|
||||
/** 查询重量段模板列表 */
|
||||
export function listTemplate(query) {
|
||||
return request({
|
||||
url: '/price/weight/template/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询重量段模板详细 */
|
||||
export function getTemplate(wstId) {
|
||||
return request({
|
||||
url: '/price/weight/template/' + wstId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增重量段模板 */
|
||||
export function addTemplate(data) {
|
||||
return request({
|
||||
url: '/price/weight/template',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 修改重量段模板 */
|
||||
export function updateTemplate(data) {
|
||||
return request({
|
||||
url: '/price/weight/template',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除重量段模板 */
|
||||
export function delTemplate(wstIds) {
|
||||
return request({
|
||||
url: '/price/weight/template/' + wstIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 重量段明细 ==========
|
||||
|
||||
/** 查询重量段明细列表(按 wstId) */
|
||||
export function listDetail(query) {
|
||||
return request({
|
||||
url: '/price/weight/detail/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增重量段明细 */
|
||||
export function addDetail(data) {
|
||||
return request({
|
||||
url: '/price/weight/detail',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 修改重量段明细 */
|
||||
export function updateDetail(data) {
|
||||
return request({
|
||||
url: '/price/weight/detail',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除重量段明细 */
|
||||
export function delDetail(wsdIds) {
|
||||
return request({
|
||||
url: '/price/weight/detail/' + wsdIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// ========== 分区方案 ==========
|
||||
|
||||
/** 查询分区方案列表 */
|
||||
export function listScheme(query) {
|
||||
return request({
|
||||
url: '/price/zone/scheme/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询分区方案详细 */
|
||||
export function getScheme(zsId) {
|
||||
return request({
|
||||
url: '/price/zone/scheme/' + zsId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增分区方案 */
|
||||
export function addScheme(data) {
|
||||
return request({
|
||||
url: '/price/zone/scheme',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 修改分区方案 */
|
||||
export function updateScheme(data) {
|
||||
return request({
|
||||
url: '/price/zone/scheme',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除分区方案 */
|
||||
export function delScheme(zsIds) {
|
||||
return request({
|
||||
url: '/price/zone/scheme/' + zsIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 区划 ==========
|
||||
|
||||
/** 查询区划列表(按 zsId) */
|
||||
export function listDivision(query) {
|
||||
return request({
|
||||
url: '/price/zone/division/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增区划 */
|
||||
export function addDivision(data) {
|
||||
return request({
|
||||
url: '/price/zone/division',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 修改区划 */
|
||||
export function updateDivision(data) {
|
||||
return request({
|
||||
url: '/price/zone/division',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除区划 */
|
||||
export function delDivision(zdIds) {
|
||||
return request({
|
||||
url: '/price/zone/division/' + zdIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 国家 ==========
|
||||
|
||||
/** 查询国家列表(按 zdId) */
|
||||
export function listCountry(query) {
|
||||
return request({
|
||||
url: '/price/zone/country/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增国家 */
|
||||
export function addCountry(data) {
|
||||
return request({
|
||||
url: '/price/zone/country',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 修改国家 */
|
||||
export function updateCountry(data) {
|
||||
return request({
|
||||
url: '/price/zone/country',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除国家 */
|
||||
export function delCountry(zcIds) {
|
||||
return request({
|
||||
url: '/price/zone/country/' + zcIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import router from './router'
|
||||
import router, { notFoundRoute } from './router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import NProgress from 'nprogress'
|
||||
import 'nprogress/nprogress.css'
|
||||
@@ -51,6 +51,8 @@ router.beforeEach(async (to, from) => {
|
||||
router.addRoute(route)
|
||||
}
|
||||
})
|
||||
// 404 必须在全部动态路由之后注册,否则字典数据等隐藏页会被通配抢走
|
||||
router.addRoute(notFoundRoute)
|
||||
// 重新导航到目标路由,确保动态路由已注册
|
||||
return { ...to, replace: true }
|
||||
} catch (err) {
|
||||
|
||||
@@ -24,7 +24,7 @@ import Layout from '@/layout'
|
||||
}
|
||||
*/
|
||||
|
||||
// 公共路由
|
||||
// 公共路由(404 不放这里,等动态路由注册后再挂,避免抢先匹配)
|
||||
export const constantRoutes = [
|
||||
{
|
||||
path: '/redirect',
|
||||
@@ -47,11 +47,6 @@ export const constantRoutes = [
|
||||
component: () => import('@/views/register'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
component: () => import('@/views/error/404'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/401',
|
||||
component: () => import('@/views/error/401'),
|
||||
@@ -131,7 +126,7 @@ export const dynamicRoutes = [
|
||||
{
|
||||
path: 'index/:dictId(\\d+)',
|
||||
component: () => import('@/views/system/dict/data'),
|
||||
name: 'Data',
|
||||
name: 'DictData',
|
||||
meta: { title: '字典数据', activeMenu: '/system/dict' }
|
||||
}
|
||||
]
|
||||
@@ -166,6 +161,13 @@ export const dynamicRoutes = [
|
||||
}
|
||||
]
|
||||
|
||||
// 404 必须在动态路由之后注册,否则会抢先匹配 /system/dict-data 等隐藏页
|
||||
export const notFoundRoute = {
|
||||
path: '/:pathMatch(.*)*',
|
||||
component: () => import('@/views/error/404'),
|
||||
hidden: true
|
||||
}
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: constantRoutes,
|
||||
|
||||
@@ -0,0 +1,542 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 重量段模板 -->
|
||||
<basic-table
|
||||
ref="templateTableRef"
|
||||
:columns="templateColumns"
|
||||
:search-columns="templateSearchColumns"
|
||||
:query="queryTemplateList"
|
||||
row-key="wstId"
|
||||
@row-click="handleTemplateRowClick"
|
||||
>
|
||||
<template #toolbar="{ selection, ids }">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleTemplateAdd"
|
||||
v-hasPermi="['price:weight:add']"
|
||||
>新建模板</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="selection.length !== 1"
|
||||
@click="handleTemplateUpdate(selection[0])"
|
||||
v-hasPermi="['price:weight:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="!selection.length"
|
||||
@click="handleTemplateDelete(ids)"
|
||||
v-hasPermi="['price:weight:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<template #col-ssCode="{ row }">
|
||||
<el-tag :type="row.ssCode === 'on' || row.ssCode === 'ON' ? 'success' : 'warning'" size="small">
|
||||
{{ (row.ssCode || '').toLowerCase() === 'on' ? 'on' : 'of' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" icon="List" @click.stop="handleShowDetail(row)" v-hasPermi="['price:weight:query']">明细</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click.stop="handleTemplateUpdate(row)" v-hasPermi="['price:weight:edit']">编辑</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click.stop="handleTemplateDelete(row.wstId)" v-hasPermi="['price:weight:remove']">删除</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<!-- 明细 + 可视化标尺 -->
|
||||
<el-card shadow="never" class="detail-card" v-loading="detailLoading">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>
|
||||
重量段明细
|
||||
<span v-if="selectedTemplate" class="sub-hint">
|
||||
当前模板:{{ selectedTemplate.wstName }}(wstId={{ selectedTemplate.wstId }},共 {{ detailList.length }} 段)
|
||||
</span>
|
||||
<span v-else class="sub-hint">请点击上方模板行或「明细」查看重量段</span>
|
||||
</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
size="small"
|
||||
:disabled="!selectedTemplate"
|
||||
@click="handleDetailAdd"
|
||||
v-hasPermi="['price:weight:add']"
|
||||
>新增明细</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="selectedTemplate && detailList.length" class="ruler-block">
|
||||
<div class="section-title">重量段可视化标尺</div>
|
||||
<div class="weight-ruler">
|
||||
<el-tag
|
||||
v-for="item in detailList"
|
||||
:key="item.wsdId"
|
||||
class="weight-chip"
|
||||
effect="plain"
|
||||
type="primary"
|
||||
round
|
||||
>
|
||||
{{ formatEndWeight(item) }}
|
||||
<span class="chip-range">{{ item.wlStartweight }}~{{ item.wlEndweight }}</span>
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="detailList"
|
||||
border
|
||||
row-key="wsdId"
|
||||
empty-text="暂无明细数据"
|
||||
>
|
||||
<el-table-column label="wsdId" prop="wsdId" align="center" width="80" />
|
||||
<el-table-column label="标题" prop="wlTitle" align="center" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column label="起始重量" prop="wlStartweight" align="center" width="110" />
|
||||
<el-table-column label="截止重量" prop="wlEndweight" align="center" width="110" />
|
||||
<el-table-column label="计费类别" prop="weightBillingCode" align="center" width="110" />
|
||||
<el-table-column label="计费单位" prop="fvUnitweight" align="center" width="110" />
|
||||
<el-table-column label="生效日期" prop="effectiveDate" align="center" width="120">
|
||||
<template #default="{ row }">{{ parseDate(row.effectiveDate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="失效日期" prop="expireDate" align="center" width="120">
|
||||
<template #default="{ row }">{{ parseDate(row.expireDate) || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="160" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" icon="Edit" @click="handleDetailUpdate(row)" v-hasPermi="['price:weight:edit']">编辑</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDetailDelete(row.wsdId)" v-hasPermi="['price:weight:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 模板表单 -->
|
||||
<el-dialog :title="templateTitle" v-model="templateOpen" width="600px" append-to-body>
|
||||
<el-form ref="templateFormRef" :model="templateForm" :rules="templateRules" label-width="100px">
|
||||
<el-form-item label="模板名称" prop="wstName">
|
||||
<el-input v-model="templateForm.wstName" placeholder="请输入模板名称" maxlength="60" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="ssCode">
|
||||
<el-radio-group v-model="templateForm.ssCode">
|
||||
<el-radio value="on">有效(on)</el-radio>
|
||||
<el-radio value="of">停用(of)</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="产品组" prop="pgCode">
|
||||
<el-input v-model="templateForm.pgCode" placeholder="如 EXP" maxlength="10" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="渠道ID" prop="snId">
|
||||
<el-input v-model="templateForm.snId" placeholder="snId" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="货物类型" prop="ctCode">
|
||||
<el-input v-model="templateForm.ctCode" placeholder="ctCode" maxlength="5" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="付款方式" prop="pmCode">
|
||||
<el-input v-model="templateForm.pmCode" placeholder="pmCode" maxlength="5" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="备注" prop="wstNote">
|
||||
<el-input v-model="templateForm.wstNote" type="textarea" placeholder="请输入备注" :rows="3" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitTemplateForm">确 定</el-button>
|
||||
<el-button @click="templateOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 明细表单 -->
|
||||
<el-dialog :title="detailTitle" v-model="detailOpen" width="600px" append-to-body>
|
||||
<el-form ref="detailFormRef" :model="detailForm" :rules="detailRules" label-width="110px">
|
||||
<el-form-item label="标题" prop="wlTitle">
|
||||
<el-input v-model="detailForm.wlTitle" placeholder="如 0.5kg段" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="起始重量" prop="wlStartweight">
|
||||
<el-input-number v-model="detailForm.wlStartweight" :min="0" :precision="3" :step="0.1" controls-position="right" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="截止重量" prop="wlEndweight">
|
||||
<el-input-number v-model="detailForm.wlEndweight" :min="0" :precision="3" :step="0.1" controls-position="right" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="计费类别" prop="weightBillingCode">
|
||||
<el-input v-model="detailForm.weightBillingCode" placeholder="如 W/P/C" maxlength="5" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="计费单位" prop="fvUnitweight">
|
||||
<el-input-number v-model="detailForm.fvUnitweight" :min="0" :precision="3" :step="0.1" controls-position="right" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="生效日期" prop="effectiveDate">
|
||||
<el-date-picker
|
||||
v-model="detailForm.effectiveDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择生效日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="失效日期" prop="expireDate">
|
||||
<el-date-picker
|
||||
v-model="detailForm.expireDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="可选"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitDetailForm">确 定</el-button>
|
||||
<el-button @click="detailOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="PriceWeight">
|
||||
import {
|
||||
listTemplate, getTemplate, addTemplate, updateTemplate, delTemplate,
|
||||
listDetail, addDetail, updateDetail, delDetail
|
||||
} from '@/api/price/weight'
|
||||
import { parseTime } from '@/utils/ruoyi'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const templateTableRef = ref()
|
||||
const selectedTemplate = ref(null)
|
||||
const detailList = ref([])
|
||||
const detailLoading = ref(false)
|
||||
|
||||
const templateOpen = ref(false)
|
||||
const templateTitle = ref('')
|
||||
const templateFormRef = ref()
|
||||
const templateForm = ref({})
|
||||
const templateRules = {
|
||||
wstName: [{ required: true, message: '模板名称不能为空', trigger: 'blur' }],
|
||||
ssCode: [{ required: true, message: '状态不能为空', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const detailOpen = ref(false)
|
||||
const detailTitle = ref('')
|
||||
const detailFormRef = ref()
|
||||
const detailForm = ref({})
|
||||
|
||||
const validateWeightRange = (rule, value, callback) => {
|
||||
const start = detailForm.value.wlStartweight
|
||||
const end = detailForm.value.wlEndweight
|
||||
if (start == null || end == null) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
if (Number(start) >= Number(end)) {
|
||||
callback(new Error('起始重量必须小于截止重量'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
const detailRules = {
|
||||
wlStartweight: [
|
||||
{ required: true, message: '起始重量不能为空', trigger: 'blur' },
|
||||
{ validator: validateWeightRange, trigger: 'blur' }
|
||||
],
|
||||
wlEndweight: [
|
||||
{ required: true, message: '截止重量不能为空', trigger: 'blur' },
|
||||
{ validator: validateWeightRange, trigger: 'blur' }
|
||||
],
|
||||
weightBillingCode: [{ required: true, message: '计费类别不能为空', trigger: 'blur' }],
|
||||
effectiveDate: [{ required: true, message: '生效日期不能为空', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const templateSearchColumns = [
|
||||
{ label: '模板名称', prop: 'wstName', component: 'input', placeholder: '请输入模板名称', width: '220px' },
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'ssCode',
|
||||
component: 'select',
|
||||
options: [
|
||||
{ label: '有效(on)', value: 'on' },
|
||||
{ label: '停用(of)', value: 'of' }
|
||||
],
|
||||
placeholder: '状态',
|
||||
width: '160px'
|
||||
},
|
||||
{ label: '产品组', prop: 'pgCode', component: 'input', placeholder: '产品组', width: '140px' }
|
||||
]
|
||||
|
||||
const templateColumns = [
|
||||
{ label: 'wstId', prop: 'wstId', align: 'center', width: 80 },
|
||||
{ label: '模板名称', prop: 'wstName', align: 'center', minWidth: 140 },
|
||||
{ label: '状态', prop: 'ssCode', align: 'center', width: 90 },
|
||||
{ label: '产品组', prop: 'pgCode', align: 'center', width: 90 },
|
||||
{ label: '渠道', prop: 'snId', align: 'center', width: 90 },
|
||||
{ label: '货物类型', prop: 'ctCode', align: 'center', width: 90 },
|
||||
{ label: '付款方式', prop: 'pmCode', align: 'center', width: 90 },
|
||||
{ label: '明细数', prop: 'detailCount', align: 'center', width: 90 },
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 220, showInToolbar: false }
|
||||
]
|
||||
|
||||
function queryTemplateList(params) {
|
||||
return listTemplate(params)
|
||||
}
|
||||
|
||||
function parseDate(val) {
|
||||
if (!val) return ''
|
||||
return parseTime(val, '{y}-{m}-{d}')
|
||||
}
|
||||
|
||||
function formatEndWeight(item) {
|
||||
const end = item.wlEndweight
|
||||
if (end == null) return item.wlTitle || ''
|
||||
return Number(end) + 'kg'
|
||||
}
|
||||
|
||||
function handleTemplateRowClick(row) {
|
||||
handleShowDetail(row)
|
||||
}
|
||||
|
||||
function handleShowDetail(row) {
|
||||
if (!row?.wstId) return
|
||||
selectedTemplate.value = row
|
||||
loadDetailList(row.wstId)
|
||||
}
|
||||
|
||||
function loadDetailList(wstId) {
|
||||
if (!wstId) {
|
||||
detailList.value = []
|
||||
return
|
||||
}
|
||||
detailLoading.value = true
|
||||
listDetail({ wstId, pageNum: 1, pageSize: 500 }).then(res => {
|
||||
const rows = res.rows || []
|
||||
detailList.value = rows.slice().sort((a, b) => Number(a.wlStartweight) - Number(b.wlStartweight))
|
||||
}).catch(() => {
|
||||
detailList.value = []
|
||||
}).finally(() => {
|
||||
detailLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeOptionalNumber(val) {
|
||||
if (val === '' || val === null || val === undefined) return undefined
|
||||
return Number(val)
|
||||
}
|
||||
|
||||
function resetTemplateForm() {
|
||||
templateForm.value = {
|
||||
wstId: undefined,
|
||||
wstName: undefined,
|
||||
ssCode: 'on',
|
||||
pgCode: undefined,
|
||||
snId: undefined,
|
||||
ctCode: undefined,
|
||||
pmCode: undefined,
|
||||
wstNote: undefined
|
||||
}
|
||||
proxy.resetForm('templateFormRef')
|
||||
}
|
||||
|
||||
function handleTemplateAdd() {
|
||||
resetTemplateForm()
|
||||
templateOpen.value = true
|
||||
templateTitle.value = '新建重量段模板'
|
||||
}
|
||||
|
||||
function handleTemplateUpdate(row) {
|
||||
resetTemplateForm()
|
||||
getTemplate(row.wstId).then(res => {
|
||||
const data = res.data || {}
|
||||
if (data.ssCode) data.ssCode = String(data.ssCode).toLowerCase()
|
||||
templateForm.value = data
|
||||
templateOpen.value = true
|
||||
templateTitle.value = '编辑重量段模板'
|
||||
})
|
||||
}
|
||||
|
||||
function submitTemplateForm() {
|
||||
proxy.$refs['templateFormRef'].validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = {
|
||||
...templateForm.value,
|
||||
snId: normalizeOptionalNumber(templateForm.value.snId)
|
||||
}
|
||||
const req = payload.wstId != null ? updateTemplate(payload) : addTemplate(payload)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess(payload.wstId != null ? '修改成功' : '新增成功')
|
||||
templateOpen.value = false
|
||||
templateTableRef.value.refresh()
|
||||
if (selectedTemplate.value?.wstId === payload.wstId) {
|
||||
loadDetailList(payload.wstId)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handleTemplateDelete(wstIds) {
|
||||
proxy.$modal.confirm('是否确认删除重量段模板编号为"' + wstIds + '"的数据项?').then(() => {
|
||||
return delTemplate(wstIds)
|
||||
}).then(() => {
|
||||
templateTableRef.value.refresh()
|
||||
if (selectedTemplate.value && String(wstIds).split(',').map(String).includes(String(selectedTemplate.value.wstId))) {
|
||||
selectedTemplate.value = null
|
||||
detailList.value = []
|
||||
}
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function resetDetailForm() {
|
||||
detailForm.value = {
|
||||
wsdId: undefined,
|
||||
wstId: selectedTemplate.value?.wstId,
|
||||
wlTitle: undefined,
|
||||
wlStartweight: undefined,
|
||||
wlEndweight: undefined,
|
||||
weightBillingCode: 'W',
|
||||
fvUnitweight: undefined,
|
||||
effectiveDate: undefined,
|
||||
expireDate: undefined
|
||||
}
|
||||
proxy.resetForm('detailFormRef')
|
||||
}
|
||||
|
||||
function handleDetailAdd() {
|
||||
if (!selectedTemplate.value) {
|
||||
proxy.$modal.msgWarning('请先选择重量段模板')
|
||||
return
|
||||
}
|
||||
resetDetailForm()
|
||||
detailOpen.value = true
|
||||
detailTitle.value = '新增重量段明细'
|
||||
}
|
||||
|
||||
function handleDetailUpdate(row) {
|
||||
resetDetailForm()
|
||||
detailForm.value = {
|
||||
...row,
|
||||
effectiveDate: parseDate(row.effectiveDate) || row.effectiveDate,
|
||||
expireDate: parseDate(row.expireDate) || row.expireDate
|
||||
}
|
||||
detailOpen.value = true
|
||||
detailTitle.value = '编辑重量段明细'
|
||||
}
|
||||
|
||||
function submitDetailForm() {
|
||||
proxy.$refs['detailFormRef'].validate(valid => {
|
||||
if (!valid) return
|
||||
const start = Number(detailForm.value.wlStartweight)
|
||||
const end = Number(detailForm.value.wlEndweight)
|
||||
if (!(start < end)) {
|
||||
proxy.$modal.msgError('起始重量必须小于截止重量')
|
||||
return
|
||||
}
|
||||
const payload = {
|
||||
...detailForm.value,
|
||||
wstId: selectedTemplate.value.wstId,
|
||||
wlStartweight: start,
|
||||
wlEndweight: end,
|
||||
fvUnitweight: normalizeOptionalNumber(detailForm.value.fvUnitweight)
|
||||
}
|
||||
const req = payload.wsdId != null ? updateDetail(payload) : addDetail(payload)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess(payload.wsdId != null ? '修改成功' : '新增成功')
|
||||
detailOpen.value = false
|
||||
loadDetailList(selectedTemplate.value.wstId)
|
||||
templateTableRef.value.refresh()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handleDetailDelete(wsdIds) {
|
||||
proxy.$modal.confirm('是否确认删除重量段明细编号为"' + wsdIds + '"的数据项?').then(() => {
|
||||
return delDetail(wsdIds)
|
||||
}).then(() => {
|
||||
loadDetailList(selectedTemplate.value.wstId)
|
||||
templateTableRef.value.refresh()
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detail-card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.sub-hint {
|
||||
margin-left: 10px;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.ruler-block {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid var(--el-color-primary);
|
||||
}
|
||||
.weight-ruler {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.weight-chip {
|
||||
height: auto;
|
||||
padding: 6px 12px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.chip-range {
|
||||
margin-left: 6px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,548 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 分区方案 -->
|
||||
<basic-table
|
||||
ref="schemeTableRef"
|
||||
:columns="schemeColumns"
|
||||
:search-columns="schemeSearchColumns"
|
||||
:query="querySchemeList"
|
||||
row-key="zsId"
|
||||
@row-click="handleSchemeRowClick"
|
||||
>
|
||||
<template #toolbar="{ selection, ids }">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleSchemeAdd"
|
||||
v-hasPermi="['price:zone:add']"
|
||||
>新建分区方案</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="selection.length !== 1"
|
||||
@click="handleSchemeUpdate(selection[0])"
|
||||
v-hasPermi="['price:zone:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="!selection.length"
|
||||
@click="handleSchemeDelete(ids)"
|
||||
v-hasPermi="['price:zone:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<template #col-ssCode="{ row }">
|
||||
<el-tag :type="row.ssCode === 'on' ? 'success' : 'info'" size="small">
|
||||
{{ row.ssCode === 'on' ? 'on' : 'of' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" icon="Edit" @click.stop="handleSchemeUpdate(row)" v-hasPermi="['price:zone:edit']">编辑</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click.stop="handleSchemeDelete(row.zsId)" v-hasPermi="['price:zone:remove']">删除</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<!-- 区划明细 -->
|
||||
<el-card shadow="never" class="detail-card" v-loading="divisionLoading">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>
|
||||
区划明细
|
||||
<span v-if="selectedScheme" class="sub-hint">当前方案:{{ selectedScheme.zsName }}(zsId={{ selectedScheme.zsId }})</span>
|
||||
<span v-else class="sub-hint">请点击上方方案行查看区划</span>
|
||||
</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
size="small"
|
||||
:disabled="!selectedScheme"
|
||||
@click="handleDivisionAdd"
|
||||
v-hasPermi="['price:zone:add']"
|
||||
>新增区划</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
:data="divisionList"
|
||||
border
|
||||
highlight-current-row
|
||||
row-key="zdId"
|
||||
empty-text="暂无区划数据"
|
||||
>
|
||||
<el-table-column label="zdId" prop="zdId" align="center" width="80" />
|
||||
<el-table-column label="区划代码" prop="zdCode" align="center" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="中文名" prop="znName" align="center" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="英文名" prop="znEname" align="center" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="国家数" prop="countryCount" align="center" width="90" />
|
||||
<el-table-column label="操作" align="center" width="240" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" icon="View" @click="openCountryDrawer(row)" v-hasPermi="['price:zone:query']">查看国家</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleDivisionUpdate(row)" v-hasPermi="['price:zone:edit']">编辑</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDivisionDelete(row.zdId)" v-hasPermi="['price:zone:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 方案表单 -->
|
||||
<el-dialog :title="schemeTitle" v-model="schemeOpen" width="560px" append-to-body>
|
||||
<el-form ref="schemeFormRef" :model="schemeForm" :rules="schemeRules" label-width="100px">
|
||||
<el-form-item label="方案名称" prop="zsName">
|
||||
<el-input v-model="schemeForm.zsName" placeholder="请输入分区方案名称" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="ssCode">
|
||||
<el-radio-group v-model="schemeForm.ssCode">
|
||||
<el-radio value="on">有效(on)</el-radio>
|
||||
<el-radio value="of">停用(of)</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品组" prop="pgCode">
|
||||
<el-input v-model="schemeForm.pgCode" placeholder="如 EXP / EMS / LINE" maxlength="10" />
|
||||
</el-form-item>
|
||||
<el-form-item label="渠道ID" prop="snId">
|
||||
<el-input v-model="schemeForm.snId" placeholder="请输入渠道 snId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="zsNote">
|
||||
<el-input v-model="schemeForm.zsNote" type="textarea" placeholder="请输入备注" :rows="3" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitSchemeForm">确 定</el-button>
|
||||
<el-button @click="schemeOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 区划表单 -->
|
||||
<el-dialog :title="divisionTitle" v-model="divisionOpen" width="560px" append-to-body>
|
||||
<el-form ref="divisionFormRef" :model="divisionForm" :rules="divisionRules" label-width="100px">
|
||||
<el-form-item label="区划代码" prop="zdCode">
|
||||
<el-input v-model="divisionForm.zdCode" placeholder="如 DHL-ASIA" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="中文名" prop="znName">
|
||||
<el-input v-model="divisionForm.znName" placeholder="请输入中文名" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="英文名" prop="znEname">
|
||||
<el-input v-model="divisionForm.znEname" placeholder="请输入英文名" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序号" prop="zdSort">
|
||||
<el-input v-model="divisionForm.zdSort" placeholder="显示排序号" maxlength="3" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="znNote">
|
||||
<el-input v-model="divisionForm.znNote" type="textarea" placeholder="请输入备注" :rows="3" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitDivisionForm">确 定</el-button>
|
||||
<el-button @click="divisionOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 国家抽屉 -->
|
||||
<el-drawer v-model="countryDrawerVisible" size="640px" append-to-body>
|
||||
<template #header>
|
||||
<div class="drawer-head">
|
||||
<span class="drawer-title">区划国家</span>
|
||||
<span v-if="selectedDivision" class="drawer-sub">
|
||||
{{ selectedDivision.zdCode }} {{ selectedDivision.znName }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="drawer-toolbar">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
size="small"
|
||||
@click="handleCountryAdd"
|
||||
v-hasPermi="['price:zone:add']"
|
||||
>新增国家</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="countryLoading" :data="countryList" border row-key="zcId" empty-text="暂无国家">
|
||||
<el-table-column label="zcId" prop="zcId" align="center" width="80" />
|
||||
<el-table-column label="国家代码" prop="countryCode" align="center" width="100" />
|
||||
<el-table-column label="划分类型" prop="zcDividekind" align="center" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="divideKindTag(row.zcDividekind)">
|
||||
{{ divideKindLabel(row.zcDividekind) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="160">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" icon="Edit" @click="handleCountryUpdate(row)" v-hasPermi="['price:zone:edit']">编辑</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleCountryDelete(row.zcId)" v-hasPermi="['price:zone:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-drawer>
|
||||
|
||||
<!-- 国家表单 -->
|
||||
<el-dialog :title="countryTitle" v-model="countryOpen" width="480px" append-to-body>
|
||||
<el-form ref="countryFormRef" :model="countryForm" :rules="countryRules" label-width="100px">
|
||||
<el-form-item label="国家代码" prop="countryCode">
|
||||
<el-input v-model="countryForm.countryCode" placeholder="ISO两位码,如 DE" maxlength="2" style="text-transform: uppercase" />
|
||||
</el-form-item>
|
||||
<el-form-item label="划分类型" prop="zcDividekind">
|
||||
<el-radio-group v-model="countryForm.zcDividekind">
|
||||
<el-radio value="C">国家(C)</el-radio>
|
||||
<el-radio value="T">城市(T)</el-radio>
|
||||
<el-radio value="P">邮编(P)</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitCountryForm">确 定</el-button>
|
||||
<el-button @click="countryOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="PriceZone">
|
||||
import {
|
||||
listScheme, getScheme, addScheme, updateScheme, delScheme,
|
||||
listDivision, addDivision, updateDivision, delDivision,
|
||||
listCountry, addCountry, updateCountry, delCountry
|
||||
} from '@/api/price/zone'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const schemeTableRef = ref()
|
||||
const selectedScheme = ref(null)
|
||||
const divisionList = ref([])
|
||||
const divisionLoading = ref(false)
|
||||
|
||||
const schemeOpen = ref(false)
|
||||
const schemeTitle = ref('')
|
||||
const schemeFormRef = ref()
|
||||
const schemeForm = ref({})
|
||||
const schemeRules = {
|
||||
zsName: [{ required: true, message: '方案名称不能为空', trigger: 'blur' }],
|
||||
ssCode: [{ required: true, message: '状态不能为空', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const divisionOpen = ref(false)
|
||||
const divisionTitle = ref('')
|
||||
const divisionFormRef = ref()
|
||||
const divisionForm = ref({})
|
||||
const divisionRules = {
|
||||
zdCode: [{ required: true, message: '区划代码不能为空', trigger: 'blur' }],
|
||||
znName: [{ required: true, message: '中文名不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const countryDrawerVisible = ref(false)
|
||||
const selectedDivision = ref(null)
|
||||
const countryList = ref([])
|
||||
const countryLoading = ref(false)
|
||||
const countryOpen = ref(false)
|
||||
const countryTitle = ref('')
|
||||
const countryFormRef = ref()
|
||||
const countryForm = ref({})
|
||||
const countryRules = {
|
||||
countryCode: [
|
||||
{ required: true, message: '国家代码不能为空', trigger: 'blur' },
|
||||
{ min: 2, max: 2, message: '国家代码须为2位', trigger: 'blur' }
|
||||
],
|
||||
zcDividekind: [{ required: true, message: '划分类型不能为空', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const schemeSearchColumns = [
|
||||
{ label: '方案名称', prop: 'zsName', component: 'input', placeholder: '请输入方案名称', width: '220px' },
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'ssCode',
|
||||
component: 'select',
|
||||
options: [
|
||||
{ label: '有效(on)', value: 'on' },
|
||||
{ label: '停用(of)', value: 'of' }
|
||||
],
|
||||
placeholder: '状态',
|
||||
width: '160px'
|
||||
}
|
||||
]
|
||||
|
||||
const schemeColumns = [
|
||||
{ label: 'zsId', prop: 'zsId', align: 'center', width: 80 },
|
||||
{ label: '方案名称', prop: 'zsName', align: 'center', minWidth: 140 },
|
||||
{ label: '状态', prop: 'ssCode', align: 'center', width: 90 },
|
||||
{ label: '产品组', prop: 'pgCode', align: 'center', width: 100 },
|
||||
{ label: '渠道', prop: 'snId', align: 'center', width: 100 },
|
||||
{ label: '区划数', prop: 'divisionCount', align: 'center', width: 90 },
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 160, showInToolbar: false }
|
||||
]
|
||||
|
||||
function querySchemeList(params) {
|
||||
return listScheme(params)
|
||||
}
|
||||
|
||||
function handleSchemeRowClick(row) {
|
||||
if (!row?.zsId) return
|
||||
selectedScheme.value = row
|
||||
loadDivisionList(row.zsId)
|
||||
}
|
||||
|
||||
function loadDivisionList(zsId) {
|
||||
if (!zsId) {
|
||||
divisionList.value = []
|
||||
return
|
||||
}
|
||||
divisionLoading.value = true
|
||||
listDivision({ zsId, pageNum: 1, pageSize: 200 }).then(res => {
|
||||
divisionList.value = res.rows || []
|
||||
}).catch(() => {
|
||||
divisionList.value = []
|
||||
}).finally(() => {
|
||||
divisionLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function resetSchemeForm() {
|
||||
schemeForm.value = {
|
||||
zsId: undefined,
|
||||
zsName: undefined,
|
||||
ssCode: 'on',
|
||||
pgCode: undefined,
|
||||
snId: undefined,
|
||||
zsNote: undefined
|
||||
}
|
||||
proxy.resetForm('schemeFormRef')
|
||||
}
|
||||
|
||||
function handleSchemeAdd() {
|
||||
resetSchemeForm()
|
||||
schemeOpen.value = true
|
||||
schemeTitle.value = '新建分区方案'
|
||||
}
|
||||
|
||||
function handleSchemeUpdate(row) {
|
||||
resetSchemeForm()
|
||||
getScheme(row.zsId).then(res => {
|
||||
schemeForm.value = res.data || {}
|
||||
schemeOpen.value = true
|
||||
schemeTitle.value = '编辑分区方案'
|
||||
})
|
||||
}
|
||||
|
||||
function submitSchemeForm() {
|
||||
proxy.$refs['schemeFormRef'].validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = { ...schemeForm.value }
|
||||
if (payload.snId === '' || payload.snId === null) {
|
||||
payload.snId = undefined
|
||||
} else if (payload.snId !== undefined) {
|
||||
payload.snId = Number(payload.snId)
|
||||
}
|
||||
const req = payload.zsId != null ? updateScheme(payload) : addScheme(payload)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess(payload.zsId != null ? '修改成功' : '新增成功')
|
||||
schemeOpen.value = false
|
||||
schemeTableRef.value.refresh()
|
||||
if (selectedScheme.value?.zsId === payload.zsId) {
|
||||
loadDivisionList(payload.zsId)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handleSchemeDelete(zsIds) {
|
||||
proxy.$modal.confirm('是否确认删除分区方案编号为"' + zsIds + '"的数据项?').then(() => {
|
||||
return delScheme(zsIds)
|
||||
}).then(() => {
|
||||
schemeTableRef.value.refresh()
|
||||
if (selectedScheme.value && String(zsIds).split(',').map(String).includes(String(selectedScheme.value.zsId))) {
|
||||
selectedScheme.value = null
|
||||
divisionList.value = []
|
||||
}
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function resetDivisionForm() {
|
||||
divisionForm.value = {
|
||||
zdId: undefined,
|
||||
zsId: selectedScheme.value?.zsId,
|
||||
zdCode: undefined,
|
||||
znName: undefined,
|
||||
znEname: undefined,
|
||||
zdSort: undefined,
|
||||
znNote: undefined
|
||||
}
|
||||
proxy.resetForm('divisionFormRef')
|
||||
}
|
||||
|
||||
function handleDivisionAdd() {
|
||||
if (!selectedScheme.value) {
|
||||
proxy.$modal.msgWarning('请先选择分区方案')
|
||||
return
|
||||
}
|
||||
resetDivisionForm()
|
||||
divisionOpen.value = true
|
||||
divisionTitle.value = '新增区划'
|
||||
}
|
||||
|
||||
function handleDivisionUpdate(row) {
|
||||
resetDivisionForm()
|
||||
divisionForm.value = { ...row }
|
||||
divisionOpen.value = true
|
||||
divisionTitle.value = '编辑区划'
|
||||
}
|
||||
|
||||
function submitDivisionForm() {
|
||||
proxy.$refs['divisionFormRef'].validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = { ...divisionForm.value, zsId: selectedScheme.value.zsId }
|
||||
const req = payload.zdId != null ? updateDivision(payload) : addDivision(payload)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess(payload.zdId != null ? '修改成功' : '新增成功')
|
||||
divisionOpen.value = false
|
||||
loadDivisionList(selectedScheme.value.zsId)
|
||||
schemeTableRef.value.refresh()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handleDivisionDelete(zdIds) {
|
||||
proxy.$modal.confirm('是否确认删除区划编号为"' + zdIds + '"的数据项?').then(() => {
|
||||
return delDivision(zdIds)
|
||||
}).then(() => {
|
||||
loadDivisionList(selectedScheme.value.zsId)
|
||||
schemeTableRef.value.refresh()
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function divideKindLabel(kind) {
|
||||
const map = { C: '国家(C)', T: '城市(T)', P: '邮编(P)' }
|
||||
return map[kind] || kind
|
||||
}
|
||||
|
||||
function divideKindTag(kind) {
|
||||
const map = { C: 'primary', T: 'warning', P: 'success' }
|
||||
return map[kind] || 'info'
|
||||
}
|
||||
|
||||
function openCountryDrawer(row) {
|
||||
selectedDivision.value = row
|
||||
countryDrawerVisible.value = true
|
||||
loadCountryList(row.zdId)
|
||||
}
|
||||
|
||||
function loadCountryList(zdId) {
|
||||
if (!zdId) {
|
||||
countryList.value = []
|
||||
return
|
||||
}
|
||||
countryLoading.value = true
|
||||
listCountry({ zdId, pageNum: 1, pageSize: 500 }).then(res => {
|
||||
countryList.value = res.rows || []
|
||||
}).catch(() => {
|
||||
countryList.value = []
|
||||
}).finally(() => {
|
||||
countryLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function resetCountryForm() {
|
||||
countryForm.value = {
|
||||
zcId: undefined,
|
||||
zdId: selectedDivision.value?.zdId,
|
||||
countryCode: undefined,
|
||||
zcDividekind: 'C'
|
||||
}
|
||||
proxy.resetForm('countryFormRef')
|
||||
}
|
||||
|
||||
function handleCountryAdd() {
|
||||
resetCountryForm()
|
||||
countryOpen.value = true
|
||||
countryTitle.value = '新增国家'
|
||||
}
|
||||
|
||||
function handleCountryUpdate(row) {
|
||||
resetCountryForm()
|
||||
countryForm.value = { ...row }
|
||||
countryOpen.value = true
|
||||
countryTitle.value = '编辑国家'
|
||||
}
|
||||
|
||||
function submitCountryForm() {
|
||||
proxy.$refs['countryFormRef'].validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = {
|
||||
...countryForm.value,
|
||||
zdId: selectedDivision.value.zdId,
|
||||
countryCode: (countryForm.value.countryCode || '').toUpperCase()
|
||||
}
|
||||
const req = payload.zcId != null ? updateCountry(payload) : addCountry(payload)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess(payload.zcId != null ? '修改成功' : '新增成功')
|
||||
countryOpen.value = false
|
||||
loadCountryList(selectedDivision.value.zdId)
|
||||
loadDivisionList(selectedScheme.value.zsId)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handleCountryDelete(zcIds) {
|
||||
proxy.$modal.confirm('是否确认删除国家编号为"' + zcIds + '"的数据项?').then(() => {
|
||||
return delCountry(zcIds)
|
||||
}).then(() => {
|
||||
loadCountryList(selectedDivision.value.zdId)
|
||||
loadDivisionList(selectedScheme.value.zsId)
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
}).catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detail-card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.sub-hint {
|
||||
margin-left: 10px;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.drawer-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.drawer-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.drawer-sub {
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
.drawer-toolbar {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,490 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-steps :active="active" finish-status="success" align-center class="wizard-steps">
|
||||
<el-step title="基本信息" />
|
||||
<el-step title="套餐明细" />
|
||||
<el-step title="绑定客户" />
|
||||
<el-step title="校验确认" />
|
||||
</el-steps>
|
||||
|
||||
<!-- 第 1 步 -->
|
||||
<el-card v-show="active === 0" shadow="never" class="step-card">
|
||||
<template #header><span>第 1 步 · 基本信息</span></template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" class="max-form">
|
||||
<el-form-item label="套餐名称" prop="priceSetName">
|
||||
<el-input v-model="form.priceSetName" maxlength="50" placeholder="如:金牌电商专属套餐" />
|
||||
<div class="form-hint">显示给客户的套餐对外名称</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="计价类型" prop="setType">
|
||||
<div class="type-row">
|
||||
<div
|
||||
class="type-card"
|
||||
:class="{ selected: form.setType === 'FIXED' }"
|
||||
@click="form.setType = 'FIXED'"
|
||||
>
|
||||
<div class="t-title">FIXED 固定价</div>
|
||||
<div class="t-desc">每个产品×国家×重量段独立配置运费单价 + 挂号费,覆盖默认价</div>
|
||||
</div>
|
||||
<div
|
||||
class="type-card"
|
||||
:class="{ selected: form.setType === 'DISCOUNT' }"
|
||||
@click="form.setType = 'DISCOUNT'"
|
||||
>
|
||||
<div class="t-title">DISCOUNT 折扣</div>
|
||||
<div class="t-desc">按默认等级价统一乘折扣,可选产品范围</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="默认折扣" prop="defaultDiscount">
|
||||
<el-input-number
|
||||
v-model="form.defaultDiscount"
|
||||
:min="1"
|
||||
:max="99"
|
||||
:disabled="form.setType !== 'DISCOUNT'"
|
||||
controls-position="right"
|
||||
/>
|
||||
<span class="unit">%</span>
|
||||
<el-tag v-if="form.setType !== 'DISCOUNT'" type="warning" size="small" style="margin-left: 8px">仅 DISCOUNT 模式生效</el-tag>
|
||||
<div class="form-hint">范围 1~99,88 表示默认价 × 0.88;FIXED 模式此值忽略</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="生效日期" prop="effectiveDate">
|
||||
<el-date-picker v-model="form.effectiveDate" type="date" value-format="YYYY-MM-DD" placeholder="生效日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="失效日期" prop="expireDate">
|
||||
<el-date-picker v-model="form.expireDate" type="date" value-format="YYYY-MM-DD" placeholder="空表示长期有效" />
|
||||
<div class="form-hint">空表示长期有效</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="说明备注" prop="priceSetNote">
|
||||
<el-input v-model="form.priceSetNote" type="textarea" :rows="3" maxlength="200" placeholder="套餐业务说明,显示给客户与运营" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 第 2 步 -->
|
||||
<el-card v-show="active === 1" shadow="never" class="step-card">
|
||||
<template #header>
|
||||
<div class="card-head">
|
||||
<span>
|
||||
第 2 步 · 套餐明细
|
||||
<span class="sub">当前:{{ form.setType === 'FIXED' ? 'FIXED 固定价' : 'DISCOUNT 折扣' }}模式</span>
|
||||
</span>
|
||||
<el-button type="primary" size="small" icon="Plus" @click="addDetailRow">添加明细行</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-alert
|
||||
v-if="form.setType === 'FIXED'"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb12"
|
||||
title="FIXED 模式:每个产品 × 国家 × 重量段独立配置运费单价(RMB/KG)+ 挂号费(RMB/票)。命中时直接取该固定价。"
|
||||
/>
|
||||
<el-alert
|
||||
v-else
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb12"
|
||||
title="DISCOUNT 模式:可不填明细(全部产品适用默认折扣),或按产品限定折扣范围。"
|
||||
/>
|
||||
<el-table :data="form.details" border empty-text="暂无明细,点击添加明细行">
|
||||
<el-table-column label="产品" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.productCode" filterable allow-create placeholder="产品" @change="onProductChange(row)">
|
||||
<el-option v-for="p in productOptions" :key="p.value" :label="p.label" :value="p.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="form.setType === 'FIXED'" label="国家" min-width="130">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.countryCode" filterable allow-create placeholder="国家" @change="onCountryChange(row)">
|
||||
<el-option v-for="c in countryOptions" :key="c.value" :label="c.label" :value="c.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="form.setType === 'FIXED'" label="重量段" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.weightLabel" filterable allow-create placeholder="重量段">
|
||||
<el-option v-for="w in weightOptions" :key="w.value" :label="w.label" :value="w.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="form.setType === 'FIXED'" label="运费单价(RMB/KG)" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-input-number v-model="row.fvValue" :min="0" :precision="4" :step="1" controls-position="right" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="form.setType === 'FIXED'" label="挂号费(RMB/票)" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<el-input-number v-model="row.fvRegistrationFee" :min="0" :precision="2" :step="1" controls-position="right" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="form.setType === 'DISCOUNT'" label="产品折扣%" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<el-input-number v-model="row.discountValue" :min="1" :max="99" :precision="2" controls-position="right" style="width: 100%" placeholder="空=默认折扣" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" @click="form.details.splice($index, 1)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 第 3 步 -->
|
||||
<el-card v-show="active === 2" shadow="never" class="step-card">
|
||||
<template #header>
|
||||
<div class="card-head">
|
||||
<span>第 3 步 · 绑定客户<span class="sub">可选,可后续补充</span></span>
|
||||
<el-button type="primary" size="small" icon="Plus" @click="addCustomerRow">添加客户</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-alert
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb12"
|
||||
title="套餐创建后处于草稿状态,可暂不绑定客户。优先级数字越小越优先(1 = 最高)。同一客户可绑定多个套餐。"
|
||||
/>
|
||||
<el-table :data="form.customers" border empty-text="暂未绑定客户">
|
||||
<el-table-column label="客户ID" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.customerId" placeholder="客户ID" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户名称" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.customerName" placeholder="客户名称" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="优先级" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-input-number v-model="row.bindPriority" :min="1" controls-position="right" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="生效日期" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-date-picker v-model="row.prBegindate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="失效日期" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-date-picker v-model="row.prEnddate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" @click="form.customers.splice($index, 1)">移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 第 4 步 -->
|
||||
<el-card v-show="active === 3" shadow="never" class="step-card">
|
||||
<template #header>
|
||||
<div class="card-head">
|
||||
<span>第 4 步 · 校验确认</span>
|
||||
<el-button type="primary" size="small" @click="runValidate">重新校验</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="checkItems" border empty-text="点击「下一步」或「重新校验」执行校验">
|
||||
<el-table-column label="校验项" prop="item" min-width="200" />
|
||||
<el-table-column label="结果" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="checkTag(row.result)" size="small">{{ checkLabel(row.result) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="说明" prop="message" min-width="280" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<div class="wizard-actions">
|
||||
<el-button @click="goBack">取消</el-button>
|
||||
<el-button v-if="active > 0" @click="active--">上一步</el-button>
|
||||
<el-button v-if="active < 3" type="primary" @click="nextStep">下一步</el-button>
|
||||
<el-button type="info" :loading="saving" @click="saveDraft">保存草稿</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="saveAndSubmit">保存并提交审核</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="PriceSetCreate">
|
||||
import { getPriceSet, savePriceSet, validatePriceSet } from '@/api/price/set'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const route = useRoute()
|
||||
const active = ref(0)
|
||||
const saving = ref(false)
|
||||
const formRef = ref()
|
||||
const checkItems = ref([])
|
||||
|
||||
const productOptions = [
|
||||
{ value: 'WSP037', label: 'WSP037 球衣' },
|
||||
{ value: 'WSP038', label: 'WSP038 普货' },
|
||||
{ value: 'WSP034', label: 'WSP034 带电' },
|
||||
{ value: 'WSP035', label: 'WSP035 敏感' },
|
||||
{ value: 'WSP066', label: 'WSP066 鞋盒' }
|
||||
]
|
||||
const countryOptions = [
|
||||
{ value: 'DE', label: '德国 DE' },
|
||||
{ value: 'FR', label: '法国 FR' },
|
||||
{ value: 'IT', label: '意大利 IT' },
|
||||
{ value: 'ES', label: '西班牙 ES' }
|
||||
]
|
||||
const weightOptions = [
|
||||
{ value: '0.01≤W≤0.3', label: '0.01≤W≤0.3' },
|
||||
{ value: '0.3<W≤1', label: '0.3<W≤1' },
|
||||
{ value: '1<W≤2', label: '1<W≤2' }
|
||||
]
|
||||
|
||||
const form = reactive({
|
||||
priceSetId: undefined,
|
||||
priceSetName: '',
|
||||
setType: 'FIXED',
|
||||
defaultDiscount: 88,
|
||||
effectiveDate: undefined,
|
||||
expireDate: undefined,
|
||||
priceSetNote: '',
|
||||
details: [],
|
||||
customers: []
|
||||
})
|
||||
|
||||
const rules = {
|
||||
priceSetName: [{ required: true, message: '套餐名称不能为空', trigger: 'blur' }],
|
||||
setType: [{ required: true, message: '请选择计价类型', trigger: 'change' }],
|
||||
effectiveDate: [{ required: true, message: '生效日期不能为空', trigger: 'change' }]
|
||||
}
|
||||
|
||||
function emptyDetail() {
|
||||
return {
|
||||
productCode: undefined,
|
||||
productName: undefined,
|
||||
countryCode: form.setType === 'FIXED' ? 'DE' : undefined,
|
||||
countryName: form.setType === 'FIXED' ? '德国' : undefined,
|
||||
weightLabel: form.setType === 'FIXED' ? '0.3<W≤1' : undefined,
|
||||
fvValue: undefined,
|
||||
fvRegistrationFee: undefined,
|
||||
discountValue: undefined
|
||||
}
|
||||
}
|
||||
|
||||
function emptyCustomer() {
|
||||
return {
|
||||
customerId: undefined,
|
||||
customerName: '',
|
||||
bindPriority: form.customers.length + 1,
|
||||
prBegindate: form.effectiveDate,
|
||||
prEnddate: form.expireDate
|
||||
}
|
||||
}
|
||||
|
||||
function addDetailRow() {
|
||||
form.details.push(emptyDetail())
|
||||
}
|
||||
|
||||
function addCustomerRow() {
|
||||
form.customers.push(emptyCustomer())
|
||||
}
|
||||
|
||||
function onProductChange(row) {
|
||||
const found = productOptions.find(p => p.value === row.productCode)
|
||||
row.productName = found ? found.label.replace(row.productCode + ' ', '') : row.productCode
|
||||
}
|
||||
|
||||
function onCountryChange(row) {
|
||||
const found = countryOptions.find(c => c.value === row.countryCode)
|
||||
row.countryName = found ? found.label.replace(' ' + row.countryCode, '') : row.countryCode
|
||||
}
|
||||
|
||||
function buildPayload(submitFlag) {
|
||||
const details = (form.details || []).filter(d => d.productCode).map(d => ({
|
||||
...d,
|
||||
countryCode: d.countryCode || undefined,
|
||||
fvValue: d.fvValue,
|
||||
fvRegistrationFee: d.fvRegistrationFee
|
||||
}))
|
||||
const customers = (form.customers || [])
|
||||
.filter(c => c.customerId)
|
||||
.map(c => ({
|
||||
...c,
|
||||
customerId: Number(c.customerId)
|
||||
}))
|
||||
return {
|
||||
priceSetId: form.priceSetId,
|
||||
priceSetName: form.priceSetName,
|
||||
setType: form.setType,
|
||||
defaultDiscount: form.setType === 'DISCOUNT' ? form.defaultDiscount : undefined,
|
||||
effectiveDate: form.effectiveDate,
|
||||
expireDate: form.expireDate || undefined,
|
||||
priceSetNote: form.priceSetNote,
|
||||
details,
|
||||
customers,
|
||||
submitFlag: submitFlag ? 'Y' : undefined
|
||||
}
|
||||
}
|
||||
|
||||
function nextStep() {
|
||||
if (active.value === 0) {
|
||||
formRef.value.validate(valid => {
|
||||
if (valid) active.value++
|
||||
})
|
||||
return
|
||||
}
|
||||
if (active.value === 2) {
|
||||
active.value++
|
||||
runValidate()
|
||||
return
|
||||
}
|
||||
active.value++
|
||||
}
|
||||
|
||||
function runValidate() {
|
||||
validatePriceSet(buildPayload(false)).then(res => {
|
||||
checkItems.value = res.data || []
|
||||
})
|
||||
}
|
||||
|
||||
function saveDraft() {
|
||||
formRef.value.validate(valid => {
|
||||
if (!valid) {
|
||||
active.value = 0
|
||||
return
|
||||
}
|
||||
doSave(false)
|
||||
})
|
||||
}
|
||||
|
||||
function saveAndSubmit() {
|
||||
formRef.value.validate(valid => {
|
||||
if (!valid) {
|
||||
active.value = 0
|
||||
return
|
||||
}
|
||||
doSave(true)
|
||||
})
|
||||
}
|
||||
|
||||
function doSave(submit) {
|
||||
saving.value = true
|
||||
savePriceSet(buildPayload(submit)).then(res => {
|
||||
form.priceSetId = res.data
|
||||
proxy.$modal.msgSuccess(submit ? '已保存并提交审核' : '草稿已保存,套餐ID=' + res.data)
|
||||
goBack()
|
||||
}).finally(() => {
|
||||
saving.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
const obj = { path: '/customer/set' }
|
||||
if (proxy.$tab) {
|
||||
proxy.$tab.closeOpenPage(obj)
|
||||
} else {
|
||||
proxy.$router.push(obj)
|
||||
}
|
||||
}
|
||||
|
||||
function checkTag(result) {
|
||||
const map = { PASS: 'success', WARN: 'warning', FAIL: 'danger', INFO: 'primary' }
|
||||
return map[result] || 'info'
|
||||
}
|
||||
|
||||
function checkLabel(result) {
|
||||
const map = { PASS: '? 通过', WARN: '? 待确认', FAIL: '? 失败', INFO: '? 提示' }
|
||||
return map[result] || result
|
||||
}
|
||||
|
||||
function loadExisting(id) {
|
||||
getPriceSet(id).then(res => {
|
||||
const data = res.data || {}
|
||||
form.priceSetId = data.priceSetId
|
||||
form.priceSetName = data.priceSetName
|
||||
form.setType = data.setType || 'FIXED'
|
||||
form.defaultDiscount = data.defaultDiscount != null ? Number(data.defaultDiscount) : 88
|
||||
form.effectiveDate = data.effectiveDate
|
||||
form.expireDate = data.expireDate
|
||||
form.priceSetNote = data.priceSetNote
|
||||
form.details = (data.details || []).map(d => ({ ...d }))
|
||||
form.customers = (data.customers || []).map(c => ({ ...c }))
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const id = route.query.priceSetId
|
||||
if (id) {
|
||||
loadExisting(id)
|
||||
} else {
|
||||
addDetailRow()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.wizard-steps {
|
||||
margin-bottom: 20px;
|
||||
background: #fff;
|
||||
padding: 20px 12px 12px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.step-card {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.sub {
|
||||
margin-left: 8px;
|
||||
color: #909399;
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
}
|
||||
.max-form {
|
||||
max-width: 760px;
|
||||
}
|
||||
.form-hint {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
line-height: 1.4;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.unit {
|
||||
margin-left: 8px;
|
||||
color: #909399;
|
||||
}
|
||||
.type-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
.type-card {
|
||||
flex: 1;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 6px;
|
||||
padding: 14px 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.type-card.selected {
|
||||
border-color: #1890ff;
|
||||
background: #e6f7ff;
|
||||
}
|
||||
.t-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.t-desc {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.mb12 {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.wizard-actions {
|
||||
text-align: center;
|
||||
padding: 8px 0 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,416 @@
|
||||
<template>
|
||||
<div class="app-container" v-loading="loading">
|
||||
<div class="detail-header">
|
||||
<h2>{{ set.priceSetCode || '-' }} · {{ set.priceSetName || '套餐详情' }}</h2>
|
||||
<div class="header-actions">
|
||||
<el-tag :type="statusTag" size="large">{{ statusLabel }}</el-tag>
|
||||
<el-tag :type="set.setType === 'FIXED' ? 'warning' : 'primary'" size="large" effect="plain">
|
||||
{{ set.setType === 'FIXED' ? 'FIXED 固定价' : 'DISCOUNT 折扣' }}
|
||||
</el-tag>
|
||||
<el-button @click="goList">返回列表</el-button>
|
||||
<el-button v-if="set.status === 'D' || set.status === 'R'" type="primary" @click="goEdit" v-hasPermi="['price:set:edit']">继续配置</el-button>
|
||||
<el-button v-if="set.status === 'D' || set.status === 'R'" type="primary" @click="onSubmit" v-hasPermi="['price:set:audit']">提交审核</el-button>
|
||||
<el-button v-if="set.status === 'A'" type="success" @click="onApprove" v-hasPermi="['price:set:audit']">审批生效</el-button>
|
||||
<el-button v-if="set.status === 'A'" type="warning" @click="onReject" v-hasPermi="['price:set:audit']">驳回</el-button>
|
||||
<el-button v-if="set.status === 'E' && !set.expired" type="danger" @click="onDisable" v-hasPermi="['price:set:audit']">停用</el-button>
|
||||
<el-button v-if="set.status === 'S'" @click="onCopy" v-hasPermi="['price:set:add']">复用新建</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24">
|
||||
<el-card shadow="never">
|
||||
<template #header>基本信息</template>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="套餐ID">{{ set.priceSetCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="套餐名称">{{ set.priceSetName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计价类型">
|
||||
{{ set.setType === 'FIXED' ? 'FIXED 固定价' : 'DISCOUNT 折扣' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="默认折扣">
|
||||
<span v-if="set.setType === 'DISCOUNT'">{{ set.defaultDiscount }}%</span>
|
||||
<el-tag v-else type="info" size="small">不适用(FIXED)</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">{{ statusLabel }}</el-descriptions-item>
|
||||
<el-descriptions-item label="生效日期">
|
||||
{{ formatDate(set.effectiveDate) }} ~ {{ set.expireDate ? formatDate(set.expireDate) : '长期' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">{{ set.createBy || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ set.createTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最后更新" :span="2">{{ set.updateTime || '-' }} {{ set.updateBy ? '(' + set.updateBy + ')' : '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="说明" :span="2">{{ set.priceSetNote || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-card shadow="never" class="mt16">
|
||||
<template #header>
|
||||
<div class="card-head">
|
||||
<span>套餐明细</span>
|
||||
<el-button
|
||||
v-if="childEditable"
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="Plus"
|
||||
@click="openDetailDialog()"
|
||||
v-hasPermi="['price:set:edit']"
|
||||
>添加明细</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="set.details || []" border empty-text="暂无明细">
|
||||
<el-table-column label="明细ID" prop="psdId" width="90" />
|
||||
<el-table-column label="产品" min-width="130">
|
||||
<template #default="{ row }">{{ row.productCode }} {{ row.productName || '' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="set.setType === 'FIXED'" label="国家" min-width="110">
|
||||
<template #default="{ row }">{{ row.countryName || row.countryCode || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="set.setType === 'FIXED'" label="重量段" prop="weightLabel" min-width="120" />
|
||||
<el-table-column v-if="set.setType === 'FIXED'" label="运费单价" min-width="120">
|
||||
<template #default="{ row }"><span class="hl">{{ row.fvValue }} RMB/KG</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="set.setType === 'FIXED'" label="挂号费" min-width="120">
|
||||
<template #default="{ row }"><span class="reg">{{ row.fvRegistrationFee }} RMB/票</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="set.setType === 'DISCOUNT'" label="产品折扣" min-width="110">
|
||||
<template #default="{ row }">{{ row.discountValue != null ? row.discountValue + '%' : '默认折扣' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关联 pr_id" prop="prId" width="110">
|
||||
<template #default="{ row }">{{ row.prId || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="childEditable" label="操作" width="140" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="openDetailDialog(row)" v-hasPermi="['price:set:edit']">编辑</el-button>
|
||||
<el-button link type="danger" @click="removeDetail(row)" v-hasPermi="['price:set:edit']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt16">
|
||||
<template #header>
|
||||
<div class="card-head">
|
||||
<span>绑定客户</span>
|
||||
<el-button
|
||||
v-if="childEditable"
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="Plus"
|
||||
@click="openCustomerDialog()"
|
||||
v-hasPermi="['price:set:edit']"
|
||||
>添加客户</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="set.customers || []" border empty-text="暂未绑定客户">
|
||||
<el-table-column label="绑定ID" prop="customerPricesetId" width="100" />
|
||||
<el-table-column label="客户ID" prop="customerId" width="110" />
|
||||
<el-table-column label="客户名称" prop="customerName" min-width="140" />
|
||||
<el-table-column label="优先级" width="110">
|
||||
<template #default="{ row }">{{ row.bindPriority }}{{ row.bindPriority === 1 ? '(最高)' : '' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="生效日期" min-width="200">
|
||||
<template #default="{ row }">
|
||||
{{ formatDate(row.prBegindate) }} ~ {{ row.prEnddate ? formatDate(row.prEnddate) : '长期' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="childEditable" label="操作" width="140" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="openCustomerDialog(row)" v-hasPermi="['price:set:edit']">编辑</el-button>
|
||||
<el-button link type="danger" @click="removeCustomer(row)" v-hasPermi="['price:set:edit']">解绑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="rel-hint">优先级数字越小越优先;同一客户绑定多个套餐时,按优先级 → 生效日期依次命中。</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog :title="detailTitle" v-model="detailOpen" width="560px" append-to-body>
|
||||
<el-form ref="detailFormRef" :model="detailForm" :rules="detailRules" label-width="120px">
|
||||
<el-form-item label="产品代码" prop="productCode">
|
||||
<el-input v-model="detailForm.productCode" maxlength="10" />
|
||||
</el-form-item>
|
||||
<el-form-item label="产品名称" prop="productName">
|
||||
<el-input v-model="detailForm.productName" maxlength="50" />
|
||||
</el-form-item>
|
||||
<template v-if="set.setType === 'FIXED'">
|
||||
<el-form-item label="国家代码" prop="countryCode">
|
||||
<el-input v-model="detailForm.countryCode" maxlength="2" />
|
||||
</el-form-item>
|
||||
<el-form-item label="国家名称" prop="countryName">
|
||||
<el-input v-model="detailForm.countryName" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="重量段" prop="weightLabel">
|
||||
<el-input v-model="detailForm.weightLabel" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="运费单价" prop="fvValue">
|
||||
<el-input-number v-model="detailForm.fvValue" :min="0" :precision="4" controls-position="right" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="挂号费" prop="fvRegistrationFee">
|
||||
<el-input-number v-model="detailForm.fvRegistrationFee" :min="0" :precision="2" controls-position="right" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item v-else label="产品折扣%" prop="discountValue">
|
||||
<el-input-number v-model="detailForm.discountValue" :min="1" :max="99" controls-position="right" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="关联 pr_id" prop="prId">
|
||||
<el-input v-model="detailForm.prId" placeholder="可选" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="submitDetail">确 定</el-button>
|
||||
<el-button @click="detailOpen = false">取 消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="customerTitle" v-model="customerOpen" width="520px" append-to-body>
|
||||
<el-form ref="customerFormRef" :model="customerForm" :rules="customerRules" label-width="110px">
|
||||
<el-form-item label="客户ID" prop="customerId">
|
||||
<el-input v-model="customerForm.customerId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="customerName">
|
||||
<el-input v-model="customerForm.customerName" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="优先级" prop="bindPriority">
|
||||
<el-input-number v-model="customerForm.bindPriority" :min="1" controls-position="right" />
|
||||
</el-form-item>
|
||||
<el-form-item label="生效日期" prop="prBegindate">
|
||||
<el-date-picker v-model="customerForm.prBegindate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="失效日期" prop="prEnddate">
|
||||
<el-date-picker v-model="customerForm.prEnddate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="submitCustomer">确 定</el-button>
|
||||
<el-button @click="customerOpen = false">取 消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="PriceSetDetail">
|
||||
import {
|
||||
getPriceSet, submitPriceSet, approvePriceSet, rejectPriceSet, disablePriceSet, copyPriceSet,
|
||||
addSetDetail, updateSetDetail, delSetDetail,
|
||||
addSetCustomer, updateSetCustomer, delSetCustomer
|
||||
} from '@/api/price/set'
|
||||
import { parseTime } from '@/utils/ruoyi'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const route = useRoute()
|
||||
const loading = ref(false)
|
||||
const set = ref({})
|
||||
|
||||
const detailOpen = ref(false)
|
||||
const detailTitle = ref('')
|
||||
const detailFormRef = ref()
|
||||
const detailForm = ref({})
|
||||
const detailRules = {
|
||||
productCode: [{ required: true, message: '产品代码不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const customerOpen = ref(false)
|
||||
const customerTitle = ref('')
|
||||
const customerFormRef = ref()
|
||||
const customerForm = ref({})
|
||||
const customerRules = {
|
||||
customerId: [{ required: true, message: '客户ID不能为空', trigger: 'blur' }],
|
||||
prBegindate: [{ required: true, message: '生效日期不能为空', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const childEditable = computed(() => ['D', 'R', 'E'].includes(set.value.status))
|
||||
|
||||
const statusLabel = computed(() => {
|
||||
if (set.value.expired) return '已过期'
|
||||
const map = { D: '草稿', A: '审核中', E: '生效中', R: '驳回', S: '已停用' }
|
||||
return map[set.value.status] || set.value.status || '-'
|
||||
})
|
||||
|
||||
const statusTag = computed(() => {
|
||||
if (set.value.expired) return 'danger'
|
||||
const map = { D: 'warning', A: 'primary', E: 'success', R: 'danger', S: 'info' }
|
||||
return map[set.value.status] || 'info'
|
||||
})
|
||||
|
||||
function formatDate(val) {
|
||||
return val ? parseTime(val, '{y}-{m}-{d}') : ''
|
||||
}
|
||||
|
||||
function load() {
|
||||
const id = route.query.priceSetId
|
||||
if (!id) return
|
||||
loading.value = true
|
||||
getPriceSet(id).then(res => {
|
||||
set.value = res.data || {}
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function goList() {
|
||||
const obj = { path: '/customer/set' }
|
||||
if (proxy.$tab) {
|
||||
proxy.$tab.closeOpenPage(obj)
|
||||
} else {
|
||||
proxy.$router.push(obj)
|
||||
}
|
||||
}
|
||||
|
||||
function goEdit() {
|
||||
proxy.$router.push({ path: '/customer/setCreate', query: { priceSetId: set.value.priceSetId } })
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
proxy.$modal.confirm('确认提交审核?').then(() => submitPriceSet(set.value.priceSetId)).then(() => {
|
||||
proxy.$modal.msgSuccess('已提交审核')
|
||||
load()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function onApprove() {
|
||||
proxy.$modal.confirm('确认审批生效?').then(() => approvePriceSet(set.value.priceSetId)).then(() => {
|
||||
proxy.$modal.msgSuccess('套餐已生效')
|
||||
load()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function onReject() {
|
||||
proxy.$modal.confirm('确认驳回该套餐?').then(() => rejectPriceSet(set.value.priceSetId)).then(() => {
|
||||
proxy.$modal.msgSuccess('已驳回')
|
||||
load()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function onDisable() {
|
||||
proxy.$modal.confirm('停用后不再参与计费命中,确认停用?').then(() => disablePriceSet(set.value.priceSetId)).then(() => {
|
||||
proxy.$modal.msgSuccess('已停用')
|
||||
load()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function onCopy() {
|
||||
copyPriceSet(set.value.priceSetId).then(res => {
|
||||
proxy.$modal.msgSuccess('已复制为草稿')
|
||||
proxy.$router.push({ path: '/customer/setCreate', query: { priceSetId: res.data } })
|
||||
})
|
||||
}
|
||||
|
||||
function openDetailDialog(row) {
|
||||
detailForm.value = row ? { ...row } : {
|
||||
priceSetId: set.value.priceSetId,
|
||||
productCode: undefined,
|
||||
productName: undefined,
|
||||
countryCode: undefined,
|
||||
countryName: undefined,
|
||||
weightLabel: undefined,
|
||||
fvValue: undefined,
|
||||
fvRegistrationFee: undefined,
|
||||
discountValue: undefined
|
||||
}
|
||||
detailTitle.value = row ? '编辑明细' : '添加明细'
|
||||
detailOpen.value = true
|
||||
}
|
||||
|
||||
function submitDetail() {
|
||||
proxy.$refs['detailFormRef'].validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = { ...detailForm.value, priceSetId: set.value.priceSetId }
|
||||
const req = payload.psdId ? updateSetDetail(payload) : addSetDetail(payload)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess('保存成功')
|
||||
detailOpen.value = false
|
||||
load()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function removeDetail(row) {
|
||||
proxy.$modal.confirm('确认删除该明细?').then(() => delSetDetail(row.psdId)).then(() => {
|
||||
proxy.$modal.msgSuccess('已删除')
|
||||
load()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function openCustomerDialog(row) {
|
||||
customerForm.value = row ? { ...row } : {
|
||||
priceSetId: set.value.priceSetId,
|
||||
customerId: undefined,
|
||||
customerName: '',
|
||||
bindPriority: 1,
|
||||
prBegindate: set.value.effectiveDate,
|
||||
prEnddate: set.value.expireDate
|
||||
}
|
||||
customerTitle.value = row ? '编辑客户绑定' : '添加客户'
|
||||
customerOpen.value = true
|
||||
}
|
||||
|
||||
function submitCustomer() {
|
||||
proxy.$refs['customerFormRef'].validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = {
|
||||
...customerForm.value,
|
||||
priceSetId: set.value.priceSetId,
|
||||
customerId: Number(customerForm.value.customerId)
|
||||
}
|
||||
const req = payload.customerPricesetId ? updateSetCustomer(payload) : addSetCustomer(payload)
|
||||
req.then(() => {
|
||||
proxy.$modal.msgSuccess('保存成功')
|
||||
customerOpen.value = false
|
||||
load()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function removeCustomer(row) {
|
||||
proxy.$modal.confirm('确认解绑该客户?').then(() => delSetCustomer(row.customerPricesetId)).then(() => {
|
||||
proxy.$modal.msgSuccess('已解绑')
|
||||
load()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
load()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detail-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.detail-header h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.mt16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.rel-hint {
|
||||
margin-top: 10px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
.hl {
|
||||
color: #1890ff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.reg {
|
||||
color: #d48806;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
row-key="priceSetId"
|
||||
>
|
||||
<template #toolbar="{ selection, ids }">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleCreate"
|
||||
v-hasPermi="['price:set:add']"
|
||||
>新建套餐</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="!selection.length"
|
||||
@click="handleDelete(ids)"
|
||||
v-hasPermi="['price:set:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<template #col-setType="{ row }">
|
||||
<el-tag :type="row.setType === 'FIXED' ? 'warning' : 'primary'" size="small" effect="plain">
|
||||
{{ row.setType === 'FIXED' ? 'FIXED 固定价' : 'DISCOUNT 折扣' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
||||
<template #col-dateRange="{ row }">
|
||||
{{ formatDate(row.effectiveDate) }} ~ {{ row.expireDate ? formatDate(row.expireDate) : '长期' }}
|
||||
</template>
|
||||
|
||||
<template #col-status="{ row }">
|
||||
<el-tag :type="statusTag(row)" size="small">{{ statusLabel(row) }}</el-tag>
|
||||
</template>
|
||||
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" @click="handleDetail(row)" v-hasPermi="['price:set:query']">详情</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'D' || row.status === 'R'"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleEdit(row)"
|
||||
v-hasPermi="['price:set:edit']"
|
||||
>{{ row.status === 'D' ? '继续配置' : '编辑' }}</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'D' || row.status === 'R'"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleSubmit(row)"
|
||||
v-hasPermi="['price:set:audit']"
|
||||
>提交审核</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'A'"
|
||||
link
|
||||
type="success"
|
||||
@click="handleApprove(row)"
|
||||
v-hasPermi="['price:set:audit']"
|
||||
>审批</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'E' && !row.expired"
|
||||
link
|
||||
type="warning"
|
||||
@click="handleDisable(row)"
|
||||
v-hasPermi="['price:set:audit']"
|
||||
>停用</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'S'"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleCopy(row)"
|
||||
v-hasPermi="['price:set:add']"
|
||||
>复用</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'D' || row.status === 'R'"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(row.priceSetId)"
|
||||
v-hasPermi="['price:set:remove']"
|
||||
>删除</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="PriceSet">
|
||||
import {
|
||||
listPriceSet, delPriceSet, submitPriceSet, approvePriceSet, disablePriceSet, copyPriceSet
|
||||
} from '@/api/price/set'
|
||||
import { parseTime } from '@/utils/ruoyi'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const tableRef = ref()
|
||||
const isFirstActivate = ref(true)
|
||||
|
||||
const searchColumns = [
|
||||
{
|
||||
label: '类型',
|
||||
prop: 'setType',
|
||||
component: 'select',
|
||||
options: [
|
||||
{ label: 'FIXED 固定价', value: 'FIXED' },
|
||||
{ label: 'DISCOUNT 折扣', value: 'DISCOUNT' }
|
||||
],
|
||||
placeholder: '全部类型',
|
||||
width: '160px'
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
component: 'select',
|
||||
options: [
|
||||
{ label: '草稿', value: 'D' },
|
||||
{ label: '审核中', value: 'A' },
|
||||
{ label: '生效', value: 'E' },
|
||||
{ label: '驳回', value: 'R' },
|
||||
{ label: '已停用', value: 'S' }
|
||||
],
|
||||
placeholder: '全部状态',
|
||||
width: '140px'
|
||||
},
|
||||
{ label: '关键字', prop: 'keyword', component: 'input', placeholder: '套餐ID / 名称 / 客户', width: '240px' }
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{ label: '套餐编码', prop: 'priceSetCode', width: 110 },
|
||||
{ label: '套餐名称', prop: 'priceSetName', minWidth: 160 },
|
||||
{ label: '类型', prop: 'setType', width: 140 },
|
||||
{ label: '说明', prop: 'priceSetNote', minWidth: 160 },
|
||||
{ label: '产品数', prop: 'productCount', width: 80 },
|
||||
{ label: '客户数', prop: 'customerCount', width: 80 },
|
||||
{ label: '生效日期', prop: 'dateRange', minWidth: 200 },
|
||||
{ label: '状态', prop: 'status', width: 90 },
|
||||
{ label: '操作', prop: 'action', width: 280, showInToolbar: false }
|
||||
]
|
||||
|
||||
function queryList(params) {
|
||||
return listPriceSet(params)
|
||||
}
|
||||
|
||||
function formatDate(val) {
|
||||
return val ? parseTime(val, '{y}-{m}-{d}') : ''
|
||||
}
|
||||
|
||||
function statusLabel(row) {
|
||||
if (row.expired) return '已过期'
|
||||
const map = { D: '草稿', A: '审核中', E: '生效', R: '驳回', S: '已停用' }
|
||||
return map[row.status] || row.status
|
||||
}
|
||||
|
||||
function statusTag(row) {
|
||||
if (row.expired) return 'danger'
|
||||
const map = { D: 'warning', A: 'primary', E: 'success', R: 'danger', S: 'info' }
|
||||
return map[row.status] || 'info'
|
||||
}
|
||||
|
||||
function handleCreate() {
|
||||
proxy.$router.push({ path: '/customer/setCreate' })
|
||||
}
|
||||
|
||||
function handleDetail(row) {
|
||||
proxy.$router.push({ path: '/customer/setDetail', query: { priceSetId: row.priceSetId } })
|
||||
}
|
||||
|
||||
function handleEdit(row) {
|
||||
proxy.$router.push({ path: '/customer/setCreate', query: { priceSetId: row.priceSetId } })
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
tableRef.value?.refresh()
|
||||
}
|
||||
|
||||
onActivated(() => {
|
||||
if (isFirstActivate.value) {
|
||||
isFirstActivate.value = false
|
||||
return
|
||||
}
|
||||
tableRef.value?.reload()
|
||||
})
|
||||
|
||||
function handleDelete(ids) {
|
||||
proxy.$modal.confirm('是否确认删除套餐编号为 "' + ids + '" 的数据项?').then(() => {
|
||||
return delPriceSet(ids)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
refresh()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function handleSubmit(row) {
|
||||
proxy.$modal.confirm('确认提交套餐【' + row.priceSetName + '】审核?').then(() => {
|
||||
return submitPriceSet(row.priceSetId)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('已提交审核')
|
||||
refresh()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function handleApprove(row) {
|
||||
proxy.$modal.confirm('确认将套餐【' + row.priceSetName + '】审批生效?').then(() => {
|
||||
return approvePriceSet(row.priceSetId)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('套餐已生效')
|
||||
refresh()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function handleDisable(row) {
|
||||
proxy.$modal.confirm('停用后该套餐不再参与计费命中,确认停用【' + row.priceSetName + '】?').then(() => {
|
||||
return disablePriceSet(row.priceSetId)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('已停用')
|
||||
refresh()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function handleCopy(row) {
|
||||
copyPriceSet(row.priceSetId).then(res => {
|
||||
proxy.$modal.msgSuccess('已复制为草稿')
|
||||
proxy.$router.push({ path: '/customer/setCreate', query: { priceSetId: res.data } })
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hint-card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.hint-body {
|
||||
font-size: 13px;
|
||||
color: #595959;
|
||||
line-height: 1.7;
|
||||
padding: 8px 12px;
|
||||
background: #f6ffed;
|
||||
border: 1px solid #b7eb8f;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.hint-body b {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
</style>
|
||||
@@ -120,7 +120,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Data">
|
||||
<script setup name="DictData">
|
||||
import useDictStore from '@/store/modules/dict'
|
||||
import { getType } from "@/api/system/dict/type"
|
||||
import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data"
|
||||
|
||||
Reference in New Issue
Block a user