feat(product): 新增产品管理模块与字典组件功能
- 新增产品管理相关路由和页面(dashboard、list、requirement、setting) - 实现产品基础信息编辑弹窗组件(base-info-dialog.vue) - 添加运行时字典功能(dict-select、dict-text、dict-tag组件) - 集成字典管理store和API调用 - 规范ID类型定义为string避免精度丢失问题 - 完善国际化资源文件支持中英文对照 - 新增对象上下文业务域入口页导航实现说明 - 添加Vue DevTools浮动入口注释说明 - 统一权限控制支持全局和对象作用域区分 - 规范分页查询参数类型定义与使用方式
This commit is contained in:
218
src/views/product/setting/modules/base-info-dialog.vue
Normal file
218
src/views/product/setting/modules/base-info-dialog.vue
Normal file
@@ -0,0 +1,218 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, reactive, watch } from 'vue';
|
||||
import { RDMS_OBJECT_DIRECTION_DICT_CODE } from '@/constants/dict';
|
||||
import { useDict } from '@/hooks/business/dict';
|
||||
import { useForm, useFormRules } from '@/hooks/common/form';
|
||||
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
|
||||
import DictSelect from '@/components/custom/dict-select.vue';
|
||||
import { getProductBaseInfoReadonlyMessage, isProductBaseInfoEditable } from '../shared';
|
||||
|
||||
defineOptions({ name: 'BaseInfoDialog' });
|
||||
|
||||
interface Props {
|
||||
baseInfo: Api.Product.ProductSettingBaseInfo | null;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'submit', payload: Api.Product.UpdateProductSettingBaseInfoParams): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
default: false
|
||||
});
|
||||
|
||||
const { getLabel: getDirectionLabel } = useDict(RDMS_OBJECT_DIRECTION_DICT_CODE);
|
||||
const { formRef, validate } = useForm();
|
||||
const { createRequiredRule } = useFormRules();
|
||||
|
||||
const model = reactive<Api.Product.UpdateProductSettingBaseInfoParams>({
|
||||
directionCode: '',
|
||||
name: '',
|
||||
description: ''
|
||||
});
|
||||
|
||||
const baseInfoEditable = computed(() => isProductBaseInfoEditable(props.baseInfo?.statusCode));
|
||||
const readonlyMessage = computed(() => getProductBaseInfoReadonlyMessage(props.baseInfo?.statusCode));
|
||||
|
||||
const confirmDisabled = computed(() => {
|
||||
if (!props.baseInfo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !baseInfoEditable.value;
|
||||
});
|
||||
|
||||
const directionDisplayName = computed(() => {
|
||||
const directionCode = props.baseInfo?.directionCode;
|
||||
|
||||
if (!directionCode) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return getDirectionLabel(directionCode, directionCode);
|
||||
});
|
||||
|
||||
const rules = {
|
||||
directionCode: [createRequiredRule('请选择产品方向')],
|
||||
name: [createRequiredRule('请输入产品名称')]
|
||||
} satisfies Record<string, App.Global.FormRule[]>;
|
||||
|
||||
async function handleConfirm() {
|
||||
if (confirmDisabled.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
await validate();
|
||||
|
||||
emit('submit', {
|
||||
directionCode: model.directionCode,
|
||||
name: model.name.trim(),
|
||||
description: model.description?.trim() || null
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => visible.value,
|
||||
async value => {
|
||||
if (!value || !props.baseInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
model.directionCode = props.baseInfo.directionCode || '';
|
||||
model.name = props.baseInfo.name || '';
|
||||
model.description = props.baseInfo.description || '';
|
||||
|
||||
await nextTick();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BusinessFormDialog
|
||||
v-model="visible"
|
||||
title="编辑基础信息"
|
||||
preset="lg"
|
||||
:confirm-disabled="confirmDisabled"
|
||||
@confirm="handleConfirm"
|
||||
>
|
||||
<ElAlert v-if="readonlyMessage" :title="readonlyMessage" type="warning" :closable="false" class="mb-16px" />
|
||||
|
||||
<ElForm ref="formRef" :model="model" :rules="rules" label-position="top">
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="产品编码">
|
||||
<ElInput :model-value="baseInfo?.code || ''" readonly class="base-info-dialog__readonly-input" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem>
|
||||
<template #label>
|
||||
<span class="business-form-label-with-tip">
|
||||
<ElTooltip
|
||||
content="如需调整产品经理,请到产品内的团队管理处处理。"
|
||||
popper-class="business-form-label-tooltip"
|
||||
placement="top-start"
|
||||
>
|
||||
<span class="business-form-label-tip">
|
||||
<icon-fe:question />
|
||||
</span>
|
||||
</ElTooltip>
|
||||
<span>产品经理</span>
|
||||
</span>
|
||||
</template>
|
||||
<ElInput
|
||||
:model-value="baseInfo?.managerUserNickname || baseInfo?.managerUserId || ''"
|
||||
readonly
|
||||
class="base-info-dialog__readonly-input"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="产品名称" prop="name">
|
||||
<ElInput v-if="baseInfoEditable" v-model="model.name" maxlength="128" placeholder="请输入产品名称" />
|
||||
<ElInput
|
||||
v-else
|
||||
:model-value="model.name"
|
||||
readonly
|
||||
class="base-info-dialog__readonly-input"
|
||||
placeholder="未获取到产品名称"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="产品方向" prop="directionCode">
|
||||
<DictSelect
|
||||
v-if="baseInfoEditable"
|
||||
v-model="model.directionCode"
|
||||
:dict-code="RDMS_OBJECT_DIRECTION_DICT_CODE"
|
||||
filterable
|
||||
placeholder="请选择产品方向"
|
||||
/>
|
||||
<ElInput
|
||||
v-else
|
||||
:model-value="directionDisplayName"
|
||||
readonly
|
||||
class="base-info-dialog__readonly-input"
|
||||
placeholder="未获取到产品方向"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="24">
|
||||
<ElFormItem label="产品描述">
|
||||
<ElInput
|
||||
v-if="baseInfoEditable"
|
||||
v-model="model.description"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
placeholder="请输入产品描述"
|
||||
/>
|
||||
<ElInput
|
||||
v-else
|
||||
:model-value="model.description"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
readonly
|
||||
class="base-info-dialog__readonly-input"
|
||||
placeholder="未填写产品描述"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElForm>
|
||||
</BusinessFormDialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.base-info-dialog__readonly-input .el-input__wrapper) {
|
||||
background: linear-gradient(180deg, rgb(241 245 249 / 98%), rgb(226 232 240 / 94%)), rgb(241 245 249);
|
||||
box-shadow: 0 0 0 1px rgb(203 213 225 / 96%) inset;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
:deep(.base-info-dialog__readonly-input .el-textarea__inner) {
|
||||
background: linear-gradient(180deg, rgb(241 245 249 / 98%), rgb(226 232 240 / 94%)), rgb(241 245 249);
|
||||
box-shadow: 0 0 0 1px rgb(203 213 225 / 96%) inset;
|
||||
cursor: default;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
:deep(.base-info-dialog__readonly-input .el-input__wrapper:hover),
|
||||
:deep(.base-info-dialog__readonly-input.is-focus .el-input__wrapper),
|
||||
:deep(.base-info-dialog__readonly-input .el-textarea__inner:hover),
|
||||
:deep(.base-info-dialog__readonly-input .el-textarea__inner:focus) {
|
||||
box-shadow: 0 0 0 1px rgb(203 213 225 / 96%) inset;
|
||||
}
|
||||
|
||||
:deep(.base-info-dialog__readonly-input .el-input__inner),
|
||||
:deep(.base-info-dialog__readonly-input .el-textarea__inner) {
|
||||
color: rgb(51 65 85 / 96%);
|
||||
cursor: default;
|
||||
-webkit-text-fill-color: rgb(51 65 85 / 96%);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user