fetch(modify):修改bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m45s

This commit is contained in:
sexygoat
2026-02-05 17:22:41 +08:00
parent d97dc5f007
commit b94c043a56
24 changed files with 2734 additions and 446 deletions

View File

@@ -5,7 +5,8 @@
<ArtSearchBar
v-model:filter="searchForm"
:items="searchFormItems"
:show-expand="false"
:show-expand="true"
label-width="85"
@reset="handleReset"
@search="handleSearch"
></ArtSearchBar>
@@ -40,70 +41,15 @@
</template>
</ArtTable>
<!-- 修改成本价对话框 -->
<ElDialog
v-model="costPriceDialogVisible"
title="修改成本价"
width="500px"
:close-on-click-modal="false"
@closed="handleCostPriceDialogClosed"
>
<ElForm
ref="costPriceFormRef"
:model="costPriceForm"
:rules="costPriceRules"
label-width="120px"
>
<ElFormItem label="套餐名称">
<ElInput v-model="costPriceForm.package_name" disabled />
</ElFormItem>
<ElFormItem label="店铺名称">
<ElInput v-model="costPriceForm.shop_name" disabled />
</ElFormItem>
<ElFormItem label="原成本价(元)">
<ElInputNumber
v-model="costPriceForm.old_cost_price"
disabled
:precision="2"
:controls="false"
style="width: 100%"
/>
</ElFormItem>
<ElFormItem label="新成本价(元)" prop="cost_price">
<ElInputNumber
v-model="costPriceForm.cost_price"
:min="0"
:precision="2"
:step="0.01"
:controls="false"
style="width: 100%"
placeholder="请输入新成本价"
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="costPriceDialogVisible = false">取消</ElButton>
<ElButton
type="primary"
@click="handleCostPriceSubmit(costPriceFormRef)"
:loading="costPriceSubmitLoading"
>
提交
</ElButton>
</div>
</template>
</ElDialog>
<!-- 新增/编辑对话框 -->
<ElDialog
v-model="dialogVisible"
:title="dialogType === 'add' ? '新增分配' : '编辑分配'"
width="600px"
width="30%"
:close-on-click-modal="false"
@closed="handleDialogClosed"
>
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
<ElForm ref="formRef" :model="form" :rules="rules" label-width="90px">
<ElFormItem label="选择套餐" prop="package_id" v-if="dialogType === 'add'">
<ElSelect
v-model="form.package_id"
@@ -125,23 +71,18 @@
</ElSelect>
</ElFormItem>
<ElFormItem label="选择店铺" prop="shop_id" v-if="dialogType === 'add'">
<ElSelect
<ElTreeSelect
v-model="form.shop_id"
:data="shopTreeData"
:props="{ label: 'shop_name', value: 'id', children: 'children' }"
placeholder="请选择店铺"
style="width: 100%"
filterable
remote
:remote-method="searchShop"
:loading="shopLoading"
clearable
>
<ElOption
v-for="shop in shopOptions"
:key="shop.id"
:label="shop.shop_name"
:value="shop.id"
/>
</ElSelect>
:loading="shopLoading"
check-strictly
:render-after-expand="false"
/>
</ElFormItem>
<ElFormItem label="成本价(元)" prop="cost_price">
<ElInputNumber
@@ -171,7 +112,8 @@
<script setup lang="ts">
import { h } from 'vue'
import { ShopPackageAllocationService, PackageManageService, ShopService } from '@/api/modules'
import { useRouter } from 'vue-router'
import { ShopPackageAllocationService, PackageManageService, ShopService, ShopSeriesAllocationService } from '@/api/modules'
import { ElMessage, ElMessageBox, ElSwitch } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type { ShopPackageAllocationResponse, PackageResponse, ShopResponse } from '@/types/api'
@@ -180,6 +122,7 @@
import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime } from '@/utils/business/format'
import { RoutesAlias } from '@/router/routesAlias'
import {
CommonStatus,
getStatusText,
@@ -190,26 +133,29 @@
defineOptions({ name: 'PackageAssign' })
const { hasAuth } = useAuth()
const router = useRouter()
const dialogVisible = ref(false)
const costPriceDialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
const costPriceSubmitLoading = ref(false)
const packageLoading = ref(false)
const shopLoading = ref(false)
const tableRef = ref()
const formRef = ref<FormInstance>()
const costPriceFormRef = ref<FormInstance>()
const packageOptions = ref<PackageResponse[]>([])
const shopOptions = ref<ShopResponse[]>([])
const shopTreeData = ref<ShopResponse[]>([])
const searchPackageOptions = ref<PackageResponse[]>([])
const searchShopOptions = ref<ShopResponse[]>([])
const searchAllocatorShopOptions = ref<ShopResponse[]>([])
const searchSeriesAllocationOptions = ref<any[]>([])
// 搜索表单初始值
const initialSearchState = {
shop_id: undefined as number | undefined,
package_id: undefined as number | undefined,
series_allocation_id: undefined as number | undefined,
allocator_shop_id: undefined as number | undefined,
status: undefined as number | undefined
}
@@ -219,7 +165,7 @@
// 搜索表单配置
const searchFormItems = computed<SearchFormItem[]>(() => [
{
label: '店铺',
label: '被分配店铺',
prop: 'shop_id',
type: 'select',
config: {
@@ -254,6 +200,44 @@
value: p.id
}))
},
{
label: '系列分配',
prop: 'series_allocation_id',
type: 'select',
config: {
clearable: true,
filterable: true,
remote: true,
remoteMethod: handleSearchSeriesAllocation,
loading: loading.value,
placeholder: '请选择或搜索系列分配'
},
options: () =>
searchSeriesAllocationOptions.value.map((s) => ({
label: `${s.series_name} - ${s.shop_name}`,
value: s.id
}))
},
{
label: '分配者店铺',
prop: 'allocator_shop_id',
type: 'select',
config: {
clearable: true,
filterable: true,
remote: true,
remoteMethod: handleSearchAllocatorShop,
loading: shopLoading.value,
placeholder: '请选择或搜索分配者店铺'
},
options: () => [
{ label: '平台', value: 0 },
...searchAllocatorShopOptions.value.map((s) => ({
label: s.shop_name,
value: s.id
}))
]
},
{
label: '状态',
prop: 'status',
@@ -278,10 +262,11 @@
// 列配置
const columnOptions = [
{ label: 'ID', prop: 'id' },
{ label: '套餐编码', prop: 'package_code' },
{ label: '套餐名称', prop: 'package_name' },
{ label: '店铺名称', prop: 'shop_name' },
{ label: '系列名称', prop: 'series_name' },
{ label: '被分配店铺', prop: 'shop_name' },
{ label: '分配者', prop: 'allocator_shop_name' },
{ label: '成本价', prop: 'cost_price' },
{ label: '状态', prop: 'status' },
{ label: '创建时间', prop: 'created_at' },
@@ -320,45 +305,47 @@
package_base_price: 0 // 存储选中套餐的成本价,用于验证
})
// 成本价表单验证规则
const costPriceRules = reactive<FormRules>({
cost_price: [{ required: true, message: '请输入新成本价', trigger: 'blur' }]
})
// 成本价表单数据
const costPriceForm = reactive<any>({
id: 0,
package_name: '',
shop_name: '',
old_cost_price: 0,
cost_price: 0
})
const allocationList = ref<ShopPackageAllocationResponse[]>([])
const dialogType = ref('add')
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'id',
label: 'ID',
width: 80
},
{
prop: 'package_code',
label: '套餐编码',
minWidth: 150
minWidth: 200,
showOverflowTooltip: true
},
{
prop: 'package_name',
label: '套餐名称',
minWidth: 180
},
{
prop: 'series_name',
label: '系列名称',
minWidth: 150
},
{
prop: 'shop_name',
label: '店铺名称',
label: '被分配店铺',
minWidth: 180
},
{
prop: 'allocator_shop_name',
label: '分配者',
formatter: (row: ShopPackageAllocationResponse) => {
// 如果是平台分配(allocator_shop_id为0),显示"平台"标签
if (row.allocator_shop_id === 0) {
return h(
'span',
{ style: 'color: #409eff; font-weight: bold' },
row.allocator_shop_name || '平台'
)
}
return row.allocator_shop_name
}
},
{
prop: 'cost_price',
label: '成本价',
@@ -399,24 +386,22 @@
{
prop: 'operation',
label: '操作',
width: 230,
width: 200,
fixed: 'right',
formatter: (row: ShopPackageAllocationResponse) => {
const buttons = []
if (hasAuth('package_assign:update_cost')) {
buttons.push(
h(ArtButtonTable, {
text: '修改成本价',
onClick: () => showCostPriceDialog(row)
})
)
}
buttons.push(
h(ArtButtonTable, {
type:"view",
onClick: () => handleViewDetail(row)
})
)
if (hasAuth('package_assign:edit')) {
buttons.push(
h(ArtButtonTable, {
type: 'edit',
type:"edit",
onClick: () => showDialog('edit', row)
})
)
@@ -425,7 +410,7 @@
if (hasAuth('package_assign:delete')) {
buttons.push(
h(ArtButtonTable, {
type: 'delete',
type:"delete",
onClick: () => deleteAllocation(row)
})
)
@@ -436,11 +421,40 @@
}
])
// 构建树形结构数据
const buildTreeData = (items: ShopResponse[]) => {
const map = new Map<number, ShopResponse & { children?: ShopResponse[] }>()
const tree: ShopResponse[] = []
// 先将所有项放入 map
items.forEach((item) => {
map.set(item.id, { ...item, children: [] })
})
// 构建树形结构
items.forEach((item) => {
const node = map.get(item.id)!
if (item.parent_id && map.has(item.parent_id)) {
// 有父节点,添加到父节点的 children 中
const parent = map.get(item.parent_id)!
if (!parent.children) parent.children = []
parent.children.push(node)
} else {
// 没有父节点或父节点不存在,作为根节点
tree.push(node)
}
})
return tree
}
onMounted(() => {
loadPackageOptions()
loadShopOptions()
loadSearchPackageOptions()
loadSearchShopOptions()
loadSearchAllocatorShopOptions()
loadSearchSeriesAllocationOptions()
getTableData()
})
@@ -466,20 +480,19 @@
}
}
// 加载店铺选项(用于新增对话框,默认加载10条
const loadShopOptions = async (shopName?: string) => {
// 加载店铺选项(用于新增对话框,加载所有店铺并构建树形结构
const loadShopOptions = async () => {
shopLoading.value = true
try {
const params: any = {
// 加载所有店铺,不分页
const res = await ShopService.getShops({
page: 1,
page_size: 10
}
if (shopName) {
params.shop_name = shopName
}
const res = await ShopService.getShops(params)
page_size: 10000 // 使用较大的值获取所有店铺
})
if (res.code === 0) {
shopOptions.value = res.data.items || []
// 构建树形结构数据
shopTreeData.value = buildTreeData(shopOptions.value)
}
} catch (error) {
console.error('加载店铺选项失败:', error)
@@ -524,15 +537,6 @@
}
}
// 搜索店铺(用于新增对话框)
const searchShop = (query: string) => {
if (query) {
loadShopOptions(query)
} else {
loadShopOptions()
}
}
// 搜索套餐(用于搜索栏)
const handleSearchPackage = async (query: string) => {
if (!query) {
@@ -573,6 +577,73 @@
}
}
// 加载搜索栏分配者店铺选项(默认加载10条)
const loadSearchAllocatorShopOptions = async () => {
try {
const res = await ShopService.getShops({ page: 1, page_size: 10 })
if (res.code === 0) {
searchAllocatorShopOptions.value = res.data.items || []
}
} catch (error) {
console.error('加载搜索栏分配者店铺选项失败:', error)
}
}
// 搜索分配者店铺(用于搜索栏)
const handleSearchAllocatorShop = async (query: string) => {
if (!query) {
loadSearchAllocatorShopOptions()
return
}
try {
const res = await ShopService.getShops({
page: 1,
page_size: 10,
shop_name: query
})
if (res.code === 0) {
searchAllocatorShopOptions.value = res.data.items || []
}
} catch (error) {
console.error('搜索分配者店铺失败:', error)
}
}
// 加载搜索栏系列分配选项(默认加载10条)
const loadSearchSeriesAllocationOptions = async () => {
try {
const res = await ShopSeriesAllocationService.getShopSeriesAllocations({
page: 1,
page_size: 10
})
if (res.code === 0) {
searchSeriesAllocationOptions.value = res.data.items || []
}
} catch (error) {
console.error('加载搜索栏系列分配选项失败:', error)
}
}
// 搜索系列分配(用于搜索栏)
const handleSearchSeriesAllocation = async (query: string) => {
if (!query) {
loadSearchSeriesAllocationOptions()
return
}
try {
const res = await ShopSeriesAllocationService.getShopSeriesAllocations({
page: 1,
page_size: 10,
series_name: query
})
if (res.code === 0) {
searchSeriesAllocationOptions.value = res.data.items || []
}
} catch (error) {
console.error('搜索系列分配失败:', error)
}
}
// 获取分配列表
const getTableData = async () => {
loading.value = true
@@ -582,6 +653,8 @@
page_size: pagination.page_size,
shop_id: searchForm.shop_id || undefined,
package_id: searchForm.package_id || undefined,
series_allocation_id: searchForm.series_allocation_id || undefined,
allocator_shop_id: searchForm.allocator_shop_id || undefined,
status: searchForm.status || undefined
}
const res = await ShopPackageAllocationService.getShopPackageAllocations(params)
@@ -657,9 +730,9 @@
// 从套餐选项中找到选中的套餐
const selectedPackage = packageOptions.value.find((pkg) => pkg.id === packageId)
if (selectedPackage) {
// 将套餐的价(分)转换为元显示
form.cost_price = selectedPackage.price / 100
form.package_base_price = selectedPackage.price // 保持原始值(分)用于验证
// 将套餐的成本价(分)转换为元显示
form.cost_price = selectedPackage.cost_price / 100
form.package_base_price = selectedPackage.cost_price // 保持原始值(分)用于验证
}
} else {
// 清空时重置成本价
@@ -744,60 +817,6 @@
})
}
// 显示修改成本价对话框
const showCostPriceDialog = (row: ShopPackageAllocationResponse) => {
costPriceDialogVisible.value = true
costPriceForm.id = row.id
costPriceForm.package_name = row.package_name
costPriceForm.shop_name = row.shop_name
costPriceForm.old_cost_price = row.cost_price / 100 // 分转换为元显示
costPriceForm.cost_price = row.cost_price / 100 // 分转换为元显示
// 重置表单验证状态
nextTick(() => {
costPriceFormRef.value?.clearValidate()
})
}
// 处理成本价弹窗关闭事件
const handleCostPriceDialogClosed = () => {
// 清除表单验证状态
costPriceFormRef.value?.clearValidate()
// 重置表单数据
costPriceForm.id = 0
costPriceForm.package_name = ''
costPriceForm.shop_name = ''
costPriceForm.old_cost_price = 0
costPriceForm.cost_price = 0
}
// 提交成本价修改
const handleCostPriceSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
costPriceSubmitLoading.value = true
try {
// 将元转换为分提交给后端
const costPriceInCents = Math.round(costPriceForm.cost_price * 100)
await ShopPackageAllocationService.updateShopPackageAllocationCostPrice(
costPriceForm.id,
costPriceInCents
)
ElMessage.success('修改成本价成功')
costPriceDialogVisible.value = false
await getTableData()
} catch (error) {
console.error(error)
} finally {
costPriceSubmitLoading.value = false
}
}
})
}
// 状态切换
const handleStatusChange = async (
row: ShopPackageAllocationResponse,
@@ -814,6 +833,11 @@
console.error(error)
}
}
// 查看详情
const handleViewDetail = (row: ShopPackageAllocationResponse) => {
router.push(`${RoutesAlias.PackageAssignDetail}/${row.id}`)
}
</script>
<style lang="scss" scoped>