产品管理

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 title: string
detailsLoader: (id?: string) => Promise<T | undefined | null | void> detailsLoader: (id?: string) => Promise<T | undefined | null | void>
doSubmit: (data: T) => Promise<boolean | 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 labelWidth: string
width: string width: string
modal: boolean modal: boolean
@ -70,7 +74,33 @@ const component = defineComponent(
showDialog.value = true showDialog.value = true
} }
expose({open}) 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 return () => (<ADialog
show={showDialog.value} show={showDialog.value}
onUpdate:show={val => showDialog.value = val} onUpdate:show={val => showDialog.value = val}
@ -86,7 +116,7 @@ const component = defineComponent(
ref="formRef" ref="formRef"
label-width={props.labelWidth} label-width={props.labelWidth}
model={formData} model={formData}
rules={props.rules} rules={formRules.value}
class={styles.formPanel}> class={styles.formPanel}>
{ {
slots.default?.(formData as T) slots.default?.(formData as T)
@ -104,7 +134,7 @@ const component = defineComponent(
}, },
{ {
name: 'AFormPanel', 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> { 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) { if (props.labelWidth == null) {
props.labelWidth = '90px' props.labelWidth = '90px'
} }
@ -128,11 +158,21 @@ export function buildFormPanelProps<T extends object>(props: Partial<Exclude<For
if (props.rules == null) { if (props.rules == null) {
props.rules = {} as RuleType<T> props.rules = {} as RuleType<T>
} }
if (props.watchForm == null) {
props.watchForm = {
formItems: '_none_',
}
}
if (props.watchForm.formItems == null) {
props.watchForm.formItems = '_none_'
}
return reactive({ return reactive({
title: props.title!, title: props.title!,
detailsLoader: props.detailsLoader!, detailsLoader: props.detailsLoader!,
doSubmit: props.doSubmit!, doSubmit: props.doSubmit!,
rules: props.rules, rules: props.rules,
watchForm: props.watchForm!,
labelWidth: props.labelWidth!, labelWidth: props.labelWidth!,
width: props.width!, width: props.width!,
modal: props.modal!, modal: props.modal!,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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