产品管理

master
lzq 2026-02-03 11:29:52 +08:00
parent f502aca92b
commit 26f63b8af9
7 changed files with 171 additions and 143 deletions

View File

@ -23,7 +23,11 @@ export interface FormPanelType<T extends object> {
title: string
detailsLoader: (id?: string) => Promise<T | undefined | null | void>
doSubmit: (data: T) => Promise<boolean | undefined | null | void>
rules: RuleType<T>
rules: RuleType<T> | ((formData: T) => RuleType<T>)
watchForm: {
formItems: string[] | '_all_' | '_none_'
callback?: (formData: T) => void
}
labelWidth: string
width: string
modal: boolean
@ -70,7 +74,33 @@ const component = defineComponent(
showDialog.value = true
}
expose({open})
const formRules = ref(typeof props.rules === 'function' ? props.rules(formData.$clone() as T) : props.rules)
if (props.watchForm.formItems === '_all_') {
watch(formData,
() => {
if (typeof props.rules === 'function') {
formRules.value = props.rules(formData.$clone() as T)
}
props.watchForm.callback?.(formData.$clone() as T)
},
{deep: true})
} else if (Array.isArray(props.watchForm.formItems)) {
props.watchForm.formItems.forEach(val => {
watch(
() => val.split('.').reduce((current, key) => {
// @ts-ignore
return current?.[key]
}, formData as T),
() => {
if (typeof props.rules === 'function') {
formRules.value = props.rules(formData.$clone() as T)
}
props.watchForm.callback?.(formData.$clone() as T)
},
{deep: true},
)
})
}
return () => (<ADialog
show={showDialog.value}
onUpdate:show={val => showDialog.value = val}
@ -86,7 +116,7 @@ const component = defineComponent(
ref="formRef"
label-width={props.labelWidth}
model={formData}
rules={props.rules}
rules={formRules.value}
class={styles.formPanel}>
{
slots.default?.(formData as T)
@ -104,7 +134,7 @@ const component = defineComponent(
},
{
name: 'AFormPanel',
props: [ 'title', 'detailsLoader', 'doSubmit', 'rules', 'labelWidth', 'width', 'modal', 'appendToBody' ],
props: [ 'title', 'detailsLoader', 'doSubmit', 'rules', 'watchForm', 'labelWidth', 'width', 'modal', 'appendToBody' ],
})
export interface AFormPanelInstance extends InstanceType<typeof component> {
@ -112,7 +142,7 @@ export interface AFormPanelInstance extends InstanceType<typeof component> {
}
export function buildFormPanelProps<T extends object>(props: Partial<Exclude<FormPanelType<T>, 'title' | 'detailsLoader' | 'doSubmit'>> & Pick<FormPanelType<T>, 'detailsLoader' | 'doSubmit'>) {
export function buildFormPanelProps<T extends object>(props: Partial<Exclude<FormPanelType<T>, 'title' | 'detailsLoader' | 'doSubmit' | 'watchForm'>> & Pick<FormPanelType<T>, 'detailsLoader' | 'doSubmit'> & Partial<Pick<FormPanelType<T>, 'watchForm'>>) {
if (props.labelWidth == null) {
props.labelWidth = '90px'
}
@ -128,11 +158,21 @@ export function buildFormPanelProps<T extends object>(props: Partial<Exclude<For
if (props.rules == null) {
props.rules = {} as RuleType<T>
}
if (props.watchForm == null) {
props.watchForm = {
formItems: '_none_',
}
}
if (props.watchForm.formItems == null) {
props.watchForm.formItems = '_none_'
}
return reactive({
title: props.title!,
detailsLoader: props.detailsLoader!,
doSubmit: props.doSubmit!,
rules: props.rules,
watchForm: props.watchForm!,
labelWidth: props.labelWidth!,
width: props.width!,
modal: props.modal!,

View File

@ -2,23 +2,9 @@
<ATablePage
ref="tablePage"
v-bind="tablePageProps">
<template #highFormItem="formData">
<ElFormItem label="编码">
<ElInput v-model="formData.sn" clearable placeholder="编码" @clear="research"/>
</ElFormItem>
<ElFormItem label="分类名称">
<ElInput v-model="formData.categoryName" clearable placeholder="分类名称" @clear="research"/>
</ElFormItem>
<ElFormItem label="创建时间">
<ADtPicker v-model="formData.createTimes" :change-handler="research"/>
</ElFormItem>
</template>
<template #simpleFormItem="formData">
<ElFormItem>
<ElInput v-model="formData.sn" clearable placeholder="编码" @clear="research"/>
</ElFormItem>
<ElFormItem>
<ElInput v-model="formData.categoryName" clearable placeholder="分类名称" @clear="research"/>
<ElInput v-model="formData.keywords" placeholder="分类名称/编码"/>
</ElFormItem>
</template>
<template #columns>
@ -41,7 +27,6 @@
import GoodsCategoryApi from '@/pages/gds/goods-category/goods-category-api.ts'
import GoodsCategoryForm from '@/pages/gds/goods-category/GoodsCategoryForm.vue'
import AppApi from '@/common/app/app-api.ts'
import ADtPicker from '@/components/a-dt-picker/ADtPicker.vue'
import { bizType } from '@/pages/gds/goods-category/constants.ts'
import ATablePage, {
type ATablePageInstance,
@ -61,12 +46,10 @@ function research() {
const tablePageProps = buildTablePageProps<GoodsCategoryTypes.SearchGoodsCategoryParam, GoodsCategoryTypes.SearchGoodsCategoryResult>({
pageLayout: {
searchFormHeight: '120px',
dataListHeight: 1,
enableHighForm: false,
},
searchForm: {
defaultData: {createTimes: [ undefined, undefined ], bizType: props.defaultBizType},
defaultData: {bizType: props.defaultBizType},
highForm: {
contentWidth: 320,
},
@ -76,9 +59,7 @@ const tablePageProps = buildTablePageProps<GoodsCategoryTypes.SearchGoodsCategor
paging(param) {
return GoodsCategoryApi.paging({
bizType: param.bizType,
categoryName: param.categoryName,
createTimeStart: param.createTimes?.[0],
createTimeEnd: param.createTimes?.[1],
keywords: param.keywords,
})
},
},

View File

@ -56,6 +56,7 @@ const formPanelProps = buildFormPanelProps<GoodsCategoryTypes.SearchGoodsCategor
status.value = 'add'
return Promise.resolve({
bizType: props.defaultBizType,
sort: 0,
} as GoodsCategoryTypes.SearchGoodsCategoryResult)
} else {
status.value = 'modify'
@ -68,19 +69,33 @@ const formPanelProps = buildFormPanelProps<GoodsCategoryTypes.SearchGoodsCategor
},
doSubmit(data) {
if (status.value === 'add') {
return GoodsCategoryApi.add(data)
return GoodsCategoryApi.add({
bizType: data.bizType,
categoryName: data.categoryName,
picture: data.picture,
sort: data.sort,
memo: data.memo,
})
.then(props.research)
} else {
return GoodsCategoryApi.modify(data)
return GoodsCategoryApi.modify({
id: data.id,
bizType: data.bizType,
categoryName: data.categoryName,
picture: data.picture,
sort: data.sort,
memo: data.memo,
})
.then(props.research)
}
},
rules: {
bizType: [ {required: true, message: '请填写业务类型', trigger: 'blur'} ],
sn: [ {required: true, message: '请填写国标码', trigger: 'blur'} ],
categoryName: [ {required: true, message: '请填写分类名称', trigger: 'blur'} ],
rules: () => {
return {
picture: [ {required: true, message: '请上传图片', trigger: 'blur'} ],
sn: [ {required: props.defaultBizType === bizType.HuiShouPin, message: '请填写国标码', trigger: 'blur'} ],
categoryName: [ {required: true, message: '请填写分类名称', trigger: 'blur'} ],
sort: [ {required: true, message: '请填写排序', trigger: 'blur'} ],
}
},
})
@ -108,11 +123,14 @@ defineExpose({
.el-form-item__content {
justify-content center
}
.el-form-item__error {
width: 100%;
text-align: center;
}
}
}
}
}
:deep(.el-form-item) {
&:last-child:nth-child(2n+1) {

View File

@ -5,12 +5,7 @@ declare global {
interface SearchGoodsCategoryParam extends G.PageParam {
// 业务类型字典代码biz_typeZaiShengPin-->再生品、HuiShouPin-->回收品、QiTa-->其他
bizType?: string
// 分类名称
categoryName?: string
// 创建时间范围
createTimes?: [ string | undefined, string | undefined ]
createTimeStart?: string
createTimeEnd?: string
keywords?: string
}
interface SearchGoodsCategoryResult {
@ -33,8 +28,6 @@ declare global {
}
interface AddGoodsCategoryParam {
// Id
id?: string
// 业务类型字典代码biz_typeZaiShengPin-->再生品、HuiShouPin-->回收品、QiTa-->其他
bizType?: string
// 分类名称
@ -43,16 +36,7 @@ declare global {
picture?: string
// 排序
sort?: number
// 创建人 Idsys_user.id
creatorId?: string
// 修改人 Idsys_user.id
modifierId?: string
// 创建时间
createTime?: string
// 修改时间
modifyTime?: string
// 是否删除; 0-->未删除、1-->已删除
deleted?: boolean
memo?: string
}
interface ModifyGoodsCategoryParam {
@ -66,16 +50,7 @@ declare global {
picture?: string
// 排序
sort?: number
// 创建人 Idsys_user.id
creatorId?: string
// 修改人 Idsys_user.id
modifierId?: string
// 创建时间
createTime?: string
// 修改时间
modifyTime?: string
// 是否删除; 0-->未删除、1-->已删除
deleted?: boolean
memo?: string
}
}
}

View File

@ -2,7 +2,7 @@
<ATablePage
ref="tablePage"
v-bind="tablePageProps">
<template #highFormItem="formData">
<!-- <template #highFormItem="formData">
<ElFormItem label="产品分类">
<GoodsCategoryDropTable v-model="formData.goodsCategoryId" :default-biz-type="defaultBizType"/>
</ElFormItem>
@ -15,13 +15,10 @@
<ElFormItem label="创建时间">
<ADtPicker v-model="formData.createTimes" :change-handler="research"/>
</ElFormItem>
</template>
</template> -->
<template #simpleFormItem="formData">
<ElFormItem>
<ElInput v-model="formData.sn" placeholder="产品编码"/>
</ElFormItem>
<ElFormItem>
<ElInput v-model="formData.goodsName" placeholder="产品名称"/>
<ElInput v-model="formData.keywords" placeholder="产品名称/编码"/>
</ElFormItem>
</template>
<template #columns>
@ -50,8 +47,6 @@
import GoodsApi from '@/pages/gds/goods/goods-api.ts'
import GoodsForm from '@/pages/gds/goods/GoodsForm.vue'
import AppApi from '@/common/app/app-api.ts'
import ADtPicker from '@/components/a-dt-picker/ADtPicker.vue'
import GoodsCategoryDropTable from '@/pages/gds/goods-category/GoodsCategoryDropTable.vue'
import ATablePage, {
type ATablePageInstance,
buildTablePageProps,
@ -72,24 +67,15 @@ function research() {
const tablePageProps = buildTablePageProps({
pageLayout: {
searchFormHeight: '120px',
dataListHeight: 1,
enableHighForm: false,
},
searchForm: {
defaultData: {createTimes: [ undefined, undefined ], bizType: props.defaultBizType},
defaultData: {bizType: props.defaultBizType},
highForm: {
contentWidth: 320,
},
simpleForm: {
colCount: 2,
},
paging(param: GoodsTypes.SearchGoodsParam) {
return GoodsApi.paging({
...param,
createTimeStart: param.createTimes[0],
createTimeEnd: param.createTimes[1],
})
return GoodsApi.paging(param)
},
},
toolBar: {

View File

@ -9,7 +9,7 @@
ref="uploader"
v-model:file="formData.picture"/>
</ElFormItem>
<ElFormItem v-if="status !== 'add'" label="产品编码" prop="sn">
<ElFormItem v-if="status === 'modify'" label="产品编码" prop="sn">
<ElInput v-model="formData.sn" placeholder="产品编码" readonly/>
</ElFormItem>
<ElFormItem label="产品分类" prop="goodsCategoryId">
@ -113,6 +113,12 @@ const formPanelProps = buildFormPanelProps<GoodsTypes.SearchGoodsResult>({
expenseStrategy: expenseStrategy.MianFei,
unit: unit.Dun,
canuse: true,
sort: 0,
taxRate: 0,
unitPrice: 0,
initialPrice: 0,
initialQuantity: 0,
everyQuantity: 0,
})
} else {
@ -127,22 +133,61 @@ const formPanelProps = buildFormPanelProps<GoodsTypes.SearchGoodsResult>({
},
doSubmit(data) {
if (status.value === 'add') {
return GoodsApi.add(data)
return GoodsApi.add({
goodsCategoryId: data.goodsCategoryId,
goodsName: data.goodsName,
specParams: data.specParams,
picture: data.picture,
unit: data.unit,
sort: data.sort,
canuse: data.canuse,
memo: data.memo,
expenseStrategy: data.expenseStrategy,
taxRate: data.taxRate,
initialPrice: data.initialPrice,
initialQuantity: data.initialQuantity,
everyQuantity: data.everyQuantity,
unitPrice: data.unitPrice,
})
.then(props.research)
} else {
return GoodsApi.modify(data)
return GoodsApi.modify({
id: data.id,
goodsCategoryId: data.goodsCategoryId,
goodsName: data.goodsName,
specParams: data.specParams,
picture: data.picture,
unit: data.unit,
sort: data.sort,
canuse: data.canuse,
memo: data.memo,
expenseStrategy: data.expenseStrategy,
taxRate: data.taxRate,
unitPrice: data.unitPrice,
initialPrice: data.initialPrice,
initialQuantity: data.initialQuantity,
everyQuantity: data.everyQuantity,
})
.then(props.research)
}
},
rules: {
goodsCategoryId: [ {required: true, message: '请填写产品类型', trigger: 'blur'} ],
sn: [ {required: true, message: '请填写产品编码', trigger: 'blur'} ],
watchForm: {
formItems: [ 'expenseStrategy' ],
},
rules: formData => {
return {
goodsCategoryId: [ {required: true, message: '请选择产品类型', trigger: 'blur'} ],
goodsName: [ {required: true, message: '请填写产品名称', trigger: 'blur'} ],
specParams: [ {required: true, message: '请填写规格', trigger: 'blur'} ],
picture: [ {required: true, message: '请填写图片', trigger: 'blur'} ],
unit: [ {required: true, message: '请填写计量单位', trigger: 'blur'} ],
picture: [ {required: true, message: '请上传图片', trigger: 'blur'} ],
unit: [ {required: true, message: '请选择计量单位', trigger: 'blur'} ],
sort: [ {required: true, message: '请填写排序', trigger: 'blur'} ],
initialPrice: [ {required: true, message: '请填写排序', trigger: 'blur'} ],
expenseStrategy: [ {required: true, message: '请填选择计费策略', trigger: 'blur'} ],
taxRate: [ {required: true, message: '请填填写税率', trigger: 'blur'} ],
unitPrice: [ {required: formData.expenseStrategy !== expenseStrategy.MianFei, message: `请填填写${formData.expenseStrategy === expenseStrategy.TanXing ? '每档单价' : (formData.expenseStrategy === expenseStrategy.DanJia ? '单价' : '价格')}`, trigger: 'blur'} ],
initialPrice: [ {required: formData.expenseStrategy === expenseStrategy.TanXing, message: '请填写起步价', trigger: 'blur'} ],
initialQuantity: [ {required: formData.expenseStrategy === expenseStrategy.TanXing, message: '请填写起步量', trigger: 'blur'} ],
everyQuantity: [ {required: formData.expenseStrategy === expenseStrategy.TanXing, message: '请填写每档的量', trigger: 'blur'} ],
}
},
})
@ -168,8 +213,12 @@ defineExpose({
.el-form-item__content {
justify-content center
}
}
.el-form-item__error {
width: 100%;
text-align: center;
}
}
&:last-child:nth-child(2n) {
grid-column: span 2;

View File

@ -3,15 +3,8 @@ export {}
declare global {
namespace GoodsTypes {
interface SearchGoodsParam extends G.PageParam {
// 产品类型 Id
goodsCategoryId?: string
// 产品编码
sn?: string
// 产品名称
goodsName?: string
createTimes: [ string | undefined, string | undefined ]
createTimeStart?: string
createTimeEnd?: string
bizType?: string
keywords?: string
}
interface SearchGoodsResult {
@ -66,12 +59,8 @@ declare global {
}
interface AddGoodsParam {
// Id
id?: string
// 产品类型 Id
goodsCategoryId?: string
// 产品编码
sn?: string
// 产品名称
goodsName?: string
// 规格
@ -80,28 +69,24 @@ declare global {
picture?: string
// 计量单位字典代码unit
unit?: string
// 是否为成品0-->否、1-->是
fg?: boolean
// 是否为半成品0-->否、1-->是
sfg?: boolean
// 是否为原料0-->否、1-->是
rg?: boolean
// 排序
sort?: number
// 是否可用0-->否、1-->是
canuse?: boolean
// 备注
memo?: string
// 创建人 Idsys_user.id
creatorId?: string
// 修改人 Idsys_user.id
modifierId?: string
// 创建时间
createTime?: string
// 修改时间
modifyTime?: string
// 是否删除; 0-->未删除、1-->已删除
deleted?: boolean
// 计费策略
expenseStrategy?: string
// 税率
taxRate?: number
// 单价
unitPrice?: number
// 起步价
initialPrice?: number
// 起步量
initialQuantity?: number
// 每档的量
everyQuantity?: number
}
interface ModifyGoodsParam {
@ -109,8 +94,6 @@ declare global {
id?: string
// 产品类型 Id
goodsCategoryId?: string
// 产品编码
sn?: string
// 产品名称
goodsName?: string
// 规格
@ -119,28 +102,24 @@ declare global {
picture?: string
// 计量单位字典代码unit
unit?: string
// 是否为成品0-->否、1-->是
fg?: boolean
// 是否为半成品0-->否、1-->是
sfg?: boolean
// 是否为原料0-->否、1-->是
rg?: boolean
// 排序
sort?: number
// 是否可用0-->否、1-->是
canuse?: boolean
// 备注
memo?: string
// 创建人 Idsys_user.id
creatorId?: string
// 修改人 Idsys_user.id
modifierId?: string
// 创建时间
createTime?: string
// 修改时间
modifyTime?: string
// 是否删除; 0-->未删除、1-->已删除
deleted?: boolean
// 计费策略
expenseStrategy?: string
// 税率
taxRate?: number
// 单价
unitPrice?: number
// 起步价
initialPrice?: number
// 起步量
initialQuantity?: number
// 每档的量
everyQuantity?: number
}
}
}